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 data: typeof import("../src/lib/boss-data.ts"); let postProgress: (typeof import("../src/app/api/v1/master-agent/tasks/[taskId]/progress/route.ts"))["POST"]; async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-task-progress-route-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [dataModule, routeModule] = await Promise.all([ import("../src/lib/boss-data.ts"), import("../src/app/api/v1/master-agent/tasks/[taskId]/progress/route.ts"), ]); data = dataModule; postProgress = routeModule.POST; } test.beforeEach(async () => { await setup(); await rm(runtimeRoot, { recursive: true, force: true }); }); test.after(async () => { if (runtimeRoot) await rm(runtimeRoot, { recursive: true, force: true }); }); test("POST task progress accepts device-token updates and keeps task running", async () => { const task = await data.queueMasterAgentTask({ taskId: "route-progress-task", projectId: "group-progress-test", taskType: "dispatch_execution", requestMessageId: "msg-route-progress", requestText: "让目标线程继续开发", executionPrompt: "让目标线程继续开发", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: "master-agent", targetThreadId: "master-agent-thread", }); await data.claimNextMasterAgentTask("mac-studio"); const response = await postProgress( new NextRequest(`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/progress`, { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": "boss-mac-studio-token", }, body: JSON.stringify({ deviceId: "mac-studio", status: "running", executionProgress: { steps: [ { text: "连接 Codex App Server", status: "done" }, { text: "启动目标线程 turn", status: "running" }, ], branch: { additions: 12, deletions: 1 }, }, }), }), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.ok, true); assert.equal(payload.task.status, "running"); assert.equal(payload.task.completedAt, undefined); const state = await data.readState(); const progressMessage = state.projects .find((project) => project.id === "master-agent") ?.messages.find((message) => message.executionProgress?.taskId === task.taskId); assert.equal(progressMessage?.executionProgress?.status, "running"); assert.equal(progressMessage?.executionProgress?.steps[0]?.text, "连接 Codex App Server"); assert.equal(progressMessage?.executionProgress?.branch?.additions, 12); }); test("POST task progress preserves Codex approval, warning, and file-change summaries", async () => { const task = await data.queueMasterAgentTask({ taskId: "route-progress-approval-task", projectId: "group-progress-test", taskType: "dispatch_execution", requestMessageId: "msg-route-progress-approval", requestText: "让目标线程继续开发并回写审批状态", executionPrompt: "让目标线程继续开发并回写审批状态", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: "master-agent", targetThreadId: "master-agent-thread", }); await data.claimNextMasterAgentTask("mac-studio"); const response = await postProgress( new NextRequest(`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/progress`, { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": "boss-mac-studio-token", }, body: JSON.stringify({ deviceId: "mac-studio", status: "running", executionProgress: { steps: [{ text: "等待 Codex 审批事件", status: "running" }], approvals: [ { id: "cmd-approval-1", kind: "command", label: "命令执行审批", status: "resolved", detail: "需要确认命令执行", }, ], warnings: [ { id: "guardian-warning-1", message: "检测到需要用户确认的命令执行。", severity: "warning", }, ], fileChanges: [ { id: "file-change-1", path: "src/app/page.tsx", kind: "update", status: "updated", }, ], }, }), }), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const state = await data.readState(); const progress = state.projects .find((project) => project.id === "master-agent") ?.messages.find((message) => message.executionProgress?.taskId === task.taskId) ?.executionProgress; assert.equal(progress?.approvals?.[0]?.label, "命令执行审批"); assert.equal(progress?.warnings?.[0]?.message, "检测到需要用户确认的命令执行。"); assert.equal(progress?.fileChanges?.[0]?.path, "src/app/page.tsx"); }); test("POST task progress preserves Codex thread status and realtime summaries", async () => { const task = await data.queueMasterAgentTask({ taskId: "route-progress-realtime-task", projectId: "group-progress-test", taskType: "dispatch_execution", requestMessageId: "msg-route-progress-realtime", requestText: "让目标线程继续开发并回写实时状态", executionPrompt: "让目标线程继续开发并回写实时状态", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: "master-agent", targetThreadId: "master-agent-thread", }); await data.claimNextMasterAgentTask("mac-studio"); const response = await postProgress( new NextRequest(`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/progress`, { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": "boss-mac-studio-token", }, body: JSON.stringify({ deviceId: "mac-studio", status: "running", executionProgress: { steps: [{ text: "监听 Codex realtime 事件", status: "running" }], threadStatus: { type: "active", activeFlags: ["waitingOnApproval", "waitingOnUserInput"], waitingOnApproval: true, waitingOnUserInput: true, }, realtime: { status: "streaming", sessionId: "rt-session-1", version: "v2", transcriptRole: "assistant", transcriptPreview: "正在分析 Codex App Server 实时事件。", audioChunkCount: 1, itemCount: 1, }, }, }), }), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const state = await data.readState(); const progress = state.projects .find((project) => project.id === "master-agent") ?.messages.find((message) => message.executionProgress?.taskId === task.taskId) ?.executionProgress; assert.equal(progress?.threadStatus?.type, "active"); assert.equal(progress?.threadStatus?.waitingOnApproval, true); assert.equal(progress?.threadStatus?.waitingOnUserInput, true); assert.equal(progress?.realtime?.status, "streaming"); assert.equal(progress?.realtime?.transcriptPreview, "正在分析 Codex App Server 实时事件。"); assert.equal(progress?.realtime?.audioChunkCount, 1); }); test("POST task progress preserves Codex runtime status summaries", async () => { const task = await data.queueMasterAgentTask({ taskId: "route-progress-runtime-task", projectId: "group-progress-test", taskType: "dispatch_execution", requestMessageId: "msg-route-progress-runtime", requestText: "让目标线程继续开发并回写运行状态", executionPrompt: "让目标线程继续开发并回写运行状态", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: "master-agent", targetThreadId: "master-agent-thread", }); await data.claimNextMasterAgentTask("mac-studio"); const response = await postProgress( new NextRequest(`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/progress`, { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": "boss-mac-studio-token", }, body: JSON.stringify({ deviceId: "mac-studio", status: "running", executionProgress: { steps: [{ text: "同步 Codex 运行状态", status: "running" }], modelRoute: { fromModel: "gpt-5.4-mini", toModel: "gpt-5.4", reason: "highRiskCyberActivity", }, tokenUsage: { totalTokens: 3000, inputTokens: 2200, cachedInputTokens: 300, outputTokens: 650, reasoningOutputTokens: 150, modelContextWindow: 200000, contextPercent: 2, }, mcpServers: [ { name: "github", status: "failed", error: "token=[redacted] failed to start", }, ], remoteControl: { status: "connected", serverName: "Mac Studio", environmentId: "env-prod", installationId: "install-secret-should-not-persist", }, windowsSandbox: { status: "failed", setupMode: "elevated", error: "token=sk-secret-should-not-persist failed", sourcePath: "C:\\Users\\kris\\secret\\sandbox.log", }, }, }), }), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const state = await data.readState(); const progress = state.projects .find((project) => project.id === "master-agent") ?.messages.find((message) => message.executionProgress?.taskId === task.taskId) ?.executionProgress; assert.equal(progress?.modelRoute?.toModel, "gpt-5.4"); assert.equal(progress?.tokenUsage?.contextPercent, 2); assert.equal(progress?.mcpServers?.[0]?.name, "github"); assert.equal(progress?.mcpServers?.[0]?.error, "token=[redacted] failed to start"); assert.equal(progress?.remoteControl?.status, "connected"); assert.equal(progress?.windowsSandbox?.status, "failed"); assert.equal(progress?.windowsSandbox?.setupMode, "elevated"); assert.equal(progress?.windowsSandbox?.error, "token=[redacted] failed"); assert.equal(JSON.stringify(progress).includes("install-secret-should-not-persist"), false); assert.equal(JSON.stringify(progress).includes("C:\\Users\\kris\\secret"), false); }); test("POST task progress preserves Codex thread goal, settings, and compaction summaries", async () => { const task = await data.queueMasterAgentTask({ taskId: "route-progress-thread-config-task", projectId: "group-progress-test", taskType: "dispatch_execution", requestMessageId: "msg-route-progress-thread-config", requestText: "让目标线程同步目标和设置", executionPrompt: "让目标线程同步目标和设置", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: "master-agent", targetThreadId: "master-agent-thread", }); await data.claimNextMasterAgentTask("mac-studio"); const response = await postProgress( new NextRequest(`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/progress`, { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": "boss-mac-studio-token", }, body: JSON.stringify({ deviceId: "mac-studio", status: "running", executionProgress: { steps: [{ text: "同步 Codex 线程配置", status: "running" }], threadGoal: { objective: "完成 App Server 线程目标同步", status: "active", tokenBudget: 120000, tokensUsed: 4800, timeUsedSeconds: 600, }, threadSettings: { model: "gpt-5.5", modelProvider: "openai", approvalPolicy: "on-request", approvalsReviewer: "user", sandboxPolicy: "workspaceWrite", permissionProfile: ":workspace", serviceTier: "fast", effort: "low", summary: "concise", collaborationMode: "plan", personality: "pragmatic", cwd: "/Users/kris/code/boss/secret-project", }, compaction: { status: "completed", message: "上下文已压缩", turnId: "turn-secret-should-not-persist", }, }, }), }), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const state = await data.readState(); const progress = state.projects .find((project) => project.id === "master-agent") ?.messages.find((message) => message.executionProgress?.taskId === task.taskId) ?.executionProgress; assert.equal(progress?.threadGoal?.objective, "完成 App Server 线程目标同步"); assert.equal(progress?.threadSettings?.model, "gpt-5.5"); assert.equal(progress?.threadSettings?.sandboxPolicy, "workspaceWrite"); assert.equal(progress?.compaction?.status, "completed"); const serialized = JSON.stringify(progress); assert.equal(serialized.includes("/Users/kris"), false); assert.equal(serialized.includes("turn-secret-should-not-persist"), false); }); test("POST task progress preserves Codex account, quota, verification, and notice summaries", async () => { const task = await data.queueMasterAgentTask({ taskId: "route-progress-account-notices-task", projectId: "group-progress-test", taskType: "dispatch_execution", requestMessageId: "msg-route-progress-account-notices", requestText: "让目标线程同步账号和告警", executionPrompt: "让目标线程同步账号和告警", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: "master-agent", targetThreadId: "master-agent-thread", }); await data.claimNextMasterAgentTask("mac-studio"); const response = await postProgress( new NextRequest(`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/progress`, { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": "boss-mac-studio-token", }, body: JSON.stringify({ deviceId: "mac-studio", status: "running", executionProgress: { steps: [{ text: "同步 Codex 账号运行态", status: "running" }], accountStatus: { authMode: "chatgpt", planType: "team", limitId: "codex", limitName: "Codex", usedPercent: 88, windowDurationMins: 180, resetsAt: 1770003600, creditsBalance: "120.5", hasCredits: true, unlimitedCredits: false, accessToken: "sk-secret-should-not-persist", }, modelVerification: { verifications: ["trustedAccessForCyber"], turnId: "turn-secret-should-not-persist", }, warnings: [ { id: "config-warning-1", message: "项目配置已忽略:openai_base_url 不能放在项目配置里", severity: "warning", path: "/Users/kris/code/boss/.codex/config.toml", }, ], }, }), }), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const state = await data.readState(); const progress = state.projects .find((project) => project.id === "master-agent") ?.messages.find((message) => message.executionProgress?.taskId === task.taskId) ?.executionProgress; assert.equal(progress?.accountStatus?.authMode, "chatgpt"); assert.equal(progress?.accountStatus?.planType, "team"); assert.equal(progress?.accountStatus?.usedPercent, 88); assert.equal(progress?.modelVerification?.verifications?.[0], "trustedAccessForCyber"); assert.equal(progress?.warnings?.[0]?.message, "项目配置已忽略:openai_base_url 不能放在项目配置里"); const serialized = JSON.stringify(progress); assert.equal(serialized.includes("/Users/kris"), false); assert.equal(serialized.includes("sk-secret-should-not-persist"), false); assert.equal(serialized.includes("turn-secret-should-not-persist"), false); }); test("POST task progress preserves Codex thread collaboration summaries without leaking thread internals", async () => { const task = await data.queueMasterAgentTask({ taskId: "route-progress-thread-collab-task", projectId: "group-progress-test", taskType: "dispatch_execution", requestMessageId: "msg-route-progress-thread-collab", requestText: "让目标线程协作推进", executionPrompt: "让目标线程协作推进", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: "master-agent", targetThreadId: "master-agent-thread", }); await data.claimNextMasterAgentTask("mac-studio"); const response = await postProgress( new NextRequest(`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/progress`, { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": "boss-mac-studio-token", }, body: JSON.stringify({ deviceId: "mac-studio", status: "running", executionProgress: { steps: [{ text: "调用 Codex 线程协作", status: "running" }], threadCollaboration: { tool: "send_input", status: "completed", target: "已有线程", agentStatus: "completed", senderThreadId: "thread-source-secret-should-not-persist", receiverThreadId: "thread-target-secret-should-not-persist", prompt: "internal prompt token=sk-secret-should-not-persist", }, compaction: { status: "completed", message: "上下文已压缩", turnId: "turn-secret-should-not-persist", }, }, }), }), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const state = await data.readState(); const progress = state.projects .find((project) => project.id === "master-agent") ?.messages.find((message) => message.executionProgress?.taskId === task.taskId) ?.executionProgress; assert.deepEqual(progress?.threadCollaboration, { tool: "send_input", status: "completed", target: "已有线程", agentStatus: "completed", }); assert.equal(progress?.compaction?.status, "completed"); const serialized = JSON.stringify(progress); assert.equal(serialized.includes("thread-source-secret-should-not-persist"), false); assert.equal(serialized.includes("thread-target-secret-should-not-persist"), false); assert.equal(serialized.includes("internal prompt"), false); assert.equal(serialized.includes("sk-secret-should-not-persist"), false); assert.equal(serialized.includes("turn-secret-should-not-persist"), false); }); test("POST task progress preserves Codex stream event counters without leaking raw deltas", async () => { const task = await data.queueMasterAgentTask({ taskId: "route-progress-stream-events-task", projectId: "group-progress-test", taskType: "dispatch_execution", requestMessageId: "msg-route-progress-stream-events", requestText: "检查 Codex 流式增量", executionPrompt: "检查 Codex 流式增量", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: "master-agent", targetThreadId: "master-agent-thread", }); await data.claimNextMasterAgentTask("mac-studio"); const response = await postProgress( new NextRequest(`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/progress`, { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": "boss-mac-studio-token", }, body: JSON.stringify({ deviceId: "mac-studio", status: "running", executionProgress: { steps: [{ text: "接收 Codex 流式事件", status: "running" }], streamEvents: { status: "streaming", agentDeltaCount: 2, planDeltaCount: 1, reasoningDeltaCount: 3, toolProgressCount: 1, commandOutputChunkCount: 4, terminalInteractionCount: 1, fileOutputChunkCount: 1, delta: "secret delta should-not-persist", text: "secret text should-not-persist", output: "secret output should-not-persist", content: "secret content should-not-persist", command: "cat secret should-not-persist", turnId: "turn-secret-should-not-persist", }, }, }), }), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const state = await data.readState(); const progress = state.projects .find((project) => project.id === "master-agent") ?.messages.find((message) => message.executionProgress?.taskId === task.taskId) ?.executionProgress; assert.deepEqual(progress?.streamEvents, { status: "streaming", agentDeltaCount: 2, planDeltaCount: 1, reasoningDeltaCount: 3, toolProgressCount: 1, commandOutputChunkCount: 4, terminalInteractionCount: 1, fileOutputChunkCount: 1, }); const serialized = JSON.stringify(progress); assert.equal(serialized.includes("should-not-persist"), false); assert.equal(serialized.includes("turn-secret"), false); }); test("POST task progress preserves Codex tool activity summaries without leaking raw payloads", async () => { const task = await data.queueMasterAgentTask({ taskId: "route-progress-tool-activity-task", projectId: "group-progress-test", taskType: "dispatch_execution", requestMessageId: "msg-route-progress-tool-activity", requestText: "检查 Codex 工具活动", executionPrompt: "检查 Codex 工具活动", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: "master-agent", targetThreadId: "master-agent-thread", }); await data.claimNextMasterAgentTask("mac-studio"); const response = await postProgress( new NextRequest(`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/progress`, { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": "boss-mac-studio-token", }, body: JSON.stringify({ deviceId: "mac-studio", status: "running", executionProgress: { steps: [{ text: "同步 Codex 工具活动", status: "running" }], toolActivities: [ { kind: "mcp", name: "github/pull_request/list", status: "failed", detail: "token=sk-secret-should-not-persist failed", arguments: { token: "sk-secret-should-not-persist" }, result: "internal tool result", }, { kind: "web_search", name: "openPage", status: "running", detail: "Codex App Server ThreadItem", url: "https://example.com/private?token=sk-secret-should-not-persist", }, { kind: "command", name: "commandExecution", status: "completed", detail: "exit 0 · 2345ms", command: "cat /Users/kris/.ssh/id_rsa", cwd: "/Users/kris/code/boss", }, ], }, }), }), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const state = await data.readState(); const progress = state.projects .find((project) => project.id === "master-agent") ?.messages.find((message) => message.executionProgress?.taskId === task.taskId) ?.executionProgress; assert.deepEqual(progress?.toolActivities, [ { kind: "mcp", name: "github/pull_request/list", status: "failed", detail: "token=[redacted] failed", }, { kind: "web_search", name: "openPage", status: "running", detail: "Codex App Server ThreadItem", }, { kind: "command", name: "commandExecution", status: "completed", detail: "exit 0 · 2345ms", }, ]); const serialized = JSON.stringify(progress); assert.equal(serialized.includes("sk-secret-should-not-persist"), false); assert.equal(serialized.includes("internal tool result"), false); assert.equal(serialized.includes("https://example.com/private"), false); assert.equal(serialized.includes("/Users/kris"), false); assert.equal(serialized.includes("id_rsa"), false); }); test("POST task progress preserves Codex reasoning summaries without leaking raw content", async () => { const task = await data.queueMasterAgentTask({ taskId: "route-progress-reasoning-summary-task", projectId: "group-progress-test", taskType: "dispatch_execution", requestMessageId: "msg-route-progress-reasoning-summary", requestText: "检查 Codex 思考摘要", executionPrompt: "检查 Codex 思考摘要", requestedBy: "krisolo", requestedByAccount: "krisolo", deviceId: "mac-studio", targetProjectId: "master-agent", targetThreadId: "master-agent-thread", }); await data.claimNextMasterAgentTask("mac-studio"); const response = await postProgress( new NextRequest(`http://127.0.0.1:3000/api/v1/master-agent/tasks/${task.taskId}/progress`, { method: "POST", headers: { "content-type": "application/json", "x-boss-device-token": "boss-mac-studio-token", }, body: JSON.stringify({ deviceId: "mac-studio", status: "running", executionProgress: { steps: [{ text: "同步 Codex 思考摘要", status: "running" }], reasoningSummary: { status: "completed", summary: "确认只展示官方 summary,不展示 raw content token=sk-secret-should-not-persist。", content: [{ text: "raw hidden chain of thought token=sk-secret-should-not-persist" }], itemId: "reasoning-secret-should-not-persist", }, }, }), }), { params: Promise.resolve({ taskId: task.taskId }) }, ); assert.equal(response.status, 200); const state = await data.readState(); const progress = state.projects .find((project) => project.id === "master-agent") ?.messages.find((message) => message.executionProgress?.taskId === task.taskId) ?.executionProgress; assert.deepEqual(progress?.reasoningSummary, { status: "completed", summary: "确认只展示官方 summary,不展示 raw content token=[redacted]", }); const serialized = JSON.stringify(progress); assert.equal(serialized.includes("raw hidden chain of thought"), false); assert.equal(serialized.includes("sk-secret-should-not-persist"), false); assert.equal(serialized.includes("reasoning-secret-should-not-persist"), false); });