import test from "node:test"; import assert from "node:assert/strict"; import os from "node:os"; import path from "node:path"; import { mkdtemp, rm } from "node:fs/promises"; import type { BossState, MasterAgentTask, Project, ThreadProgressEvent, ThreadStatusDocument } from "../src/lib/boss-data.ts"; let runtimeRoot = ""; let readState: (typeof import("../src/lib/boss-data"))["readState"]; let writeState: (typeof import("../src/lib/boss-data"))["writeState"]; let appendProjectMessage: (typeof import("../src/lib/boss-data"))["appendProjectMessage"]; let updateProjectAgentControls: (typeof import("../src/lib/boss-data"))["updateProjectAgentControls"]; type MutableBossState = BossState & { threadStatusDocuments: ThreadStatusDocument[]; threadProgressEvents: ThreadProgressEvent[]; masterAgentTasks: MasterAgentTask[]; projects: Project[]; }; async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-thread-status-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const data = await import("../src/lib/boss-data.ts"); readState = data.readState; writeState = data.writeState; appendProjectMessage = data.appendProjectMessage; updateProjectAgentControls = data.updateProjectAgentControls; } test.after(async () => { if (runtimeRoot) { await rm(runtimeRoot, { recursive: true, force: true }); } }); test("thread status documents and progress events normalize, sort, and trim correctly", async () => { await setup(); const state = (await readState()) as MutableBossState; const baseDocTime = Date.parse("2026-04-04T10:00:00.000Z"); state.threadStatusDocuments = Array.from({ length: 81 }, (_, index) => ({ documentId: `doc-${index}`, projectId: "master-agent", threadId: `thread-${index}`, threadDisplayName: index === 80 ? " 树莓派二代查询 " : `线程 ${index}`, folderName: index === 80 ? " Talking " : `文件夹 ${index}`, deviceId: " mac-studio ", projectGoal: index === 80 ? " 完成树莓派二代查询链路 " : `目标 ${index}`, currentPhase: index === 80 ? " 功能实现 " : `阶段 ${index}`, currentProgress: index === 80 ? " 已完成排序修复 " : `进度 ${index}`, technicalArchitecture: index === 80 ? " Next.js API + Android 原生客户端 " : `架构 ${index}`, currentBlockers: index === 80 ? " " : `阻塞 ${index}`, recommendedNextStep: index === 80 ? " 补会话页展示与排序 " : `下一步 ${index}`, keyFiles: index === 80 ? [" src/lib/boss-data.ts ", " tests/thread-status-sync.test.ts "] : [`file-${index}.ts`], keyCommands: index === 80 ? [" npm run build ", " npm run lint "] : [`cmd-${index}`], updatedAt: new Date(baseDocTime + index * 60_000).toISOString(), sourceTaskId: `task-${index}`, sourceKind: index === 80 ? undefined : "full_sync", })); state.threadProgressEvents = [ ...Array.from({ length: 25 }, (_, index) => ({ eventId: `event-a-${index}`, projectId: "master-agent", threadId: "thread-a", threadDisplayName: "线程 A", deviceId: "mac-studio", eventType: "progress_updated", summary: `线程 A 进展 ${index}`, phase: "功能实现", createdAt: `2026-04-04T19:${String(index).padStart(2, "0")}:00+08:00`, sourceTaskId: `task-a-${index}`, })), ...Array.from({ length: 381 }, (_, index) => ({ eventId: `event-b-${index}`, projectId: "master-agent", threadId: `thread-b-${index}`, threadDisplayName: `线程 B${index}`, deviceId: "mac-studio", eventType: "progress_updated", summary: `线程 B${index} 进展`, createdAt: `2026-04-04T17:${String(index % 60).padStart(2, "0")}:${String(index % 60).padStart(2, "0")}+08:00`, sourceTaskId: `task-b-${index}`, })), ]; await writeState(state); const normalized = (await readState()) as MutableBossState; assert.equal(normalized.threadStatusDocuments.length, 80); assert.equal(normalized.threadStatusDocuments[0]?.documentId, "doc-80"); assert.equal(normalized.threadStatusDocuments[0]?.threadDisplayName, "树莓派二代查询"); assert.equal(normalized.threadStatusDocuments[0]?.folderName, "Talking"); assert.equal(normalized.threadStatusDocuments[0]?.deviceId, "mac-studio"); assert.equal(normalized.threadStatusDocuments[0]?.projectGoal, "完成树莓派二代查询链路"); assert.equal(normalized.threadStatusDocuments[0]?.currentPhase, "功能实现"); assert.deepEqual(normalized.threadStatusDocuments[0]?.keyFiles, [ "src/lib/boss-data.ts", "tests/thread-status-sync.test.ts", ]); assert.deepEqual(normalized.threadStatusDocuments[0]?.keyCommands, ["npm run build", "npm run lint"]); assert.equal(normalized.threadStatusDocuments[0]?.sourceKind, "incremental_sync"); assert.equal(normalized.threadProgressEvents.length, 400); assert.equal(normalized.threadProgressEvents[0]?.eventId, "event-a-24"); assert.equal( normalized.threadProgressEvents.filter((event) => event.projectId === "master-agent" && event.threadId === "thread-a") .length, 20, ); }); test("thread replies append lightweight progress events and skip redundant understanding sync when status document is fresh", async () => { await setup(); const state = (await readState()) as MutableBossState; state.threadProgressEvents = []; state.projects.push({ id: "thread-sync-demo", name: "线程状态演示", pinned: false, deviceIds: ["mac-studio"], preview: "初始状态", updatedAt: "2026-04-04T18:00:00+08:00", lastMessageAt: "2026-04-04T18:00:00+08:00", isGroup: false, threadMeta: { projectId: "thread-sync-demo", threadId: "thread-sync-demo-thread", threadDisplayName: "线程状态演示", folderName: "演示文件夹", activityIconCount: 1, updatedAt: "2026-04-04T18:00:00+08:00", lastObservedCodexActivityAt: "2026-04-04T18:00:00+08:00", lastProjectUnderstandingRequestedAt: "2026-04-04T17:00:00+08:00", lastProjectUnderstandingSyncedAt: "2026-04-04T18:00:00+08:00", codexThreadRef: "thread-sync-demo-thread", codexFolderRef: "thread-sync-demo-folder", }, groupMembers: [], createdByAgent: false, collaborationMode: "development", approvalState: "not_required", unreadCount: 0, riskLevel: "low", projectUnderstanding: { projectGoal: "完成线程状态回归", currentProgress: "旧进度", technicalArchitecture: "旧架构", currentBlockers: "", recommendedNextStep: "旧下一步", sourceTaskId: "task-old", updatedAt: "2026-04-04T18:00:00+08:00", sourceKind: "thread_sync", }, messages: [], goals: [], versions: [], } as Project); state.threadStatusDocuments = [ { documentId: "doc-old", projectId: "thread-sync-demo", threadId: "thread-sync-demo-thread", threadDisplayName: "线程状态演示", folderName: "演示文件夹", deviceId: "mac-studio", projectGoal: "完成线程状态回归", currentPhase: "全量理解", currentProgress: "旧进度", technicalArchitecture: "旧架构", currentBlockers: "", recommendedNextStep: "旧下一步", keyFiles: ["src/lib/boss-data.ts"], keyCommands: ["npm run build"], updatedAt: "2026-04-04T18:00:00+08:00", sourceTaskId: "task-old", sourceKind: "full_sync", }, ]; await writeState(state); const before = (await readState()) as MutableBossState; const beforeCount = before.threadProgressEvents.filter( (event) => event.projectId === "thread-sync-demo", ).length; const message = await appendProjectMessage({ projectId: "thread-sync-demo", sender: "device", senderLabel: "线程执行器", body: "已完成手机端排序修复", kind: "text", }); assert.equal(message.body, "已完成手机端排序修复"); const after = (await readState()) as MutableBossState; const events = after.threadProgressEvents.filter((event) => event.projectId === "thread-sync-demo"); assert.equal(events.length, beforeCount + 1); assert.equal(events[0]?.summary, "已完成手机端排序修复"); assert.equal(events[0]?.eventType, "progress_updated"); assert.equal( after.masterAgentTasks.some( (task) => task.projectUnderstandingTargetProjectId === "thread-sync-demo" && task.status === "queued", ), false, ); }); test("turning off single-thread takeover clears pending project understanding sync tasks for that thread", async () => { await setup(); const projectId = "thread-sync-takeover-clear"; const state = (await readState()) as MutableBossState; state.projects = state.projects.filter((project) => project.id !== projectId); state.userProjectAgentControls = state.userProjectAgentControls.filter( (item) => item.projectId !== projectId, ); state.masterAgentTasks = state.masterAgentTasks.filter( (task) => task.projectUnderstandingTargetProjectId !== projectId, ); state.projects.push({ id: projectId, name: "接管关闭清理演示", pinned: false, deviceIds: ["mac-studio"], preview: "等待同步", updatedAt: "2026-04-04T18:00:00+08:00", lastMessageAt: "2026-04-04T18:00:00+08:00", isGroup: false, threadMeta: { projectId, threadId: "thread-sync-takeover-clear-thread", threadDisplayName: "接管关闭清理演示", folderName: "演示文件夹", activityIconCount: 1, updatedAt: "2026-04-04T18:00:00+08:00", codexThreadRef: "thread-sync-takeover-clear-thread", codexFolderRef: "thread-sync-takeover-clear-folder", }, groupMembers: [], createdByAgent: false, collaborationMode: "development", approvalState: "not_required", unreadCount: 0, riskLevel: "low", messages: [], goals: [], versions: [], } as Project); state.userProjectAgentControls.push({ account: "krisolo", projectId, controls: { takeoverEnabled: true, updatedAt: "2026-04-04T18:00:00+08:00", }, }); state.masterAgentTasks.unshift( { taskId: "queued-understanding-clear", projectId: "master-agent", taskType: "conversation_reply", requestMessageId: "message-understanding-clear", requestText: "请同步项目状态", executionPrompt: "你正在向主 Agent 同步当前项目状态。", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: projectId, targetThreadId: "thread-sync-takeover-clear-thread", targetThreadDisplayName: "接管关闭清理演示", projectUnderstandingTargetProjectId: projectId, projectUnderstandingReason: "heartbeat_activity", status: "queued", requestedAt: "2026-04-04T18:01:00+08:00", }, { taskId: "completed-understanding-kept", projectId: "master-agent", taskType: "conversation_reply", requestMessageId: "message-understanding-completed", requestText: "请同步项目状态", executionPrompt: "你正在向主 Agent 同步当前项目状态。", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: projectId, targetThreadId: "thread-sync-takeover-clear-thread", targetThreadDisplayName: "接管关闭清理演示", projectUnderstandingTargetProjectId: projectId, projectUnderstandingReason: "heartbeat_activity", status: "completed", requestedAt: "2026-04-04T18:00:30+08:00", completedAt: "2026-04-04T18:00:45+08:00", replyBody: "{}", }, ); await writeState(state); const controls = await updateProjectAgentControls(projectId, { takeoverEnabled: false }, "krisolo"); assert.equal(controls?.effectiveTakeoverEnabled, false); const after = (await readState()) as MutableBossState; assert.equal( after.masterAgentTasks.some((task) => task.taskId === "queued-understanding-clear"), false, ); assert.equal( after.masterAgentTasks.some((task) => task.taskId === "completed-understanding-kept"), true, ); }); test("turning off global takeover clears pending project understanding sync tasks", async () => { await setup(); const projectId = "thread-sync-global-clear"; const state = (await readState()) as MutableBossState; state.masterAgentTasks = state.masterAgentTasks.filter( (task) => task.projectUnderstandingTargetProjectId !== projectId, ); state.masterAgentTasks.unshift({ taskId: "running-global-understanding-clear", projectId: "master-agent", taskType: "conversation_reply", requestMessageId: "message-global-understanding-clear", requestText: "请同步项目状态", executionPrompt: "你正在向主 Agent 同步当前项目状态。", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: projectId, targetThreadId: "thread-sync-global-clear-thread", targetThreadDisplayName: "全局接管清理演示", projectUnderstandingTargetProjectId: projectId, projectUnderstandingReason: "heartbeat_activity", status: "running", requestedAt: "2026-04-04T18:02:00+08:00", claimedAt: "2026-04-04T18:02:05+08:00", }); await writeState(state); await updateProjectAgentControls("master-agent", { globalTakeoverEnabled: true }, "krisolo"); const controls = await updateProjectAgentControls("master-agent", { globalTakeoverEnabled: false }, "krisolo"); assert.equal(controls?.globalTakeoverEnabled, false); const after = (await readState()) as MutableBossState; assert.equal( after.masterAgentTasks.some((task) => task.taskId === "running-global-understanding-clear"), false, ); });