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 postMessageRoute: (typeof import("../src/app/api/v1/projects/[projectId]/messages/route"))["POST"]; let completeMasterTaskRoute: (typeof import("../src/app/api/v1/master-agent/tasks/[taskId]/complete/route"))["POST"]; let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"]; let readState: (typeof import("../src/lib/boss-data"))["readState"]; let writeState: (typeof import("../src/lib/boss-data"))["writeState"]; let AUTH_SESSION_COOKIE = ""; const TEST_ACCOUNT = "17600003315"; async function setup() { if (runtimeRoot) { return; } runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-single-thread-message-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [messageModule, completeModule, data, auth] = await Promise.all([ import("../src/app/api/v1/projects/[projectId]/messages/route.ts"), import("../src/app/api/v1/master-agent/tasks/[taskId]/complete/route.ts"), import("../src/lib/boss-data.ts"), import("../src/lib/boss-auth.ts"), ]); postMessageRoute = messageModule.POST; completeMasterTaskRoute = completeModule.POST; createAuthSession = data.createAuthSession; readState = data.readState; writeState = data.writeState; 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: "POST", body: unknown) { const session = await createAuthSession({ account: TEST_ACCOUNT, role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); return new NextRequest(url, { method, headers: { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }, body: JSON.stringify(body), }); } function findSingleThreadProject( state: Awaited>, ) { return state.projects.find((project) => project.id !== "master-agent" && !project.isGroup); } function buildSingleThreadProject(projectId: string) { return { id: projectId, name: "测试线程", pinned: false, systemPinned: false, deviceIds: ["mac-studio"], preview: "测试线程等待继续处理。", updatedAt: "2026-04-04T11:30:00+08:00", lastMessageAt: "2026-04-04T11:30:00+08:00", isGroup: false, threadMeta: { projectId, threadId: `${projectId}-thread`, threadDisplayName: "测试线程", folderName: "测试项目", activityIconCount: 0, updatedAt: "2026-04-04T11:30:00+08:00", codexThreadRef: `${projectId}-thread`, codexFolderRef: `/Users/kris/code/${projectId}`, }, groupMembers: [], createdByAgent: true, collaborationMode: "development" as const, approvalState: "not_required" as const, unreadCount: 0, riskLevel: "low" as const, messages: [], goals: [], versions: [], }; } function buildProjectFolderKey(project: ReturnType) { const folderRef = (project.threadMeta.codexFolderRef?.trim() || project.threadMeta.folderName.trim()).toLowerCase(); return `${project.deviceIds[0]}:${folderRef}`; } async function ensureSingleThreadProject() { const state = await readState(); const existing = findSingleThreadProject(state); if (existing) { return existing; } const project = buildSingleThreadProject("single-thread-test"); await writeState({ ...state, projects: state.projects.concat(project), }); const nextState = await readState(); return findSingleThreadProject(nextState); } async function setProjectTakeover(projectId: string, enabled: boolean) { const state = await readState(); state.userProjectAgentControls = state.userProjectAgentControls.filter( (item) => !(item.projectId === projectId && item.account === TEST_ACCOUNT), ); state.userProjectAgentControls.unshift({ account: TEST_ACCOUNT, projectId, controls: { takeoverEnabled: enabled, updatedAt: enabled ? "2026-04-17T10:00:00.000Z" : "2026-04-17T10:10:00.000Z", }, }); await writeState(state); } async function resetThreadExecutionState(projectId: string) { const state = await readState(); const project = state.projects.find((item) => item.id === projectId); const targetDevice = project ? state.devices.find((device) => device.id === project.deviceIds[0]) : null; if (targetDevice) { targetDevice.preferredExecutionMode = "cli"; } state.projectExecutionPolicies = state.projectExecutionPolicies.filter((policy) => policy.projectId !== projectId); state.userProjectAgentControls = state.userProjectAgentControls.filter( (item) => !(item.projectId === projectId && item.account === TEST_ACCOUNT), ); await writeState(state); } test("POST /api/v1/projects/[projectId]/messages enqueues a conversation task for single-thread projects", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await resetThreadExecutionState(singleProject.id); const response = await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "请同步一下当前阻塞情况" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string; taskType: string; status: string } | null; dispatchPlan: null; }; assert.equal(payload.ok, true); assert.equal(payload.dispatchPlan, null); assert.ok(payload.task, "expected single-thread message to return a queued task"); assert.equal(payload.task?.taskType, "conversation_reply"); assert.equal(payload.task?.status, "queued"); const nextState = await readState(); const task = nextState.masterAgentTasks.find( (item) => item.taskType === "conversation_reply" && item.projectId === singleProject.id && item.requestText === "请同步一下当前阻塞情况", ); assert.ok(task, "expected a queued conversation_reply task for the single-thread project"); assert.equal(task?.targetProjectId, singleProject.id); assert.equal(task?.targetThreadId, singleProject.threadMeta.threadId); assert.equal(task?.targetCodexThreadRef, singleProject.threadMeta.codexThreadRef); assert.equal(task?.targetCodexFolderRef, singleProject.threadMeta.codexFolderRef); assert.ok(task?.executionPrompt?.includes("请同步一下当前阻塞情况")); assert.ok(task?.executionPrompt?.includes(singleProject.threadMeta.threadDisplayName)); assert.ok(!task?.executionPrompt?.includes("threadProjectId:"), "thread prompt should not include project id labels"); assert.ok(!task?.executionPrompt?.includes("folderName:"), "thread prompt should not include folder labels"); assert.ok(!task?.executionPrompt?.includes("deviceIds:"), "thread prompt should not include device id labels"); assert.equal(task?.relayViaMasterAgent, undefined); }); test("POST /api/v1/projects/[projectId]/messages routes takeover mode to master-agent conversation first", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await resetThreadExecutionState(singleProject.id); await setProjectTakeover(singleProject.id, true); const response = await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "请继续同步当前线程进展" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; replyPresenter?: "thread" | "master"; task?: { taskId: string; taskType: string; status: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.replyPresenter, "master"); assert.equal(payload.task?.taskType, "conversation_reply"); const nextState = await readState(); const task = nextState.masterAgentTasks.find( (item) => item.taskType === "conversation_reply" && item.projectId === singleProject.id && item.requestText === "请继续同步当前线程进展", ); assert.ok(task, "expected a queued conversation_reply task for takeover mode"); assert.equal(task?.relayViaMasterAgent, true); assert.equal(task?.targetProjectId, undefined); assert.equal(task?.targetThreadId, undefined); assert.equal(task?.targetCodexThreadRef, undefined); assert.equal(task?.targetCodexFolderRef, undefined); assert.ok(task?.executionPrompt?.includes("主 Agent")); assert.ok(task?.executionPrompt?.includes("协同接管")); assert.ok(task?.executionPrompt?.includes("先准确理解并确认用户意图")); }); test("takeover prompt asks master agent to sync verified goals and version records when user requests review", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await resetThreadExecutionState(singleProject.id); await setProjectTakeover(singleProject.id, true); const response = await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "核对一下项目目标和版本记录,确认后同步到顶部入口" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string; taskType: string; status: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.task?.taskType, "conversation_reply"); const nextState = await readState(); const task = nextState.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); assert.ok(task, "expected a queued takeover task"); assert.equal(task?.relayViaMasterAgent, true); assert.match(task!.executionPrompt, /用户要求核对或更新项目目标、版本记录时/); assert.match(task!.executionPrompt, /先让当前线程基于本地开发文档和实际代码重新汇总/); assert.match(task!.executionPrompt, /自动同步到当前会话顶部的“项目目标”和“版本记录”入口/); const understandingTask = nextState.masterAgentTasks.find( (item) => item.projectId === "master-agent" && item.projectUnderstandingTargetProjectId === singleProject.id && item.status === "queued", ); assert.ok(understandingTask, "expected a hidden project understanding sync task"); assert.match(understandingTask!.executionPrompt, /先基于当前项目本地可见的开发文档和实际代码进行汇总/); }); test("POST /api/v1/projects/[projectId]/messages still lets takeover mode talk to master agent during gui conflict", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await resetThreadExecutionState(singleProject.id); await setProjectTakeover(singleProject.id, true); const state = await readState(); const targetDevice = state.devices.find((device) => device.id === singleProject.deviceIds[0]); assert.ok(targetDevice, "expected a seeded target device"); targetDevice.preferredExecutionMode = "gui"; await writeState(state); const response = await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "我先和你确认一下接下来怎么推进" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; replyPresenter?: "thread" | "master"; task?: { taskId: string; taskType: string; status: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.replyPresenter, "master"); assert.equal(payload.task?.taskType, "conversation_reply"); const nextState = await readState(); const task = nextState.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); assert.ok(task, "expected takeover mode to queue a master-agent task"); assert.equal(task?.relayViaMasterAgent, true); assert.equal(task?.targetProjectId, undefined); }); test("POST /api/v1/projects/[projectId]/messages blocks single-thread sends when the target device prefers gui mode", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await setProjectTakeover(singleProject.id, false); const state = await readState(); const targetDevice = state.devices.find((device) => device.id === singleProject.deviceIds[0]); assert.ok(targetDevice, "expected a seeded target device"); targetDevice.preferredExecutionMode = "gui"; await writeState(state); const response = await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "继续推进当前线程" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); assert.equal(response.status, 409); const payload = (await response.json()) as { ok: boolean; code?: string; message?: string; executionConflict?: { projectId: string; deviceId: string; preferredExecutionMode: "gui" | "cli"; allowPolicy: "forbid" | "allow_once" | "allow_always"; conflictState: "none" | "warning" | "blocked"; reason: string; actions: string[]; }; }; assert.equal(payload.ok, false); assert.equal(payload.code, "THREAD_EXECUTION_CONFLICT"); assert.equal(payload.executionConflict?.projectId, singleProject.id); assert.equal(payload.executionConflict?.deviceId, singleProject.deviceIds[0]); assert.equal(payload.executionConflict?.preferredExecutionMode, "gui"); assert.equal(payload.executionConflict?.allowPolicy, "forbid"); assert.equal(payload.executionConflict?.conflictState, "blocked"); assert.equal(payload.executionConflict?.reason, "preferred_gui_mode"); assert.deepEqual(payload.executionConflict?.actions, ["forbid", "allow_once", "allow_always"]); const nextState = await readState(); const updatedProject = nextState.projects.find((project) => project.id === singleProject.id); const blockedMessage = updatedProject?.messages.find((message) => message.body.includes("继续推进当前线程")); assert.equal(blockedMessage, undefined, "blocked send should not append a local chat message"); const queuedTask = nextState.masterAgentTasks.find( (item) => item.taskType === "conversation_reply" && item.projectId === singleProject.id && item.requestText === "继续推进当前线程", ); assert.equal(queuedTask, undefined, "blocked send should not enqueue a conversation task"); }); test("POST /api/v1/projects/[projectId]/messages blocks single-thread sends when the current project folder is forbidden", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await setProjectTakeover(singleProject.id, false); const state = await readState(); const targetDevice = state.devices.find((device) => device.id === singleProject.deviceIds[0]); assert.ok(targetDevice, "expected a seeded target device"); targetDevice.preferredExecutionMode = "cli"; state.projectExecutionPolicies = [ { deviceId: singleProject.deviceIds[0], folderKey: buildProjectFolderKey(singleProject), projectId: singleProject.id, allowPolicy: "forbid", conflictState: "blocked", updatedAt: "2026-04-06T13:20:00.000Z", }, ]; await writeState(state); const response = await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "继续同步项目进度" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); assert.equal(response.status, 409); const payload = (await response.json()) as { ok: boolean; code?: string; executionConflict?: { projectId: string; folderKey?: string; preferredExecutionMode: "gui" | "cli"; allowPolicy: "forbid" | "allow_once" | "allow_always"; conflictState: "none" | "warning" | "blocked"; reason: string; }; }; assert.equal(payload.ok, false); assert.equal(payload.code, "THREAD_EXECUTION_CONFLICT"); assert.equal(payload.executionConflict?.projectId, singleProject.id); assert.equal(payload.executionConflict?.folderKey, buildProjectFolderKey(singleProject)); assert.equal(payload.executionConflict?.preferredExecutionMode, "cli"); assert.equal(payload.executionConflict?.allowPolicy, "forbid"); assert.equal(payload.executionConflict?.conflictState, "blocked"); assert.equal(payload.executionConflict?.reason, "project_conflict_forbid"); const nextState = await readState(); const updatedProject = nextState.projects.find((project) => project.id === singleProject.id); const blockedMessage = updatedProject?.messages.find((message) => message.body.includes("继续同步项目进度")); assert.equal(blockedMessage, undefined, "blocked send should not append a local chat message"); const queuedTask = nextState.masterAgentTasks.find( (item) => item.taskType === "conversation_reply" && item.projectId === singleProject.id && item.requestText === "继续同步项目进度", ); assert.equal(queuedTask, undefined, "blocked send should not enqueue a conversation task"); }); test("POST /api/v1/projects/[projectId]/messages blocks before queueing when recent codex activity exists without a stored policy", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await resetThreadExecutionState(singleProject.id); await setProjectTakeover(singleProject.id, false); const recentExternalActivityAt = new Date(Date.now() - 60_000).toISOString(); const state = await readState(); await writeState({ ...state, projects: state.projects.map((project) => project.id === singleProject.id ? { ...project, threadMeta: { ...project.threadMeta, lastObservedCodexActivityAt: recentExternalActivityAt, }, } : project, ), projectExecutionPolicies: state.projectExecutionPolicies.filter( (policy) => policy.projectId !== singleProject.id, ), }); const response = await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "请看一下这个项目现在卡在哪里" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); assert.equal(response.status, 409); const payload = (await response.json()) as { ok: boolean; code?: string; executionConflict?: { projectId: string; preferredExecutionMode: "gui" | "cli"; allowPolicy: "forbid" | "allow_once" | "allow_always"; conflictState: "none" | "warning" | "blocked"; reason: string; }; }; assert.equal(payload.ok, false); assert.equal(payload.code, "THREAD_EXECUTION_CONFLICT"); assert.equal(payload.executionConflict?.projectId, singleProject.id); assert.equal(payload.executionConflict?.preferredExecutionMode, "cli"); assert.equal(payload.executionConflict?.allowPolicy, "forbid"); assert.equal(payload.executionConflict?.conflictState, "blocked"); assert.equal(payload.executionConflict?.reason, "project_conflict_forbid"); const nextState = await readState(); const updatedProject = nextState.projects.find((project) => project.id === singleProject.id); const blockedMessage = updatedProject?.messages.find((message) => message.body.includes("请看一下这个项目现在卡在哪里"), ); assert.equal(blockedMessage, undefined, "blocked send should not append a local chat message"); const queuedTask = nextState.masterAgentTasks.find( (item) => item.taskType === "conversation_reply" && item.projectId === singleProject.id && item.requestText === "请看一下这个项目现在卡在哪里", ); assert.equal(queuedTask, undefined, "blocked send should not enqueue a conversation task"); }); test("POST /api/v1/projects/[projectId]/messages ignores stale scoped conflict policies", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await resetThreadExecutionState(singleProject.id); const staleExternalActivityAt = new Date(Date.now() - 30 * 60_000).toISOString(); const state = await readState(); await writeState({ ...state, projects: state.projects.map((project) => project.id === singleProject.id ? { ...project, threadMeta: { ...project.threadMeta, lastObservedCodexActivityAt: staleExternalActivityAt, }, } : project, ), projectExecutionPolicies: [ ...state.projectExecutionPolicies.filter((policy) => policy.projectId !== singleProject.id), { deviceId: singleProject.deviceIds[0], folderKey: buildProjectFolderKey(singleProject), projectId: singleProject.id, allowPolicy: "forbid" as const, conflictState: "blocked" as const, recentExternalActivityAt: staleExternalActivityAt, updatedAt: staleExternalActivityAt, }, ], }); const response = await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "继续同步这个线程" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string; taskType: string; status: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.task?.taskType, "conversation_reply"); assert.equal(payload.task?.status, "queued"); }); test("POST /api/v1/master-agent/tasks/[taskId]/complete writes the raw thread reply back to the single-thread project", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await resetThreadExecutionState(singleProject.id); await setProjectTakeover(singleProject.id, false); const sendResponse = await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "请同步一下当前阻塞情况" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); const sendPayload = (await sendResponse.json()) as { task?: { taskId: string }; }; const queuedState = await readState(); const task = queuedState.masterAgentTasks.find( (item) => item.taskId === sendPayload.task?.taskId, ); assert.ok(task, "expected a queued conversation_reply task"); const response = await completeMasterTaskRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/complete`, "POST", { deviceId: task.deviceId, status: "completed", targetProjectId: singleProject.id, targetThreadId: singleProject.threadMeta.threadId, replyBody: "当前阻塞点已经同步:视觉验收待今晚回归。", }, ), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const nextState = await readState(); const updatedProject = nextState.projects.find((project) => project.id === singleProject.id); const mirroredReply = updatedProject?.messages.find((message) => message.body.includes("当前阻塞点已经同步:视觉验收待今晚回归。"), ); assert.ok(mirroredReply, "expected single-thread reply to be written back to the project"); assert.equal(mirroredReply?.sender, "device"); }); test("POST /api/v1/master-agent/tasks/[taskId]/complete writes takeover master replies to the current project", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await resetThreadExecutionState(singleProject.id); await setProjectTakeover(singleProject.id, true); const sendResponse = await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "托管后请帮我问一下当前阻塞" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); const sendPayload = (await sendResponse.json()) as { task?: { taskId: string }; }; const queuedState = await readState(); const task = queuedState.masterAgentTasks.find( (item) => item.taskId === sendPayload.task?.taskId, ); assert.ok(task, "expected a queued conversation_reply task"); assert.equal(task?.relayViaMasterAgent, true); assert.equal(task?.targetProjectId, undefined); assert.equal(task?.targetThreadId, undefined); await setProjectTakeover(singleProject.id, false); const response = await completeMasterTaskRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/complete`, "POST", { deviceId: task.deviceId, status: "completed", replyBody: "我先确认一下:你是希望我梳理当前阻塞后,再协调目标线程继续推进,对吗?", }, ), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const nextState = await readState(); const updatedProject = nextState.projects.find((project) => project.id === singleProject.id); const relayedReply = updatedProject?.messages.find((message) => message.body.includes("我先确认一下:你是希望我梳理当前阻塞后,再协调目标线程继续推进,对吗?"), ); assert.ok(relayedReply, "expected a master reply to be written back to the current project"); assert.equal(relayedReply?.sender, "master"); assert.match(relayedReply?.senderLabel ?? "", /主 Agent/); }); test("POST /api/v1/master-agent/tasks/[taskId]/complete blocks leaked thread environment diagnostics from the chat transcript", async () => { await setup(); const singleProject = await ensureSingleThreadProject(); assert.ok(singleProject, "expected a seeded single-thread project"); await resetThreadExecutionState(singleProject.id); await setProjectTakeover(singleProject.id, false); await postMessageRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/projects/${singleProject.id}/messages`, "POST", { body: "请继续推进当前线程" }, ), { params: Promise.resolve({ projectId: singleProject.id }) }, ); const queuedState = await readState(); const task = queuedState.masterAgentTasks.find( (item) => item.taskType === "conversation_reply" && item.projectId === singleProject.id && item.targetProjectId === singleProject.id, ); assert.ok(task, "expected a queued conversation_reply task"); const response = await completeMasterTaskRoute( await createAuthedRequest( `http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/complete`, "POST", { deviceId: task.deviceId, status: "completed", targetProjectId: singleProject.id, targetThreadId: singleProject.threadMeta.threadId, replyBody: "我不能直接把当前会话环境从只读改回可写,也不能替你修改这层运行配置。cwd 我可以在命令里指向 /Users/kris/code/gptpluscontrol。", }, ), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const nextState = await readState(); const updatedProject = nextState.projects.find((project) => project.id === singleProject.id); const leakedReply = updatedProject?.messages.find((message) => message.body.includes("当前会话环境从只读改回可写"), ); assert.equal(leakedReply, undefined); const opsNotice = updatedProject?.messages.find((message) => message.body.includes("线程环境异常,请重新绑定到正确项目或工作目录后再试。"), ); assert.ok(opsNotice, "expected a user-facing system notice instead of raw environment diagnostics"); });