Files
boss/tests/local-agent-codex-app-server-runner.test.mjs

1854 lines
70 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import test from "node:test";
import assert from "node:assert/strict";
import crypto from "node:crypto";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import http from "node:http";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
discoverCodexAppServerCapabilities,
executeCodexAppServerTask,
getCodexAppServerRunnerConfig,
shouldUseCodexAppServerTaskRunner,
} from "../local-agent/codex-app-server-runner.mjs";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
test("codex app-server discovery includes governance and MCP summaries without leaking internals", async () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
codexAppServerDiscoveryLimit: 20,
codexAppServerSkillExtraRoots: [
"/Users/kris/enterprise/boss-shared-skills",
"/Users/kris/enterprise/team-skills",
],
});
const metadata = await discoverCodexAppServerCapabilities(runnerConfig);
assert.equal(metadata.experimentalFeatures[0].name, "multi_agent");
assert.equal(metadata.experimentalFeatures[0].stage, "stable");
assert.equal(metadata.experimentalFeatures[0].enabled, true);
assert.equal(metadata.collaborationModes[1].id, "plan");
assert.equal(metadata.permissionProfiles[0].id, ":workspace");
assert.equal(metadata.mcpServers[0].name, "github");
assert.equal(metadata.mcpServers[0].toolCount, 2);
assert.equal(metadata.mcpServers[0].authStatus, "oAuth");
assert.deepEqual(metadata.accountSummary, {
signedIn: true,
authMode: "chatgpt",
planType: "pro",
requiresOpenaiAuth: true,
});
assert.equal(metadata.rateLimitSummary.bucketCount, 2);
assert.equal(metadata.rateLimitSummary.maxUsedPercent, 42);
assert.equal(metadata.rateLimitSummary.reached, false);
assert.deepEqual(metadata.appConfigSummary, {
appCount: 2,
enabledAppCount: 1,
defaultEnabled: true,
destructiveEnabled: false,
openWorldEnabled: false,
});
assert.deepEqual(metadata.configRequirements, {
managed: true,
requirementCount: 2,
warningCount: 1,
});
assert.deepEqual(metadata.externalAgentMigration, {
itemCount: 3,
homeItemCount: 1,
projectItemCount: 2,
itemTypes: ["AGENTS_MD", "MCP_SERVER_CONFIG", "SKILLS"],
});
assert.deepEqual(metadata.skillExtraRootsSummary, {
configured: true,
status: "applied",
rootCount: 2,
rootLabels: ["boss-shared-skills", "team-skills"],
});
assert.deepEqual(metadata.hookSummary, {
workspaceCount: 1,
hookCount: 2,
enabledHookCount: 1,
managedHookCount: 1,
trustedHookCount: 1,
modifiedHookCount: 1,
untrustedHookCount: 0,
warningCount: 1,
errorCount: 1,
eventNames: ["PreToolUse", "SessionStart"],
handlerTypes: ["command", "prompt"],
});
assert.equal(metadata.threadSummary.threadCount, 3);
assert.equal(metadata.threadSummary.loadedThreadCount, 2);
assert.equal(metadata.threadSummary.activeThreadCount, 1);
assert.equal(metadata.threadSummary.archivedThreadCount, 1);
assert.equal(metadata.threadSummary.latestUpdatedAt, "2026-06-03T08:20:00.000Z");
assert.deepEqual(metadata.threadSummary.sourceKinds, ["app", "cli"]);
assert.deepEqual(metadata.threadSummary.visibleThreads[0], {
id: "thr-active",
name: "Boss App Server rollout",
sourceKind: "app",
status: "active",
archived: false,
loaded: true,
updatedAt: "2026-06-03T08:20:00.000Z",
});
assert.equal(metadata.threadTurnSummary.threadCount, 2);
assert.equal(metadata.threadTurnSummary.totalTurnCount, 3);
assert.equal(metadata.threadTurnSummary.runningTurnCount, 1);
assert.equal(metadata.threadTurnSummary.completedTurnCount, 2);
assert.equal(metadata.threadTurnSummary.latestUpdatedAt, "2026-06-03T08:21:00.000Z");
assert.deepEqual(metadata.threadTurnSummary.threads[0], {
threadId: "thr-active",
turnCount: 2,
runningTurnCount: 1,
completedTurnCount: 1,
latestTurnStatus: "running",
latestTurnUpdatedAt: "2026-06-03T08:21:00.000Z",
recentAssistantMessages: [
{
messageId: "codex-app-server:thr-active:turn-active-1:agent-final-app-server-reply",
body: "App Server 最终回复已完成同步。",
sentAt: "2026-06-03T08:10:00.000Z",
phase: "final_answer",
},
],
});
assert.deepEqual(metadata.threadActionSummary, {
actionCount: 11,
lifecycleActionCount: 5,
metadataActionCount: 2,
liveTurnActionCount: 2,
shellActionAvailable: true,
userInitiatedOnly: true,
labels: ["归档", "恢复", "分叉", "压缩", "回滚", "改名", "元数据", "活跃干预", "中断", "Shell", "取消订阅"],
});
assert.deepEqual(metadata.threadCollaborationSummary, {
bossBrokerAvailable: true,
collabToolCallHandlerAvailable: true,
directThreadChatSupported: false,
collaborationModeCount: 2,
userInitiatedOnly: true,
labels: ["源线程读取", "上下文注入", "目标线程执行", "协作事件"],
});
assert.deepEqual(metadata.pluginGovernanceSummary, {
actionCount: 9,
lifecycleActionCount: 2,
shareActionCount: 4,
readActionCount: 3,
skillReadAvailable: true,
userInitiatedOnly: true,
labels: ["安装", "卸载", "读取", "Skill 读取", "共享保存", "共享拉取", "共享删除", "共享目标", "共享列表"],
});
assert.deepEqual(metadata.accountGovernanceSummary, {
actionCount: 6,
loginActionCount: 3,
sessionActionCount: 1,
tokenRefreshAvailable: true,
billingNudgeAvailable: true,
userInitiatedOnly: true,
labels: ["登录开始", "登录取消", "登录完成", "退出登录", "刷新令牌", "额度提醒"],
});
assert.deepEqual(metadata.configGovernanceSummary, {
actionCount: 5,
writeActionCount: 3,
reloadActionCount: 1,
readActionAvailable: true,
userInitiatedOnly: true,
labels: ["配置读取", "单项写入", "批量写入", "MCP 重载", "Skill 配置"],
});
assert.deepEqual(metadata.fileSystemGovernanceSummary, {
actionCount: 9,
readActionCount: 3,
writeActionCount: 3,
destructiveActionCount: 1,
watchActionCount: 2,
userInitiatedOnly: true,
labels: ["读取文件", "读取目录", "元数据", "写入文件", "创建目录", "复制", "删除", "监听", "取消监听"],
});
assert.deepEqual(metadata.commandSessionSummary, {
actionCount: 5,
controlActionCount: 3,
streamAvailable: true,
terminationAvailable: true,
sandboxedCommandAvailable: true,
userInitiatedOnly: true,
labels: ["执行命令", "写入 stdin", "调整 PTY", "终止命令", "输出流"],
});
assert.deepEqual(metadata.externalAgentGovernanceSummary, {
actionCount: 3,
importActionCount: 1,
notificationActionCount: 1,
detectActionAvailable: true,
userInitiatedOnly: true,
labels: ["迁移检测", "迁移导入", "导入完成"],
});
assert.deepEqual(metadata.marketplaceGovernanceSummary, {
actionCount: 3,
writeActionCount: 3,
upgradeAvailable: true,
userInitiatedOnly: true,
labels: ["添加市场", "移除市场", "升级市场"],
});
assert.deepEqual(metadata.experimentalFeatureGovernanceSummary, {
actionCount: 2,
writeActionCount: 1,
listAvailable: true,
userInitiatedOnly: true,
labels: ["实验列表", "启用设置"],
});
assert.deepEqual(metadata.reviewGovernanceSummary, {
actionCount: 1,
reviewStartAvailable: true,
userInitiatedOnly: true,
labels: ["启动审查"],
});
assert.deepEqual(metadata.windowsSandboxGovernanceSummary, {
actionCount: 3,
setupActionCount: 1,
readinessAvailable: true,
notificationAvailable: true,
userInitiatedOnly: true,
labels: ["准备检查", "启动设置", "设置完成"],
});
assert.deepEqual(metadata.fuzzyFileSearchSummary, {
eventCount: 2,
completedEventAvailable: true,
notificationOnly: true,
labels: ["搜索更新", "搜索完成"],
});
assert.deepEqual(metadata.mcpGovernanceSummary, {
actionCount: 5,
oauthActionCount: 2,
resourceActionCount: 1,
toolActionCount: 1,
elicitationAvailable: true,
userInitiatedOnly: true,
labels: ["OAuth 登录", "OAuth 完成", "资源读取", "工具调用", "交互请求"],
});
assert.deepEqual(metadata.userInteractionGovernanceSummary, {
actionCount: 1,
requestUserInputAvailable: true,
userInitiatedOnly: true,
labels: ["请求用户输入"],
});
assert.deepEqual(metadata.guardianGovernanceSummary, {
actionCount: 2,
approvalActionCount: 1,
permissionRequestEventAvailable: true,
userInitiatedOnly: true,
labels: ["Guardian 放行", "权限请求"],
});
assert.deepEqual(metadata.runtimeEventSummary, {
eventCount: 3,
processEventCount: 2,
rawResponseEventAvailable: true,
notificationOnly: true,
labels: ["进程输出", "进程退出", "原始响应完成"],
});
assert.deepEqual(metadata.extensionEventSummary, {
eventCount: 2,
skillChangeEventAvailable: true,
pluginInstallEventAvailable: true,
notificationOnly: true,
labels: ["Skill 变更", "插件安装"],
});
assert.deepEqual(metadata.threadLifecycleEventSummary, {
eventCount: 5,
archiveEventCount: 2,
nameEventAvailable: true,
closeEventAvailable: true,
notificationOnly: true,
labels: ["线程启动", "线程关闭", "已归档", "已恢复", "改名完成"],
});
assert.deepEqual(metadata.streamDeltaEventSummary, {
eventCount: 9,
reasoningDeltaEventCount: 3,
commandStreamEventCount: 2,
toolProgressEventAvailable: true,
fileChangeOutputEventAvailable: true,
notificationOnly: true,
labels: ["Agent 增量", "计划增量", "思考新增", "思考文本", "原始思考", "MCP 进度", "命令输出", "终端交互", "文件输出"],
});
const serialized = JSON.stringify(metadata);
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
assert.equal(serialized.includes("/Users/kris"), false);
assert.equal(serialized.includes("id_ed25519"), false);
assert.equal(serialized.includes("filesystem"), false);
assert.equal(serialized.includes("resources"), false);
assert.equal(serialized.includes("private-user@example.com"), false);
assert.equal(serialized.includes("CLAUDE.md"), false);
assert.equal(serialized.includes("AGENTS.md"), false);
assert.equal(serialized.includes("/Users/kris/enterprise"), false);
assert.equal(serialized.includes("secret user text should not leak"), false);
assert.equal(serialized.includes("Old private thread"), false);
assert.equal(serialized.includes("private active turn text should not leak"), false);
assert.equal(serialized.includes("private item content should not leak"), false);
assert.equal(serialized.includes("private idle turn text should not leak"), false);
assert.equal(serialized.includes("private-hook.toml"), false);
assert.equal(serialized.includes("private-user-hook.toml"), false);
assert.equal(serialized.includes("broken-hook.toml"), false);
assert.equal(serialized.includes("private hook status"), false);
assert.equal(serialized.includes("private hook warning"), false);
assert.equal(serialized.includes("private hook error"), false);
assert.equal(serialized.includes("session-start-private-key"), false);
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
});
function encodeWsTextFrame(value) {
const payload = Buffer.from(value);
if (payload.length < 126) {
return Buffer.concat([Buffer.from([0x81, payload.length]), payload]);
}
if (payload.length <= 0xffff) {
const header = Buffer.alloc(4);
header[0] = 0x81;
header[1] = 126;
header.writeUInt16BE(payload.length, 2);
return Buffer.concat([header, payload]);
}
const header = Buffer.alloc(10);
header[0] = 0x81;
header[1] = 127;
header.writeBigUInt64BE(BigInt(payload.length), 2);
return Buffer.concat([header, payload]);
}
function decodeWsFrames(buffer, socket, onText) {
let offset = 0;
while (buffer.length - offset >= 2) {
const first = buffer[offset];
const second = buffer[offset + 1];
const opcode = first & 0x0f;
const masked = (second & 0x80) !== 0;
let payloadLength = second & 0x7f;
let headerLength = 2;
if (payloadLength === 126) {
if (buffer.length - offset < 4) break;
payloadLength = buffer.readUInt16BE(offset + 2);
headerLength = 4;
} else if (payloadLength === 127) {
if (buffer.length - offset < 10) break;
payloadLength = Number(buffer.readBigUInt64BE(offset + 2));
headerLength = 10;
}
const maskLength = masked ? 4 : 0;
const frameLength = headerLength + maskLength + payloadLength;
if (buffer.length - offset < frameLength) break;
let payload = buffer.subarray(
offset + headerLength + maskLength,
offset + frameLength,
);
if (masked) {
const mask = buffer.subarray(offset + headerLength, offset + headerLength + 4);
const unmaskedPayload = Buffer.alloc(payload.length);
for (let index = 0; index < payload.length; index += 1) {
unmaskedPayload[index] = payload[index] ^ mask[index % 4];
}
payload = unmaskedPayload;
}
if (opcode === 0x1) {
onText(payload.toString("utf8"));
} else if (opcode === 0x8) {
socket.end();
} else if (opcode === 0x9) {
socket.write(Buffer.concat([Buffer.from([0x8a, payload.length]), payload]));
}
offset += frameLength;
}
return buffer.subarray(offset);
}
async function createCodexAppServerWebSocketFixture(options = {}) {
const server = http.createServer();
const sockets = new Set();
let lastAuthorization = "";
server.on("upgrade", (request, socket) => {
sockets.add(socket);
socket.on("close", () => {
sockets.delete(socket);
});
lastAuthorization = String(request.headers.authorization || "");
const key = request.headers["sec-websocket-key"];
const accept = crypto
.createHash("sha1")
.update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`)
.digest("base64");
socket.write(
[
"HTTP/1.1 101 Switching Protocols",
"Upgrade: websocket",
"Connection: Upgrade",
`Sec-WebSocket-Accept: ${accept}`,
"",
"",
].join("\r\n"),
);
let buffered = Buffer.alloc(0);
const send = (message) => {
socket.write(encodeWsTextFrame(JSON.stringify(message)));
};
socket.on("data", (chunk) => {
buffered = decodeWsFrames(Buffer.concat([buffered, chunk]), socket, (line) => {
const message = JSON.parse(line);
if (message.method === "initialize") {
send({
id: message.id,
result: {
userAgent: "boss-test-codex-ws-app-server",
platformFamily: "mac",
platformOs: "darwin",
},
});
return;
}
if (message.method === "initialized") {
return;
}
if (message.method === "thread/resume") {
send({
id: message.id,
result: {
thread: {
id: message.params?.threadId,
name: "ws fixture thread",
},
},
});
return;
}
if (message.method === "turn/start") {
const text = message.params?.input?.find?.((item) => item?.type === "text")?.text ?? "";
send({
id: message.id,
result: {
turn: {
id: "ws-turn-fixture",
threadId: message.params?.threadId,
},
},
});
send({
method: "turn/plan/updated",
params: {
threadId: message.params?.threadId,
turnId: "ws-turn-fixture",
plan: [{ text: "通过 WebSocket 接入 Codex App Server", status: "completed" }],
},
});
send({
method: "item/agentMessage/delta",
params: {
threadId: message.params?.threadId,
turnId: "ws-turn-fixture",
delta: `WS_APP_SERVER_REPLY:${text}`,
},
});
send({
method: "turn/completed",
params: {
threadId: message.params?.threadId,
turn: { id: "ws-turn-fixture", status: "completed" },
},
});
return;
}
send({
id: message.id,
error: {
code: -32601,
message: `unknown method ${message.method}`,
},
});
});
});
});
let socketPath;
if (options.unixSocketPath) {
socketPath = options.unixSocketPath;
await new Promise((resolveServer) => server.listen(socketPath, resolveServer));
} else {
await new Promise((resolveServer) => server.listen(0, "127.0.0.1", resolveServer));
}
const address = server.address();
return {
url: socketPath ? `unix://${socketPath}` : `ws://127.0.0.1:${address.port}`,
getLastAuthorization: () => lastAuthorization,
close: () =>
new Promise((resolveServer) => {
for (const socket of sockets) {
socket.destroy();
}
server.close(resolveServer);
}),
};
}
test("codex app-server runner resumes a thread and collects streamed agent text", async () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const task = {
taskId: "task-app-server-1",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "继续开发并给出结果",
};
assert.equal(shouldUseCodexAppServerTaskRunner(runnerConfig, task), true);
const result = await executeCodexAppServerTask(runnerConfig, task);
assert.equal(result.status, "completed");
assert.equal(result.threadId, "019d-app-server-thread");
assert.equal(result.cwd, repoRoot);
assert.equal(result.replyBody, "APP_SERVER_REPLY:继续开发并给出结果");
assert.equal(result.transport, "stdio");
});
test("codex app-server runner injects Boss user message into the target Codex thread before starting a turn", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_ECHO_INJECTED_USER_MESSAGE;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_ECHO_INJECTED_USER_MESSAGE = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const task = {
taskId: "task-app-server-sync-user-message",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "请修复登录页跳转问题",
mirrorBossUserMessageToCodexDesktop: true,
userMessageId: "boss-msg-1",
sourceMessageId: "boss-msg-1",
sourceMessageBody: "请修复登录页跳转问题",
userDisplayName: "你",
};
const result = await executeCodexAppServerTask(runnerConfig, task);
assert.equal(result.status, "completed");
assert.equal(result.threadId, "019d-app-server-thread");
assert.equal(result.turnControl, "start");
assert.equal(result.threadHistorySync?.threadId, "019d-app-server-thread");
assert.equal(result.threadHistorySync?.injectedItemCount, 1);
assert.equal(result.threadHistorySync?.source, "boss_user_message");
assert.match(result.replyBody, /INJECTED_USER_MESSAGE:请修复登录页跳转问题/);
assert.doesNotMatch(JSON.stringify(result), /boss-msg-1/);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_ECHO_INJECTED_USER_MESSAGE;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_ECHO_INJECTED_USER_MESSAGE = previous;
}
}
});
test("codex app-server runner converts protocol progress events into Boss execution progress", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_PROGRESS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_PROGRESS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-progress",
taskType: "dispatch_execution",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "实现并回归",
});
assert.equal(result.status, "completed");
assert.equal(result.executionProgress.steps[0].text, "读取 Codex 官方 app-server 协议");
assert.equal(result.executionProgress.steps[0].status, "done");
assert.equal(result.executionProgress.steps[1].text, "执行 targeted/full test");
assert.equal(result.executionProgress.steps[1].status, "running");
assert.equal(result.executionProgress.branch.changedFiles, 3);
assert.equal(result.executionProgress.branch.additions, 181);
assert.equal(result.executionProgress.branch.deletions, 52);
assert.deepEqual(result.executionProgress.artifacts, [
{
id: "artifact-1",
label: "codex-app-server-protocol-0.135.0.json",
kind: "file",
},
]);
assert.deepEqual(result.executionProgress.agents, [
{
name: "Mendel",
role: "explorer",
status: "running",
},
]);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_PROGRESS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_PROGRESS = previous;
}
}
});
test("codex app-server runner maps guardian approval and file-change events without leaking secrets", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_GUARDIAN_EVENTS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_GUARDIAN_EVENTS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-guardian",
taskType: "dispatch_execution",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "需要审批和文件变更摘要",
});
assert.equal(result.status, "completed");
assert.equal(result.replyBody, "APP_SERVER_REPLY:需要审批和文件变更摘要");
assert.deepEqual(result.executionProgress.approvals, [
{
id: "cmd-approval-1",
kind: "command",
label: "命令执行审批",
status: "resolved",
detail: "需要确认命令执行",
},
{
id: "review-1",
kind: "auto_review",
label: "自动审批复核",
status: "approved",
riskLevel: "medium",
},
]);
assert.deepEqual(result.executionProgress.warnings, [
{
id: "guardian-warning-1",
message: "检测到需要用户确认的命令执行。",
severity: "warning",
},
]);
assert.deepEqual(result.executionProgress.fileChanges, [
{
id: "file-change-item-1-1",
path: "src/app/page.tsx",
kind: "update",
status: "updated",
},
{
id: "file-change-item-1-2",
path: "docs/architecture/codex_server_progress_card_cn.md",
kind: "add",
status: "updated",
},
]);
assert.equal(result.executionProgress.branch.changedFiles, 2);
assert.equal(JSON.stringify(result.executionProgress).includes("sk-secret-should-not-leak"), false);
assert.equal(JSON.stringify(result.executionProgress).includes("internal prompt"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_GUARDIAN_EVENTS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_GUARDIAN_EVENTS = previous;
}
}
});
test("codex app-server runner maps thread status and realtime events without leaking transport payloads", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_REALTIME_EVENTS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_REALTIME_EVENTS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-realtime",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "开启实时协作并回写状态",
});
assert.equal(result.status, "completed");
assert.deepEqual(result.executionProgress.threadStatus, {
type: "active",
activeFlags: ["waitingOnApproval", "waitingOnUserInput"],
waitingOnApproval: true,
waitingOnUserInput: true,
});
assert.deepEqual(result.executionProgress.realtime, {
status: "closed",
sessionId: "rt-session-1",
version: "v2",
transcriptRole: "assistant",
transcriptPreview: "正在分析 Codex App Server 实时事件。",
audioChunkCount: 1,
itemCount: 1,
closeReason: "completed",
});
const serialized = JSON.stringify(result.executionProgress);
assert.equal(serialized.includes("audio-secret-payload"), false);
assert.equal(serialized.includes("v=0 secret"), false);
assert.equal(serialized.includes("raw realtime item should not be persisted"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_REALTIME_EVENTS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_REALTIME_EVENTS = previous;
}
}
});
test("codex app-server runner maps runtime status events without leaking internal identifiers", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_RUNTIME_STATUS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_RUNTIME_STATUS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-runtime-status",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "检查模型和运行状态",
});
assert.equal(result.status, "completed");
assert.deepEqual(result.executionProgress.modelRoute, {
fromModel: "gpt-5.4-mini",
toModel: "gpt-5.4",
reason: "highRiskCyberActivity",
});
assert.deepEqual(result.executionProgress.tokenUsage, {
totalTokens: 3000,
inputTokens: 2200,
cachedInputTokens: 300,
outputTokens: 650,
reasoningOutputTokens: 150,
modelContextWindow: 200000,
contextPercent: 2,
});
assert.deepEqual(result.executionProgress.mcpServers, [
{
name: "github",
status: "failed",
error: "token=[redacted] failed to start",
},
]);
assert.deepEqual(result.executionProgress.remoteControl, {
status: "connected",
serverName: "Mac Studio",
environmentId: "env-prod",
});
assert.deepEqual(result.executionProgress.windowsSandbox, {
status: "failed",
setupMode: "elevated",
error: "token=[redacted] failed in [path]",
});
const serialized = JSON.stringify(result.executionProgress);
assert.equal(serialized.includes("install-secret-should-not-leak"), false);
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
assert.equal(serialized.includes("C:\\Users\\kris\\secret"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_RUNTIME_STATUS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_RUNTIME_STATUS = previous;
}
}
});
test("codex app-server runner maps thread goal, settings, and compaction events without leaking local paths", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_THREAD_CONFIG_EVENTS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_THREAD_CONFIG_EVENTS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-thread-config",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "同步线程目标和设置",
});
assert.equal(result.status, "completed");
assert.deepEqual(result.executionProgress.threadGoal, {
objective: "完成 App Server 线程目标同步",
status: "active",
tokenBudget: 120000,
tokensUsed: 4800,
timeUsedSeconds: 600,
});
assert.deepEqual(result.executionProgress.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",
});
assert.deepEqual(result.executionProgress.compaction, {
status: "completed",
message: "上下文已压缩",
});
const serialized = JSON.stringify(result.executionProgress);
assert.equal(serialized.includes("/Users/kris"), false);
assert.equal(serialized.includes("internal prompt"), false);
assert.equal(serialized.includes("secret-instructions"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_THREAD_CONFIG_EVENTS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_THREAD_CONFIG_EVENTS = previous;
}
}
});
test("codex app-server runner maps account, quota, verification, and notices without leaking config paths", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_ACCOUNT_NOTICE_EVENTS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_ACCOUNT_NOTICE_EVENTS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-account-notices",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "同步账号与告警状态",
});
assert.equal(result.status, "completed");
assert.deepEqual(result.executionProgress.accountStatus, {
authMode: "chatgpt",
planType: "team",
limitId: "codex",
limitName: "Codex",
usedPercent: 88,
windowDurationMins: 180,
resetsAt: 1770003600,
creditsBalance: "120.5",
hasCredits: true,
unlimitedCredits: false,
});
assert.deepEqual(result.executionProgress.modelVerification, {
verifications: ["trustedAccessForCyber"],
});
assert.deepEqual(result.executionProgress.warnings, [
{
id: "codex-warning-1",
message: "模型切换提醒 token=[redacted]",
severity: "warning",
},
{
id: "config-warning-2",
message: "项目配置已忽略openai_base_url 不能放在项目配置里",
severity: "warning",
},
{
id: "deprecation-notice-3",
message: "on-failure 已废弃:请改用 on-request",
severity: "info",
},
]);
const serialized = JSON.stringify(result.executionProgress);
assert.equal(serialized.includes("/Users/kris"), false);
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_ACCOUNT_NOTICE_EVENTS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_ACCOUNT_NOTICE_EVENTS = previous;
}
}
});
test("codex app-server runner maps collab tool calls and context compaction without leaking thread internals", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_COLLAB_EVENTS;
const previousV2 = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_COLLAB_EVENTS_V2;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_COLLAB_EVENTS = "1";
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_COLLAB_EVENTS_V2 = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-collab-events",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "让目标线程协作推进",
});
assert.equal(result.status, "completed");
assert.deepEqual(result.executionProgress.threadCollaboration, {
tool: "send_input",
status: "completed",
target: "已有线程",
agentStatus: "completed · errored",
receiverCount: 2,
});
assert.deepEqual(result.executionProgress.compaction, {
status: "completed",
message: "上下文已压缩",
});
const serialized = JSON.stringify(result.executionProgress);
assert.equal(serialized.includes("thread-source-secret-should-not-leak"), false);
assert.equal(serialized.includes("thread-target-secret-should-not-leak"), false);
assert.equal(serialized.includes("thread-target-2-secret-should-not-leak"), false);
assert.equal(serialized.includes("internal prompt"), false);
assert.equal(serialized.includes("private agent status message"), false);
assert.equal(serialized.includes("private agent completed message"), false);
assert.equal(serialized.includes("private agent error message"), false);
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
assert.equal(serialized.includes("context-compaction-secret-should-not-leak"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_COLLAB_EVENTS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_COLLAB_EVENTS = previous;
}
if (previousV2 === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_COLLAB_EVENTS_V2;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_COLLAB_EVENTS_V2 = previousV2;
}
}
});
test("codex app-server runner summarizes stream deltas without leaking raw delta content", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_STREAM_DELTA_EVENTS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_STREAM_DELTA_EVENTS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-stream-deltas",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "检查 Codex 流式增量进度",
});
assert.equal(result.status, "completed");
assert.deepEqual(result.executionProgress.streamEvents, {
status: "completed",
agentDeltaCount: 1,
planDeltaCount: 1,
reasoningDeltaCount: 3,
toolProgressCount: 1,
commandOutputChunkCount: 1,
terminalInteractionCount: 1,
fileOutputChunkCount: 1,
});
const serialized = JSON.stringify(result.executionProgress);
assert.equal(serialized.includes("secret plan delta should not leak"), false);
assert.equal(serialized.includes("secret reasoning summary part should not leak"), false);
assert.equal(serialized.includes("secret reasoning summary delta should not leak"), false);
assert.equal(serialized.includes("secret raw reasoning delta should not leak"), false);
assert.equal(serialized.includes("secret mcp progress should not leak"), false);
assert.equal(serialized.includes("secret command output should not leak"), false);
assert.equal(serialized.includes("secret terminal input should not leak"), false);
assert.equal(serialized.includes("secret file output should not leak"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_STREAM_DELTA_EVENTS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_STREAM_DELTA_EVENTS = previous;
}
}
});
test("codex app-server runner maps tool, search, image, review, and command activities without leaking payloads", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_TOOL_ACTIVITY_EVENTS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_TOOL_ACTIVITY_EVENTS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-tool-activity-events",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "检查 Codex 工具活动",
});
assert.equal(result.status, "completed");
assert.deepEqual(result.executionProgress.toolActivities, [
{
kind: "mcp",
name: "github/pull_request/list",
status: "failed",
detail: "token=[redacted] failed",
},
{
kind: "dynamic",
name: "browser.open",
status: "completed",
detail: "成功 · 1234ms",
},
{
kind: "web_search",
name: "openPage",
status: "running",
detail: "Codex App Server ThreadItem",
},
{
kind: "image_view",
name: "imageView",
status: "completed",
detail: "private-screenshot.png",
},
{
kind: "review",
name: "reviewMode",
status: "exited",
detail: "completed",
},
{
kind: "command",
name: "commandExecution",
status: "completed",
detail: "exit 0 · 2345ms",
},
]);
const serialized = JSON.stringify(result.executionProgress);
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
assert.equal(serialized.includes("/Users/kris"), false);
assert.equal(serialized.includes("internal tool result"), false);
assert.equal(serialized.includes("internal review prompt"), false);
assert.equal(serialized.includes("id_rsa"), false);
assert.equal(serialized.includes("https://example.com/private"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_TOOL_ACTIVITY_EVENTS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_TOOL_ACTIVITY_EVENTS = previous;
}
}
});
test("codex app-server runner maps image generation as a safe activity and artifact", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_IMAGE_GENERATION_EVENTS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_IMAGE_GENERATION_EVENTS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-image-generation-events",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "检查 Codex imageGeneration item",
});
assert.equal(result.status, "completed");
assert.deepEqual(result.executionProgress.toolActivities, [
{
kind: "image_generation",
name: "imageGeneration",
status: "completed",
detail: "generated-secret-image.png",
},
]);
assert.deepEqual(result.executionProgress.artifacts, [
{
id: "artifact-1",
label: "generated-secret-image.png",
kind: "image",
},
]);
const serialized = JSON.stringify(result.executionProgress);
assert.equal(serialized.includes("image-generation-secret-should-not-leak"), false);
assert.equal(serialized.includes("raw-secret-should-not-leak"), false);
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
assert.equal(serialized.includes("/Users/kris"), false);
assert.equal(serialized.includes("internal prompt"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_IMAGE_GENERATION_EVENTS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_IMAGE_GENERATION_EVENTS = previous;
}
}
});
test("codex app-server runner maps hook lifecycle events without leaking hook internals", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_HOOK_EVENTS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_HOOK_EVENTS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-hook-events",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "检查 Codex hook 生命周期",
});
assert.equal(result.status, "completed");
assert.deepEqual(result.executionProgress.toolActivities, [
{
kind: "hook",
name: "postToolUse/command",
status: "completed",
detail: "project · async · 42ms",
},
]);
const serialized = JSON.stringify(result.executionProgress);
assert.equal(serialized.includes("hook-secret-should-not-leak"), false);
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
assert.equal(serialized.includes("/Users/kris"), false);
assert.equal(serialized.includes("private-hook.toml"), false);
assert.equal(serialized.includes("internal hook output"), false);
assert.equal(serialized.includes("private hook result"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_HOOK_EVENTS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_HOOK_EVENTS = previous;
}
}
});
test("codex app-server runner maps item plan and reasoning summary without leaking raw reasoning", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_REASONING_PLAN_EVENTS;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_REASONING_PLAN_EVENTS = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-reasoning-plan-events",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "检查 Codex plan 和 reasoning item",
});
assert.equal(result.status, "completed");
assert.deepEqual(result.executionProgress.steps, [
{
id: "plan-item-1",
text: "回读官方 App Server item 协议",
status: "pending",
},
{
id: "plan-item-2",
text: "补充 reasoning summary 安全映射",
status: "pending",
},
]);
assert.deepEqual(result.executionProgress.reasoningSummary, {
status: "completed",
summary: "确认只展示官方 summary不展示 raw content。",
});
const serialized = JSON.stringify(result.executionProgress);
assert.equal(serialized.includes("raw hidden reasoning"), false);
assert.equal(serialized.includes("raw hidden chain of thought"), false);
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
assert.equal(serialized.includes("reasoning-secret-should-not-leak"), false);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_REASONING_PLAN_EVENTS;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EMIT_REASONING_PLAN_EVENTS = previous;
}
}
});
test("codex app-server runner bridges source thread context into target thread through inject_items", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_INTER_THREAD;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_INTER_THREAD = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-inter-thread",
taskType: "conversation_reply",
intentCategory: "thread_collaboration",
sourceCodexThreadRef: "source-thread-1",
sourceThreadDisplayName: "源线程",
targetCodexThreadRef: "target-thread-2",
targetThreadDisplayName: "目标线程",
targetCodexFolderRef: repoRoot,
executionPrompt: "请基于源线程结论继续实现",
});
assert.equal(result.status, "completed");
assert.equal(result.threadId, "target-thread-2");
assert.equal(result.interThreadBroker.sourceThreadId, "source-thread-1");
assert.equal(result.interThreadBroker.targetThreadId, "target-thread-2");
assert.equal(result.interThreadBroker.injectedItemCount, 1);
assert.match(result.replyBody, /INTER_THREAD_INJECTED/);
assert.match(result.replyBody, /源线程最近结论:优先使用 app-server 协议/);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_INTER_THREAD;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_INTER_THREAD = previous;
}
}
});
test("codex app-server runner steers an active turn when a target turn id is present", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_STEER;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_STEER = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-turn-steer",
taskType: "conversation_reply",
targetCodexThreadRef: "active-thread-1",
targetCodexTurnId: "active-turn-1",
targetCodexFolderRef: repoRoot,
executionPrompt: "先暂停写入,改为只做 diff 检查",
});
assert.equal(result.status, "completed");
assert.equal(result.threadId, "active-thread-1");
assert.equal(result.turnId, "active-turn-1");
assert.equal(result.turnControl, "steer");
assert.equal(result.replyBody, "STEERED:先暂停写入,改为只做 diff 检查");
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_STEER;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_STEER = previous;
}
}
});
test("codex app-server runner interrupts the active turn when the task is canceled while running", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_WAIT_FOR_INTERRUPT;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_WAIT_FOR_INTERRUPT = "1";
try {
const interruptChecks = [];
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(
{
...runnerConfig,
interruptPollIntervalMs: 10,
shouldInterruptActiveTurn: async (activeTurn) => {
interruptChecks.push(activeTurn);
return activeTurn.taskId === "task-turn-interrupt";
},
},
{
taskId: "task-turn-interrupt",
taskType: "conversation_reply",
targetCodexThreadRef: "active-thread-2",
targetCodexFolderRef: repoRoot,
executionPrompt: "继续执行一个可取消的长任务",
},
);
assert.equal(result.status, "interrupted");
assert.equal(result.threadId, "active-thread-2");
assert.equal(result.turnId, "turn-fixture");
assert.equal(result.turnControl, "interrupt");
assert.equal(result.replyBody, "已按用户要求中断当前 Codex turn。");
assert.equal(interruptChecks[0]?.taskId, "task-turn-interrupt");
assert.equal(interruptChecks[0]?.threadId, "active-thread-2");
assert.equal(interruptChecks[0]?.turnId, "turn-fixture");
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_WAIT_FOR_INTERRUPT;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_WAIT_FOR_INTERRUPT = previous;
}
}
});
test("codex app-server config exposes ws transport without mutating stdio defaults", () => {
const previousTransport = process.env.BOSS_CODEX_APP_SERVER_TRANSPORT;
const previousUrl = process.env.BOSS_CODEX_APP_SERVER_URL;
process.env.BOSS_CODEX_APP_SERVER_TRANSPORT = "ws";
process.env.BOSS_CODEX_APP_SERVER_URL = "ws://127.0.0.1:4500";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
});
assert.equal(runnerConfig.transport, "ws");
assert.equal(runnerConfig.url, "ws://127.0.0.1:4500");
assert.equal(runnerConfig.args[0], "app-server");
} finally {
if (previousTransport === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_TRANSPORT;
} else {
process.env.BOSS_CODEX_APP_SERVER_TRANSPORT = previousTransport;
}
if (previousUrl === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_URL;
} else {
process.env.BOSS_CODEX_APP_SERVER_URL = previousUrl;
}
}
});
test("codex app-server runner connects to a ws app-server endpoint without spawning stdio", async () => {
const fixture = await createCodexAppServerWebSocketFixture();
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerTransport: "ws",
codexAppServerUrl: fixture.url,
codexAppServerCommand: "definitely-not-a-real-codex-app-server",
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
masterAgentModel: "gpt-5.4",
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-ws-app-server",
taskType: "conversation_reply",
targetCodexThreadRef: "ws-thread-1",
targetCodexFolderRef: repoRoot,
executionPrompt: "用 ws 路径回复",
});
assert.equal(result.status, "completed");
assert.equal(result.transport, "ws");
assert.equal(result.threadId, "ws-thread-1");
assert.equal(result.turnId, "ws-turn-fixture");
assert.equal(result.replyBody, "WS_APP_SERVER_REPLY:用 ws 路径回复");
assert.equal(result.executionProgress.steps[0].text, "通过 WebSocket 接入 Codex App Server");
} finally {
await fixture.close();
}
});
test("codex app-server runner sends bearer auth during ws handshake", async () => {
const fixture = await createCodexAppServerWebSocketFixture();
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerTransport: "ws",
codexAppServerUrl: fixture.url,
codexAppServerAuthToken: "boss-ws-token",
codexAppServerCommand: "definitely-not-a-real-codex-app-server",
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-ws-auth",
taskType: "conversation_reply",
targetCodexThreadRef: "ws-auth-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "验证 ws 鉴权",
});
assert.equal(result.status, "completed");
assert.equal(result.replyBody, "WS_APP_SERVER_REPLY:验证 ws 鉴权");
assert.equal(fixture.getLastAuthorization(), "Bearer boss-ws-token");
} finally {
await fixture.close();
}
});
test("codex app-server runner can load bearer auth from a local token file", async () => {
const runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-codex-app-server-auth-"));
const tokenPath = path.join(runtimeRoot, "ws-token.txt");
await writeFile(tokenPath, "boss-token-from-file\n", "utf8");
const fixture = await createCodexAppServerWebSocketFixture();
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerTransport: "ws",
codexAppServerUrl: fixture.url,
codexAppServerAuthTokenFile: tokenPath,
codexAppServerCommand: "definitely-not-a-real-codex-app-server",
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-ws-auth-file",
taskType: "conversation_reply",
targetCodexThreadRef: "ws-auth-file-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "验证 token file",
});
assert.equal(result.status, "completed");
assert.equal(fixture.getLastAuthorization(), "Bearer boss-token-from-file");
} finally {
await fixture.close();
await rm(runtimeRoot, { recursive: true, force: true });
}
});
test("codex app-server runner connects to a unix socket app-server endpoint", async () => {
const runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-codex-app-server-unix-"));
const fixture = await createCodexAppServerWebSocketFixture({
unixSocketPath: path.join(runtimeRoot, "codex-app-server.sock"),
});
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerTransport: "unix",
codexAppServerUrl: fixture.url,
codexAppServerCommand: "definitely-not-a-real-codex-app-server",
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-unix-app-server",
taskType: "conversation_reply",
targetCodexThreadRef: "unix-thread-1",
targetCodexFolderRef: repoRoot,
executionPrompt: "用 unix socket 回复",
});
assert.equal(result.status, "completed");
assert.equal(result.transport, "unix");
assert.equal(result.threadId, "unix-thread-1");
assert.equal(result.replyBody, "WS_APP_SERVER_REPLY:用 unix socket 回复");
} finally {
await fixture.close();
await rm(runtimeRoot, { recursive: true, force: true });
}
});
test("codex app-server runner retries transient overloaded JSON-RPC requests", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_OVERLOAD_ON_TURN_START;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_OVERLOAD_ON_TURN_START = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-overloaded",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "拥塞后重试",
});
assert.equal(result.status, "completed");
assert.equal(result.replyBody, "APP_SERVER_REPLY:拥塞后重试");
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_OVERLOAD_ON_TURN_START;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_OVERLOAD_ON_TURN_START = previous;
}
}
});
test("codex app-server runner rolls back a thread without starting a new turn or leaking history", async () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-thread-rollback",
taskType: "conversation_reply",
intentCategory: "thread_rollback",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
rollbackNumTurns: 2,
executionPrompt: "回滚最近 2 轮 Codex 线程历史。",
});
assert.equal(result.status, "completed");
assert.equal(result.threadId, "019d-app-server-thread");
assert.equal(result.turnControl, "rollback");
assert.equal(result.rollback?.numTurns, 2);
assert.match(result.replyBody, /已回滚 Codex 线程最近 2 轮/);
assert.match(result.replyBody, /不会自动还原本地文件变更/);
assert.doesNotMatch(result.replyBody, /private rollback turn text/);
assert.equal(result.turnId, undefined);
});
test("codex app-server runner starts thread compaction without starting a normal turn or leaking history", async () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-thread-compact",
taskType: "conversation_reply",
intentCategory: "thread_compact",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "压缩当前 Codex 线程上下文。",
});
assert.equal(result.status, "completed");
assert.equal(result.threadId, "019d-app-server-thread");
assert.equal(result.turnControl, "compact");
assert.equal(result.compaction?.status, "completed");
assert.match(result.replyBody, /已发起 Codex 线程上下文压缩/);
assert.doesNotMatch(JSON.stringify(result), /context-compaction-secret-should-not-leak/);
assert.doesNotMatch(JSON.stringify(result), /private compaction summary should not leak/);
assert.equal(result.turnId, undefined);
});
test("codex app-server runner archives and unarchives a thread without starting a normal turn", async () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const archiveResult = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-thread-archive",
taskType: "conversation_reply",
intentCategory: "thread_archive",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
threadLifecycleAction: "archive",
executionPrompt: "归档当前 Codex 线程。",
});
const unarchiveResult = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-thread-unarchive",
taskType: "conversation_reply",
intentCategory: "thread_unarchive",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
threadLifecycleAction: "unarchive",
executionPrompt: "恢复当前 Codex 线程。",
});
assert.equal(archiveResult.status, "completed");
assert.equal(archiveResult.threadId, "019d-app-server-thread");
assert.equal(archiveResult.turnControl, "archive");
assert.match(archiveResult.replyBody, /已归档 Codex 线程/);
assert.equal(archiveResult.turnId, undefined);
assert.equal(unarchiveResult.status, "completed");
assert.equal(unarchiveResult.threadId, "019d-app-server-thread");
assert.equal(unarchiveResult.turnControl, "unarchive");
assert.match(unarchiveResult.replyBody, /已恢复 Codex 线程/);
assert.equal(unarchiveResult.turnId, undefined);
const serialized = JSON.stringify({ archiveResult, unarchiveResult });
assert.doesNotMatch(serialized, /thread-archive-secret-should-not-leak/);
assert.doesNotMatch(serialized, /thread-unarchive-secret-should-not-leak/);
assert.doesNotMatch(serialized, /private unarchived thread name should not leak/);
});
test("codex app-server runner renames a thread without starting a normal turn", async () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-thread-rename",
taskType: "conversation_reply",
intentCategory: "thread_rename",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
threadRenameName: "Boss 量产治理线程",
executionPrompt: "同步 Codex 线程名称。",
});
assert.equal(result.status, "completed");
assert.equal(result.threadId, "019d-app-server-thread");
assert.equal(result.turnControl, "rename");
assert.equal(result.threadRename?.name, "Boss 量产治理线程");
assert.match(result.replyBody, /已同步 Codex 线程名称/);
assert.equal(result.turnId, undefined);
assert.doesNotMatch(JSON.stringify(result), /thread-name-secret-should-not-leak/);
});
test("codex app-server runner syncs a thread goal without starting a normal turn", async () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-thread-goal-sync",
taskType: "conversation_reply",
intentCategory: "thread_goal_sync",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
threadGoalObjective: "完成 App Server 线程目标双向同步。",
threadGoalStatus: "active",
threadGoalTokenBudget: 120000,
executionPrompt: "同步 Codex 线程目标。",
});
assert.equal(result.status, "completed");
assert.equal(result.threadId, "019d-app-server-thread");
assert.equal(result.turnControl, "goal_sync");
assert.equal(result.threadGoal?.objective, "完成 App Server 线程目标双向同步。");
assert.equal(result.threadGoal?.status, "active");
assert.equal(result.threadGoal?.tokenBudget, 120000);
assert.match(result.replyBody, /已同步 Codex 线程目标/);
assert.equal(result.turnId, undefined);
assert.doesNotMatch(JSON.stringify(result), /thread-goal-secret-should-not-leak/);
});
test("codex app-server runner syncs thread git metadata without starting a normal turn", async () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-thread-metadata-sync",
taskType: "conversation_reply",
intentCategory: "thread_metadata_sync",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
threadMetadataGitInfo: {
sha: "0186ef7",
branch: "codex/wechat-native-ui-rollback",
originUrl: "https://git.hyzq.site/krisolo/boss.git",
},
executionPrompt: "同步 Codex 线程 Git 元数据。",
});
assert.equal(result.status, "completed");
assert.equal(result.threadId, "019d-app-server-thread");
assert.equal(result.turnControl, "metadata_sync");
assert.deepEqual(result.threadMetadata?.gitInfo, {
sha: "0186ef7",
branch: "codex/wechat-native-ui-rollback",
originUrl: "https://git.hyzq.site/krisolo/boss.git",
});
assert.match(result.replyBody, /已同步 Codex 线程 Git 元数据/);
assert.equal(result.turnId, undefined);
assert.doesNotMatch(JSON.stringify(result), /thread-metadata-secret-should-not-leak/);
});
test("codex app-server runner forks a thread without starting a normal turn", async () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-thread-fork",
taskType: "conversation_reply",
intentCategory: "thread_fork",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
threadForkEphemeral: false,
executionPrompt: "分叉当前 Codex 线程。",
});
assert.equal(result.status, "completed");
assert.equal(result.threadId, "019d-app-server-thread");
assert.equal(result.turnControl, "fork");
assert.equal(result.threadFork?.sourceThreadId, "019d-app-server-thread");
assert.equal(result.threadFork?.forkedThreadId, "019d-app-server-thread-fork");
assert.equal(result.threadFork?.forkedThreadName, "Forked working thread");
assert.equal(result.threadFork?.ephemeral, false);
assert.equal(result.turnId, undefined);
assert.doesNotMatch(JSON.stringify(result), /thread-fork-secret-should-not-leak/);
});
test("codex app-server runner stays disabled unless feature flag is explicit", () => {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
});
assert.equal(
shouldUseCodexAppServerTaskRunner(runnerConfig, {
taskType: "conversation_reply",
targetCodexThreadRef: "thread-disabled",
executionPrompt: "不会执行",
}),
false,
);
});
test("codex app-server runner fails fast when the server exits before turn completion", async () => {
const previous = process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EXIT_AFTER_TURN_START;
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EXIT_AFTER_TURN_START = "1";
try {
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
codexAppServerEnabled: true,
codexAppServerCommand: process.execPath,
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
codexAppServerWorkdir: repoRoot,
codexAppServerTimeoutMs: 5000,
});
const result = await executeCodexAppServerTask(runnerConfig, {
taskId: "task-app-server-exit",
taskType: "conversation_reply",
targetCodexThreadRef: "019d-app-server-thread",
targetCodexFolderRef: repoRoot,
executionPrompt: "不要等到超时",
});
assert.equal(result.status, "failed");
assert.equal(result.canFallbackToCli, false);
assert.match(result.errorMessage, /CODEX_APP_SERVER_EXITED:0/);
} finally {
if (previous === undefined) {
delete process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EXIT_AFTER_TURN_START;
} else {
process.env.BOSS_CODEX_APP_SERVER_FIXTURE_EXIT_AFTER_TURN_START = previous;
}
}
});