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 { NextRequest } from "next/server"; let runtimeRoot = ""; let createEnrollmentRoute: (typeof import("../src/app/api/v1/devices/enrollments/route"))["POST"]; let deviceHeartbeatRoute: (typeof import("../src/app/api/device-heartbeat/route"))["POST"]; let getImportDraftRoute: (typeof import("../src/app/api/v1/devices/[deviceId]/import-draft/route"))["GET"]; let selectImportDraftRoute: (typeof import("../src/app/api/v1/devices/[deviceId]/import-draft/select/route"))["POST"]; let reviewImportDraftRoute: (typeof import("../src/app/api/v1/devices/[deviceId]/import-draft/review/route"))["POST"]; let completeMasterTaskRoute: (typeof import("../src/app/api/v1/master-agent/tasks/[taskId]/complete/route"))["POST"]; let applyImportDraftRoute: (typeof import("../src/app/api/v1/devices/[deviceId]/import-draft/apply/route"))["POST"]; let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"]; let readState: (typeof import("../src/lib/boss-data"))["readState"]; let AUTH_SESSION_COOKIE = ""; async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-device-import-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [enrollmentModule, heartbeatModule, importDraftModule, selectModule, reviewModule, completeModule, applyModule, data, auth] = await Promise.all([ import("../src/app/api/v1/devices/enrollments/route.ts"), import("../src/app/api/device-heartbeat/route.ts"), import("../src/app/api/v1/devices/[deviceId]/import-draft/route.ts"), import("../src/app/api/v1/devices/[deviceId]/import-draft/select/route.ts"), import("../src/app/api/v1/devices/[deviceId]/import-draft/review/route.ts"), import("../src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts"), import("../src/app/api/v1/devices/[deviceId]/import-draft/apply/route.ts"), import("../src/lib/boss-data.ts"), import("../src/lib/boss-auth.ts"), ]); createEnrollmentRoute = enrollmentModule.POST; deviceHeartbeatRoute = heartbeatModule.POST; getImportDraftRoute = importDraftModule.GET; selectImportDraftRoute = selectModule.POST; reviewImportDraftRoute = reviewModule.POST; completeMasterTaskRoute = completeModule.POST; applyImportDraftRoute = applyModule.POST; createAuthSession = data.createAuthSession; readState = data.readState; AUTH_SESSION_COOKIE = auth.AUTH_SESSION_COOKIE; } test.after(async () => { if (runtimeRoot) { await rm(runtimeRoot, { recursive: true, force: true }); } }); async function createAuthedRequest(url: string, method: "GET" | "POST", body?: unknown) { return createAuthedRequestFor("17600003315", "highest_admin", url, method, body); } async function createAuthedRequestFor( account: string, role: "member" | "admin" | "highest_admin", url: string, method: "GET" | "POST", body?: unknown, ) { const session = await createAuthSession({ account, role, displayName: "Boss 超级管理员", loginMethod: "password", }); return new NextRequest(url, { method, headers: { cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, ...(body ? { "content-type": "application/json" } : {}), }, body: body ? JSON.stringify(body) : undefined, }); } test("device import draft review queues a master-agent task, then completion writes back a ready resolution and apply still works", async () => { await setup(); const enrollmentResponse = await createEnrollmentRoute( await createAuthedRequest("http://127.0.0.1:3000/api/v1/devices/enrollments", "POST", { name: "MacBook Pro", avatar: "P", account: "17600003315", endpoint: "mac://mbp.local", note: "待导入新设备", }), ); assert.equal(enrollmentResponse.status, 200); const enrollmentPayload = (await enrollmentResponse.json()) as { enrollment: { deviceId: string; pairingCode: string }; device: { id: string }; }; const heartbeatResponse = await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ deviceId: enrollmentPayload.device.id, pairingCode: enrollmentPayload.enrollment.pairingCode, name: "MacBook Pro", avatar: "P", account: "17600003315", status: "online", quota5h: 72, quota7d: 88, projects: [], endpoint: "mac://mbp.local", projectCandidates: [ { folderName: "北区试产线", folderRef: "north-line", threadId: "thread-north-regression", threadDisplayName: "北区试产线回归", codexFolderRef: "north-line", codexThreadRef: "thread-north-regression", lastActiveAt: "2026-03-30T10:18:00+08:00", suggestedImport: true, }, { folderName: "北区试产线", folderRef: "north-line", threadId: "thread-north-audit", threadDisplayName: "北区试产线审计", codexFolderRef: "north-line", codexThreadRef: "thread-north-audit", lastActiveAt: "2026-03-30T10:20:00+08:00", suggestedImport: true, }, ], }), }), ); assert.equal(heartbeatResponse.status, 200); const draftResponse = await getImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft`, "GET", ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(draftResponse.status, 200); const draftPayload = (await draftResponse.json()) as { draft: { draftId: string; candidates: Array<{ candidateId: string; threadDisplayName: string }> } | null; }; assert.ok(draftPayload.draft, "expected an import draft after heartbeat candidates"); assert.equal(draftPayload.draft.candidates.length, 2); const selectedCandidateIds = draftPayload.draft.candidates .filter((candidate) => candidate.threadDisplayName === "北区试产线回归") .map((candidate) => candidate.candidateId); assert.equal(selectedCandidateIds.length, 1); const selectResponse = await selectImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/select`, "POST", { selectedCandidateIds }, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(selectResponse.status, 200); const reviewResponse = await reviewImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/review`, "POST", {}, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(reviewResponse.status, 200); const reviewPayload = (await reviewResponse.json()) as { draft?: { status: string; selectedCandidateIds: string[] }; resolution?: { summary: string; items: Array<{ action: string; threadDisplayName: string }> }; understandingTasks?: Array<{ taskId: string; status: "queued" | "running" | "completed" | "failed" }>; task: { taskId: string; status: "queued" | "running" | "completed" | "failed" }; }; assert.equal(reviewPayload.task.status, "queued"); assert.equal(reviewPayload.draft?.status, "pending_resolution"); assert.equal(reviewPayload.resolution, undefined); assert.equal(reviewPayload.understandingTasks?.length, 1); assert.equal(reviewPayload.understandingTasks?.[0]?.status, "queued"); const reviewedState = await readState(); const resolutionTask = reviewedState.masterAgentTasks.find( (task) => task.taskType === "device_import_resolution" && task.deviceImportDraftId && task.status === "queued", ); assert.ok(resolutionTask, "expected import review to leave a queued master-agent task trace"); const understandingTask = reviewedState.masterAgentTasks.find( (task) => task.taskType === "conversation_reply" && task.deviceImportDraftId === draftPayload.draft?.draftId && task.status === "queued", ); assert.ok(understandingTask, "expected import review to leave a queued thread understanding task trace"); assert.equal(understandingTask?.targetThreadDisplayName, "北区试产线回归"); const completionResponse = await completeMasterTaskRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/master-agent/tasks/${reviewPayload.task.taskId}/complete`, "POST", { deviceId: reviewPayload.task.deviceId, status: "completed", replyBody: JSON.stringify( { summary: "MacBook Pro 导入建议:将回归线程导入为独立会话。", items: [ { candidateId: draftPayload.draft?.candidates[0]?.candidateId, action: "create_thread_conversation", reason: "需要保留独立上下文,建议新建会话。", }, ], }, null, 2, ), }, ), { params: Promise.resolve({ taskId: reviewPayload.task.taskId }) }, ); assert.equal(completionResponse.status, 200); const understandingCompletionResponse = await completeMasterTaskRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/master-agent/tasks/${understandingTask?.taskId}/complete`, "POST", { deviceId: enrollmentPayload.device.id, status: "completed", replyBody: JSON.stringify( { projectGoal: "完成北区试产线树莓派二代接入与联调。", currentProgress: "已经完成线程导入准备,当前在核对设备接线和控制链路。", technicalArchitecture: "前台是 Next.js 控制台,设备端通过 local-agent 与 Codex 线程联动。", currentBlockers: "还缺少树莓派二代的串口稳定性验证。", recommendedNextStep: "先确认接线和串口日志,再继续设备控制指令联调。", }, null, 2, ), }, ), { params: Promise.resolve({ taskId: understandingTask?.taskId ?? "" }) }, ); assert.equal(understandingCompletionResponse.status, 200); const completedState = await readState(); const completedDraft = completedState.deviceImportDrafts.find( (draft) => draft.deviceId === enrollmentPayload.device.id, ); const completedResolution = completedState.deviceImportResolutions.find( (resolution) => resolution.deviceId === enrollmentPayload.device.id, ); assert.equal(completedDraft?.status, "resolved"); assert.equal(completedResolution?.status, "ready"); assert.match(completedResolution?.summary ?? "", /MacBook Pro 导入建议/); const refreshedDraftResponse = await getImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft`, "GET", ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(refreshedDraftResponse.status, 200); const refreshedDraftPayload = (await refreshedDraftResponse.json()) as { projectUnderstandings?: Array<{ threadDisplayName: string; projectGoal: string; currentProgress: string; technicalArchitecture: string; currentBlockers: string; recommendedNextStep: string; }>; understandingTasks?: Array<{ taskId: string; status: string }>; }; assert.equal(refreshedDraftPayload.projectUnderstandings?.length, 1); assert.equal(refreshedDraftPayload.projectUnderstandings?.[0]?.threadDisplayName, "北区试产线回归"); assert.match( refreshedDraftPayload.projectUnderstandings?.[0]?.projectGoal ?? "", /树莓派二代接入与联调/, ); assert.equal(refreshedDraftPayload.understandingTasks?.[0]?.status, "completed"); const applyResponse = await applyImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/apply`, "POST", {}, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(applyResponse.status, 200); const applyPayload = (await applyResponse.json()) as { importedProjects?: Array<{ id: string; name: string }>; }; assert.equal(applyPayload.importedProjects?.length, 1); assert.equal(applyPayload.importedProjects?.[0]?.name, "北区试产线回归"); const nextState = await readState(); const importedProject = nextState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-north-regression", ); assert.ok(importedProject, "expected selected candidate to become a real chat window"); assert.equal(importedProject?.threadMeta.threadDisplayName, "北区试产线回归"); assert.equal(importedProject?.threadMeta.folderName, "北区试产线"); assert.equal(importedProject?.projectUnderstanding?.projectGoal, "完成北区试产线树莓派二代接入与联调。"); assert.match(importedProject?.projectUnderstanding?.technicalArchitecture ?? "", /local-agent 与 Codex 线程联动/); assert.equal(importedProject?.projectUnderstanding?.sourceKind, "device_import"); assert.ok(importedProject?.threadMeta.lastProjectUnderstandingSyncedAt); const importedMemories = nextState.masterAgentMemories.filter( (memory) => memory.projectId === importedProject?.id, ); assert.equal(importedMemories.length, 5); assert.equal( importedMemories.find((memory) => memory.title === "项目目标 · 北区试产线回归")?.content, "完成北区试产线树莓派二代接入与联调。", ); assert.match( importedMemories.find((memory) => memory.title === "下一步建议 · 北区试产线回归")?.content ?? "", /确认接线和串口日志/, ); const device = nextState.devices.find((item) => item.id === enrollmentPayload.device.id); assert.deepEqual(device?.projects, ["北区试产线"]); const appliedDraft = nextState.deviceImportDrafts.find( (draft) => draft.deviceId === enrollmentPayload.device.id, ); const appliedResolution = nextState.deviceImportResolutions.find( (resolution) => resolution.deviceId === enrollmentPayload.device.id, ); assert.equal(appliedDraft?.status, "applied"); assert.deepEqual(appliedDraft?.appliedProjectNames, ["北区试产线回归"]); assert.equal(appliedResolution?.status, "applied"); }); test("imported thread projects queue hidden understanding sync tasks on newer activity and refresh project understanding", async () => { await setup(); const enrollmentResponse = await createEnrollmentRoute( await createAuthedRequest("http://127.0.0.1:3000/api/v1/devices/enrollments", "POST", { name: "Mac mini", avatar: "M", account: "17600003315", endpoint: "mac://mini.local", note: "project sync follow-up", }), ); assert.equal(enrollmentResponse.status, 200); const enrollmentPayload = (await enrollmentResponse.json()) as { enrollment: { pairingCode: string }; device: { id: string }; }; const heartbeatBody = { deviceId: enrollmentPayload.device.id, pairingCode: enrollmentPayload.enrollment.pairingCode, name: "Mac mini", avatar: "M", account: "17600003315", status: "online" as const, quota5h: 73, quota7d: 84, projects: [], endpoint: "mac://mini.local", projectCandidates: [ { folderName: "智能看板", folderRef: "smart-board", threadId: "thread-smart-board", threadDisplayName: "智能看板主线程", codexFolderRef: "smart-board", codexThreadRef: "thread-smart-board", lastActiveAt: "2026-03-30T11:00:00+08:00", suggestedImport: true, }, ], }; const firstHeartbeatResponse = await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(heartbeatBody), }), ); assert.equal(firstHeartbeatResponse.status, 200); const draftResponse = await getImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft`, "GET", ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); const draftPayload = (await draftResponse.json()) as { draft: { candidates: Array<{ candidateId: string }> }; }; const selectedCandidateIds = draftPayload.draft.candidates.map((candidate) => candidate.candidateId); assert.equal( ( await selectImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/select`, "POST", { selectedCandidateIds }, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ) ).status, 200, ); const reviewResponse = await reviewImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/review`, "POST", {}, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(reviewResponse.status, 200); const reviewPayload = (await reviewResponse.json()) as { task: { taskId: string; deviceId: string }; }; const stateAfterReview = await readState(); const initialUnderstandingTask = stateAfterReview.masterAgentTasks.find( (task) => task.taskType === "conversation_reply" && task.deviceImportDraftId && task.deviceImportCandidateId && task.status === "queued", ); assert.ok(initialUnderstandingTask); assert.equal( ( await completeMasterTaskRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/master-agent/tasks/${reviewPayload.task.taskId}/complete`, "POST", { deviceId: reviewPayload.task.deviceId, status: "completed", replyBody: JSON.stringify( { summary: "Mac mini 导入建议:将智能看板主线程导入为独立会话。", items: selectedCandidateIds.map((candidateId) => ({ candidateId, action: "create_thread_conversation", reason: "需要保留独立上下文,建议新建会话。", })), }, null, 2, ), }, ), { params: Promise.resolve({ taskId: reviewPayload.task.taskId }) }, ) ).status, 200, ); assert.equal( ( await completeMasterTaskRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/master-agent/tasks/${initialUnderstandingTask.taskId}/complete`, "POST", { deviceId: enrollmentPayload.device.id, status: "completed", replyBody: JSON.stringify( { projectGoal: "让智能看板项目能够稳定接入主控面板。", currentProgress: "已经完成导入前梳理,准备开始界面和设备联调。", technicalArchitecture: "Android 原生端连接 Boss Web,再通过 local-agent 对接 Codex 线程。", currentBlockers: "还缺少设备端实时推送状态的统一协议。", recommendedNextStep: "先对齐状态推送协议,再做前后端联调。", }, null, 2, ), }, ), { params: Promise.resolve({ taskId: initialUnderstandingTask.taskId }) }, ) ).status, 200, ); const applyResponse = await applyImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/apply`, "POST", {}, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(applyResponse.status, 200); let currentState = await readState(); const importedProject = currentState.projects.find( (project) => project.threadMeta.codexThreadRef === "thread-smart-board", ); assert.ok(importedProject); assert.equal(importedProject?.projectUnderstanding?.currentProgress, "已经完成导入前梳理,准备开始界面和设备联调。"); const secondHeartbeatResponse = await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ ...heartbeatBody, lastSeenAt: "2026-04-05T11:40:00+08:00", projectCandidates: [ { ...heartbeatBody.projectCandidates[0], lastActiveAt: "2026-04-05T11:35:00+08:00", }, ], }), }), ); assert.equal(secondHeartbeatResponse.status, 200); currentState = await readState(); const hiddenSyncTask = currentState.masterAgentTasks.find( (task) => task.taskType === "conversation_reply" && task.projectId === "master-agent" && task.projectUnderstandingTargetProjectId === importedProject?.id && task.projectUnderstandingReason === "heartbeat_activity" && task.status === "queued", ); assert.ok(hiddenSyncTask, "expected a hidden follow-up sync task for newer thread activity"); assert.equal( ( await completeMasterTaskRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/master-agent/tasks/${hiddenSyncTask.taskId}/complete`, "POST", { deviceId: enrollmentPayload.device.id, status: "completed", replyBody: JSON.stringify( { projectGoal: "让智能看板项目能够稳定接入主控面板。", currentProgress: "用户已经继续推进到实时状态同步和 UI 联调阶段。", technicalArchitecture: "Android 原生端通过 SSE 接收 Boss 更新,local-agent 负责把线程状态回流到控制台。", currentBlockers: "高刷设备上的 UI 更新仍需继续优化。", recommendedNextStep: "优先压平实时状态刷新抖动,再验证群聊调度链路。", }, null, 2, ), }, ), { params: Promise.resolve({ taskId: hiddenSyncTask.taskId }) }, ) ).status, 200, ); currentState = await readState(); const refreshedProject = currentState.projects.find((project) => project.id === importedProject?.id); assert.equal(refreshedProject?.projectUnderstanding?.currentProgress, "用户已经继续推进到实时状态同步和 UI 联调阶段。"); assert.match(refreshedProject?.projectUnderstanding?.technicalArchitecture ?? "", /SSE 接收 Boss 更新/); assert.equal(refreshedProject?.projectUnderstanding?.sourceKind, "thread_sync"); assert.ok(refreshedProject?.threadMeta.lastProjectUnderstandingRequestedAt); assert.ok(refreshedProject?.threadMeta.lastProjectUnderstandingSyncedAt); assert.equal( currentState.masterAgentMemories.find( (memory) => memory.projectId === refreshedProject?.id && memory.title === "项目进度 · 智能看板主线程", )?.content, "用户已经继续推进到实时状态同步和 UI 联调阶段。", ); assert.equal( currentState.masterAgentMemories.find( (memory) => memory.projectId === refreshedProject?.id && memory.title === "下一步建议 · 智能看板主线程", )?.content, "优先压平实时状态刷新抖动,再验证群聊调度链路。", ); }); test("heartbeat candidates no longer auto-create chat windows from legacy projects when import draft is present", async () => { await setup(); const enrollmentResponse = await createEnrollmentRoute( await createAuthedRequest("http://127.0.0.1:3000/api/v1/devices/enrollments", "POST", { name: "ThinkPad", avatar: "T", account: "17600003315", endpoint: "pc://thinkpad.local", note: "legacy projects should not auto import", }), ); assert.equal(enrollmentResponse.status, 200); const enrollmentPayload = (await enrollmentResponse.json()) as { enrollment: { pairingCode: string }; device: { id: string }; }; const beforeState = await readState(); const beforeCount = beforeState.projects.filter((project) => project.name === "Legacy Folder").length; const heartbeatResponse = await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ deviceId: enrollmentPayload.device.id, pairingCode: enrollmentPayload.enrollment.pairingCode, name: "ThinkPad", avatar: "T", account: "17600003315", status: "online", quota5h: 60, quota7d: 75, projects: ["Legacy Folder"], endpoint: "pc://thinkpad.local", projectCandidates: [ { folderName: "Legacy Folder", folderRef: "legacy-folder", threadId: "thread-legacy-1", threadDisplayName: "Legacy 线程", codexFolderRef: "legacy-folder", codexThreadRef: "thread-legacy-1", lastActiveAt: "2026-03-30T10:30:00+08:00", suggestedImport: true, }, ], }), }), ); assert.equal(heartbeatResponse.status, 200); const nextState = await readState(); const afterCount = nextState.projects.filter((project) => project.name === "Legacy Folder").length; assert.equal(afterCount, beforeCount, "legacy project folders should wait for import apply"); const draft = nextState.deviceImportDrafts.find((item) => item.deviceId === enrollmentPayload.device.id); assert.ok(draft, "expected import draft to be created"); }); test("device import apply is idempotent and heartbeat preserves applied status", async () => { await setup(); const enrollmentResponse = await createEnrollmentRoute( await createAuthedRequest("http://127.0.0.1:3000/api/v1/devices/enrollments", "POST", { name: "Studio Mac", avatar: "S", account: "17600003315", endpoint: "mac://studio.local", note: "idempotent import apply", }), ); assert.equal(enrollmentResponse.status, 200); const enrollmentPayload = (await enrollmentResponse.json()) as { enrollment: { pairingCode: string }; device: { id: string }; }; const heartbeatPayload = { deviceId: enrollmentPayload.device.id, pairingCode: enrollmentPayload.enrollment.pairingCode, name: "Studio Mac", avatar: "S", account: "17600003315", status: "online" as const, quota5h: 68, quota7d: 82, projects: [], endpoint: "mac://studio.local", projectCandidates: [ { folderName: "导入目录", folderRef: "import-folder", threadId: "thread-import-1", threadDisplayName: "导入线程一", codexFolderRef: "import-folder", codexThreadRef: "thread-import-1", lastActiveAt: "2026-03-30T10:40:00+08:00", suggestedImport: true, }, ], }; assert.equal( ( await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(heartbeatPayload), }), ) ).status, 200, ); const draftResponse = await getImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft`, "GET", ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); const draftPayload = (await draftResponse.json()) as { draft: { candidates: Array<{ candidateId: string }> }; }; const selectedCandidateIds = draftPayload.draft.candidates.map((candidate) => candidate.candidateId); assert.equal( ( await selectImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/select`, "POST", { selectedCandidateIds }, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ) ).status, 200, ); const reviewResponse = await reviewImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/review`, "POST", {}, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(reviewResponse.status, 200); const reviewPayload = (await reviewResponse.json()) as { task: { taskId: string; deviceId: string; status: "queued" }; }; assert.equal( ( await completeMasterTaskRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/master-agent/tasks/${reviewPayload.task.taskId}/complete`, "POST", { deviceId: reviewPayload.task.deviceId, status: "completed", replyBody: JSON.stringify( { summary: "Studio Mac 导入建议:将导入目录里的线程导入为独立会话。", items: selectedCandidateIds.map((candidateId) => ({ candidateId, action: "create_thread_conversation", reason: "需要保留独立上下文,建议新建会话。", })), }, null, 2, ), }, ), { params: Promise.resolve({ taskId: reviewPayload.task.taskId }) }, ) ).status, 200, ); const applyUrl = `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/apply`; const firstApply = await applyImportDraftRoute( await createAuthedRequest(applyUrl, "POST", {}), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(firstApply.status, 200); const secondApply = await applyImportDraftRoute( await createAuthedRequest(applyUrl, "POST", {}), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(secondApply.status, 200); let nextState = await readState(); const importedProjects = nextState.projects.filter( (project) => project.threadMeta.codexThreadRef === "thread-import-1", ); assert.equal(importedProjects.length, 1, "replaying apply should not duplicate imported thread windows"); assert.equal( ( await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(heartbeatPayload), }), ) ).status, 200, ); nextState = await readState(); const appliedDraft = nextState.deviceImportDrafts.find((item) => item.deviceId === enrollmentPayload.device.id); assert.equal(appliedDraft?.status, "applied", "later heartbeats should not regress applied drafts"); }); test("clearing device import selection resets draft back to pending_selection and drops old resolution", async () => { await setup(); const enrollmentResponse = await createEnrollmentRoute( await createAuthedRequest("http://127.0.0.1:3000/api/v1/devices/enrollments", "POST", { name: "Review Mac", avatar: "R", account: "17600003315", endpoint: "mac://review.local", note: "selection reset", }), ); assert.equal(enrollmentResponse.status, 200); const enrollmentPayload = (await enrollmentResponse.json()) as { enrollment: { pairingCode: string }; device: { id: string }; }; assert.equal( ( await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ deviceId: enrollmentPayload.device.id, pairingCode: enrollmentPayload.enrollment.pairingCode, name: "Review Mac", avatar: "R", account: "17600003315", status: "online", quota5h: 66, quota7d: 79, projects: [], endpoint: "mac://review.local", projectCandidates: [ { folderName: "回归目录", folderRef: "review-folder", threadId: "thread-review-1", threadDisplayName: "回归线程一", codexFolderRef: "review-folder", codexThreadRef: "thread-review-1", lastActiveAt: "2026-03-30T11:00:00+08:00", suggestedImport: true, }, ], }), }), ) ).status, 200, ); const draftResponse = await getImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft`, "GET", ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); const draftPayload = (await draftResponse.json()) as { draft: { candidates: Array<{ candidateId: string }> }; }; const selectedCandidateIds = draftPayload.draft.candidates.map((candidate) => candidate.candidateId); assert.equal( ( await selectImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/select`, "POST", { selectedCandidateIds }, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ) ).status, 200, ); assert.equal( ( await reviewImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/review`, "POST", {}, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ) ).status, 200, ); const clearSelectionResponse = await selectImportDraftRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft/select`, "POST", { selectedCandidateIds: [] }, ), { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }) }, ); assert.equal(clearSelectionResponse.status, 200); const nextState = await readState(); const draft = nextState.deviceImportDrafts.find((item) => item.deviceId === enrollmentPayload.device.id); const resolution = nextState.deviceImportResolutions.find((item) => item.deviceId === enrollmentPayload.device.id); assert.equal(draft?.status, "pending_selection"); assert.deepEqual(draft?.selectedCandidateIds, []); assert.equal(draft?.resolutionId, undefined); assert.equal(resolution, undefined); }); test("device import routes reject unrelated logged-in members", async () => { await setup(); const enrollmentResponse = await createEnrollmentRoute( await createAuthedRequest("http://127.0.0.1:3000/api/v1/devices/enrollments", "POST", { name: "Build Mac", avatar: "B", account: "17600003315", endpoint: "mac://build.local", note: "route auth test", }), ); assert.equal(enrollmentResponse.status, 200); const enrollmentPayload = (await enrollmentResponse.json()) as { enrollment: { pairingCode: string }; device: { id: string }; }; assert.equal( ( await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ deviceId: enrollmentPayload.device.id, pairingCode: enrollmentPayload.enrollment.pairingCode, name: "Build Mac", avatar: "B", account: "17600003315", status: "online", quota5h: 71, quota7d: 80, projects: [], endpoint: "mac://build.local", projectCandidates: [ { folderName: "受控目录", folderRef: "secured-folder", threadId: "thread-secured", threadDisplayName: "受控线程", codexFolderRef: "secured-folder", codexThreadRef: "thread-secured", lastActiveAt: "2026-03-30T10:50:00+08:00", suggestedImport: true, }, ], }), }), ) ).status, 200, ); const outsiderRequest = await createAuthedRequestFor( "15500001111", "member", `http://127.0.0.1:3000/api/v1/devices/${enrollmentPayload.device.id}/import-draft`, "GET", ); const getResponse = await getImportDraftRoute(outsiderRequest, { params: Promise.resolve({ deviceId: enrollmentPayload.device.id }), }); assert.equal(getResponse.status, 403); }); test("existing bound production devices auto-sync suggested candidates into conversations on heartbeat", async () => { await setup(); const heartbeatResponse = await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ deviceId: "mac-studio", token: "boss-mac-studio-token", name: "Mac Studio", avatar: "M", account: "17600003315", status: "online", quota5h: 68, quota7d: 81, projects: ["Boss 移动控制台", "硬件审计协作"], endpoint: "mac://kris.local", projectCandidates: [ { folderName: "yuandi", folderRef: "/Users/kris/code/yuandi", threadId: "session-yuandi-1", threadDisplayName: "Epicurus", codexFolderRef: "/Users/kris/code/yuandi", codexThreadRef: "session-yuandi-1", lastActiveAt: "2026-03-30T12:42:56+08:00", suggestedImport: true, }, { folderName: "wenshenapp", folderRef: "/Users/kris/code/wenshenapp", threadId: "session-wenshenapp-1", threadDisplayName: "wenshenapp · ea5f", codexFolderRef: "/Users/kris/code/wenshenapp", codexThreadRef: "session-wenshenapp-1", lastActiveAt: "2026-03-30T12:34:51+08:00", suggestedImport: true, }, ], }), }), ); assert.equal(heartbeatResponse.status, 200); const payload = (await heartbeatResponse.json()) as { importDraft?: { status: string; selectedCandidateIds: string[] } | null; }; assert.equal(payload.importDraft?.status, "applied"); assert.equal(payload.importDraft?.selectedCandidateIds.length, 2); let nextState = await readState(); const yuandiProject = nextState.projects.find( (project) => project.threadMeta.codexThreadRef === "session-yuandi-1", ); const wenshenProject = nextState.projects.find( (project) => project.threadMeta.codexThreadRef === "session-wenshenapp-1", ); assert.ok(yuandiProject, "expected a discovered yuandi session to become a real chat window"); assert.ok(wenshenProject, "expected a discovered wenshenapp session to become a real chat window"); assert.equal(yuandiProject?.threadMeta.folderName, "yuandi"); assert.equal(wenshenProject?.threadMeta.folderName, "wenshenapp"); const device = nextState.devices.find((item) => item.id === "mac-studio"); assert.deepEqual(device?.projects, ["yuandi", "wenshenapp"]); const followupHeartbeat = await deviceHeartbeatRoute( new NextRequest("http://127.0.0.1:3000/api/device-heartbeat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ deviceId: "mac-studio", token: "boss-mac-studio-token", name: "Mac Studio", avatar: "M", account: "17600003315", status: "online", quota5h: 68, quota7d: 81, projects: ["Boss 移动控制台", "硬件审计协作"], endpoint: "mac://kris.local", projectCandidates: [ { folderName: "yuandi", folderRef: "/Users/kris/code/yuandi", threadId: "session-yuandi-1", threadDisplayName: "Epicurus", codexFolderRef: "/Users/kris/code/yuandi", codexThreadRef: "session-yuandi-1", lastActiveAt: "2026-03-30T12:55:56+08:00", suggestedImport: true, }, ], }), }), ); assert.equal(followupHeartbeat.status, 200); nextState = await readState(); const remainingYuandi = nextState.projects.find( (project) => project.threadMeta.codexThreadRef === "session-yuandi-1", ); const removedWenshen = nextState.projects.find( (project) => project.threadMeta.codexThreadRef === "session-wenshenapp-1", ); assert.ok(remainingYuandi, "expected still-selected candidate to stay imported"); assert.equal(removedWenshen, undefined, "auto-sync should prune stale imported threads that disappeared from candidates"); assert.deepEqual( nextState.devices.find((item) => item.id === "mac-studio")?.projects, ["yuandi"], ); });