mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 02:00:43 +00:00
revert: restore progress draft behavior
This commit is contained in:
@@ -12,7 +12,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Control UI: read the Quick Settings exec policy badge from `tools.exec.security` instead of the non-schema `agents.defaults.exec.security` path, so configured `full`/`deny` values render accurately. Fixes #78311. Thanks @FriedBack.
|
||||
- Control UI/usage: add transcript-backed historical lineage rollups for rotated logical sessions, with current-instance vs historical-lineage scope controls and long-range presets so usage history stays visible after restarts and updates. Fixes #50701. Thanks @dev-gideon-llc and @BunsDev.
|
||||
- Agents/failover: harden state-aware lane suspension by persisting quota resume transitions, restoring configured lane concurrency, preserving non-quota failure reasons, and exporting model failover events through diagnostics OTLP. Thanks @BunsDev.
|
||||
- Channels/streaming: render structured tool rows as compact emoji/title/details, show web-search queries from provider-native argument shapes, and skip empty Discord apply-patch starts until a patch summary exists. (#79146)
|
||||
- Channels/streaming: make progress draft labels scroll away with other progress lines, render structured tool rows as compact emoji/title/details, show web-search queries from provider-native argument shapes, and skip empty Discord apply-patch starts until a patch summary exists. (#79146)
|
||||
- Workspace/oc-path: add the `oc://` addressing substrate (`src/oc-path/`) — a universal, kind-dispatched path scheme for addressing leaves and nodes inside markdown, jsonc, jsonl, and yaml workspace files, with `parseOcPath`/`formatOcPath`, per-kind `parseXxx`/`emitXxx`, universal `resolveOcPath`/`setOcPath`/`findOcPaths` verbs, the `__OPENCLAW_REDACTED__` sentinel emit guard, and the new `openclaw path resolve|find|set|validate|emit` CLI for shell-level inspection and surgical edits. Implements #78051. (#78678) Thanks @giodl73-repo.
|
||||
- Runtime/performance: avoid full-array sorting while auto-selecting providers, resolving supported thinking levels, picking node last-seen timestamps, and extracting Codex usage-limit messages. Thanks @shakkernerd.
|
||||
- Plugins/doctor: avoid full-array sorting while selecting ClawHub search/archive results and bounded dreaming doctor entries. Thanks @shakkernerd.
|
||||
@@ -185,7 +185,6 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Agents/compaction: keep the recent tail after manual `/compact` when Pi returns an empty or no-op compaction summary, preventing blank checkpoints from replacing the live context.
|
||||
- Channels/streaming: keep progress draft labels visible above the last `streaming.progress.maxLines` progress rows instead of counting the label against the rolling line limit. Thanks @shakkernerd.
|
||||
- fix(discord): gate user allowlist name resolution [AI]. (#79002) Thanks @pgondhi987.
|
||||
- fix(msteams): gate startup user allowlist resolution [AI]. (#79003) Thanks @pgondhi987.
|
||||
- Harden macOS shell wrapper allowlist parsing [AI]. (#78518) Thanks @pgondhi987.
|
||||
|
||||
@@ -662,7 +662,7 @@ Default slash command settings:
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Live stream preview">
|
||||
OpenClaw can stream draft replies by sending a temporary message and editing it as text arrives. `channels.discord.streaming` takes `off` | `partial` | `block` | `progress` (default). `progress` keeps one editable status draft and updates it with tool progress until final delivery; the shared starter label stays visible while `streaming.progress.maxLines` limits the rolling progress lines below it. `streamMode` is a legacy runtime alias. Run `openclaw doctor --fix` to rewrite persisted config to the canonical key.
|
||||
OpenClaw can stream draft replies by sending a temporary message and editing it as text arrives. `channels.discord.streaming` takes `off` | `partial` | `block` | `progress` (default). `progress` keeps one editable status draft and updates it with tool progress until final delivery; the shared starter label is a rolling line, so it scrolls away like the rest once enough work appears. `streamMode` is a legacy runtime alias. Run `openclaw doctor --fix` to rewrite persisted config to the canonical key.
|
||||
|
||||
Set `channels.discord.streaming.mode` to `off` to disable Discord preview edits. If Discord block streaming is explicitly enabled, OpenClaw skips the preview stream to avoid double-streaming.
|
||||
|
||||
|
||||
@@ -57,10 +57,9 @@ A progress draft has two parts:
|
||||
| Progress lines | Compact run updates using the same tool icons and detail formatter as verbose output. |
|
||||
|
||||
The label appears after the agent starts meaningful work and either remains busy
|
||||
for five seconds or emits a second work event. It stays visible while the agent
|
||||
is still working; `streaming.progress.maxLines` limits only the rolling progress
|
||||
lines below the label. In other words, progress drafts render as `label + last N
|
||||
progress lines`. Plain text-only replies do not show a progress draft. Progress lines are added
|
||||
for five seconds or emits a second work event. It is part of the rolling progress
|
||||
line list, so the starter status scrolls away once enough concrete work appears.
|
||||
Plain text-only replies do not show a progress draft. Progress lines are added
|
||||
only when the agent emits useful work updates, for example `🛠️ Bash: run tests`,
|
||||
`🔎 Web Search: for "discord edit message"`, or `✍️ Write: to /tmp/file`.
|
||||
By default they use the same compact explain mode as `/verbose`; set
|
||||
|
||||
@@ -1656,7 +1656,7 @@ describe("processDiscordMessage draft streaming", () => {
|
||||
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n🛠️ Exec\n• done");
|
||||
});
|
||||
|
||||
it("keeps Discord progress labels visible above rolling lines", async () => {
|
||||
it("keeps Discord progress labels as rolling lines", async () => {
|
||||
const draftStream = createMockDraftStreamForTest();
|
||||
|
||||
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {
|
||||
@@ -1680,7 +1680,7 @@ describe("processDiscordMessage draft streaming", () => {
|
||||
|
||||
await runProcessDiscordMessage(ctx);
|
||||
|
||||
expect(draftStream.update).toHaveBeenCalledWith("Clawing...\n🧩 First\n🧩 Second\n🧩 Third");
|
||||
expect(draftStream.update).toHaveBeenCalledWith("🧩 First\n🧩 Second\n🧩 Third");
|
||||
});
|
||||
|
||||
it("skips empty apply_patch starts and renders the patch summary", async () => {
|
||||
|
||||
@@ -320,9 +320,7 @@ describe("createTeamsReplyStreamController", () => {
|
||||
|
||||
expect(ctrl.shouldSuppressDefaultToolProgressMessages()).toBe(true);
|
||||
expect(ctrl.shouldStreamPreviewToolProgress()).toBe(true);
|
||||
expect(streamInstances[0]?.sendInformativeUpdate).toHaveBeenLastCalledWith(
|
||||
"Working\n- tool: exec",
|
||||
);
|
||||
expect(streamInstances[0]?.sendInformativeUpdate).toHaveBeenLastCalledWith("- tool: exec");
|
||||
});
|
||||
|
||||
it("suppresses Teams default progress messages without stream lines when tool progress is disabled", async () => {
|
||||
|
||||
@@ -354,8 +354,9 @@ vi.mock("openclaw/plugin-sdk/channel-streaming", () => ({
|
||||
const label = params.entry?.streaming?.progress?.label;
|
||||
const maxLines = params.entry?.streaming?.progress?.maxLines ?? 8;
|
||||
const formatLine = params.formatLine ?? ((line: string) => line);
|
||||
const progressLines = params.lines
|
||||
.map((line) => {
|
||||
const lines = [
|
||||
label === false ? undefined : (label ?? "Thinking"),
|
||||
...params.lines.map((line) => {
|
||||
const text =
|
||||
typeof line === "string"
|
||||
? line
|
||||
@@ -366,12 +367,10 @@ vi.mock("openclaw/plugin-sdk/channel-streaming", () => ({
|
||||
: line.text;
|
||||
const formatted = formatLine(text);
|
||||
return /^\p{Extended_Pictographic}/u.test(text) ? formatted : `• ${formatted}`;
|
||||
})
|
||||
}),
|
||||
]
|
||||
.filter((line): line is string => Boolean(line))
|
||||
.slice(-maxLines);
|
||||
const lines = [label === false ? undefined : (label ?? "Thinking"), ...progressLines].filter(
|
||||
(line): line is string => Boolean(line),
|
||||
);
|
||||
return lines.join("\n");
|
||||
},
|
||||
formatChannelProgressDraftLine: (params: {
|
||||
@@ -828,7 +827,6 @@ describe("dispatchPreparedSlackMessage preview fallback", () => {
|
||||
|
||||
expect(draftStream.update).toHaveBeenLastCalledWith(
|
||||
[
|
||||
"Shelling",
|
||||
"• step 1",
|
||||
"• step 2",
|
||||
"• step 3",
|
||||
|
||||
@@ -211,28 +211,16 @@ describe("channel-streaming", () => {
|
||||
lines: [" tool: read ", "patch applied", "tests done"],
|
||||
formatLine: (line) => `\`${line}\``,
|
||||
}),
|
||||
).toBe("Shelling\n• `patch applied`\n• `tests done`");
|
||||
).toBe("• `patch applied`\n• `tests done`");
|
||||
expect(
|
||||
formatChannelProgressDraftText({
|
||||
entry,
|
||||
lines: ["🛠️ Exec", "plain update"],
|
||||
}),
|
||||
).toBe("Shelling\n🛠️ Exec\n• plain update");
|
||||
).toBe("🛠️ Exec\n• plain update");
|
||||
});
|
||||
|
||||
it("keeps progress labels outside the rolling line limit", () => {
|
||||
const entry = { streaming: { progress: { label: "Working", maxLines: 1 } } };
|
||||
|
||||
expect(
|
||||
formatChannelProgressDraftText({
|
||||
entry,
|
||||
lines: ["tool: search", "tool: exec"],
|
||||
bullet: "-",
|
||||
}),
|
||||
).toBe("Working\n- tool: exec");
|
||||
});
|
||||
|
||||
it("keeps progress labels visible with bounded rolling lines", () => {
|
||||
it("renders progress labels as rolling lines", () => {
|
||||
const entry = { streaming: { progress: { label: "Shelling", maxLines: 3 } } };
|
||||
|
||||
expect(
|
||||
@@ -240,7 +228,7 @@ describe("channel-streaming", () => {
|
||||
entry,
|
||||
lines: ["🛠️ Exec", "📖 Read", "🩹 Patch"],
|
||||
}),
|
||||
).toBe("Shelling\n🛠️ Exec\n📖 Read\n🩹 Patch");
|
||||
).toBe("🛠️ Exec\n📖 Read\n🩹 Patch");
|
||||
});
|
||||
|
||||
it("renders structured progress lines with compact details", () => {
|
||||
|
||||
@@ -792,21 +792,25 @@ export function formatChannelProgressDraftText(params: {
|
||||
const maxLines = resolveChannelProgressDraftMaxLines(params.entry);
|
||||
const formatLine = params.formatLine ?? ((line: string) => line);
|
||||
const bullet = params.bullet ?? "•";
|
||||
const labelLine = label
|
||||
? compactChannelProgressDraftLine(label, DEFAULT_PROGRESS_DRAFT_MAX_LINE_CHARS)
|
||||
: "";
|
||||
const progressLines = params.lines
|
||||
const rawLines: Array<string | ChannelProgressDraftLine | { draftLabel: string }> = label
|
||||
? [{ draftLabel: label }, ...params.lines]
|
||||
: params.lines;
|
||||
const lines = rawLines
|
||||
.map((line) => {
|
||||
const rawText = typeof line === "string" ? line : getProgressDraftLineText(line);
|
||||
const isLabelLine = typeof line === "object" && line !== null && "draftLabel" in line;
|
||||
const rawText = isLabelLine
|
||||
? line.draftLabel
|
||||
: typeof line === "string"
|
||||
? line
|
||||
: getProgressDraftLineText(line);
|
||||
const text = compactChannelProgressDraftLine(rawText, DEFAULT_PROGRESS_DRAFT_MAX_LINE_CHARS);
|
||||
return text || undefined;
|
||||
return text ? { text, isLabelLine } : undefined;
|
||||
})
|
||||
.filter((line): line is string => Boolean(line))
|
||||
.filter((line): line is { text: string; isLabelLine: boolean } => Boolean(line))
|
||||
.slice(-maxLines)
|
||||
.map((text) => {
|
||||
const formatted = formatLine(text);
|
||||
return shouldPrefixProgressLine(text) ? `${bullet} ${formatted}` : formatted;
|
||||
.map(({ text, isLabelLine }) => {
|
||||
const formatted = isLabelLine ? text : formatLine(text);
|
||||
return !isLabelLine && shouldPrefixProgressLine(text) ? `${bullet} ${formatted}` : formatted;
|
||||
});
|
||||
const lines = labelLine ? [labelLine, ...progressLines] : progressLines;
|
||||
return lines.join("\n");
|
||||
return lines.filter((line): line is string => Boolean(line)).join("\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user