
























@@ -2950,12 +2950,24 @@ describe("matrix monitor handler draft streaming", () => {
29502950) => Promise<void> | void;
29512951onAssistantMessageStart?: () => void;
29522952suppressDefaultToolProgressMessages?: boolean;
2953-onToolStart?: (payload: { name?: string }) => Promise<void>;
2953+onToolStart?: (payload: {
2954+itemId?: string;
2955+toolCallId?: string;
2956+name?: string;
2957+phase?: string;
2958+args?: Record<string, unknown>;
2959+detailMode?: "explain" | "raw";
2960+}) => Promise<void>;
29542961onItemEvent?: (payload: {
2962+itemId?: string;
2963+toolCallId?: string;
29552964progressText?: string;
29562965summary?: string;
29572966title?: string;
29582967name?: string;
2968+kind?: string;
2969+phase?: string;
2970+status?: string;
29592971}) => Promise<void>;
29602972onPlanUpdate?: (payload: {
29612973phase: string;
@@ -2964,15 +2976,24 @@ describe("matrix monitor handler draft streaming", () => {
29642976}) => Promise<void>;
29652977onApprovalEvent?: (payload: { phase: string; command?: string }) => Promise<void>;
29662978onCommandOutput?: (payload: {
2979+itemId?: string;
2980+toolCallId?: string;
29672981phase: string;
29682982name?: string;
29692983exitCode?: number;
2984+status?: string;
29702985title?: string;
29712986}) => Promise<void>;
29722987onPatchSummary?: (payload: {
2988+itemId?: string;
2989+toolCallId?: string;
29732990phase: string;
2991+name?: string;
29742992summary?: string;
29752993title?: string;
2994+added?: string[];
2995+modified?: string[];
2996+deleted?: string[];
29762997}) => Promise<void>;
29772998disableBlockStreaming?: boolean;
29782999};
@@ -3138,6 +3159,167 @@ describe("matrix monitor handler draft streaming", () => {
31383159await finish();
31393160});
314031613162+it("replaces recovered Matrix command progress instead of leaving stale failed text", async () => {
3163+const { dispatch } = createStreamingHarness({
3164+streaming: "progress",
3165+previewToolProgressEnabled: true,
3166+accountConfig: {
3167+streaming: { mode: "progress", progress: { label: "Working" } },
3168+} as never,
3169+});
3170+const { opts, finish } = await dispatch();
3171+3172+await opts.onItemEvent?.({
3173+itemId: "command-1",
3174+kind: "command",
3175+name: "exec",
3176+phase: "end",
3177+status: "failed",
3178+progressText: "run openclaw cron -> run jq (agent) failed",
3179+});
3180+await opts.onItemEvent?.({
3181+itemId: "command-1",
3182+kind: "command",
3183+name: "exec",
3184+phase: "end",
3185+status: "failed",
3186+progressText: "run openclaw cron -> run jq (agent) failed",
3187+});
3188+3189+await vi.waitFor(() => {
3190+expect(sendSingleTextMessageMatrixMock).toHaveBeenCalledTimes(1);
3191+});
3192+expect(singleTextMessageBody()).toContain("failed");
3193+3194+await opts.onCommandOutput?.({
3195+itemId: "command-1",
3196+toolCallId: "call-1",
3197+phase: "end",
3198+name: "exec",
3199+status: "completed",
3200+exitCode: 0,
3201+});
3202+3203+await finish();
3204+expect(editMessageMatrixMock).toHaveBeenCalledWith(
3205+"!room:example.org",
3206+"$draft1",
3207+expect.stringContaining("completed"),
3208+expect.any(Object),
3209+);
3210+const recoveredEdit = mockCalls(editMessageMatrixMock, "editMessageMatrix").find(
3211+([, eventId, body]) =>
3212+eventId === "$draft1" && typeof body === "string" && body.includes("completed"),
3213+);
3214+expect(recoveredEdit?.[2]).not.toContain("failed");
3215+expect(recoveredEdit?.[2]).not.toContain("run openclaw cron -> run jq");
3216+});
3217+3218+it("replaces Matrix tool-start progress when command output completes", async () => {
3219+const { dispatch } = createStreamingHarness({
3220+streaming: "progress",
3221+previewToolProgressEnabled: true,
3222+accountConfig: {
3223+streaming: { mode: "progress", progress: { label: "Working" } },
3224+} as never,
3225+});
3226+const { opts, finish } = await dispatch();
3227+3228+await opts.onToolStart?.({
3229+itemId: "fc-call-2",
3230+toolCallId: "call-2",
3231+name: "exec",
3232+phase: "start",
3233+args: { command: "npm install" },
3234+});
3235+await opts.onToolStart?.({
3236+itemId: "fc-call-2",
3237+toolCallId: "call-2",
3238+name: "exec",
3239+phase: "update",
3240+args: { command: "npm install" },
3241+});
3242+3243+await vi.waitFor(() => {
3244+expect(sendSingleTextMessageMatrixMock).toHaveBeenCalledTimes(1);
3245+});
3246+expect(singleTextMessageBody()).toContain("install dependencies");
3247+3248+await opts.onItemEvent?.({
3249+itemId: "fc-call-2",
3250+toolCallId: "call-2",
3251+kind: "command",
3252+name: "exec",
3253+phase: "update",
3254+progressText: "install dependencies",
3255+});
3256+3257+await opts.onCommandOutput?.({
3258+itemId: "fc-call-2-output",
3259+toolCallId: "call-2",
3260+phase: "end",
3261+name: "exec",
3262+status: "completed",
3263+exitCode: 0,
3264+});
3265+3266+await finish();
3267+const completedEdit = mockCalls(editMessageMatrixMock, "editMessageMatrix").find(
3268+([, eventId, body]) =>
3269+eventId === "$draft1" && typeof body === "string" && body.includes("completed"),
3270+);
3271+expect(completedEdit?.[2]).not.toContain("install dependencies");
3272+});
3273+3274+it("replaces Matrix patch progress when the patch summary completes", async () => {
3275+const { dispatch } = createStreamingHarness({
3276+streaming: "progress",
3277+previewToolProgressEnabled: true,
3278+accountConfig: {
3279+streaming: { mode: "progress", progress: { label: "Working" } },
3280+} as never,
3281+});
3282+const { opts, finish } = await dispatch();
3283+3284+await opts.onItemEvent?.({
3285+itemId: "patch:call-3",
3286+toolCallId: "call-3",
3287+kind: "patch",
3288+name: "apply_patch",
3289+phase: "update",
3290+progressText: "updating Matrix progress handling",
3291+});
3292+await opts.onItemEvent?.({
3293+itemId: "patch:call-3",
3294+toolCallId: "call-3",
3295+kind: "patch",
3296+name: "apply_patch",
3297+phase: "update",
3298+progressText: "updating Matrix progress handling",
3299+});
3300+3301+await vi.waitFor(() => {
3302+expect(sendSingleTextMessageMatrixMock).toHaveBeenCalledTimes(1);
3303+});
3304+expect(singleTextMessageBody()).toContain("updating Matrix progress handling");
3305+3306+await opts.onPatchSummary?.({
3307+itemId: "patch:call-3",
3308+toolCallId: "call-3",
3309+phase: "end",
3310+name: "apply_patch",
3311+modified: ["extensions/matrix/src/matrix/monitor/handler.ts"],
3312+summary: "1 file modified",
3313+});
3314+3315+await finish();
3316+const patchEdit = mockCalls(editMessageMatrixMock, "editMessageMatrix").find(
3317+([, eventId, body]) =>
3318+eventId === "$draft1" && typeof body === "string" && body.includes("1 file modified"),
3319+);
3320+expect(patchEdit?.[2]).not.toContain("updating Matrix progress handling");
3321+});
3322+31413323it("keeps Matrix tool progress mentions inside code formatting", async () => {
31423324const { dispatch } = createStreamingHarness({
31433325previewToolProgressEnabled: true,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。