@ai-sdk/harness-codex 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/bridge/index.mjs +24 -65
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.js +76 -10
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/bridge/cli-relay.ts +6 -39
- package/src/bridge/index.ts +33 -57
- package/src/codex-bridge-protocol.ts +0 -1
- package/src/codex-harness.ts +102 -20
package/CHANGELOG.md
CHANGED
package/dist/bridge/index.mjs
CHANGED
|
@@ -412,7 +412,7 @@ function serialiseError(err) {
|
|
|
412
412
|
|
|
413
413
|
// src/bridge/index.ts
|
|
414
414
|
import { randomUUID } from "crypto";
|
|
415
|
-
import { writeFile as writeFile2 } from "fs/promises";
|
|
415
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
416
416
|
import { createServer } from "http";
|
|
417
417
|
|
|
418
418
|
// src/bridge/cli-relay.ts
|
|
@@ -454,31 +454,6 @@ if (!response.ok || payload.error) {
|
|
|
454
454
|
console.log(JSON.stringify(payload.result ?? payload, null, 2));
|
|
455
455
|
`;
|
|
456
456
|
}
|
|
457
|
-
function composeToolUsageInstructions({
|
|
458
|
-
tools,
|
|
459
|
-
cliShimPath
|
|
460
|
-
}) {
|
|
461
|
-
const lines = [
|
|
462
|
-
"## Host tools",
|
|
463
|
-
"",
|
|
464
|
-
"You have access to the following host-provided tools. To use one, run the following command via your built-in `bash` tool:",
|
|
465
|
-
"",
|
|
466
|
-
` node ${cliShimPath} <toolName> '<jsonInput>'`,
|
|
467
|
-
"",
|
|
468
|
-
"The script prints the JSON result to stdout. Do not invent another way to call these tools \u2014 only this CLI invocation will work. Pass the JSON input as a single-quoted argument.",
|
|
469
|
-
"For every user request that depends on a host-provided tool, run a separate CLI invocation for each needed tool call in the current turn before answering. Do not reuse previous tool results, and do not say you used a host tool unless the command has completed in the current turn.",
|
|
470
|
-
""
|
|
471
|
-
];
|
|
472
|
-
for (const tool of tools) {
|
|
473
|
-
lines.push(`### ${tool.name}`);
|
|
474
|
-
if (tool.description) lines.push(tool.description);
|
|
475
|
-
lines.push(
|
|
476
|
-
`Input schema: \`${JSON.stringify(tool.inputSchema ?? {})}\``,
|
|
477
|
-
""
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
return lines.join("\n");
|
|
481
|
-
}
|
|
482
457
|
function isToolRelayCommand({
|
|
483
458
|
command,
|
|
484
459
|
cliShimPath
|
|
@@ -497,14 +472,15 @@ function toCommonName(nativeName) {
|
|
|
497
472
|
return NATIVE_TO_COMMON[nativeName] ?? nativeName;
|
|
498
473
|
}
|
|
499
474
|
var args = parseArgs(argv.slice(2));
|
|
500
|
-
var workdir = args.workdir;
|
|
501
|
-
var bridgeStateDir =
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
475
|
+
var workdir = requireArg({ value: args.workdir, name: "--workdir" });
|
|
476
|
+
var bridgeStateDir = requireArg({
|
|
477
|
+
value: args.bridgeStateDir,
|
|
478
|
+
name: "--bridge-state-dir"
|
|
479
|
+
});
|
|
480
|
+
var cliShimDir = requireArg({
|
|
481
|
+
value: args.cliShimDir,
|
|
482
|
+
name: "--cli-shim-dir"
|
|
483
|
+
});
|
|
508
484
|
var bootstrapDir = args.bootstrapDir ?? workdir;
|
|
509
485
|
var codexSdk = codexSdkModule;
|
|
510
486
|
var threadState = { id: void 0 };
|
|
@@ -546,7 +522,8 @@ async function runTurn(start, turn) {
|
|
|
546
522
|
TOOL_RELAY_TOKEN: relayToken
|
|
547
523
|
}
|
|
548
524
|
};
|
|
549
|
-
cliShimPath = `${
|
|
525
|
+
cliShimPath = `${cliShimDir}/${CLI_SHIM_FILENAME}`;
|
|
526
|
+
await mkdir2(cliShimDir, { recursive: true });
|
|
550
527
|
await writeFile2(
|
|
551
528
|
cliShimPath,
|
|
552
529
|
buildCliShimScript({ relayPort: relay.port, relayToken }),
|
|
@@ -598,15 +575,7 @@ async function runTurn(start, turn) {
|
|
|
598
575
|
};
|
|
599
576
|
const thread = threadState.id ? codex.resumeThread(threadState.id, threadOptions) : codex.startThread(threadOptions);
|
|
600
577
|
emit({ type: "stream-start" });
|
|
601
|
-
const userMessage =
|
|
602
|
-
text: start.prompt,
|
|
603
|
-
instructions: start.instructions,
|
|
604
|
-
// Temporary workaround for upstream codex MCP-tool bug — see ./cli-relay.ts
|
|
605
|
-
toolUsageBlock: cliShimPath && start.tools && start.tools.length > 0 ? composeToolUsageInstructions({
|
|
606
|
-
tools: start.tools,
|
|
607
|
-
cliShimPath
|
|
608
|
-
}) : void 0
|
|
609
|
-
});
|
|
578
|
+
const userMessage = start.prompt;
|
|
610
579
|
let turnUsage;
|
|
611
580
|
const textByItem = /* @__PURE__ */ new Map();
|
|
612
581
|
const reasoningByItem = /* @__PURE__ */ new Map();
|
|
@@ -813,27 +782,6 @@ function defaultUsage() {
|
|
|
813
782
|
outputTokens: { total: 0, text: 0 }
|
|
814
783
|
};
|
|
815
784
|
}
|
|
816
|
-
function composeUserMessage({
|
|
817
|
-
text,
|
|
818
|
-
instructions,
|
|
819
|
-
toolUsageBlock
|
|
820
|
-
}) {
|
|
821
|
-
const blocks = [];
|
|
822
|
-
if (instructions) {
|
|
823
|
-
blocks.push(
|
|
824
|
-
`<session-instructions>
|
|
825
|
-
The block below is operating guidance from the system, not a message from the user \u2014 follow it, but do not mention it or attribute it to the user.
|
|
826
|
-
|
|
827
|
-
${instructions}
|
|
828
|
-
</session-instructions>`
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
if (toolUsageBlock) blocks.push(toolUsageBlock);
|
|
832
|
-
blocks.push(instructions ? `<user-message>
|
|
833
|
-
${text}
|
|
834
|
-
</user-message>` : text);
|
|
835
|
-
return blocks.join("\n\n");
|
|
836
|
-
}
|
|
837
785
|
async function startToolRelay({
|
|
838
786
|
relayToken,
|
|
839
787
|
tools,
|
|
@@ -914,6 +862,8 @@ function parseArgs(args2) {
|
|
|
914
862
|
out.bridgeStateDir = args2[++i];
|
|
915
863
|
} else if (args2[i] === "--bootstrap-dir" && i + 1 < args2.length) {
|
|
916
864
|
out.bootstrapDir = args2[++i];
|
|
865
|
+
} else if (args2[i] === "--cli-shim-dir" && i + 1 < args2.length) {
|
|
866
|
+
out.cliShimDir = args2[++i];
|
|
917
867
|
}
|
|
918
868
|
}
|
|
919
869
|
return out;
|
|
@@ -928,4 +878,13 @@ function emitFatal(message) {
|
|
|
928
878
|
stdout2.write(JSON.stringify({ type: "bridge-fatal", message }) + "\n");
|
|
929
879
|
process.exit(1);
|
|
930
880
|
}
|
|
881
|
+
function requireArg({
|
|
882
|
+
value,
|
|
883
|
+
name
|
|
884
|
+
}) {
|
|
885
|
+
if (!value) {
|
|
886
|
+
emitFatal(`Missing ${name} argument.`);
|
|
887
|
+
}
|
|
888
|
+
return value;
|
|
889
|
+
}
|
|
931
890
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../harness/src/bridge/index.ts","../../src/bridge/index.ts","../../src/bridge/cli-relay.ts"],"sourcesContent":["// Shared in-sandbox bridge runtime. Adapter `bridge.mjs` bundles re-bundle\n// this module (tsup inlines it; `ws` stays external and resolves from the\n// sandbox-installed node_modules). It owns everything generic to the bridge\n// transport — the WebSocket server, token auth, single-flight connection\n// replacement, the in-memory event log + monotonic `seq`, resume replay, and\n// the lifecycle/meta files. The adapter supplies only `onStart` (drive its\n// CLI/SDK and translate to wire events) and `onDetach` (its resume payload).\n\nimport { appendFile, mkdir, writeFile } from 'node:fs/promises';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { env as procEnv, pid, stdout } from 'node:process';\nimport { WebSocketServer, type WebSocket } from 'ws';\n\nexport type BridgeState = 'init' | 'waiting' | 'running' | 'draining' | 'done';\n\n/** Outbound turn event the adapter emits. `seq` is added by the runtime. */\nexport type BridgeEvent = Record<string, unknown> & { type: string };\n\nexport type BridgeDebugLevel = 'error' | 'warn' | 'info' | 'debug' | 'trace';\n\n/**\n * Per-session diagnostics config. The host resolves it from settings +\n * env and sends it on `start.debug`; the bridge gates console capture and\n * structured `debug-event`s on it. When disabled, nothing is captured or\n * emitted and no `seq` is consumed.\n */\nexport interface BridgeDebugConfig {\n enabled?: boolean;\n level?: BridgeDebugLevel;\n subsystems?: string[];\n}\n\nconst DEBUG_LEVEL_WEIGHT: Record<BridgeDebugLevel, number> = {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n trace: 4,\n};\n\n/** Exact-or-dotted-prefix subsystem match (`'bridge'` matches `'bridge.turn'`). */\nfunction subsystemMatches(\n filters: string[] | undefined,\n subsystem: string,\n): boolean {\n if (!filters || filters.length === 0) return true;\n return filters.some(\n filter => subsystem === filter || subsystem.startsWith(`${filter}.`),\n );\n}\n\nfunction formatBridgeError(err: unknown): {\n name?: string;\n message: string;\n stack?: string;\n} {\n if (err instanceof Error) {\n return { name: err.name, message: err.message, stack: err.stack };\n }\n return { message: String(err) };\n}\n\nfunction parseEnvList(value: string | undefined): string[] | undefined {\n if (!value) return undefined;\n const items = value\n .split(',')\n .map(item => item.trim())\n .filter(Boolean);\n return items.length > 0 ? items : undefined;\n}\n\nconst ENV_TRUTHY = new Set(['1', 'true', 'yes', 'on']);\n\n/**\n * Per-turn surface handed to {@link RunBridgeOptions.onStart}. The adapter\n * drives its runtime against these primitives; the runtime owns the transport.\n */\nexport interface BridgeTurn {\n /**\n * Emit a turn event to the host. Stamps a monotonic `seq`, appends to the\n * in-memory replay log, and sends to the live socket (best-effort — if the\n * host is mid-reconnect the event waits in the log and is replayed on\n * resume).\n */\n emit(event: BridgeEvent): void;\n\n /**\n * Register interest in a host-executed tool result and resolve when the\n * matching `tool-result` arrives. The adapter emits the `tool-call` event\n * itself (via {@link emit}) using the same `toolCallId`.\n */\n requestToolResult(\n toolCallId: string,\n ): Promise<{ output: unknown; isError?: boolean }>;\n\n /**\n * Register interest in a host approval decision and resolve when the matching\n * `tool-approval-response` arrives. The adapter emits the\n * `tool-approval-request` event itself using the same `approvalId`.\n */\n requestToolApproval(\n approvalId: string,\n ): Promise<{ approved: boolean; reason?: string }>;\n\n /**\n * Live queue of mid-turn user messages. The runtime pushes inbound\n * `user-message` text here; the adapter drains it as its runtime accepts\n * interactive input.\n */\n readonly pendingUserMessages: string[];\n\n /** Aborts when the host sends `abort`. */\n readonly abortSignal: AbortSignal;\n\n /** True for the first turn since this bridge process started. */\n readonly firstTurn: boolean;\n\n /**\n * Emit a structured diagnostic. Gated by the session's debug level +\n * subsystem filter; a no-op when diagnostics are disabled. Adapters use this\n * for runtime-level instrumentation; raw `console.*` output is captured and\n * forwarded automatically.\n */\n bridgeLog(input: {\n level?: BridgeDebugLevel;\n subsystem: string;\n message: string;\n attrs?: Record<string, unknown>;\n error?: unknown;\n }): void;\n}\n\nexport interface RunBridgeOptions<TStart extends { type: 'start' }> {\n /** Identifier written into `bridge-meta.json` (`'claude-code'` / `'codex'`). */\n bridgeType: string;\n /** Directory for `bridge-meta.json` / `start-config.json`. Created if absent. */\n bridgeStateDir: string;\n /** Drive one prompt turn. Rejections surface to the host as an `error` event. */\n onStart(start: TStart, turn: BridgeTurn): Promise<void>;\n /** Produce the adapter-defined resume payload for a `detach`. Defaults to `{}`. */\n onDetach?(): unknown | Promise<unknown>;\n /** WS port. Defaults to `BRIDGE_WS_PORT` env (0 = OS-assigned). */\n port?: number;\n /** Auth token. Defaults to `BRIDGE_CHANNEL_TOKEN` env. */\n token?: string;\n /** Called with the bound port once the server is listening. */\n onListening?(port: number): void;\n /**\n * Tear the process down after a `shutdown` / `detach`. Defaults to closing\n * the server and calling `process.exit(0)`. Overridable for tests.\n */\n onExit?(): void;\n}\n\ntype InboundControl =\n | {\n type: 'tool-result';\n toolCallId: string;\n output: unknown;\n isError?: boolean;\n }\n | {\n type: 'tool-approval-response';\n approvalId: string;\n approved: boolean;\n reason?: string;\n }\n | { type: 'user-message'; text: string }\n | { type: 'abort' }\n | { type: 'shutdown' }\n | { type: 'detach' }\n | { type: 'resume'; lastSeenEventId: number };\n\nconst WS_OPEN = 1;\n\n/**\n * Boot the bridge: bind the WebSocket server, announce `bridge-ready`, and\n * service host connections for the lifetime of the process. Resolves once the\n * server is listening; the process then stays alive on the server until a\n * `shutdown` / `detach` exits it.\n */\nexport interface BridgeHandle {\n /** The port the WebSocket server bound to. */\n readonly port: number;\n /** Close the WebSocket server. Does not call `process.exit`. */\n close(): Promise<void>;\n}\n\nexport async function runBridge<TStart extends { type: 'start' }>(\n options: RunBridgeOptions<TStart>,\n): Promise<BridgeHandle> {\n const { bridgeType, bridgeStateDir, onStart, onDetach } = options;\n const expectedToken = options.token ?? procEnv.BRIDGE_CHANNEL_TOKEN ?? '';\n const bridgeWsPort =\n options.port ?? parseInt(procEnv.BRIDGE_WS_PORT ?? '0', 10);\n\n const bridgeMetaPath = `${bridgeStateDir}/bridge-meta.json`;\n const startConfigPath = `${bridgeStateDir}/start-config.json`;\n const rerunStartConfigPath = `${bridgeStateDir}/rerun-start-config.json`;\n const eventLogPath = `${bridgeStateDir}/event-log.ndjson`;\n\n try {\n await mkdir(bridgeStateDir, { recursive: true });\n } catch {\n // Best-effort; the bridge still runs without its state files.\n }\n\n // ─── mutable runtime state ──────────────────────────────────────────\n let currentBoundPort = 0;\n let currentTurnState: BridgeState = 'init';\n let activeSocket: WebSocket | undefined;\n let isFirstTurn = true;\n let turnAbort: AbortController | undefined;\n let currentUserMessages: string[] | undefined;\n\n // Diagnostics. Resolved per turn from `start.debug` with a sandbox-side\n // env fallback; gates console capture + structured `debug-event`s.\n let debugConfig: BridgeDebugConfig | undefined;\n let consoleCaptureInstalled = false;\n const envDebugEnabled = ENV_TRUTHY.has(\n (procEnv.HARNESS_DEBUG ?? '').toLowerCase(),\n );\n\n // Replay log. `seq` is monotonic across the whole process — never reset —\n // because the host's `SandboxChannel` cursor (`lastSeenEventId`) lives across\n // turns. The log *contents* are cleared at the start of each turn to bound\n // memory; the just-finished turn stays replayable until the next `start`.\n let seqCounter = 0;\n let eventLog: Array<{ seq: number; line: string }> = [];\n\n /*\n * Disk mirror of the in-memory replay log. The in-memory log is lost when the\n * bridge process dies; the on-disk `event-log.ndjson` survives in the sandbox\n * filesystem so a respawned bridge (started with `BRIDGE_REPLAY_FROM_DISK=1`)\n * can reload the just-interrupted turn and serve a host's resume cursor —\n * `replay` recovery. Writes are batched on `setImmediate` (single-flight via\n * `flushPromise`) to keep `emit` off the disk hot path.\n */\n let diskBuffer = '';\n let flushPromise: Promise<void> | null = null;\n\n const flushEventsToDisk = async (): Promise<void> => {\n while (diskBuffer.length > 0) {\n const buf = diskBuffer;\n diskBuffer = '';\n await appendFile(eventLogPath, buf).catch(() => {\n // Best-effort crash-recovery mirror; the in-memory log is the source of\n // truth for the live connection.\n });\n }\n };\n\n const scheduleEventFlush = (): void => {\n if (flushPromise) return;\n flushPromise = new Promise<void>(resolve => {\n setImmediate(() => {\n void flushEventsToDisk().finally(resolve);\n });\n }).finally(() => {\n flushPromise = null;\n if (diskBuffer.length > 0) {\n scheduleEventFlush();\n }\n });\n };\n\n const flushPendingEventsToDisk = async (): Promise<void> => {\n if (diskBuffer.length > 0 && !flushPromise) {\n scheduleEventFlush();\n }\n // Await each in-flight flush, re-reading `flushPromise` after every await\n // since a fresh flush may have been scheduled for buffer that arrived while\n // we waited.\n let inFlight = flushPromise;\n while (inFlight) {\n await inFlight;\n inFlight = flushPromise;\n }\n };\n\n /*\n * When respawned for `replay`, reload the previous turn's log from disk before\n * accepting any connection so the very first `resume{lastSeenEventId}` can be\n * served the tail (including the terminal `finish`). The seq counter is\n * restored to the last persisted seq so it stays aligned with the host's\n * long-lived cursor. The file is NOT truncated in this mode — only a fresh\n * `start` (next turn) clears it.\n */\n const replayFromDisk = procEnv.BRIDGE_REPLAY_FROM_DISK === '1';\n if (replayFromDisk && existsSync(eventLogPath)) {\n try {\n const lines = readFileSync(eventLogPath, 'utf8')\n .split('\\n')\n .map(line => line.trim())\n .filter(Boolean);\n eventLog = lines.map(line => ({\n seq: (JSON.parse(line) as { seq: number }).seq,\n line,\n }));\n seqCounter = eventLog.at(-1)?.seq ?? 0;\n } catch {\n // Corrupt/partial log: fall back to an empty log; the host then degrades\n // to `rerun` instead of replaying a malformed tail.\n eventLog = [];\n seqCounter = 0;\n }\n }\n\n const pendingToolResults = new Map<\n string,\n (output: { output: unknown; isError?: boolean }) => void\n >();\n const pendingToolApprovals = new Map<\n string,\n (response: { approved: boolean; reason?: string }) => void\n >();\n\n // ─── persistence (best-effort meta + start config) ──────────────────\n const writeBridgeMeta = async (state: BridgeState): Promise<void> => {\n try {\n await writeFile(\n bridgeMetaPath,\n JSON.stringify({\n type: bridgeType,\n port: currentBoundPort,\n state,\n pid,\n }),\n );\n } catch {\n // Best-effort resilience metadata; not load-bearing for the active turn.\n }\n };\n\n const writeStartConfig = async (start: unknown): Promise<void> => {\n try {\n const serialized = JSON.stringify(start);\n await writeFile(startConfigPath, serialized);\n // Frozen copy: written once, restored over start-config.json by future\n // rerun-mode recovery to re-run the original turn from scratch.\n if (!existsSync(rerunStartConfigPath)) {\n await writeFile(rerunStartConfigPath, serialized);\n }\n } catch {\n // Best-effort.\n }\n };\n\n // ─── wire send + replay ─────────────────────────────────────────────\n const sendControl = (msg: Record<string, unknown>): void => {\n if (activeSocket?.readyState === WS_OPEN) {\n try {\n activeSocket.send(JSON.stringify(msg));\n } catch {\n // best-effort\n }\n }\n };\n\n const emit = (event: BridgeEvent): void => {\n const seq = ++seqCounter;\n const line = JSON.stringify({ ...event, seq });\n eventLog.push({ seq, line });\n diskBuffer += `${line}\\n`;\n scheduleEventFlush();\n if (activeSocket?.readyState === WS_OPEN) {\n try {\n activeSocket.send(line);\n } catch {\n // Send is best-effort: a dropped socket leaves the event in the log,\n // replayed once the host reconnects and sends `resume`.\n }\n }\n };\n\n const replay = (ws: WebSocket, afterSeq: number): void => {\n for (const entry of eventLog) {\n if (entry.seq > afterSeq && ws.readyState === WS_OPEN) {\n ws.send(entry.line);\n }\n }\n };\n\n // ─── diagnostics ──────────────────────────────────────────────\n const shouldEmitDebugEvent = (\n level: BridgeDebugLevel,\n subsystem: string,\n ): boolean => {\n if (!debugConfig?.enabled) return false;\n const threshold = debugConfig.level ?? 'debug';\n if (DEBUG_LEVEL_WEIGHT[level] > DEBUG_LEVEL_WEIGHT[threshold]) return false;\n return subsystemMatches(debugConfig.subsystems, subsystem);\n };\n\n /*\n * Forward sandbox console output. We line-buffer the original writers (kept so\n * output still reaches the real fds) and emit one `sandbox-log` per complete\n * line. `emit` never writes to stdout/stderr, so there is no recursion.\n * Installed lazily the first time a turn enables diagnostics; once installed,\n * capture is gated per-write on `debugConfig.enabled` so a later turn can\n * disable it. Console capture is independent of the subsystem/level filter.\n */\n const rawStdoutWrite = process.stdout.write.bind(process.stdout);\n const rawStderrWrite = process.stderr.write.bind(process.stderr);\n const installConsoleCapture = (): void => {\n if (consoleCaptureInstalled) return;\n consoleCaptureInstalled = true;\n const buffers: { stdout: string; stderr: string } = {\n stdout: '',\n stderr: '',\n };\n const patch =\n (stream: 'stdout' | 'stderr', raw: typeof process.stdout.write) =>\n (chunk: unknown, encoding?: unknown, cb?: unknown): boolean => {\n if (debugConfig?.enabled) {\n try {\n const enc = typeof encoding === 'string' ? encoding : 'utf8';\n const text =\n typeof chunk === 'string'\n ? chunk\n : Buffer.from(chunk as Uint8Array).toString(\n enc as BufferEncoding,\n );\n const combined = buffers[stream] + text.replace(/\\r\\n/g, '\\n');\n const parts = combined.split('\\n');\n buffers[stream] = parts.pop() ?? '';\n for (const line of parts) {\n const trimmed = line.replace(/\\s+$/, '');\n if (trimmed) {\n emit({\n type: 'sandbox-log',\n source: bridgeType,\n stream,\n line: trimmed,\n });\n }\n }\n } catch {\n // Never let capture break real output.\n }\n }\n return (raw as (c: unknown, e?: unknown, cb?: unknown) => boolean)(\n chunk,\n encoding,\n cb,\n );\n };\n process.stdout.write = patch(\n 'stdout',\n rawStdoutWrite,\n ) as typeof process.stdout.write;\n process.stderr.write = patch(\n 'stderr',\n rawStderrWrite,\n ) as typeof process.stderr.write;\n };\n\n // ─── inbound routing ────────────────────────────────────────────────\n const handleInbound = async (\n msg: TStart | InboundControl,\n ws: WebSocket,\n ): Promise<void> => {\n switch (msg.type) {\n case 'start': {\n const firstTurn = isFirstTurn;\n isFirstTurn = false;\n eventLog = []; // clear previous turn; keep seqCounter monotonic\n // Mirror the in-memory clear to disk: the log tracks only the current\n // turn. Discard any unflushed tail from the prior turn first.\n diskBuffer = '';\n void writeFile(eventLogPath, '').catch(() => {});\n turnAbort = new AbortController();\n currentTurnState = 'running';\n void writeStartConfig(msg);\n void writeBridgeMeta('running');\n const startDebug = (msg as { debug?: BridgeDebugConfig }).debug;\n debugConfig = {\n enabled: startDebug?.enabled ?? envDebugEnabled,\n level:\n startDebug?.level ??\n (procEnv.HARNESS_DEBUG_LEVEL as BridgeDebugLevel | undefined),\n subsystems:\n startDebug?.subsystems ??\n parseEnvList(procEnv.HARNESS_DEBUG_SUBSYSTEMS),\n };\n if (debugConfig.enabled) {\n installConsoleCapture();\n }\n const turn: BridgeTurn = {\n emit,\n requestToolResult: toolCallId =>\n new Promise(resolve => {\n pendingToolResults.set(toolCallId, resolve);\n }),\n requestToolApproval: approvalId =>\n new Promise(resolve => {\n pendingToolApprovals.set(approvalId, resolve);\n }),\n pendingUserMessages: [],\n abortSignal: turnAbort.signal,\n firstTurn,\n bridgeLog: input => {\n const level = input.level ?? 'debug';\n if (!shouldEmitDebugEvent(level, input.subsystem)) return;\n emit({\n type: 'debug-event',\n level,\n subsystem: input.subsystem,\n message: input.message,\n ...(input.attrs ? { attrs: input.attrs } : {}),\n ...(input.error !== undefined\n ? { error: formatBridgeError(input.error) }\n : {}),\n });\n },\n };\n currentUserMessages = turn.pendingUserMessages;\n try {\n await onStart(msg as TStart, turn);\n } catch (err) {\n emit({ type: 'error', error: serialiseError(err) });\n } finally {\n currentTurnState = 'waiting';\n void writeBridgeMeta('waiting');\n }\n return;\n }\n case 'tool-result': {\n const resolver = pendingToolResults.get(msg.toolCallId);\n if (resolver) {\n pendingToolResults.delete(msg.toolCallId);\n resolver({ output: msg.output, isError: msg.isError });\n }\n return;\n }\n case 'tool-approval-response': {\n const resolver = pendingToolApprovals.get(msg.approvalId);\n if (resolver) {\n pendingToolApprovals.delete(msg.approvalId);\n resolver({ approved: msg.approved, reason: msg.reason });\n }\n return;\n }\n case 'user-message':\n currentUserMessages?.push(msg.text);\n return;\n case 'abort':\n turnAbort?.abort();\n return;\n case 'resume':\n replay(ws, msg.lastSeenEventId);\n return;\n case 'shutdown':\n currentTurnState = 'done';\n void writeBridgeMeta('done');\n drainThenExit(ws, 1000, 'shutdown');\n return;\n case 'detach': {\n currentTurnState = 'done';\n void writeBridgeMeta('done');\n const data = (await onDetach?.()) ?? {};\n sendControl({ type: 'bridge-detach', data });\n drainThenExit(ws, 1000, 'detach');\n return;\n }\n }\n };\n\n // ─── server ─────────────────────────────────────────────────────────\n void writeBridgeMeta('init');\n\n const wss = new WebSocketServer({ port: bridgeWsPort, host: '0.0.0.0' });\n\n const exit = (): void => {\n if (options.onExit) {\n options.onExit();\n return;\n }\n wss.close(() => process.exit(0));\n setTimeout(() => process.exit(0), 1000).unref();\n };\n\n const drainThenExit = (ws: WebSocket, code: number, reason: string): void => {\n const start = Date.now();\n const tick = (): void => {\n const drained = ws.bufferedAmount === 0 || ws.readyState !== WS_OPEN;\n if (drained || Date.now() - start >= 5_000) {\n // Flush the on-disk log so a clean shutdown/detach leaves a complete\n // event-log.ndjson for any later replay recovery.\n void flushPendingEventsToDisk().finally(() => {\n try {\n ws.close(code, reason);\n } finally {\n exit();\n }\n });\n return;\n }\n setTimeout(tick, 10).unref();\n };\n tick();\n };\n\n wss.on('listening', () => {\n const addr = wss.address();\n currentBoundPort = typeof addr === 'object' && addr ? addr.port : 0;\n currentTurnState = 'waiting';\n void writeBridgeMeta('waiting');\n stdout.write(\n JSON.stringify({\n type: 'bridge-ready',\n port: currentBoundPort,\n }) + '\\n',\n );\n options.onListening?.(currentBoundPort);\n });\n\n wss.on('connection', (ws: WebSocket, req: { url?: string }) => {\n const url = new URL(req.url ?? '/', 'http://localhost');\n if (url.searchParams.get('agent_bridge_token') !== expectedToken) {\n ws.close(1008, 'unauthorized');\n return;\n }\n\n // Single-flight: a fresh authorized connection *replaces* the active one\n // (the host reconnecting after a drop). The previous socket's close is a\n // no-op below because it is no longer `activeSocket`.\n activeSocket = ws;\n\n // Announce liveness the instant we accept. Some sandbox runtimes complete\n // the host-side WS handshake before the connection is forwarded here; the\n // host waits for this frame before sending `start`/`resume`.\n sendControl({\n type: 'bridge-hello',\n state: currentTurnState,\n lastSeq: seqCounter,\n });\n\n ws.on('message', (raw: ArrayBufferLike | string) => {\n let parsed: TStart | InboundControl;\n try {\n const text =\n typeof raw === 'string' ? raw : Buffer.from(raw).toString('utf8');\n parsed = JSON.parse(text) as TStart | InboundControl;\n } catch (err) {\n sendControl({\n type: 'error',\n error: `protocol parse error: ${(err as Error).message}`,\n });\n return;\n }\n void handleInbound(parsed, ws);\n });\n\n ws.on('close', () => {\n // Only the *current* socket's close matters. A stale socket (already\n // replaced by a reconnect) closing is a no-op. Crucially we do NOT abort\n // the in-flight turn — it keeps running and its events accumulate in the\n // log for replay when the host reconnects.\n if (activeSocket === ws) {\n activeSocket = undefined;\n }\n });\n\n ws.on('error', () => {\n // 'close' follows; nothing to do beyond keeping the process alive.\n });\n });\n\n // Surface bridge-internal crashes to the host instead of dying silently.\n process.on('uncaughtException', err => {\n emit({ type: 'error', error: serialiseError(err) });\n });\n process.on('unhandledRejection', err => {\n emit({ type: 'error', error: serialiseError(err) });\n });\n\n await new Promise<void>((resolve, reject) => {\n if (wss.address() != null) {\n resolve();\n return;\n }\n\n wss.once('listening', resolve);\n wss.once('error', reject);\n });\n\n return {\n port: currentBoundPort,\n close: () =>\n new Promise<void>(resolve => {\n wss.close(() => resolve());\n }),\n };\n}\n\nfunction serialiseError(err: unknown): unknown {\n if (err instanceof Error) {\n return { name: err.name, message: err.message, stack: err.stack };\n }\n return err;\n}\n","// Long-running process that runs alongside the `codex` CLI in the sandbox.\n// The generic transport — WebSocket server, token auth, single-flight\n// reconnect, the in-memory event log + `seq`, resume replay, and the\n// lifecycle/meta files — lives in the shared `@ai-sdk/harness/bridge` runtime.\n// This file supplies only the Codex-specific turn driver.\n//\n// Host-defined tools are routed through an HTTP relay bound to\n// `127.0.0.1:0` with bearer-token auth. The codex CLI spawns\n// `host-tool-mcp.mjs` (shipped alongside this file) as a stdio MCP server;\n// the shim POSTs each tool call to the relay, which emits `tool-call` to\n// the host and waits for the matching `tool-result`.\n\nimport {\n runBridge,\n type BridgeEvent,\n type BridgeTurn,\n} from '@ai-sdk/harness/bridge';\nimport type { HarnessV1BuiltinToolName } from '@ai-sdk/harness';\nimport type { StartMessage } from '../codex-bridge-protocol';\nimport { randomUUID } from 'node:crypto';\nimport { writeFile } from 'node:fs/promises';\nimport { createServer, type Server } from 'node:http';\n// Temporary workaround for upstream codex MCP-tool bug — see ./cli-relay.ts\nimport {\n CLI_SHIM_FILENAME,\n buildCliShimScript,\n composeToolUsageInstructions,\n isToolRelayCommand,\n} from './cli-relay';\nimport { argv, env as procEnv, stdout } from 'node:process';\n\n/*\n * CONSTRAINT — the third-party imports below are NEVER bundled into the\n * compiled `bridge/index.mjs`. They are declared `external` in\n * tsup.config.ts and resolved at runtime from the node_modules that this\n * bridge installs *inside the sandbox* from `src/bridge/package.json` (and\n * its pinned `pnpm-lock.yaml`). That bridge package.json — NOT this host\n * package — is the single source of truth for these packages and their\n * versions; the published `@ai-sdk/harness-codex` package does not provide\n * them at runtime.\n *\n * When adding or changing a third-party import here you MUST keep all three\n * in sync, or the bridge will either get the dependency bundled in or fail\n * to resolve it in the sandbox:\n * 1. the import statement below,\n * 2. the `external` array in tsup.config.ts, and\n * 3. the dependency entry in `src/bridge/package.json`.\n */\nimport * as codexSdkModule from '@openai/codex-sdk';\n\n/*\n * Native Codex tool name → cross-harness common name. Tools outside this map\n * (e.g. MCP tools the model invokes by name) have no common equivalent; their\n * native name is forwarded as-is on `tool-call` events.\n */\nconst NATIVE_TO_COMMON: Readonly<Record<string, HarnessV1BuiltinToolName>> = {\n shell: 'bash',\n web_search: 'webSearch',\n};\n\nfunction toCommonName(nativeName: string): HarnessV1BuiltinToolName | string {\n return NATIVE_TO_COMMON[nativeName] ?? nativeName;\n}\n\nconst args = parseArgs(argv.slice(2));\nconst workdir = args.workdir;\nconst bridgeStateDir = args.bridgeStateDir;\nif (!workdir) {\n emitFatal('Missing --workdir argument.');\n}\nif (!bridgeStateDir) {\n emitFatal('Missing --bridge-state-dir argument.');\n}\nconst bootstrapDir = args.bootstrapDir ?? workdir;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst codexSdk = codexSdkModule as any;\n\n// Codex thread id — survives across turns within this bridge process and is\n// returned to the host on `detach` so a future process can resume the thread.\nconst threadState: { id: string | undefined } = { id: undefined };\n\nawait runBridge<StartMessage>({\n bridgeType: 'codex',\n bridgeStateDir,\n onStart: runTurn,\n onDetach: () => (threadState.id ? { threadId: threadState.id } : {}),\n});\n\ntype Emit = (msg: Record<string, unknown>) => void;\n\nasync function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {\n const emit: Emit = msg => turn.emit(msg as BridgeEvent);\n\n // Cross-process resume: the host carries the threadId we returned on detach.\n // Seed `threadState.id` so the codex SDK call below takes the `resumeThread`\n // branch.\n if (\n typeof start.resumeThreadId === 'string' &&\n start.resumeThreadId.length > 0\n ) {\n threadState.id = start.resumeThreadId;\n }\n\n /*\n * Known limitation: codex CLI does not currently surface MCP tools to the\n * model in `codex exec --experimental-json` mode (the path the\n * `@openai/codex-sdk` uses). The MCP handshake completes and `tools/list`\n * returns the host tool, but codex never registers it as a model-callable\n * function. Built-in MCP-resource accessors (`list_mcp_resources` etc.) are\n * exposed; tools are not. Tracked upstream at\n * https://github.com/openai/codex/issues/19425.\n *\n * Until that's fixed, host tools are made available to the model via a\n * separate CLI-relay workaround (see `./cli-relay.ts`). The MCP server\n * config below is kept so that the day codex starts exposing MCP tools\n * properly, host tools work both ways. Three hookpoints in this file\n * (writeFile for the shim, composeUserMessage's toolUsageBlock, and the\n * isToolRelayCommand filter in the event loop) implement the workaround\n * and can be removed once the upstream bug is fixed.\n */\n const mcpServers: Record<string, unknown> = {};\n let relay: { port: number; close(): void } | undefined;\n let cliShimPath: string | undefined;\n if (start.tools && start.tools.length > 0) {\n const relayToken = randomUUID();\n relay = await startToolRelay({\n relayToken,\n tools: start.tools,\n emit,\n requestToolResult: turn.requestToolResult,\n });\n mcpServers['harness-tools'] = {\n enabled: true,\n command: 'node',\n args: [`${bootstrapDir}/host-tool-mcp.mjs`],\n env: {\n TOOL_SCHEMAS: JSON.stringify(\n start.tools.map(t => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n ),\n TOOL_RELAY_URL: `http://127.0.0.1:${relay.port}`,\n TOOL_RELAY_TOKEN: relayToken,\n },\n };\n // Temporary workaround for upstream codex MCP-tool bug — see ./cli-relay.ts\n cliShimPath = `${workdir}/${CLI_SHIM_FILENAME}`;\n await writeFile(\n cliShimPath,\n buildCliShimScript({ relayPort: relay.port, relayToken }),\n 'utf8',\n );\n }\n\n const codexConfig: Record<string, unknown> = {};\n if (Object.keys(mcpServers).length > 0) codexConfig.mcp_servers = mcpServers;\n\n const gatewayBaseUrl = procEnv.AI_GATEWAY_BASE_URL;\n const hasGatewayAuth = Boolean(procEnv.AI_GATEWAY_API_KEY || gatewayBaseUrl);\n if (hasGatewayAuth && !gatewayBaseUrl) {\n throw new Error(\n 'AI Gateway auth was selected but AI_GATEWAY_BASE_URL is missing from the Codex bridge environment.',\n );\n }\n const apiBaseUrl = hasGatewayAuth ? gatewayBaseUrl : procEnv.OPENAI_BASE_URL;\n if (apiBaseUrl) {\n codexConfig.preferred_auth_method = 'apikey';\n codexConfig.model_provider = 'agent_bridge_openai';\n codexConfig.model_providers = {\n agent_bridge_openai: {\n name: procEnv.CODEX_MODEL_PROVIDER_NAME || 'Agent Bridge OpenAI',\n base_url: apiBaseUrl,\n env_key: 'CODEX_API_KEY',\n wire_api: 'responses',\n supports_websockets: false,\n },\n };\n }\n const usesConfiguredModelProvider =\n typeof codexConfig.model_provider === 'string';\n\n const codex = new codexSdk.Codex({\n ...(procEnv.CODEX_API_KEY ? { apiKey: procEnv.CODEX_API_KEY } : {}),\n ...(!usesConfiguredModelProvider && apiBaseUrl\n ? { baseUrl: apiBaseUrl }\n : {}),\n env: Object.fromEntries(\n Object.entries(procEnv).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n ),\n ),\n ...(Object.keys(codexConfig).length > 0 ? { config: codexConfig } : {}),\n });\n\n const threadOptions = {\n ...(start.model ? { model: start.model } : {}),\n sandboxMode: 'danger-full-access',\n approvalPolicy: 'never',\n workingDirectory: workdir,\n skipGitRepoCheck: true,\n ...(start.reasoningEffort\n ? { modelReasoningEffort: start.reasoningEffort }\n : {}),\n webSearchMode: start.webSearch ? 'live' : 'disabled',\n };\n const thread = threadState.id\n ? codex.resumeThread(threadState.id, threadOptions)\n : codex.startThread(threadOptions);\n\n emit({ type: 'stream-start' });\n\n const userMessage = composeUserMessage({\n text: start.prompt,\n instructions: start.instructions,\n // Temporary workaround for upstream codex MCP-tool bug — see ./cli-relay.ts\n toolUsageBlock:\n cliShimPath && start.tools && start.tools.length > 0\n ? composeToolUsageInstructions({\n tools: start.tools,\n cliShimPath,\n })\n : undefined,\n });\n let turnUsage: Record<string, unknown> | undefined;\n const textByItem = new Map<string, string>();\n const reasoningByItem = new Map<string, string>();\n\n try {\n const { events } = await thread.runStreamed(userMessage, {\n signal: turn.abortSignal,\n });\n for await (const event of events as AsyncIterable<CodexEvent>) {\n if (turn.abortSignal.aborted) break;\n if (\n event.type === 'thread.started' &&\n typeof event.thread_id === 'string'\n ) {\n threadState.id = event.thread_id;\n // Announce to the host so it can include the id in resume state.\n emit({ type: 'bridge-thread', threadId: event.thread_id });\n }\n // Temporary workaround for upstream codex MCP-tool bug — see ./cli-relay.ts\n if (\n cliShimPath &&\n event.item?.type === 'command_execution' &&\n typeof event.item.command === 'string' &&\n isToolRelayCommand({ command: event.item.command, cliShimPath })\n ) {\n continue;\n }\n translateAndEmit(event, {\n send: emit,\n textByItem,\n reasoningByItem,\n setTurnUsage: u => (turnUsage = u),\n });\n }\n } catch (err) {\n emit({ type: 'error', error: serialiseError(err) });\n return;\n } finally {\n relay?.close();\n }\n\n emit({\n type: 'finish',\n finishReason: { unified: 'stop', raw: 'stop' },\n totalUsage: turnUsage ?? defaultUsage(),\n });\n\n void turn.pendingUserMessages; // accepted but only consumed when codex supports streamed user input\n}\n\ntype CodexItem = {\n type: string;\n id?: string;\n text?: string;\n command?: string;\n exit_code?: number;\n aggregated_output?: string;\n status?: 'in_progress' | 'completed' | 'failed';\n server?: string;\n tool?: string;\n arguments?: unknown;\n result?: { content?: unknown; structured_content?: unknown } | unknown;\n error?: { message?: string };\n query?: string;\n message?: string;\n changes?: ReadonlyArray<{\n path: string;\n kind: 'add' | 'delete' | 'update';\n }>;\n};\n\nfunction extractMcpToolCallResult(item: CodexItem): unknown {\n if (\n item.result === undefined ||\n item.result === null ||\n typeof item.result !== 'object'\n ) {\n return item.error?.message ? { error: item.error.message } : null;\n }\n const result = item.result as {\n content?: unknown;\n structured_content?: unknown;\n };\n if (\n result.structured_content !== undefined &&\n result.structured_content !== null\n ) {\n return result.structured_content;\n }\n return result.content ?? null;\n}\n\ntype CodexEvent = {\n type:\n | 'thread.started'\n | 'turn.completed'\n | 'turn.failed'\n | 'error'\n | 'item.started'\n | 'item.updated'\n | 'item.completed';\n item?: CodexItem;\n usage?: Record<string, number>;\n error?: { message: string };\n message?: string;\n thread_id?: string;\n};\n\nfunction translateAndEmit(\n event: CodexEvent,\n ctx: {\n send: Emit;\n textByItem: Map<string, string>;\n reasoningByItem: Map<string, string>;\n setTurnUsage: (u: Record<string, unknown>) => void;\n },\n): void {\n if (event.type === 'turn.completed') {\n if (event.usage) ctx.setTurnUsage(mapUsage(event.usage));\n ctx.send({\n type: 'finish-step',\n finishReason: { unified: 'stop', raw: 'stop' },\n usage: event.usage ? mapUsage(event.usage) : defaultUsage(),\n });\n return;\n }\n if (event.type === 'turn.failed') {\n ctx.send({\n type: 'error',\n error: event.error?.message ?? 'codex turn failed',\n });\n return;\n }\n if (event.type === 'error') {\n ctx.send({ type: 'error', error: event.message ?? 'codex error' });\n return;\n }\n if (!event.item) return;\n const item = event.item;\n const id = item.id ?? randomUUID();\n\n if (item.type === 'agent_message' && typeof item.text === 'string') {\n /*\n * The presence of `id` in `textByItem` — not the `item.started` event —\n * marks the text part as opened. Codex does not guarantee an\n * `item.started` event carrying text precedes the first `item.updated`\n * with text, so keying the `text-start` off the event type can emit a\n * `text-delta` for a part that was never opened. Opening lazily on the\n * first event with text keeps `text-start` before any `text-delta`.\n */\n if (!ctx.textByItem.has(id)) {\n ctx.send({ type: 'text-start', id });\n ctx.textByItem.set(id, '');\n }\n const last = ctx.textByItem.get(id) ?? '';\n const next = item.text;\n if (next.length > last.length) {\n ctx.send({ type: 'text-delta', id, delta: next.slice(last.length) });\n ctx.textByItem.set(id, next);\n }\n if (event.type === 'item.completed') ctx.send({ type: 'text-end', id });\n return;\n }\n\n if (item.type === 'reasoning' && typeof item.text === 'string') {\n if (!ctx.reasoningByItem.has(id)) {\n ctx.send({ type: 'reasoning-start', id });\n ctx.reasoningByItem.set(id, '');\n }\n const last = ctx.reasoningByItem.get(id) ?? '';\n const next = item.text;\n if (next.length > last.length) {\n ctx.send({ type: 'reasoning-delta', id, delta: next.slice(last.length) });\n ctx.reasoningByItem.set(id, next);\n }\n if (event.type === 'item.completed')\n ctx.send({ type: 'reasoning-end', id });\n return;\n }\n\n if (item.type === 'command_execution') {\n const nativeName = 'shell';\n if (event.type === 'item.started') {\n ctx.send({\n type: 'tool-call',\n toolCallId: id,\n toolName: toCommonName(nativeName),\n nativeName,\n input: JSON.stringify({ command: item.command ?? '' }),\n providerExecuted: true,\n });\n } else if (event.type === 'item.completed') {\n ctx.send({\n type: 'tool-result',\n toolCallId: id,\n toolName: toCommonName(nativeName),\n result: {\n exitCode: item.exit_code ?? null,\n output: item.aggregated_output ?? '',\n status: item.status ?? 'completed',\n },\n });\n }\n return;\n }\n\n if (item.type === 'mcp_tool_call') {\n const isHostTool = item.server === 'harness-tools';\n if (event.type === 'item.started') {\n ctx.send({\n type: 'tool-call',\n toolCallId: id,\n toolName: item.tool ?? 'unknown',\n ...(isHostTool ? {} : { nativeName: item.tool ?? 'unknown' }),\n input: JSON.stringify(item.arguments ?? {}),\n providerExecuted: !isHostTool,\n });\n } else if (event.type === 'item.completed') {\n ctx.send({\n type: 'tool-result',\n toolCallId: id,\n toolName: item.tool ?? 'unknown',\n result: extractMcpToolCallResult(item),\n });\n }\n return;\n }\n\n if (item.type === 'web_search') {\n const nativeName = 'web_search';\n if (event.type === 'item.started') {\n ctx.send({\n type: 'tool-call',\n toolCallId: id,\n toolName: toCommonName(nativeName),\n nativeName,\n input: JSON.stringify({ query: item.query ?? '' }),\n providerExecuted: true,\n });\n } else if (event.type === 'item.completed') {\n ctx.send({\n type: 'tool-result',\n toolCallId: id,\n toolName: toCommonName(nativeName),\n result: item.result ?? null,\n });\n }\n return;\n }\n\n if (item.type === 'file_change' && event.type === 'item.completed') {\n for (const change of item.changes ?? []) {\n ctx.send({\n type: 'file-change',\n event:\n change.kind === 'add'\n ? 'create'\n : change.kind === 'delete'\n ? 'delete'\n : 'modify',\n path: change.path,\n });\n }\n return;\n }\n\n if (item.type === 'error' && event.type === 'item.completed') {\n ctx.send({\n type: 'error',\n error: (item as { message?: string }).message ?? 'codex item error',\n });\n return;\n }\n}\n\nfunction mapUsage(usage: Record<string, number>): Record<string, unknown> {\n const input = usage.input_tokens ?? 0;\n const cacheRead = usage.cached_input_tokens ?? 0;\n return {\n inputTokens: {\n total: input,\n noCache: Math.max(0, input - cacheRead),\n cacheRead,\n cacheWrite: 0,\n },\n outputTokens: {\n total: usage.output_tokens ?? 0,\n text: usage.output_tokens ?? 0,\n },\n };\n}\n\nfunction defaultUsage(): Record<string, unknown> {\n return {\n inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },\n outputTokens: { total: 0, text: 0 },\n };\n}\n\nfunction composeUserMessage({\n text,\n instructions,\n toolUsageBlock,\n}: {\n text: string;\n instructions: string | undefined;\n toolUsageBlock: string | undefined;\n}): string {\n const blocks: string[] = [];\n /*\n * Frame instructions as system-provided operating guidance, not something\n * the user wrote, so the agent does not echo the prepended text back as if\n * the user had asked for it. Only present on the first user message of a\n * fresh session (the host gates it), so the matching `<user-message>` fence\n * is added only when instructions are present too.\n */\n if (instructions) {\n blocks.push(\n '<session-instructions>\\n' +\n 'The block below is operating guidance from the system, not a message from the user — follow it, but do not mention it or attribute it to the user.\\n\\n' +\n `${instructions}\\n` +\n '</session-instructions>',\n );\n }\n if (toolUsageBlock) blocks.push(toolUsageBlock);\n blocks.push(instructions ? `<user-message>\\n${text}\\n</user-message>` : text);\n return blocks.join('\\n\\n');\n}\n\n/**\n * Tool relay — HTTP server on 127.0.0.1:0 with bearer-token auth. The MCP\n * stdio shim spawned by codex POSTs each tool invocation here; the relay\n * forwards the call to the host (via the shared runtime's `emit`), awaits the\n * matching `tool-result` (via `requestToolResult`), and responds with\n * `{ result }`.\n */\nasync function startToolRelay({\n relayToken,\n tools,\n emit,\n requestToolResult,\n}: {\n relayToken: string;\n tools: ReadonlyArray<{ name: string }>;\n emit: Emit;\n requestToolResult: (\n toolCallId: string,\n ) => Promise<{ output: unknown; isError?: boolean }>;\n}): Promise<{ port: number; close(): void }> {\n const toolNames = new Set(tools.map(t => t.name));\n\n const server = createServer(async (req, res) => {\n try {\n if (\n req.method !== 'POST' ||\n req.url !== '/' ||\n req.headers.authorization !== `Bearer ${relayToken}`\n ) {\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'unauthorized tool relay request' }));\n return;\n }\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk as Buffer);\n }\n const body = Buffer.concat(chunks).toString('utf8');\n const { requestId, toolName, input } = JSON.parse(body) as {\n requestId: string;\n toolName: string;\n input: unknown;\n };\n\n if (!toolNames.has(toolName)) {\n res.writeHead(403, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({ error: `Tool \"${toolName}\" is not available` }),\n );\n return;\n }\n\n emit({\n type: 'tool-call',\n toolCallId: requestId,\n toolName,\n input: JSON.stringify(input ?? {}),\n providerExecuted: false,\n });\n\n const { output, isError } = await requestToolResult(requestId);\n emit({\n type: 'tool-result',\n toolCallId: requestId,\n toolName,\n result: output ?? null,\n isError: !!isError,\n });\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ result: output }));\n } catch (error) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n });\n\n await new Promise<void>(resolve =>\n server.listen(0, '127.0.0.1', () => resolve()),\n );\n const address = server.address();\n if (!address || typeof address === 'string') {\n throw new Error('tool relay did not expose a numeric port');\n }\n return {\n port: address.port,\n close: () => closeServer(server),\n };\n}\n\nfunction closeServer(server: Server): void {\n try {\n server.close();\n } catch {}\n}\n\nfunction parseArgs(args: string[]): {\n workdir?: string;\n bridgeStateDir?: string;\n bootstrapDir?: string;\n} {\n const out: {\n workdir?: string;\n bridgeStateDir?: string;\n bootstrapDir?: string;\n } = {};\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--workdir' && i + 1 < args.length) {\n out.workdir = args[++i];\n } else if (args[i] === '--bridge-state-dir' && i + 1 < args.length) {\n out.bridgeStateDir = args[++i];\n } else if (args[i] === '--bootstrap-dir' && i + 1 < args.length) {\n out.bootstrapDir = args[++i];\n }\n }\n return out;\n}\n\nfunction serialiseError(err: unknown): unknown {\n if (err instanceof Error) {\n return { name: err.name, message: err.message, stack: err.stack };\n }\n return err;\n}\n\nfunction emitFatal(message: string): never {\n stdout.write(JSON.stringify({ type: 'bridge-fatal', message }) + '\\n');\n process.exit(1);\n}\n","/*\n * Temporary workaround for upstream codex CLI bug\n * https://github.com/openai/codex/issues/19425 — MCP tools registered via\n * `mcp_servers.*` are not exposed to the model in `codex exec --experimental-json`\n * mode (which is what `@openai/codex-sdk` uses). The MCP handshake completes\n * and `tools/list` succeeds, but codex never registers the tools as\n * model-callable functions.\n *\n * Until that's fixed upstream, this file implements a CLI-based relay:\n *\n * 1. A small Node script is written into the workdir at turn start\n * (`buildCliShimScript`). It accepts `<toolName> <jsonInput>` as argv,\n * POSTs to the same HTTP relay the MCP shim uses, and prints the\n * result to stdout.\n *\n * 2. Tool descriptions and invocation instructions are injected into the\n * user prompt (`composeToolUsageInstructions`), telling the model to\n * call host tools by running `node <shim-path> <toolName> '<jsonInput>'`\n * via its built-in `bash` tool.\n *\n * 3. The bridge's event loop suppresses the matching `command_execution`\n * events (`isToolRelayCommand`) so callers receive clean `tool-call` /\n * `tool-result` events from the HTTP relay rather than seeing the\n * relay invocations as raw bash commands.\n *\n * Once #19425 is fixed and MCP tools are properly exposed in exec mode, the\n * three hookpoints in `bridge/index.ts` can be removed along with this file.\n */\n\nexport const CLI_SHIM_FILENAME = 'harness-tool.mjs';\n\nexport function buildCliShimScript({\n relayPort,\n relayToken,\n}: {\n relayPort: number;\n relayToken: string;\n}): string {\n return `#!/usr/bin/env node\nconst [toolName, inputJson = '{}'] = process.argv.slice(2);\nif (!toolName) {\n console.error('Usage: harness-tool <tool_name> <json_input>');\n process.exit(64);\n}\nlet input;\ntry {\n input = JSON.parse(inputJson);\n} catch (error) {\n console.error('Invalid JSON input: ' + (error instanceof Error ? error.message : String(error)));\n process.exit(64);\n}\nconst requestId = 'cli-' + Date.now() + '-' + Math.random().toString(16).slice(2);\nconst response = await fetch('http://127.0.0.1:${relayPort}', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ${relayToken}' },\n body: JSON.stringify({ requestId, toolName, input }),\n});\nconst text = await response.text();\nlet payload;\ntry {\n payload = text ? JSON.parse(text) : {};\n} catch {\n payload = { error: text };\n}\nif (!response.ok || payload.error) {\n console.error(String(payload.error ?? ('tool relay failed with HTTP ' + response.status)));\n process.exit(1);\n}\nconsole.log(JSON.stringify(payload.result ?? payload, null, 2));\n`;\n}\n\nexport function composeToolUsageInstructions({\n tools,\n cliShimPath,\n}: {\n tools: ReadonlyArray<{\n name: string;\n description?: string;\n inputSchema?: unknown;\n }>;\n cliShimPath: string;\n}): string {\n const lines: string[] = [\n '## Host tools',\n '',\n 'You have access to the following host-provided tools. To use one, run the following command via your built-in `bash` tool:',\n '',\n ` node ${cliShimPath} <toolName> '<jsonInput>'`,\n '',\n 'The script prints the JSON result to stdout. Do not invent another way to call these tools — only this CLI invocation will work. Pass the JSON input as a single-quoted argument.',\n 'For every user request that depends on a host-provided tool, run a separate CLI invocation for each needed tool call in the current turn before answering. Do not reuse previous tool results, and do not say you used a host tool unless the command has completed in the current turn.',\n '',\n ];\n for (const tool of tools) {\n lines.push(`### ${tool.name}`);\n if (tool.description) lines.push(tool.description);\n lines.push(\n `Input schema: \\`${JSON.stringify(tool.inputSchema ?? {})}\\``,\n '',\n );\n }\n return lines.join('\\n');\n}\n\nexport function isToolRelayCommand({\n command,\n cliShimPath,\n}: {\n command: string;\n cliShimPath: string;\n}): boolean {\n return command.includes(cliShimPath);\n}\n"],"mappings":"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","names":["bridgeStateDir","writeFile","procEnv","stdout","writeFile","serialiseError","args"]}
|
|
1
|
+
{"version":3,"sources":["../../../harness/src/bridge/index.ts","../../src/bridge/index.ts","../../src/bridge/cli-relay.ts"],"sourcesContent":["// Shared in-sandbox bridge runtime. Adapter `bridge.mjs` bundles re-bundle\n// this module (tsup inlines it; `ws` stays external and resolves from the\n// sandbox-installed node_modules). It owns everything generic to the bridge\n// transport — the WebSocket server, token auth, single-flight connection\n// replacement, the in-memory event log + monotonic `seq`, resume replay, and\n// the lifecycle/meta files. The adapter supplies only `onStart` (drive its\n// CLI/SDK and translate to wire events) and `onDetach` (its resume payload).\n\nimport { appendFile, mkdir, writeFile } from 'node:fs/promises';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { env as procEnv, pid, stdout } from 'node:process';\nimport { WebSocketServer, type WebSocket } from 'ws';\n\nexport type BridgeState = 'init' | 'waiting' | 'running' | 'draining' | 'done';\n\n/** Outbound turn event the adapter emits. `seq` is added by the runtime. */\nexport type BridgeEvent = Record<string, unknown> & { type: string };\n\nexport type BridgeDebugLevel = 'error' | 'warn' | 'info' | 'debug' | 'trace';\n\n/**\n * Per-session diagnostics config. The host resolves it from settings +\n * env and sends it on `start.debug`; the bridge gates console capture and\n * structured `debug-event`s on it. When disabled, nothing is captured or\n * emitted and no `seq` is consumed.\n */\nexport interface BridgeDebugConfig {\n enabled?: boolean;\n level?: BridgeDebugLevel;\n subsystems?: string[];\n}\n\nconst DEBUG_LEVEL_WEIGHT: Record<BridgeDebugLevel, number> = {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n trace: 4,\n};\n\n/** Exact-or-dotted-prefix subsystem match (`'bridge'` matches `'bridge.turn'`). */\nfunction subsystemMatches(\n filters: string[] | undefined,\n subsystem: string,\n): boolean {\n if (!filters || filters.length === 0) return true;\n return filters.some(\n filter => subsystem === filter || subsystem.startsWith(`${filter}.`),\n );\n}\n\nfunction formatBridgeError(err: unknown): {\n name?: string;\n message: string;\n stack?: string;\n} {\n if (err instanceof Error) {\n return { name: err.name, message: err.message, stack: err.stack };\n }\n return { message: String(err) };\n}\n\nfunction parseEnvList(value: string | undefined): string[] | undefined {\n if (!value) return undefined;\n const items = value\n .split(',')\n .map(item => item.trim())\n .filter(Boolean);\n return items.length > 0 ? items : undefined;\n}\n\nconst ENV_TRUTHY = new Set(['1', 'true', 'yes', 'on']);\n\n/**\n * Per-turn surface handed to {@link RunBridgeOptions.onStart}. The adapter\n * drives its runtime against these primitives; the runtime owns the transport.\n */\nexport interface BridgeTurn {\n /**\n * Emit a turn event to the host. Stamps a monotonic `seq`, appends to the\n * in-memory replay log, and sends to the live socket (best-effort — if the\n * host is mid-reconnect the event waits in the log and is replayed on\n * resume).\n */\n emit(event: BridgeEvent): void;\n\n /**\n * Register interest in a host-executed tool result and resolve when the\n * matching `tool-result` arrives. The adapter emits the `tool-call` event\n * itself (via {@link emit}) using the same `toolCallId`.\n */\n requestToolResult(\n toolCallId: string,\n ): Promise<{ output: unknown; isError?: boolean }>;\n\n /**\n * Register interest in a host approval decision and resolve when the matching\n * `tool-approval-response` arrives. The adapter emits the\n * `tool-approval-request` event itself using the same `approvalId`.\n */\n requestToolApproval(\n approvalId: string,\n ): Promise<{ approved: boolean; reason?: string }>;\n\n /**\n * Live queue of mid-turn user messages. The runtime pushes inbound\n * `user-message` text here; the adapter drains it as its runtime accepts\n * interactive input.\n */\n readonly pendingUserMessages: string[];\n\n /** Aborts when the host sends `abort`. */\n readonly abortSignal: AbortSignal;\n\n /** True for the first turn since this bridge process started. */\n readonly firstTurn: boolean;\n\n /**\n * Emit a structured diagnostic. Gated by the session's debug level +\n * subsystem filter; a no-op when diagnostics are disabled. Adapters use this\n * for runtime-level instrumentation; raw `console.*` output is captured and\n * forwarded automatically.\n */\n bridgeLog(input: {\n level?: BridgeDebugLevel;\n subsystem: string;\n message: string;\n attrs?: Record<string, unknown>;\n error?: unknown;\n }): void;\n}\n\nexport interface RunBridgeOptions<TStart extends { type: 'start' }> {\n /** Identifier written into `bridge-meta.json` (`'claude-code'` / `'codex'`). */\n bridgeType: string;\n /** Directory for `bridge-meta.json` / `start-config.json`. Created if absent. */\n bridgeStateDir: string;\n /** Drive one prompt turn. Rejections surface to the host as an `error` event. */\n onStart(start: TStart, turn: BridgeTurn): Promise<void>;\n /** Produce the adapter-defined resume payload for a `detach`. Defaults to `{}`. */\n onDetach?(): unknown | Promise<unknown>;\n /** WS port. Defaults to `BRIDGE_WS_PORT` env (0 = OS-assigned). */\n port?: number;\n /** Auth token. Defaults to `BRIDGE_CHANNEL_TOKEN` env. */\n token?: string;\n /** Called with the bound port once the server is listening. */\n onListening?(port: number): void;\n /**\n * Tear the process down after a `shutdown` / `detach`. Defaults to closing\n * the server and calling `process.exit(0)`. Overridable for tests.\n */\n onExit?(): void;\n}\n\ntype InboundControl =\n | {\n type: 'tool-result';\n toolCallId: string;\n output: unknown;\n isError?: boolean;\n }\n | {\n type: 'tool-approval-response';\n approvalId: string;\n approved: boolean;\n reason?: string;\n }\n | { type: 'user-message'; text: string }\n | { type: 'abort' }\n | { type: 'shutdown' }\n | { type: 'detach' }\n | { type: 'resume'; lastSeenEventId: number };\n\nconst WS_OPEN = 1;\n\n/**\n * Boot the bridge: bind the WebSocket server, announce `bridge-ready`, and\n * service host connections for the lifetime of the process. Resolves once the\n * server is listening; the process then stays alive on the server until a\n * `shutdown` / `detach` exits it.\n */\nexport interface BridgeHandle {\n /** The port the WebSocket server bound to. */\n readonly port: number;\n /** Close the WebSocket server. Does not call `process.exit`. */\n close(): Promise<void>;\n}\n\nexport async function runBridge<TStart extends { type: 'start' }>(\n options: RunBridgeOptions<TStart>,\n): Promise<BridgeHandle> {\n const { bridgeType, bridgeStateDir, onStart, onDetach } = options;\n const expectedToken = options.token ?? procEnv.BRIDGE_CHANNEL_TOKEN ?? '';\n const bridgeWsPort =\n options.port ?? parseInt(procEnv.BRIDGE_WS_PORT ?? '0', 10);\n\n const bridgeMetaPath = `${bridgeStateDir}/bridge-meta.json`;\n const startConfigPath = `${bridgeStateDir}/start-config.json`;\n const rerunStartConfigPath = `${bridgeStateDir}/rerun-start-config.json`;\n const eventLogPath = `${bridgeStateDir}/event-log.ndjson`;\n\n try {\n await mkdir(bridgeStateDir, { recursive: true });\n } catch {\n // Best-effort; the bridge still runs without its state files.\n }\n\n // ─── mutable runtime state ──────────────────────────────────────────\n let currentBoundPort = 0;\n let currentTurnState: BridgeState = 'init';\n let activeSocket: WebSocket | undefined;\n let isFirstTurn = true;\n let turnAbort: AbortController | undefined;\n let currentUserMessages: string[] | undefined;\n\n // Diagnostics. Resolved per turn from `start.debug` with a sandbox-side\n // env fallback; gates console capture + structured `debug-event`s.\n let debugConfig: BridgeDebugConfig | undefined;\n let consoleCaptureInstalled = false;\n const envDebugEnabled = ENV_TRUTHY.has(\n (procEnv.HARNESS_DEBUG ?? '').toLowerCase(),\n );\n\n // Replay log. `seq` is monotonic across the whole process — never reset —\n // because the host's `SandboxChannel` cursor (`lastSeenEventId`) lives across\n // turns. The log *contents* are cleared at the start of each turn to bound\n // memory; the just-finished turn stays replayable until the next `start`.\n let seqCounter = 0;\n let eventLog: Array<{ seq: number; line: string }> = [];\n\n /*\n * Disk mirror of the in-memory replay log. The in-memory log is lost when the\n * bridge process dies; the on-disk `event-log.ndjson` survives in the sandbox\n * filesystem so a respawned bridge (started with `BRIDGE_REPLAY_FROM_DISK=1`)\n * can reload the just-interrupted turn and serve a host's resume cursor —\n * `replay` recovery. Writes are batched on `setImmediate` (single-flight via\n * `flushPromise`) to keep `emit` off the disk hot path.\n */\n let diskBuffer = '';\n let flushPromise: Promise<void> | null = null;\n\n const flushEventsToDisk = async (): Promise<void> => {\n while (diskBuffer.length > 0) {\n const buf = diskBuffer;\n diskBuffer = '';\n await appendFile(eventLogPath, buf).catch(() => {\n // Best-effort crash-recovery mirror; the in-memory log is the source of\n // truth for the live connection.\n });\n }\n };\n\n const scheduleEventFlush = (): void => {\n if (flushPromise) return;\n flushPromise = new Promise<void>(resolve => {\n setImmediate(() => {\n void flushEventsToDisk().finally(resolve);\n });\n }).finally(() => {\n flushPromise = null;\n if (diskBuffer.length > 0) {\n scheduleEventFlush();\n }\n });\n };\n\n const flushPendingEventsToDisk = async (): Promise<void> => {\n if (diskBuffer.length > 0 && !flushPromise) {\n scheduleEventFlush();\n }\n // Await each in-flight flush, re-reading `flushPromise` after every await\n // since a fresh flush may have been scheduled for buffer that arrived while\n // we waited.\n let inFlight = flushPromise;\n while (inFlight) {\n await inFlight;\n inFlight = flushPromise;\n }\n };\n\n /*\n * When respawned for `replay`, reload the previous turn's log from disk before\n * accepting any connection so the very first `resume{lastSeenEventId}` can be\n * served the tail (including the terminal `finish`). The seq counter is\n * restored to the last persisted seq so it stays aligned with the host's\n * long-lived cursor. The file is NOT truncated in this mode — only a fresh\n * `start` (next turn) clears it.\n */\n const replayFromDisk = procEnv.BRIDGE_REPLAY_FROM_DISK === '1';\n if (replayFromDisk && existsSync(eventLogPath)) {\n try {\n const lines = readFileSync(eventLogPath, 'utf8')\n .split('\\n')\n .map(line => line.trim())\n .filter(Boolean);\n eventLog = lines.map(line => ({\n seq: (JSON.parse(line) as { seq: number }).seq,\n line,\n }));\n seqCounter = eventLog.at(-1)?.seq ?? 0;\n } catch {\n // Corrupt/partial log: fall back to an empty log; the host then degrades\n // to `rerun` instead of replaying a malformed tail.\n eventLog = [];\n seqCounter = 0;\n }\n }\n\n const pendingToolResults = new Map<\n string,\n (output: { output: unknown; isError?: boolean }) => void\n >();\n const pendingToolApprovals = new Map<\n string,\n (response: { approved: boolean; reason?: string }) => void\n >();\n\n // ─── persistence (best-effort meta + start config) ──────────────────\n const writeBridgeMeta = async (state: BridgeState): Promise<void> => {\n try {\n await writeFile(\n bridgeMetaPath,\n JSON.stringify({\n type: bridgeType,\n port: currentBoundPort,\n state,\n pid,\n }),\n );\n } catch {\n // Best-effort resilience metadata; not load-bearing for the active turn.\n }\n };\n\n const writeStartConfig = async (start: unknown): Promise<void> => {\n try {\n const serialized = JSON.stringify(start);\n await writeFile(startConfigPath, serialized);\n // Frozen copy: written once, restored over start-config.json by future\n // rerun-mode recovery to re-run the original turn from scratch.\n if (!existsSync(rerunStartConfigPath)) {\n await writeFile(rerunStartConfigPath, serialized);\n }\n } catch {\n // Best-effort.\n }\n };\n\n // ─── wire send + replay ─────────────────────────────────────────────\n const sendControl = (msg: Record<string, unknown>): void => {\n if (activeSocket?.readyState === WS_OPEN) {\n try {\n activeSocket.send(JSON.stringify(msg));\n } catch {\n // best-effort\n }\n }\n };\n\n const emit = (event: BridgeEvent): void => {\n const seq = ++seqCounter;\n const line = JSON.stringify({ ...event, seq });\n eventLog.push({ seq, line });\n diskBuffer += `${line}\\n`;\n scheduleEventFlush();\n if (activeSocket?.readyState === WS_OPEN) {\n try {\n activeSocket.send(line);\n } catch {\n // Send is best-effort: a dropped socket leaves the event in the log,\n // replayed once the host reconnects and sends `resume`.\n }\n }\n };\n\n const replay = (ws: WebSocket, afterSeq: number): void => {\n for (const entry of eventLog) {\n if (entry.seq > afterSeq && ws.readyState === WS_OPEN) {\n ws.send(entry.line);\n }\n }\n };\n\n // ─── diagnostics ──────────────────────────────────────────────\n const shouldEmitDebugEvent = (\n level: BridgeDebugLevel,\n subsystem: string,\n ): boolean => {\n if (!debugConfig?.enabled) return false;\n const threshold = debugConfig.level ?? 'debug';\n if (DEBUG_LEVEL_WEIGHT[level] > DEBUG_LEVEL_WEIGHT[threshold]) return false;\n return subsystemMatches(debugConfig.subsystems, subsystem);\n };\n\n /*\n * Forward sandbox console output. We line-buffer the original writers (kept so\n * output still reaches the real fds) and emit one `sandbox-log` per complete\n * line. `emit` never writes to stdout/stderr, so there is no recursion.\n * Installed lazily the first time a turn enables diagnostics; once installed,\n * capture is gated per-write on `debugConfig.enabled` so a later turn can\n * disable it. Console capture is independent of the subsystem/level filter.\n */\n const rawStdoutWrite = process.stdout.write.bind(process.stdout);\n const rawStderrWrite = process.stderr.write.bind(process.stderr);\n const installConsoleCapture = (): void => {\n if (consoleCaptureInstalled) return;\n consoleCaptureInstalled = true;\n const buffers: { stdout: string; stderr: string } = {\n stdout: '',\n stderr: '',\n };\n const patch =\n (stream: 'stdout' | 'stderr', raw: typeof process.stdout.write) =>\n (chunk: unknown, encoding?: unknown, cb?: unknown): boolean => {\n if (debugConfig?.enabled) {\n try {\n const enc = typeof encoding === 'string' ? encoding : 'utf8';\n const text =\n typeof chunk === 'string'\n ? chunk\n : Buffer.from(chunk as Uint8Array).toString(\n enc as BufferEncoding,\n );\n const combined = buffers[stream] + text.replace(/\\r\\n/g, '\\n');\n const parts = combined.split('\\n');\n buffers[stream] = parts.pop() ?? '';\n for (const line of parts) {\n const trimmed = line.replace(/\\s+$/, '');\n if (trimmed) {\n emit({\n type: 'sandbox-log',\n source: bridgeType,\n stream,\n line: trimmed,\n });\n }\n }\n } catch {\n // Never let capture break real output.\n }\n }\n return (raw as (c: unknown, e?: unknown, cb?: unknown) => boolean)(\n chunk,\n encoding,\n cb,\n );\n };\n process.stdout.write = patch(\n 'stdout',\n rawStdoutWrite,\n ) as typeof process.stdout.write;\n process.stderr.write = patch(\n 'stderr',\n rawStderrWrite,\n ) as typeof process.stderr.write;\n };\n\n // ─── inbound routing ────────────────────────────────────────────────\n const handleInbound = async (\n msg: TStart | InboundControl,\n ws: WebSocket,\n ): Promise<void> => {\n switch (msg.type) {\n case 'start': {\n const firstTurn = isFirstTurn;\n isFirstTurn = false;\n eventLog = []; // clear previous turn; keep seqCounter monotonic\n // Mirror the in-memory clear to disk: the log tracks only the current\n // turn. Discard any unflushed tail from the prior turn first.\n diskBuffer = '';\n void writeFile(eventLogPath, '').catch(() => {});\n turnAbort = new AbortController();\n currentTurnState = 'running';\n void writeStartConfig(msg);\n void writeBridgeMeta('running');\n const startDebug = (msg as { debug?: BridgeDebugConfig }).debug;\n debugConfig = {\n enabled: startDebug?.enabled ?? envDebugEnabled,\n level:\n startDebug?.level ??\n (procEnv.HARNESS_DEBUG_LEVEL as BridgeDebugLevel | undefined),\n subsystems:\n startDebug?.subsystems ??\n parseEnvList(procEnv.HARNESS_DEBUG_SUBSYSTEMS),\n };\n if (debugConfig.enabled) {\n installConsoleCapture();\n }\n const turn: BridgeTurn = {\n emit,\n requestToolResult: toolCallId =>\n new Promise(resolve => {\n pendingToolResults.set(toolCallId, resolve);\n }),\n requestToolApproval: approvalId =>\n new Promise(resolve => {\n pendingToolApprovals.set(approvalId, resolve);\n }),\n pendingUserMessages: [],\n abortSignal: turnAbort.signal,\n firstTurn,\n bridgeLog: input => {\n const level = input.level ?? 'debug';\n if (!shouldEmitDebugEvent(level, input.subsystem)) return;\n emit({\n type: 'debug-event',\n level,\n subsystem: input.subsystem,\n message: input.message,\n ...(input.attrs ? { attrs: input.attrs } : {}),\n ...(input.error !== undefined\n ? { error: formatBridgeError(input.error) }\n : {}),\n });\n },\n };\n currentUserMessages = turn.pendingUserMessages;\n try {\n await onStart(msg as TStart, turn);\n } catch (err) {\n emit({ type: 'error', error: serialiseError(err) });\n } finally {\n currentTurnState = 'waiting';\n void writeBridgeMeta('waiting');\n }\n return;\n }\n case 'tool-result': {\n const resolver = pendingToolResults.get(msg.toolCallId);\n if (resolver) {\n pendingToolResults.delete(msg.toolCallId);\n resolver({ output: msg.output, isError: msg.isError });\n }\n return;\n }\n case 'tool-approval-response': {\n const resolver = pendingToolApprovals.get(msg.approvalId);\n if (resolver) {\n pendingToolApprovals.delete(msg.approvalId);\n resolver({ approved: msg.approved, reason: msg.reason });\n }\n return;\n }\n case 'user-message':\n currentUserMessages?.push(msg.text);\n return;\n case 'abort':\n turnAbort?.abort();\n return;\n case 'resume':\n replay(ws, msg.lastSeenEventId);\n return;\n case 'shutdown':\n currentTurnState = 'done';\n void writeBridgeMeta('done');\n drainThenExit(ws, 1000, 'shutdown');\n return;\n case 'detach': {\n currentTurnState = 'done';\n void writeBridgeMeta('done');\n const data = (await onDetach?.()) ?? {};\n sendControl({ type: 'bridge-detach', data });\n drainThenExit(ws, 1000, 'detach');\n return;\n }\n }\n };\n\n // ─── server ─────────────────────────────────────────────────────────\n void writeBridgeMeta('init');\n\n const wss = new WebSocketServer({ port: bridgeWsPort, host: '0.0.0.0' });\n\n const exit = (): void => {\n if (options.onExit) {\n options.onExit();\n return;\n }\n wss.close(() => process.exit(0));\n setTimeout(() => process.exit(0), 1000).unref();\n };\n\n const drainThenExit = (ws: WebSocket, code: number, reason: string): void => {\n const start = Date.now();\n const tick = (): void => {\n const drained = ws.bufferedAmount === 0 || ws.readyState !== WS_OPEN;\n if (drained || Date.now() - start >= 5_000) {\n // Flush the on-disk log so a clean shutdown/detach leaves a complete\n // event-log.ndjson for any later replay recovery.\n void flushPendingEventsToDisk().finally(() => {\n try {\n ws.close(code, reason);\n } finally {\n exit();\n }\n });\n return;\n }\n setTimeout(tick, 10).unref();\n };\n tick();\n };\n\n wss.on('listening', () => {\n const addr = wss.address();\n currentBoundPort = typeof addr === 'object' && addr ? addr.port : 0;\n currentTurnState = 'waiting';\n void writeBridgeMeta('waiting');\n stdout.write(\n JSON.stringify({\n type: 'bridge-ready',\n port: currentBoundPort,\n }) + '\\n',\n );\n options.onListening?.(currentBoundPort);\n });\n\n wss.on('connection', (ws: WebSocket, req: { url?: string }) => {\n const url = new URL(req.url ?? '/', 'http://localhost');\n if (url.searchParams.get('agent_bridge_token') !== expectedToken) {\n ws.close(1008, 'unauthorized');\n return;\n }\n\n // Single-flight: a fresh authorized connection *replaces* the active one\n // (the host reconnecting after a drop). The previous socket's close is a\n // no-op below because it is no longer `activeSocket`.\n activeSocket = ws;\n\n // Announce liveness the instant we accept. Some sandbox runtimes complete\n // the host-side WS handshake before the connection is forwarded here; the\n // host waits for this frame before sending `start`/`resume`.\n sendControl({\n type: 'bridge-hello',\n state: currentTurnState,\n lastSeq: seqCounter,\n });\n\n ws.on('message', (raw: ArrayBufferLike | string) => {\n let parsed: TStart | InboundControl;\n try {\n const text =\n typeof raw === 'string' ? raw : Buffer.from(raw).toString('utf8');\n parsed = JSON.parse(text) as TStart | InboundControl;\n } catch (err) {\n sendControl({\n type: 'error',\n error: `protocol parse error: ${(err as Error).message}`,\n });\n return;\n }\n void handleInbound(parsed, ws);\n });\n\n ws.on('close', () => {\n // Only the *current* socket's close matters. A stale socket (already\n // replaced by a reconnect) closing is a no-op. Crucially we do NOT abort\n // the in-flight turn — it keeps running and its events accumulate in the\n // log for replay when the host reconnects.\n if (activeSocket === ws) {\n activeSocket = undefined;\n }\n });\n\n ws.on('error', () => {\n // 'close' follows; nothing to do beyond keeping the process alive.\n });\n });\n\n // Surface bridge-internal crashes to the host instead of dying silently.\n process.on('uncaughtException', err => {\n emit({ type: 'error', error: serialiseError(err) });\n });\n process.on('unhandledRejection', err => {\n emit({ type: 'error', error: serialiseError(err) });\n });\n\n await new Promise<void>((resolve, reject) => {\n if (wss.address() != null) {\n resolve();\n return;\n }\n\n wss.once('listening', resolve);\n wss.once('error', reject);\n });\n\n return {\n port: currentBoundPort,\n close: () =>\n new Promise<void>(resolve => {\n wss.close(() => resolve());\n }),\n };\n}\n\nfunction serialiseError(err: unknown): unknown {\n if (err instanceof Error) {\n return { name: err.name, message: err.message, stack: err.stack };\n }\n return err;\n}\n","// Long-running process that runs alongside the `codex` CLI in the sandbox.\n// The generic transport — WebSocket server, token auth, single-flight\n// reconnect, the in-memory event log + `seq`, resume replay, and the\n// lifecycle/meta files — lives in the shared `@ai-sdk/harness/bridge` runtime.\n// This file supplies only the Codex-specific turn driver.\n//\n// Host-defined tools are routed through an HTTP relay bound to\n// `127.0.0.1:0` with bearer-token auth. The codex CLI spawns\n// `host-tool-mcp.mjs` (shipped alongside this file) as a stdio MCP server;\n// the shim POSTs each tool call to the relay, which emits `tool-call` to\n// the host and waits for the matching `tool-result`.\n\nimport {\n runBridge,\n type BridgeEvent,\n type BridgeTurn,\n} from '@ai-sdk/harness/bridge';\nimport type { HarnessV1BuiltinToolName } from '@ai-sdk/harness';\nimport type { StartMessage } from '../codex-bridge-protocol';\nimport { randomUUID } from 'node:crypto';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { createServer, type Server } from 'node:http';\n// Temporary workaround for upstream codex MCP-tool bug — see ./cli-relay.ts\nimport {\n CLI_SHIM_FILENAME,\n buildCliShimScript,\n isToolRelayCommand,\n} from './cli-relay';\nimport { argv, env as procEnv, stdout } from 'node:process';\n\n/*\n * CONSTRAINT — the third-party imports below are NEVER bundled into the\n * compiled `bridge/index.mjs`. They are declared `external` in\n * tsup.config.ts and resolved at runtime from the node_modules that this\n * bridge installs *inside the sandbox* from `src/bridge/package.json` (and\n * its pinned `pnpm-lock.yaml`). That bridge package.json — NOT this host\n * package — is the single source of truth for these packages and their\n * versions; the published `@ai-sdk/harness-codex` package does not provide\n * them at runtime.\n *\n * When adding or changing a third-party import here you MUST keep all three\n * in sync, or the bridge will either get the dependency bundled in or fail\n * to resolve it in the sandbox:\n * 1. the import statement below,\n * 2. the `external` array in tsup.config.ts, and\n * 3. the dependency entry in `src/bridge/package.json`.\n */\nimport * as codexSdkModule from '@openai/codex-sdk';\n\n/*\n * Native Codex tool name → cross-harness common name. Tools outside this map\n * (e.g. MCP tools the model invokes by name) have no common equivalent; their\n * native name is forwarded as-is on `tool-call` events.\n */\nconst NATIVE_TO_COMMON: Readonly<Record<string, HarnessV1BuiltinToolName>> = {\n shell: 'bash',\n web_search: 'webSearch',\n};\n\nfunction toCommonName(nativeName: string): HarnessV1BuiltinToolName | string {\n return NATIVE_TO_COMMON[nativeName] ?? nativeName;\n}\n\nconst args = parseArgs(argv.slice(2));\nconst workdir = requireArg({ value: args.workdir, name: '--workdir' });\nconst bridgeStateDir = requireArg({\n value: args.bridgeStateDir,\n name: '--bridge-state-dir',\n});\nconst cliShimDir = requireArg({\n value: args.cliShimDir,\n name: '--cli-shim-dir',\n});\nconst bootstrapDir = args.bootstrapDir ?? workdir;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst codexSdk = codexSdkModule as any;\n\n// Codex thread id — survives across turns within this bridge process and is\n// returned to the host on `detach` so a future process can resume the thread.\nconst threadState: { id: string | undefined } = { id: undefined };\n\nawait runBridge<StartMessage>({\n bridgeType: 'codex',\n bridgeStateDir,\n onStart: runTurn,\n onDetach: () => (threadState.id ? { threadId: threadState.id } : {}),\n});\n\ntype Emit = (msg: Record<string, unknown>) => void;\n\nasync function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {\n const emit: Emit = msg => turn.emit(msg as BridgeEvent);\n\n // Cross-process resume: the host carries the threadId we returned on detach.\n // Seed `threadState.id` so the codex SDK call below takes the `resumeThread`\n // branch.\n if (\n typeof start.resumeThreadId === 'string' &&\n start.resumeThreadId.length > 0\n ) {\n threadState.id = start.resumeThreadId;\n }\n\n /*\n * Known limitation: codex CLI does not currently surface MCP tools to the\n * model in `codex exec --experimental-json` mode (the path the\n * `@openai/codex-sdk` uses). The MCP handshake completes and `tools/list`\n * returns the host tool, but codex never registers it as a model-callable\n * function. Built-in MCP-resource accessors (`list_mcp_resources` etc.) are\n * exposed; tools are not. Tracked upstream at\n * https://github.com/openai/codex/issues/19425.\n *\n * Until that's fixed, host tools are made available to the model via a\n * separate CLI-relay workaround (see `./cli-relay.ts`). The MCP server\n * config below is kept so that the day codex starts exposing MCP tools\n * properly, host tools work both ways. Writing the shim here, adding matching\n * prompt guidance in the host adapter, and filtering the shim command below\n * implement the workaround and can be removed once the upstream bug is fixed.\n */\n const mcpServers: Record<string, unknown> = {};\n let relay: { port: number; close(): void } | undefined;\n let cliShimPath: string | undefined;\n if (start.tools && start.tools.length > 0) {\n const relayToken = randomUUID();\n relay = await startToolRelay({\n relayToken,\n tools: start.tools,\n emit,\n requestToolResult: turn.requestToolResult,\n });\n mcpServers['harness-tools'] = {\n enabled: true,\n command: 'node',\n args: [`${bootstrapDir}/host-tool-mcp.mjs`],\n env: {\n TOOL_SCHEMAS: JSON.stringify(\n start.tools.map(t => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n ),\n TOOL_RELAY_URL: `http://127.0.0.1:${relay.port}`,\n TOOL_RELAY_TOKEN: relayToken,\n },\n };\n // Temporary workaround for upstream codex MCP-tool bug — see ./cli-relay.ts\n cliShimPath = `${cliShimDir}/${CLI_SHIM_FILENAME}`;\n await mkdir(cliShimDir, { recursive: true });\n await writeFile(\n cliShimPath,\n buildCliShimScript({ relayPort: relay.port, relayToken }),\n 'utf8',\n );\n }\n\n const codexConfig: Record<string, unknown> = {};\n if (Object.keys(mcpServers).length > 0) codexConfig.mcp_servers = mcpServers;\n\n const gatewayBaseUrl = procEnv.AI_GATEWAY_BASE_URL;\n const hasGatewayAuth = Boolean(procEnv.AI_GATEWAY_API_KEY || gatewayBaseUrl);\n if (hasGatewayAuth && !gatewayBaseUrl) {\n throw new Error(\n 'AI Gateway auth was selected but AI_GATEWAY_BASE_URL is missing from the Codex bridge environment.',\n );\n }\n const apiBaseUrl = hasGatewayAuth ? gatewayBaseUrl : procEnv.OPENAI_BASE_URL;\n if (apiBaseUrl) {\n codexConfig.preferred_auth_method = 'apikey';\n codexConfig.model_provider = 'agent_bridge_openai';\n codexConfig.model_providers = {\n agent_bridge_openai: {\n name: procEnv.CODEX_MODEL_PROVIDER_NAME || 'Agent Bridge OpenAI',\n base_url: apiBaseUrl,\n env_key: 'CODEX_API_KEY',\n wire_api: 'responses',\n supports_websockets: false,\n },\n };\n }\n const usesConfiguredModelProvider =\n typeof codexConfig.model_provider === 'string';\n\n const codex = new codexSdk.Codex({\n ...(procEnv.CODEX_API_KEY ? { apiKey: procEnv.CODEX_API_KEY } : {}),\n ...(!usesConfiguredModelProvider && apiBaseUrl\n ? { baseUrl: apiBaseUrl }\n : {}),\n env: Object.fromEntries(\n Object.entries(procEnv).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n ),\n ),\n ...(Object.keys(codexConfig).length > 0 ? { config: codexConfig } : {}),\n });\n\n const threadOptions = {\n ...(start.model ? { model: start.model } : {}),\n sandboxMode: 'danger-full-access',\n approvalPolicy: 'never',\n workingDirectory: workdir,\n skipGitRepoCheck: true,\n ...(start.reasoningEffort\n ? { modelReasoningEffort: start.reasoningEffort }\n : {}),\n webSearchMode: start.webSearch ? 'live' : 'disabled',\n };\n const thread = threadState.id\n ? codex.resumeThread(threadState.id, threadOptions)\n : codex.startThread(threadOptions);\n\n emit({ type: 'stream-start' });\n\n const userMessage = start.prompt;\n let turnUsage: Record<string, unknown> | undefined;\n const textByItem = new Map<string, string>();\n const reasoningByItem = new Map<string, string>();\n\n try {\n const { events } = await thread.runStreamed(userMessage, {\n signal: turn.abortSignal,\n });\n for await (const event of events as AsyncIterable<CodexEvent>) {\n if (turn.abortSignal.aborted) break;\n if (\n event.type === 'thread.started' &&\n typeof event.thread_id === 'string'\n ) {\n threadState.id = event.thread_id;\n // Announce to the host so it can include the id in resume state.\n emit({ type: 'bridge-thread', threadId: event.thread_id });\n }\n // Temporary workaround for upstream codex MCP-tool bug — see ./cli-relay.ts\n if (\n cliShimPath &&\n event.item?.type === 'command_execution' &&\n typeof event.item.command === 'string' &&\n isToolRelayCommand({ command: event.item.command, cliShimPath })\n ) {\n continue;\n }\n translateAndEmit(event, {\n send: emit,\n textByItem,\n reasoningByItem,\n setTurnUsage: u => (turnUsage = u),\n });\n }\n } catch (err) {\n emit({ type: 'error', error: serialiseError(err) });\n return;\n } finally {\n relay?.close();\n }\n\n emit({\n type: 'finish',\n finishReason: { unified: 'stop', raw: 'stop' },\n totalUsage: turnUsage ?? defaultUsage(),\n });\n\n void turn.pendingUserMessages; // accepted but only consumed when codex supports streamed user input\n}\n\ntype CodexItem = {\n type: string;\n id?: string;\n text?: string;\n command?: string;\n exit_code?: number;\n aggregated_output?: string;\n status?: 'in_progress' | 'completed' | 'failed';\n server?: string;\n tool?: string;\n arguments?: unknown;\n result?: { content?: unknown; structured_content?: unknown } | unknown;\n error?: { message?: string };\n query?: string;\n message?: string;\n changes?: ReadonlyArray<{\n path: string;\n kind: 'add' | 'delete' | 'update';\n }>;\n};\n\nfunction extractMcpToolCallResult(item: CodexItem): unknown {\n if (\n item.result === undefined ||\n item.result === null ||\n typeof item.result !== 'object'\n ) {\n return item.error?.message ? { error: item.error.message } : null;\n }\n const result = item.result as {\n content?: unknown;\n structured_content?: unknown;\n };\n if (\n result.structured_content !== undefined &&\n result.structured_content !== null\n ) {\n return result.structured_content;\n }\n return result.content ?? null;\n}\n\ntype CodexEvent = {\n type:\n | 'thread.started'\n | 'turn.completed'\n | 'turn.failed'\n | 'error'\n | 'item.started'\n | 'item.updated'\n | 'item.completed';\n item?: CodexItem;\n usage?: Record<string, number>;\n error?: { message: string };\n message?: string;\n thread_id?: string;\n};\n\nfunction translateAndEmit(\n event: CodexEvent,\n ctx: {\n send: Emit;\n textByItem: Map<string, string>;\n reasoningByItem: Map<string, string>;\n setTurnUsage: (u: Record<string, unknown>) => void;\n },\n): void {\n if (event.type === 'turn.completed') {\n if (event.usage) ctx.setTurnUsage(mapUsage(event.usage));\n ctx.send({\n type: 'finish-step',\n finishReason: { unified: 'stop', raw: 'stop' },\n usage: event.usage ? mapUsage(event.usage) : defaultUsage(),\n });\n return;\n }\n if (event.type === 'turn.failed') {\n ctx.send({\n type: 'error',\n error: event.error?.message ?? 'codex turn failed',\n });\n return;\n }\n if (event.type === 'error') {\n ctx.send({ type: 'error', error: event.message ?? 'codex error' });\n return;\n }\n if (!event.item) return;\n const item = event.item;\n const id = item.id ?? randomUUID();\n\n if (item.type === 'agent_message' && typeof item.text === 'string') {\n /*\n * The presence of `id` in `textByItem` — not the `item.started` event —\n * marks the text part as opened. Codex does not guarantee an\n * `item.started` event carrying text precedes the first `item.updated`\n * with text, so keying the `text-start` off the event type can emit a\n * `text-delta` for a part that was never opened. Opening lazily on the\n * first event with text keeps `text-start` before any `text-delta`.\n */\n if (!ctx.textByItem.has(id)) {\n ctx.send({ type: 'text-start', id });\n ctx.textByItem.set(id, '');\n }\n const last = ctx.textByItem.get(id) ?? '';\n const next = item.text;\n if (next.length > last.length) {\n ctx.send({ type: 'text-delta', id, delta: next.slice(last.length) });\n ctx.textByItem.set(id, next);\n }\n if (event.type === 'item.completed') ctx.send({ type: 'text-end', id });\n return;\n }\n\n if (item.type === 'reasoning' && typeof item.text === 'string') {\n if (!ctx.reasoningByItem.has(id)) {\n ctx.send({ type: 'reasoning-start', id });\n ctx.reasoningByItem.set(id, '');\n }\n const last = ctx.reasoningByItem.get(id) ?? '';\n const next = item.text;\n if (next.length > last.length) {\n ctx.send({ type: 'reasoning-delta', id, delta: next.slice(last.length) });\n ctx.reasoningByItem.set(id, next);\n }\n if (event.type === 'item.completed')\n ctx.send({ type: 'reasoning-end', id });\n return;\n }\n\n if (item.type === 'command_execution') {\n const nativeName = 'shell';\n if (event.type === 'item.started') {\n ctx.send({\n type: 'tool-call',\n toolCallId: id,\n toolName: toCommonName(nativeName),\n nativeName,\n input: JSON.stringify({ command: item.command ?? '' }),\n providerExecuted: true,\n });\n } else if (event.type === 'item.completed') {\n ctx.send({\n type: 'tool-result',\n toolCallId: id,\n toolName: toCommonName(nativeName),\n result: {\n exitCode: item.exit_code ?? null,\n output: item.aggregated_output ?? '',\n status: item.status ?? 'completed',\n },\n });\n }\n return;\n }\n\n if (item.type === 'mcp_tool_call') {\n const isHostTool = item.server === 'harness-tools';\n if (event.type === 'item.started') {\n ctx.send({\n type: 'tool-call',\n toolCallId: id,\n toolName: item.tool ?? 'unknown',\n ...(isHostTool ? {} : { nativeName: item.tool ?? 'unknown' }),\n input: JSON.stringify(item.arguments ?? {}),\n providerExecuted: !isHostTool,\n });\n } else if (event.type === 'item.completed') {\n ctx.send({\n type: 'tool-result',\n toolCallId: id,\n toolName: item.tool ?? 'unknown',\n result: extractMcpToolCallResult(item),\n });\n }\n return;\n }\n\n if (item.type === 'web_search') {\n const nativeName = 'web_search';\n if (event.type === 'item.started') {\n ctx.send({\n type: 'tool-call',\n toolCallId: id,\n toolName: toCommonName(nativeName),\n nativeName,\n input: JSON.stringify({ query: item.query ?? '' }),\n providerExecuted: true,\n });\n } else if (event.type === 'item.completed') {\n ctx.send({\n type: 'tool-result',\n toolCallId: id,\n toolName: toCommonName(nativeName),\n result: item.result ?? null,\n });\n }\n return;\n }\n\n if (item.type === 'file_change' && event.type === 'item.completed') {\n for (const change of item.changes ?? []) {\n ctx.send({\n type: 'file-change',\n event:\n change.kind === 'add'\n ? 'create'\n : change.kind === 'delete'\n ? 'delete'\n : 'modify',\n path: change.path,\n });\n }\n return;\n }\n\n if (item.type === 'error' && event.type === 'item.completed') {\n ctx.send({\n type: 'error',\n error: (item as { message?: string }).message ?? 'codex item error',\n });\n return;\n }\n}\n\nfunction mapUsage(usage: Record<string, number>): Record<string, unknown> {\n const input = usage.input_tokens ?? 0;\n const cacheRead = usage.cached_input_tokens ?? 0;\n return {\n inputTokens: {\n total: input,\n noCache: Math.max(0, input - cacheRead),\n cacheRead,\n cacheWrite: 0,\n },\n outputTokens: {\n total: usage.output_tokens ?? 0,\n text: usage.output_tokens ?? 0,\n },\n };\n}\n\nfunction defaultUsage(): Record<string, unknown> {\n return {\n inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },\n outputTokens: { total: 0, text: 0 },\n };\n}\n\n/**\n * Tool relay — HTTP server on 127.0.0.1:0 with bearer-token auth. The MCP\n * stdio shim spawned by codex POSTs each tool invocation here; the relay\n * forwards the call to the host (via the shared runtime's `emit`), awaits the\n * matching `tool-result` (via `requestToolResult`), and responds with\n * `{ result }`.\n */\nasync function startToolRelay({\n relayToken,\n tools,\n emit,\n requestToolResult,\n}: {\n relayToken: string;\n tools: ReadonlyArray<{ name: string }>;\n emit: Emit;\n requestToolResult: (\n toolCallId: string,\n ) => Promise<{ output: unknown; isError?: boolean }>;\n}): Promise<{ port: number; close(): void }> {\n const toolNames = new Set(tools.map(t => t.name));\n\n const server = createServer(async (req, res) => {\n try {\n if (\n req.method !== 'POST' ||\n req.url !== '/' ||\n req.headers.authorization !== `Bearer ${relayToken}`\n ) {\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'unauthorized tool relay request' }));\n return;\n }\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk as Buffer);\n }\n const body = Buffer.concat(chunks).toString('utf8');\n const { requestId, toolName, input } = JSON.parse(body) as {\n requestId: string;\n toolName: string;\n input: unknown;\n };\n\n if (!toolNames.has(toolName)) {\n res.writeHead(403, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({ error: `Tool \"${toolName}\" is not available` }),\n );\n return;\n }\n\n emit({\n type: 'tool-call',\n toolCallId: requestId,\n toolName,\n input: JSON.stringify(input ?? {}),\n providerExecuted: false,\n });\n\n const { output, isError } = await requestToolResult(requestId);\n emit({\n type: 'tool-result',\n toolCallId: requestId,\n toolName,\n result: output ?? null,\n isError: !!isError,\n });\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ result: output }));\n } catch (error) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n });\n\n await new Promise<void>(resolve =>\n server.listen(0, '127.0.0.1', () => resolve()),\n );\n const address = server.address();\n if (!address || typeof address === 'string') {\n throw new Error('tool relay did not expose a numeric port');\n }\n return {\n port: address.port,\n close: () => closeServer(server),\n };\n}\n\nfunction closeServer(server: Server): void {\n try {\n server.close();\n } catch {}\n}\n\nfunction parseArgs(args: string[]): {\n workdir?: string;\n bridgeStateDir?: string;\n bootstrapDir?: string;\n cliShimDir?: string;\n} {\n const out: {\n workdir?: string;\n bridgeStateDir?: string;\n bootstrapDir?: string;\n cliShimDir?: string;\n } = {};\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--workdir' && i + 1 < args.length) {\n out.workdir = args[++i];\n } else if (args[i] === '--bridge-state-dir' && i + 1 < args.length) {\n out.bridgeStateDir = args[++i];\n } else if (args[i] === '--bootstrap-dir' && i + 1 < args.length) {\n out.bootstrapDir = args[++i];\n } else if (args[i] === '--cli-shim-dir' && i + 1 < args.length) {\n out.cliShimDir = args[++i];\n }\n }\n return out;\n}\n\nfunction serialiseError(err: unknown): unknown {\n if (err instanceof Error) {\n return { name: err.name, message: err.message, stack: err.stack };\n }\n return err;\n}\n\nfunction emitFatal(message: string): never {\n stdout.write(JSON.stringify({ type: 'bridge-fatal', message }) + '\\n');\n process.exit(1);\n}\n\nfunction requireArg({\n value,\n name,\n}: {\n value: string | undefined;\n name: string;\n}): string {\n if (!value) {\n emitFatal(`Missing ${name} argument.`);\n }\n return value;\n}\n","/*\n * Temporary workaround for upstream codex CLI bug\n * https://github.com/openai/codex/issues/19425 — MCP tools registered via\n * `mcp_servers.*` are not exposed to the model in `codex exec --experimental-json`\n * mode (which is what `@openai/codex-sdk` uses). The MCP handshake completes\n * and `tools/list` succeeds, but codex never registers the tools as\n * model-callable functions.\n *\n * Until that's fixed upstream, this file implements a CLI-based relay:\n *\n * 1. A small Node script is written into harness-owned session state at turn\n * start (`buildCliShimScript`). It accepts `<toolName> <jsonInput>` as\n * argv, POSTs to the same HTTP relay the MCP shim uses, and prints the\n * result to stdout.\n *\n * 2. Tool descriptions and invocation instructions are injected into the\n * initial user prompt by the host adapter, telling the model to call host\n * tools by running `node <shim-path> <toolName> '<jsonInput>'` via its\n * built-in `bash` tool.\n *\n * 3. The bridge's event loop suppresses the matching `command_execution`\n * events (`isToolRelayCommand`) so callers receive clean `tool-call` /\n * `tool-result` events from the HTTP relay rather than seeing the\n * relay invocations as raw bash commands.\n *\n * Once #19425 is fixed and MCP tools are properly exposed in exec mode, the\n * three hookpoints in `bridge/index.ts` can be removed along with this file.\n */\n\nexport const CLI_SHIM_FILENAME = 'harness-tool.mjs';\n\nexport function buildCliShimScript({\n relayPort,\n relayToken,\n}: {\n relayPort: number;\n relayToken: string;\n}): string {\n return `#!/usr/bin/env node\nconst [toolName, inputJson = '{}'] = process.argv.slice(2);\nif (!toolName) {\n console.error('Usage: harness-tool <tool_name> <json_input>');\n process.exit(64);\n}\nlet input;\ntry {\n input = JSON.parse(inputJson);\n} catch (error) {\n console.error('Invalid JSON input: ' + (error instanceof Error ? error.message : String(error)));\n process.exit(64);\n}\nconst requestId = 'cli-' + Date.now() + '-' + Math.random().toString(16).slice(2);\nconst response = await fetch('http://127.0.0.1:${relayPort}', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ${relayToken}' },\n body: JSON.stringify({ requestId, toolName, input }),\n});\nconst text = await response.text();\nlet payload;\ntry {\n payload = text ? JSON.parse(text) : {};\n} catch {\n payload = { error: text };\n}\nif (!response.ok || payload.error) {\n console.error(String(payload.error ?? ('tool relay failed with HTTP ' + response.status)));\n process.exit(1);\n}\nconsole.log(JSON.stringify(payload.result ?? payload, null, 2));\n`;\n}\n\nexport function isToolRelayCommand({\n command,\n cliShimPath,\n}: {\n command: string;\n cliShimPath: string;\n}): boolean {\n return command.includes(cliShimPath);\n}\n"],"mappings":"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","names":["bridgeStateDir","mkdir","writeFile","procEnv","stdout","mkdir","writeFile","serialiseError","args"]}
|