mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 17:53:39 +00:00
## What Problem This Solves Repeated process polls crowded out useful progress. This larger change replaces separate channel and client decisions with shared activity preparation. ## Why This Change Was Made This presentation state has one writer. Channels, web, terminal and native clients consume prepared facts; raw tool output remains available under details. ## User Impact Routine polls stay quiet. Failures and approvals remain visible. Unmatched historical calls show an unknown outcome, not a permanent spinner. ## Evidence - Real Telegram: repeated rows before; nine quiet polls, commentary, correct final result and draft cleanup after. - Deliberate exit 2 remains visible without changing raw execution fields. - Package build, focused channel/history/web/terminal tests and core/UI types pass. - The same unchanged plugin compiles and runs against the released and candidate packages. - Browser red/green: expanded item-only failures, blocked calls and unknown outcomes keep their prepared status live and after reload; 420 focused UI tests pass. Raw results remain unchanged. - Reconnect testing includes main's fix in #151567 for the observed route loop. - The maintainer explicitly waived native before/after screenshots and manual native UI verification for this PR. Hosted native tests remain required; device UI behavior is not claimed. | Before | After | | --- | --- | |  |  |  Web expanded tool details (actual browser, cropped): | Before: false completion | After: preserved outcome | | --- | --- | |  |  | ## Compatibility Optional chat/task history activity; no protocol-version, user-config, store or migration change. Released plugin callbacks remain functional. Updated bundled adapters opt into prepared items. The shared SDK exports and two updated Slack progress snapshots are approved. ## Consumers Channels, web, terminal and native adapters share the prepared facts. ## Invalidation Existing run ownership, final-delivery fences and history refresh rules remain. ## Tests Failure, approval, privacy, history, plugin-upgrade and authority regressions remain. File-size repairs move existing tests without removing their assertions. Co-authored-by: Ayaan Zaidi <hi@obviy.us>
109 lines
3.2 KiB
TypeScript
109 lines
3.2 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import {
|
|
buildChannelProgressDraftLine,
|
|
createChannelProgressDraftCompositor,
|
|
} from "openclaw/plugin-sdk/channel-outbound";
|
|
|
|
// Run these unchanged consumer bytes against both the released and candidate packages.
|
|
const published: string[] = [];
|
|
const entry = {
|
|
streaming: {
|
|
mode: "partial" as const,
|
|
preview: { toolProgress: true, commandText: "raw" as const },
|
|
},
|
|
};
|
|
const progress = createChannelProgressDraftCompositor({
|
|
entry,
|
|
mode: "partial",
|
|
active: true,
|
|
seed: "legacy-sdk-consumer",
|
|
update: (text) => {
|
|
published.push(text);
|
|
return true;
|
|
},
|
|
});
|
|
|
|
await progress.pushToolEvent({
|
|
toolCallId: "exec-1",
|
|
name: "exec",
|
|
phase: "start",
|
|
args: { command: "ls -alh" },
|
|
detailMode: "raw",
|
|
});
|
|
assert.match(published.at(-1) ?? "", /ls -alh/u);
|
|
const beforeTerminal = published.length;
|
|
await progress.pushCommandOutputEvent({ toolCallId: "exec-1", phase: "delta" });
|
|
await progress.pushCommandOutputEvent({ toolCallId: "exec-1" });
|
|
await progress.pushPatchEvent({ phase: "update", modified: ["pending.ts"] });
|
|
await progress.pushPatchEvent({ modified: ["missing-phase.ts"] });
|
|
assert.equal(published.length, beforeTerminal);
|
|
|
|
await progress.pushCommandOutputEvent({
|
|
toolCallId: "exec-1",
|
|
name: "exec",
|
|
phase: "end",
|
|
title: "ls -alh",
|
|
exitCode: 7,
|
|
});
|
|
assert.match(published.at(-1) ?? "", /exit 7/u);
|
|
assert.match(published.at(-1) ?? "", /ls -alh/u);
|
|
await progress.pushPatchEvent({
|
|
itemId: "patch-1",
|
|
phase: "end",
|
|
name: "apply_patch",
|
|
summary: "1 modified",
|
|
modified: ["src/example.ts"],
|
|
});
|
|
assert.match(published.at(-1) ?? "", /1 modified/u);
|
|
assert.match(published.at(-1) ?? "", /src\/example\.ts/u);
|
|
progress.cancel();
|
|
|
|
const built: string[] = [];
|
|
const custom = createChannelProgressDraftCompositor({
|
|
entry,
|
|
mode: "partial",
|
|
active: true,
|
|
seed: "legacy-sdk-custom-builder",
|
|
buildProgressEventLine: (input, options) => {
|
|
built.push(input.event);
|
|
if (input.event === "tool") {
|
|
assert.deepEqual(input.args, { command: "ls -alh" });
|
|
assert.equal(options?.detailMode, "raw");
|
|
}
|
|
const line = buildChannelProgressDraftLine(input, { ...options, commandText: "raw" });
|
|
return line ? `Custom ${line.text}` : undefined;
|
|
},
|
|
update: (text) => {
|
|
published.push(text);
|
|
return true;
|
|
},
|
|
});
|
|
await custom.pushToolEvent({
|
|
toolCallId: "custom-exec",
|
|
name: "exec",
|
|
phase: "start",
|
|
args: { command: "ls -alh" },
|
|
detailMode: "raw",
|
|
});
|
|
assert.match(published.at(-1) ?? "", /Custom .*ls -alh/u);
|
|
await custom.pushCommandOutputEvent({ phase: "delta" });
|
|
await custom.pushPatchEvent({ phase: "start", summary: "hidden" });
|
|
assert.deepEqual(built, ["tool"]);
|
|
await custom.pushCommandOutputEvent({
|
|
phase: "end",
|
|
name: "exec",
|
|
title: "custom command",
|
|
exitCode: 3,
|
|
});
|
|
await custom.pushPatchEvent({
|
|
phase: "end",
|
|
name: "apply_patch",
|
|
summary: "1 added",
|
|
added: ["new.ts"],
|
|
});
|
|
assert.deepEqual(built, ["tool", "command-output", "patch"]);
|
|
assert.match(published.at(-1) ?? "", /Custom .*exit 3/u);
|
|
assert.match(published.at(-1) ?? "", /Custom .*1 added/u);
|
|
custom.cancel();
|
|
|
|
console.log("legacy progress consumer: raw tool, command, patch, and custom builder passed");
|