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"; let runtimeRoot = ""; let readState: (typeof import("../src/lib/boss-data"))["readState"]; let writeState: (typeof import("../src/lib/boss-data"))["writeState"]; let toggleGoal: (typeof import("../src/lib/boss-data"))["toggleGoal"]; let updateGoalText: (typeof import("../src/lib/boss-data"))["updateGoalText"]; let createGoal: (typeof import("../src/lib/boss-data"))["createGoal"]; let updateProjectAgentControls: (typeof import("../src/lib/boss-data"))["updateProjectAgentControls"]; let queueMasterAgentTask: (typeof import("../src/lib/boss-data"))["queueMasterAgentTask"]; let completeMasterAgentTask: (typeof import("../src/lib/boss-data"))["completeMasterAgentTask"]; let forceProjectUnderstandingSyncTask: (typeof import("../src/lib/boss-data"))["forceProjectUnderstandingSyncTask"]; let subscribeBossEvents: (typeof import("../src/lib/boss-events"))["subscribeBossEvents"]; async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-goal-events-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [data, events] = await Promise.all([ import("../src/lib/boss-data.ts"), import("../src/lib/boss-events.ts"), ]); readState = data.readState; writeState = data.writeState; toggleGoal = data.toggleGoal; updateGoalText = data.updateGoalText; createGoal = data.createGoal; updateProjectAgentControls = data.updateProjectAgentControls; queueMasterAgentTask = data.queueMasterAgentTask; completeMasterAgentTask = data.completeMasterAgentTask; forceProjectUnderstandingSyncTask = data.forceProjectUnderstandingSyncTask; subscribeBossEvents = events.subscribeBossEvents; } async function resetGoalState() { const state = await readState(); const existingProject = state.projects.find((project) => project.id !== "master-agent") ?? state.projects[0]; const project = structuredClone(existingProject); project.id = "project-goal-events"; project.name = "项目目标事件测试"; project.goals = [ { id: "goal-1", text: "完成接入验证", state: "pending", note: "等待执行", }, ]; project.versions = []; project.projectUnderstanding = undefined; project.messages = []; project.lastMessageAt = "2026-04-07T10:00:00.000Z"; state.projects = state.projects.filter((item) => item.id !== "project-goal-events"); state.projects.unshift(project); await writeState(state); } test.beforeEach(async () => { await setup(); await resetGoalState(); }); test.after(async () => { if (runtimeRoot) { await rm(runtimeRoot, { recursive: true, force: true }); } }); test("toggleGoal publishes project goal refresh marker for the project", async () => { const events: Array<{ event: string; payload: { projectId?: string; note?: string } }> = []; const unsubscribe = subscribeBossEvents((event, payload) => { events.push({ event, payload }); }); await toggleGoal("project-goal-events", "goal-1"); unsubscribe(); const latest = events.at(-1); assert.ok(latest); assert.equal(latest.event, "conversation.updated"); assert.equal(latest.payload.projectId, "project-goal-events"); assert.equal(latest.payload.note, "project_goals.updated"); }); test("updateGoalText publishes project goal refresh marker for the project", async () => { const events: Array<{ event: string; payload: { projectId?: string; note?: string } }> = []; const unsubscribe = subscribeBossEvents((event, payload) => { events.push({ event, payload }); }); await updateGoalText("project-goal-events", "goal-1", "完成接入验证并复盘"); unsubscribe(); const latest = events.at(-1); assert.ok(latest); assert.equal(latest.event, "conversation.updated"); assert.equal(latest.payload.projectId, "project-goal-events"); assert.equal(latest.payload.note, "project_goals.updated"); }); test("createGoal publishes project goal refresh marker for the project", async () => { const events: Array<{ event: string; payload: { projectId?: string; note?: string } }> = []; const unsubscribe = subscribeBossEvents((event, payload) => { events.push({ event, payload }); }); await createGoal("project-goal-events", "补一条回归目标"); unsubscribe(); const latest = events.at(-1); assert.ok(latest); assert.equal(latest.event, "conversation.updated"); assert.equal(latest.payload.projectId, "project-goal-events"); assert.equal(latest.payload.note, "project_goals.updated"); }); test("project understanding sync completion also publishes project goal refresh marker", async () => { const events: Array<{ event: string; payload: { projectId?: string; note?: string } }> = []; const unsubscribe = subscribeBossEvents((event, payload) => { events.push({ event, payload }); }); const state = await readState(); const existingProject = state.projects.find((project) => project.id !== "master-agent"); assert.ok(existingProject); const project = structuredClone(existingProject); project.id = "project-goal-understanding-sync"; project.name = "项目目标理解同步测试"; project.goals = []; project.versions = []; project.messages = []; project.lastMessageAt = "2026-04-07T10:00:00.000Z"; project.threadMeta.lastObservedCodexActivityAt = "2026-04-07T10:00:00.000Z"; state.projects = state.projects.filter((item) => item.id !== project.id); state.projects.unshift(project); await writeState(state); await updateProjectAgentControls(project.id, { takeoverEnabled: true }); const queuedTask = await forceProjectUnderstandingSyncTask({ projectId: project.id, observedActivityAt: "2026-04-07T10:00:00.000Z", reason: "thread_reply", }); assert.ok(queuedTask); await completeMasterAgentTask({ taskId: queuedTask!.taskId, deviceId: "mac-studio", status: "completed", replyBody: JSON.stringify({ projectGoal: "完成项目目标和版本记录自动同步", currentProgress: "项目目标页需要展示主 Agent 最新核对结果", technicalArchitecture: "Boss Web 与 Android 共用文件账本状态", currentBlockers: "", recommendedNextStep: "继续优化聊天阅读排版", versionRecord: "已补项目目标/版本记录的自动同步链路。", }), }); unsubscribe(); const goalRefreshEvent = events.find( (item) => item.event === "conversation.updated" && item.payload.projectId === project.id && item.payload.note === "project_goals.updated", ); assert.ok(goalRefreshEvent, "expected project understanding sync to publish a goal refresh marker"); }); test("takeover conversation summary reply writes project goal and version record back to the current conversation", async () => { const events: Array<{ event: string; payload: { projectId?: string; note?: string } }> = []; const unsubscribe = subscribeBossEvents((event, payload) => { events.push({ event, payload }); }); const task = await queueMasterAgentTask({ projectId: "project-goal-events", requestMessageId: "message-summary-request", requestText: "请汇总当前项目目标和版本记录,并同步到当前对话顶部入口", executionPrompt: "请汇总当前项目目标和版本记录", requestedBy: "Boss 超级管理员", requestedByAccount: "krisolo", deviceId: "mac-studio", accountLabel: "主 GPT", relayViaMasterAgent: true, }); await completeMasterAgentTask({ taskId: task.taskId, deviceId: "mac-studio", status: "completed", replyBody: [ "项目目标:完成主 Agent 汇总内容自动回写到当前对话。", "当前进度:已确认普通接管回复需要同步项目摘要。", "技术架构:Boss 文件账本保存项目理解,Android 对话页通过实时事件刷新。", "当前阻塞:无。", "建议下一步:继续做真机回归。", "版本记录:新增主 Agent 接管汇总回复自动写入项目目标和版本记录。", ].join("\n"), }); unsubscribe(); const refreshedProject = (await readState()).projects.find((project) => project.id === "project-goal-events"); assert.ok(refreshedProject, "expected project to exist"); assert.equal( refreshedProject!.projectUnderstanding?.projectGoal, "完成主 Agent 汇总内容自动回写到当前对话。", ); assert.equal( refreshedProject!.projectUnderstanding?.currentProgress, "已确认普通接管回复需要同步项目摘要。", ); assert.equal( refreshedProject!.versions[0]?.summary, "新增主 Agent 接管汇总回复自动写入项目目标和版本记录。", ); assert.ok( events.some( (item) => item.event === "conversation.updated" && item.payload.projectId === "project-goal-events" && item.payload.note === "project_goals.updated", ), "expected project goal refresh marker", ); assert.ok( events.some( (item) => item.event === "conversation.updated" && item.payload.projectId === "project-goal-events" && item.payload.note === "project_versions.updated", ), "expected project version refresh marker", ); }); test("takeover conversation summary wording writes project goal and version record back to the current conversation", async () => { const events: Array<{ event: string; payload: { projectId?: string; note?: string } }> = []; const unsubscribe = subscribeBossEvents((event, payload) => { events.push({ event, payload }); }); const state = await readState(); const project = state.projects.find((item) => item.id === "project-goal-events"); assert.ok(project, "expected project to exist"); project!.projectUnderstanding = undefined; project!.versions = []; await writeState(state); const task = await queueMasterAgentTask({ projectId: "project-goal-events", requestMessageId: "message-summary-wording-request", requestText: "请总结当前项目目标和版本记录", executionPrompt: "请总结当前项目目标和版本记录", requestedBy: "Boss 超级管理员", requestedByAccount: "krisolo", deviceId: "mac-studio", accountLabel: "主 GPT", relayViaMasterAgent: true, }); await completeMasterAgentTask({ taskId: task.taskId, deviceId: "mac-studio", status: "completed", replyBody: [ "项目目标:稳定总结类请求的项目目标回写。", "当前进度:已进入总结语义回归。", "技术架构:Boss 文件账本保存项目理解。", "当前阻塞:无。", "建议下一步:继续跑真机回归。", "版本记录:总结类请求也能写入版本记录。", ].join("\n"), }); unsubscribe(); const refreshedProject = (await readState()).projects.find((item) => item.id === "project-goal-events"); assert.equal(refreshedProject!.projectUnderstanding?.projectGoal, "稳定总结类请求的项目目标回写。"); assert.equal(refreshedProject!.versions[0]?.summary, "总结类请求也能写入版本记录。"); assert.ok( events.some( (item) => item.event === "conversation.updated" && item.payload.projectId === "project-goal-events" && item.payload.note === "project_goals.updated", ), "expected project goal refresh marker for summary wording", ); assert.ok( events.some( (item) => item.event === "conversation.updated" && item.payload.projectId === "project-goal-events" && item.payload.note === "project_versions.updated", ), "expected project version refresh marker for summary wording", ); });