import { describe, expect, it } from "vitest"; import { applyPermissionResponse, applyQuestionResponse, finalizeAssistantMessageAfterAbort, toTodoUpdate, upsertPermission, upsertProgress, upsertQuestion } from "./session-state"; import type { AgentChatMessage } from "./types"; describe("agent session state", () => { it("upserts progress events by id", () => { const first = upsertProgress(undefined, { id: "tool-1", phase: "tool", status: "running", title: "调用工具", elapsed_ms: 20 }); const next = upsertProgress(first, { id: "tool-1", phase: "tool", status: "completed", title: "工具完成", duration_ms: 40 }); expect(next).toHaveLength(1); expect(next[0]).toMatchObject({ id: "tool-1", status: "completed", title: "工具完成", durationMs: 40 }); }); it("normalizes todo updates", () => { expect( toTodoUpdate({ session_id: "session-1", todos: [{ id: "todo-1", content: "分析压力", status: "in_progress", priority: "high" }], created_at: 123 }) ).toEqual({ sessionId: "session-1", messageId: undefined, todos: [{ id: "todo-1", content: "分析压力", status: "in_progress", priority: "high" }], createdAt: 123 }); }); it("tracks permission request and response", () => { const permissions = upsertPermission(undefined, { session_id: "session-1", request_id: "permission-1", permission: "bash", patterns: ["pnpm test"], always: [], created_at: 100 }); const next = applyPermissionResponse(permissions, { request_id: "permission-1", reply: "once" }); expect(next?.[0]).toMatchObject({ requestId: "permission-1", status: "approved_once" }); }); it("dedupes question requests by tool call id", () => { const questions = upsertQuestion(undefined, { session_id: "session-1", request_id: "call-1", tool: { messageID: "message-1", callID: "call-1" }, questions: [{ header: "范围", question: "选择范围", options: [] }], created_at: 100 }); const next = upsertQuestion(questions, { session_id: "session-1", request_id: "question-1", tool: { messageID: "message-1", callID: "call-1" }, questions: [{ header: "范围", question: "选择范围", options: [] }], created_at: 120 }); const answered = applyQuestionResponse(next, { request_id: "question-1", answers: [["东部"]] }); expect(next).toHaveLength(1); expect(answered?.[0]).toMatchObject({ requestId: "question-1", status: "answered", answers: [["东部"]] }); }); it("finalizes open assistant state after abort", () => { const message: AgentChatMessage = { id: "assistant-1", role: "assistant", content: "", progress: [{ id: "run", phase: "run", status: "running", title: "处理中", startedAt: Date.now() }], permissions: [ { requestId: "permission-1", sessionId: "session-1", permission: "bash", patterns: [], always: [], createdAt: 100, status: "pending" } ] }; const next = finalizeAssistantMessageAfterAbort(message); expect(next.content).toBe("请求已中止。"); expect(next.progress?.[0].status).toBe("completed"); expect(next.permissions?.[0].status).toBe("aborted"); }); });