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 upsertDeviceHeartbeat: (typeof import("../src/lib/boss-data"))["upsertDeviceHeartbeat"]; let writeState: (typeof import("../src/lib/boss-data"))["writeState"]; async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-device-heartbeat-message-sync-")); 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; upsertDeviceHeartbeat = data.upsertDeviceHeartbeat; writeState = data.writeState; } test.after(async () => { if (runtimeRoot) { await rm(runtimeRoot, { recursive: true, force: true }); } }); test("device heartbeat records recent codex desktop activity without mirroring reply text", async () => { await setup(); const seedHeartbeat = { deviceId: "device-message-sync", token: "device-message-sync-token", name: "Mac Studio", avatar: "M", account: "krisolo", status: "online" as const, quota5h: 76, quota7d: 85, projects: [], endpoint: "mac://kris.local", projectCandidates: [ { folderName: "boss", folderRef: "/Users/kris/code/boss", threadId: "thread-boss-main", threadDisplayName: "Boss开发主线程", codexFolderRef: "/Users/kris/code/boss", codexThreadRef: "thread-boss-main", lastActiveAt: "2026-04-20T10:00:00.000Z", suggestedImport: true, }, ], }; await upsertDeviceHeartbeat(seedHeartbeat); await upsertDeviceHeartbeat(seedHeartbeat); const initialState = await readState(); const importedProject = initialState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-boss-main", ); assert.ok(importedProject, "expected heartbeat auto-import to create the thread conversation"); await upsertDeviceHeartbeat({ ...seedHeartbeat, projectCandidates: [ { ...seedHeartbeat.projectCandidates[0], lastActiveAt: "2026-04-20T10:02:10.000Z", recentAssistantMessages: [ { messageId: "codex-thread:thread-boss-main:2026-04-20T10:02:10.000Z:reply-1", body: "桌面 Codex 已经把会话实时同步链路修好了。", sentAt: "2026-04-20T10:02:10.000Z", }, ], }, ], }); let nextState = await readState(); let nextProject = nextState.projects.find((project) => project.id === importedProject?.id); const mirroredMessage = nextProject?.messages.find( (message) => message.externalMessageId === "codex-thread:thread-boss-main:2026-04-20T10:02:10.000Z:reply-1", ); assert.equal(mirroredMessage, undefined); assert.equal(nextProject?.messages.some((message) => message.externalMessageId), false); assert.equal(nextProject?.threadMeta.lastObservedCodexActivityAt, "2026-04-20T10:02:10.000Z"); assert.equal(nextProject?.unreadCount, 0); await upsertDeviceHeartbeat({ ...seedHeartbeat, projectCandidates: [ { ...seedHeartbeat.projectCandidates[0], recentAssistantMessages: [ { messageId: "codex-thread:thread-boss-main:2026-04-20T10:02:10.000Z:reply-1", body: "桌面 Codex 已经把会话实时同步链路修好了。", sentAt: "2026-04-20T10:02:10.000Z", }, ], }, ], }); nextState = await readState(); nextProject = nextState.projects.find((project) => project.id === importedProject?.id); const mirroredCopies = nextProject?.messages.filter( (message) => message.externalMessageId === "codex-thread:thread-boss-main:2026-04-20T10:02:10.000Z:reply-1", ); assert.equal(mirroredCopies?.length, 0); assert.equal(nextProject?.unreadCount, 0); }); test("device heartbeat records codex activity without appending uncorrelated desktop replies", async () => { await setup(); const seedHeartbeat = { deviceId: "device-message-activity-only", token: "device-message-activity-only-token", name: "Mac Studio", avatar: "M", account: "krisolo", status: "online" as const, quota5h: 76, quota7d: 85, projects: [], endpoint: "mac://kris.local", projectCandidates: [ { folderName: "juyuwan", folderRef: "/Users/kris/Documents/juyuwan", threadId: "thread-juyuwan-activity-only", threadDisplayName: "juyuwan", codexFolderRef: "/Users/kris/Documents/juyuwan", codexThreadRef: "thread-juyuwan-activity-only", lastActiveAt: "2026-06-07T16:30:00.000Z", suggestedImport: true, }, ], }; await upsertDeviceHeartbeat(seedHeartbeat); await upsertDeviceHeartbeat(seedHeartbeat); const initialState = await readState(); const importedProject = initialState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-juyuwan-activity-only", ); assert.ok(importedProject, "expected heartbeat auto-import to create the thread conversation"); importedProject!.messages = []; importedProject!.preview = ""; importedProject!.unreadCount = 0; await writeState(initialState); await upsertDeviceHeartbeat({ ...seedHeartbeat, projectCandidates: [ { ...seedHeartbeat.projectCandidates[0], lastActiveAt: "2026-06-07T16:34:15.000Z", recentAssistantMessages: [ { messageId: "codex-thread:thread-juyuwan-activity-only:2026-06-07T16:34:15.000Z:final-1", body: "已完成下一步,并实机验证了。APK 已安装到 K30 Ultra。", sentAt: "2026-06-07T16:34:15.000Z", phase: "final_answer", }, ], }, ], }); const nextState = await readState(); const nextProject = nextState.projects.find((project) => project.id === importedProject?.id); const mirroredMessage = nextProject?.messages.find( (message) => message.externalMessageId === "codex-thread:thread-juyuwan-activity-only:2026-06-07T16:34:15.000Z:final-1", ); const progressEvent = nextState.threadProgressEvents.find( (event) => event.projectId === importedProject?.id && event.createdAt === "2026-06-07T16:34:15.000Z", ); assert.equal(mirroredMessage, undefined); assert.equal(nextProject?.messages.length, 0); assert.equal(nextProject?.preview, ""); assert.equal(nextProject?.unreadCount, 0); assert.equal(nextProject?.threadMeta.lastObservedCodexActivityAt, "2026-06-07T16:34:15.000Z"); assert.ok(progressEvent, "expected heartbeat activity to remain visible as a thread progress event"); }); test("device heartbeat does not duplicate a reply already written by task completion", async () => { await setup(); const seedHeartbeat = { deviceId: "device-message-completion-dedupe", token: "device-message-completion-dedupe-token", name: "Mac Studio", avatar: "M", account: "krisolo", status: "online" as const, quota5h: 76, quota7d: 85, projects: [], endpoint: "mac://kris.local", projectCandidates: [ { folderName: "boss", folderRef: "/Users/kris/code/boss", threadId: "thread-boss-completion-dedupe", threadDisplayName: "Boss开发主线程", codexFolderRef: "/Users/kris/code/boss", codexThreadRef: "thread-boss-completion-dedupe", lastActiveAt: "2026-04-20T10:00:00.000Z", suggestedImport: true, }, ], }; await upsertDeviceHeartbeat(seedHeartbeat); await upsertDeviceHeartbeat(seedHeartbeat); const initialState = await readState(); const importedProject = initialState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-boss-completion-dedupe", ); assert.ok(importedProject); const directReplyBody = "BOSS回归APP消息已收到。"; const targetProject = initialState.projects.find((project) => project.id === importedProject?.id); assert.ok(targetProject); targetProject!.messages = [ { id: "msg-direct-completion", sender: "device", senderLabel: "Boss开发主线程", body: directReplyBody, sentAt: "2026-04-20T10:02:10.000Z", kind: "text", }, ]; targetProject!.preview = directReplyBody; targetProject!.lastMessageAt = "2026-04-20T10:02:10.000Z"; targetProject!.unreadCount = 1; await writeState(initialState); await upsertDeviceHeartbeat({ ...seedHeartbeat, projectCandidates: [ { ...seedHeartbeat.projectCandidates[0], recentAssistantMessages: [ { messageId: "codex-thread:thread-boss-completion-dedupe:2026-04-20T10:02:10.001Z:reply-1", body: directReplyBody, sentAt: "2026-04-20T10:02:10.001Z", phase: "final_answer", }, ], }, ], }); const nextState = await readState(); const nextProject = nextState.projects.find((project) => project.id === importedProject?.id); const matchingReplies = nextProject?.messages.filter((message) => message.body === directReplyBody); assert.equal(matchingReplies?.length, 1); assert.equal(matchingReplies?.[0]?.externalMessageId, undefined); assert.equal(nextProject?.unreadCount, 1); assert.equal(nextProject?.preview, directReplyBody); }); test("device heartbeat does not duplicate a takeover reply already written by master agent", async () => { await setup(); const seedHeartbeat = { deviceId: "device-message-master-dedupe", token: "device-message-master-dedupe-token", name: "Mac Studio", avatar: "M", account: "krisolo", status: "online" as const, quota5h: 76, quota7d: 85, projects: [], endpoint: "mac://kris.local", projectCandidates: [ { folderName: "boss", folderRef: "/Users/kris/code/boss", threadId: "thread-boss-master-dedupe", threadDisplayName: "Boss开发主线程", codexFolderRef: "/Users/kris/code/boss", codexThreadRef: "thread-boss-master-dedupe", lastActiveAt: "2026-04-20T10:00:00.000Z", suggestedImport: true, }, ], }; await upsertDeviceHeartbeat(seedHeartbeat); await upsertDeviceHeartbeat(seedHeartbeat); const initialState = await readState(); const importedProject = initialState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-boss-master-dedupe", ); assert.ok(importedProject); const replyBody = "主Agent托管链路已收到。"; const targetProject = initialState.projects.find((project) => project.id === importedProject?.id); assert.ok(targetProject); targetProject!.messages = [ { id: "msg-master-completion", sender: "master", senderLabel: "主 Agent", body: replyBody, sentAt: "2026-04-20T10:02:10.000Z", kind: "text", }, ]; targetProject!.preview = replyBody; targetProject!.lastMessageAt = "2026-04-20T10:02:10.000Z"; targetProject!.unreadCount = 1; await writeState(initialState); await upsertDeviceHeartbeat({ ...seedHeartbeat, projectCandidates: [ { ...seedHeartbeat.projectCandidates[0], recentAssistantMessages: [ { messageId: "codex-thread:thread-boss-master-dedupe:2026-04-20T10:02:10.001Z:reply-1", body: replyBody, sentAt: "2026-04-20T10:02:10.001Z", phase: "final_answer", }, ], }, ], }); const nextState = await readState(); const nextProject = nextState.projects.find((project) => project.id === importedProject?.id); const matchingReplies = nextProject?.messages.filter((message) => message.body === replyBody); assert.equal(matchingReplies?.length, 1); assert.equal(matchingReplies?.[0]?.sender, "master"); assert.equal(matchingReplies?.[0]?.externalMessageId, undefined); assert.equal(nextProject?.unreadCount, 1); assert.equal(nextProject?.preview, replyBody); }); test("device heartbeat ignores commentary and final assistant text as chat messages", async () => { await setup(); const seedHeartbeat = { deviceId: "device-message-phase", token: "device-message-phase-token", name: "Mac Studio", avatar: "M", account: "krisolo", status: "online" as const, quota5h: 76, quota7d: 85, projects: [], endpoint: "mac://kris.local", projectCandidates: [ { folderName: "boss", folderRef: "/Users/kris/code/boss", threadId: "thread-boss-phase", threadDisplayName: "Boss开发主线程", codexFolderRef: "/Users/kris/code/boss", codexThreadRef: "thread-boss-phase", lastActiveAt: "2026-04-20T10:00:00.000Z", suggestedImport: true, }, ], }; await upsertDeviceHeartbeat(seedHeartbeat); await upsertDeviceHeartbeat(seedHeartbeat); const initialState = await readState(); const importedProject = initialState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-boss-phase", ); assert.ok(importedProject); importedProject!.messages = []; importedProject!.preview = ""; importedProject!.unreadCount = 0; await writeState(initialState); await upsertDeviceHeartbeat({ ...seedHeartbeat, projectCandidates: [ { ...seedHeartbeat.projectCandidates[0], lastActiveAt: "2026-04-20T10:05:00.000Z", recentAssistantMessages: [ { messageId: "codex-thread:thread-boss-phase:2026-04-20T10:03:00.000Z:commentary-1", body: "我先检查聊天折叠链路,确认过程消息不会直接展开。", sentAt: "2026-04-20T10:03:00.000Z", phase: "commentary", }, { messageId: "codex-thread:thread-boss-phase:2026-04-20T10:05:00.000Z:final-1", body: "这轮已经完成折叠修复,未读现在只会算最终结果。", sentAt: "2026-04-20T10:05:00.000Z", phase: "final_answer", }, ], }, ], }); const nextState = await readState(); const nextProject = nextState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-boss-phase", ); const processMessage = nextProject?.messages.find( (message) => message.externalMessageId === "codex-thread:thread-boss-phase:2026-04-20T10:03:00.000Z:commentary-1", ); const finalMessage = nextProject?.messages.find( (message) => message.externalMessageId === "codex-thread:thread-boss-phase:2026-04-20T10:05:00.000Z:final-1", ); assert.ok(nextProject); assert.equal(processMessage, undefined); assert.equal(finalMessage, undefined); assert.equal(nextProject?.messages.length, 0); assert.equal(nextProject?.preview, ""); assert.equal(nextProject?.unreadCount, 0); assert.equal(nextProject?.threadMeta.lastObservedCodexActivityAt, "2026-04-20T10:05:00.000Z"); }); test("device heartbeat does not replay old desktop replies after conversation history is cleared", async () => { await setup(); const seedHeartbeat = { deviceId: "device-message-reset", token: "device-message-reset-token", name: "Mac Studio", avatar: "M", account: "krisolo", status: "online" as const, quota5h: 76, quota7d: 85, projects: [], endpoint: "mac://kris.local", projectCandidates: [ { folderName: "boss", folderRef: "/Users/kris/code/boss", threadId: "thread-boss-reset", threadDisplayName: "Boss开发主线程", codexFolderRef: "/Users/kris/code/boss", codexThreadRef: "thread-boss-reset", lastActiveAt: "2026-04-20T10:00:00.000Z", suggestedImport: true, }, ], }; await upsertDeviceHeartbeat(seedHeartbeat); await upsertDeviceHeartbeat(seedHeartbeat); const initialState = await readState(); const importedProject = initialState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-boss-reset", ); assert.ok(importedProject); initialState.conversationHistoryClearedAt = "2026-04-20T10:10:00.000Z"; const targetProject = initialState.projects.find((project) => project.id === importedProject?.id); assert.ok(targetProject); targetProject!.messages = []; targetProject!.preview = ""; targetProject!.unreadCount = 0; await writeState(initialState); await upsertDeviceHeartbeat({ ...seedHeartbeat, projectCandidates: [ { ...seedHeartbeat.projectCandidates[0], lastActiveAt: "2026-04-20T10:12:00.000Z", recentAssistantMessages: [ { messageId: "codex-thread:thread-boss-reset:2026-04-20T10:05:00.000Z:old-final", body: "这条旧回复不应该在清空历史后被重新导回。", sentAt: "2026-04-20T10:05:00.000Z", phase: "final_answer", }, { messageId: "codex-thread:thread-boss-reset:2026-04-20T10:11:00.000Z:new-final", body: "这条新回复应该继续同步回来。", sentAt: "2026-04-20T10:11:00.000Z", phase: "final_answer", }, ], }, ], }); const nextState = await readState(); const nextProject = nextState.projects.find((project) => project.id === importedProject?.id); assert.ok(nextProject); assert.equal( nextProject?.messages.some( (message) => message.externalMessageId === "codex-thread:thread-boss-reset:2026-04-20T10:05:00.000Z:old-final", ), false, ); assert.equal( nextProject?.messages.some( (message) => message.externalMessageId === "codex-thread:thread-boss-reset:2026-04-20T10:11:00.000Z:new-final", ), false, ); assert.equal(nextProject?.preview, ""); assert.equal(nextProject?.unreadCount, 0); assert.equal(nextProject?.threadMeta.lastObservedCodexActivityAt, "2026-04-20T10:12:00.000Z"); }); test("device heartbeat process text is kept out of the chat transcript", async () => { await setup(); const seedHeartbeat = { deviceId: "device-message-legacy-process", token: "device-message-legacy-process-token", name: "Mac Studio", avatar: "M", account: "krisolo", status: "online" as const, quota5h: 76, quota7d: 85, projects: [], endpoint: "mac://kris.local", projectCandidates: [ { folderName: "boss", folderRef: "/Users/kris/code/boss", threadId: "thread-boss-legacy-process", threadDisplayName: "Boss开发主线程", codexFolderRef: "/Users/kris/code/boss", codexThreadRef: "thread-boss-legacy-process", lastActiveAt: "2026-04-20T10:00:00.000Z", suggestedImport: true, }, ], }; await upsertDeviceHeartbeat(seedHeartbeat); await upsertDeviceHeartbeat(seedHeartbeat); const resetState = await readState(); resetState.conversationHistoryClearedAt = undefined; const importedProject = resetState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-boss-legacy-process", ); assert.ok(importedProject); importedProject!.messages = []; importedProject!.preview = ""; importedProject!.unreadCount = 0; await writeState(resetState); await upsertDeviceHeartbeat({ ...seedHeartbeat, projectCandidates: [ { ...seedHeartbeat.projectCandidates[0], lastActiveAt: "2026-04-20T10:05:00.000Z", recentAssistantMessages: [ { messageId: "codex-thread:thread-boss-legacy-process:2026-04-20T10:03:00.000Z:commentary-legacy", body: "我继续把这条链路又往下收了一层,补的是“历史脏消息”的兼容,不只是新消息规则。", sentAt: "2026-04-20T10:03:00.000Z", phase: "commentary", }, { messageId: "codex-thread:thread-boss-legacy-process:2026-04-20T10:05:00.000Z:final-1", body: "这轮已经完成折叠修复,未读现在只会算最终结果。", sentAt: "2026-04-20T10:05:00.000Z", phase: "final_answer", }, ], }, ], }); const nextState = await readState(); const nextProject = nextState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-boss-legacy-process", ); const legacyProcessMessage = nextProject?.messages.find( (message) => message.externalMessageId === "codex-thread:thread-boss-legacy-process:2026-04-20T10:03:00.000Z:commentary-legacy", ); assert.ok(nextProject); assert.equal(legacyProcessMessage, undefined); assert.equal(nextProject?.messages.length, 0); assert.equal(nextProject?.preview, ""); assert.equal(nextProject?.unreadCount, 0); assert.equal(nextProject?.threadMeta.lastObservedCodexActivityAt, "2026-04-20T10:05:00.000Z"); });