import { describe, expect, it } from "bun:test"; import { createActivityTracker } from "../../src/routes/chatActivityTracker.js"; import { initialActivity } from "../../src/routes/chatStreamEvents.js"; describe("createActivityTracker", () => { it("moves real tool work out of preparation when the model omits activity updates", () => { const events: Array<{ event: string; data: Record }> = []; const tracker = createActivityTracker({ clientSessionId: "client-session-1", write: (event, data) => events.push({ event, data }), }); const action = (id: string, tool: string, status: string) => tracker.upsertAction( { id, tool, state: { status, input: {} } } as never, {}, ); tracker.start(initialActivity.id, initialActivity.title, initialActivity.reason); action("skill-1", "skill", "completed"); action("read-1", "read", "completed"); action("data-1", "tjwater_cli", "completed"); action("data-1", "tjwater_cli", "completed"); action("compute-1", "bash", "completed"); action("map-1", "show_chart", "completed"); tracker.finalize("completed"); const started = events .filter(({ data }) => (data.activity as { actions: unknown[] }).actions.length === 0 && (data.activity as { status: string }).status === "running", ) .map(({ data }) => (data.activity as { title: string }).title); expect(started).toEqual([ initialActivity.title, "查找所需资料", "获取业务数据", "处理分析数据", "更新地图与图表", ]); expect(tracker.getActionContext("read-1")?.title).toBe("查找所需资料"); expect(tracker.getActionContext("data-1")?.title).toBe("获取业务数据"); expect(tracker.getActionContext("map-1")?.title).toBe("更新地图与图表"); }); it("keeps a model-provided stage across tool categories", () => { const events: Array<{ event: string; data: Record }> = []; const tracker = createActivityTracker({ clientSessionId: "client-session-1", write: (event, data) => events.push({ event, data }), }); tracker.start(initialActivity.id, initialActivity.title, initialActivity.reason); tracker.start("model-stage", "验证分析结果", "核对本次计算与数据。"); for (const [id, tool] of [["tool-1", "tjwater_cli"], ["tool-2", "show_chart"]]) { tracker.upsertAction( { id, tool, state: { status: "completed", input: {} } } as never, {}, ); } expect(tracker.getActionContext("tool-1")?.title).toBe("验证分析结果"); expect(tracker.getActionContext("tool-2")?.title).toBe("验证分析结果"); expect( events.filter(({ data }) => (data.activity as { actions: unknown[] }).actions.length === 0 && (data.activity as { status: string }).status === "running", ), ).toHaveLength(2); }); it("groups actions and closes running children with their activity", () => { const events: Array<{ event: string; data: Record }> = []; const tracker = createActivityTracker({ clientSessionId: "client-session-1", write: (event, data) => events.push({ event, data }), }); tracker.start("activity-1", "准备分析数据", "需要先获取分析输入。"); tracker.upsertAction( { id: "tool-1", tool: "tjwater_cli", state: { status: "running", input: { command: "data list" }, }, } as never, { command: "data list" }, ); tracker.finalize("cancelled"); expect(tracker.getCurrentContext()).toEqual({ id: "activity-1", title: "准备分析数据", reason: "需要先获取分析输入。", }); expect(events.at(-1)).toMatchObject({ event: "activity_update", data: { session_id: "client-session-1", activity: { id: "activity-1", status: "cancelled", actions: [ expect.objectContaining({ id: "tool-1", status: "completed", target: "data list", }), ], }, }, }); }); });