@ai-sdk/harness-codex 1.0.0-canary.1 → 1.0.0-canary.3

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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # @ai-sdk/harness-codex
2
2
 
3
+ ## 1.0.0-canary.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 1ea15a3: fix(harness): fix various bugs with harness skills not being correctly processed by the harness adapters
8
+ - e551763: fix(harness): avoid using peer dependencies for underlying harness and sandbox SDKs
9
+ - Updated dependencies [3d87086]
10
+ - Updated dependencies [aeda373]
11
+ - Updated dependencies [1ea15a3]
12
+ - Updated dependencies [375fdd7]
13
+ - Updated dependencies [b4507d5]
14
+ - @ai-sdk/harness@1.0.0-canary.7
15
+ - @ai-sdk/provider-utils@5.0.0-canary.48
16
+
17
+ ## 1.0.0-canary.2
18
+
19
+ ### Patch Changes
20
+
21
+ - @ai-sdk/harness@1.0.0-canary.6
22
+
3
23
  ## 1.0.0-canary.1
4
24
 
5
25
  ### Patch Changes
@@ -593,7 +593,6 @@ async function runTurn(start, turn) {
593
593
  const userMessage = composeUserMessage({
594
594
  text: start.prompt,
595
595
  instructions: start.instructions,
596
- skills: start.skills,
597
596
  // Temporary workaround for upstream codex MCP-tool bug — see ./cli-relay.ts
598
597
  toolUsageBlock: cliShimPath && start.tools && start.tools.length > 0 ? composeToolUsageInstructions({
599
598
  tools: start.tools,
@@ -809,7 +808,6 @@ function defaultUsage() {
809
808
  function composeUserMessage({
810
809
  text,
811
810
  instructions,
812
- skills,
813
811
  toolUsageBlock
814
812
  }) {
815
813
  const blocks = [];
@@ -822,13 +820,6 @@ ${instructions}
822
820
  </session-instructions>`
823
821
  );
824
822
  }
825
- if (skills && skills.length > 0) {
826
- const lines = ["## Available skills"];
827
- for (const skill of skills) {
828
- lines.push("", `### ${skill.name}`, skill.description, "", skill.content);
829
- }
830
- blocks.push(lines.join("\n"));
831
- }
832
823
  if (toolUsageBlock) blocks.push(toolUsageBlock);
833
824
  blocks.push(instructions ? `<user-message>
834
825
  ${text}
@@ -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 => {\n if (wss.address() != null) {\n resolve();\n return;\n }\n wss.on('listening', () => resolve());\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 apiBaseUrl = procEnv.AI_GATEWAY_API_KEY\n ? procEnv.AI_GATEWAY_BASE_URL || 'https://ai-gateway.vercel.sh/v1'\n : 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 skills: start.skills,\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 skills,\n toolUsageBlock,\n}: {\n text: string;\n instructions: string | undefined;\n skills:\n | ReadonlyArray<{ name: string; description: string; content: string }>\n | 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 (skills && skills.length > 0) {\n const lines: string[] = ['## Available skills'];\n for (const skill of skills) {\n lines.push('', `### ${skill.name}`, skill.description, '', skill.content);\n }\n blocks.push(lines.join('\\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":";AAQA,SAAS,YAAY,OAAO,iBAAiB;AAC7C,SAAS,YAAY,oBAAoB;AACzC,SAAS,OAAO,SAAS,KAAK,cAAc;AAC5C,SAAS,uBAAuC;AAqBhD,IAAM,qBAAuD;EAC3D,OAAO;EACP,MAAM;EACN,MAAM;EACN,OAAO;EACP,OAAO;AACT;AAGA,SAAS,iBACP,SACA,WACS;AACT,MAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO;AAC7C,SAAO,QAAQ;IACb,CAAA,WAAU,cAAc,UAAU,UAAU,WAAW,GAAG,MAAM,GAAG;EACrE;AACF;AAEA,SAAS,kBAAkB,KAIzB;AACA,MAAI,eAAe,OAAO;AACxB,WAAO,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,SAAS,OAAO,IAAI,MAAM;EAClE;AACA,SAAO,EAAE,SAAS,OAAO,GAAG,EAAE;AAChC;AAEA,SAAS,aAAa,OAAiD;AACrE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,QAAQ,MACX,MAAM,GAAG,EACT,IAAI,CAAA,SAAQ,KAAK,KAAK,CAAC,EACvB,OAAO,OAAO;AACjB,SAAO,MAAM,SAAS,IAAI,QAAQ;AACpC;AAEA,IAAM,aAAa,oBAAI,IAAI,CAAC,KAAK,QAAQ,OAAO,IAAI,CAAC;AAsGrD,IAAM,UAAU;AAehB,eAAsB,UACpB,SACuB;AACvB,QAAM,EAAE,YAAY,gBAAAA,iBAAgB,SAAS,SAAS,IAAI;AAC1D,QAAM,gBAAgB,QAAQ,SAAS,QAAQ,wBAAwB;AACvE,QAAM,eACJ,QAAQ,QAAQ,SAAS,QAAQ,kBAAkB,KAAK,EAAE;AAE5D,QAAM,iBAAiB,GAAGA,eAAc;AACxC,QAAM,kBAAkB,GAAGA,eAAc;AACzC,QAAM,uBAAuB,GAAGA,eAAc;AAC9C,QAAM,eAAe,GAAGA,eAAc;AAEtC,MAAI;AACF,UAAM,MAAMA,iBAAgB,EAAE,WAAW,KAAK,CAAC;EACjD,QAAQ;EAER;AAGA,MAAI,mBAAmB;AACvB,MAAI,mBAAgC;AACpC,MAAI;AACJ,MAAI,cAAc;AAClB,MAAI;AACJ,MAAI;AAIJ,MAAI;AACJ,MAAI,0BAA0B;AAC9B,QAAM,kBAAkB,WAAW;KAChC,QAAQ,iBAAiB,IAAI,YAAY;EAC5C;AAMA,MAAI,aAAa;AACjB,MAAI,WAAiD,CAAC;AAUtD,MAAI,aAAa;AACjB,MAAI,eAAqC;AAEzC,QAAM,oBAAoB,YAA2B;AACnD,WAAO,WAAW,SAAS,GAAG;AAC5B,YAAM,MAAM;AACZ,mBAAa;AACb,YAAM,WAAW,cAAc,GAAG,EAAE,MAAM,MAAM;MAGhD,CAAC;IACH;EACF;AAEA,QAAM,qBAAqB,MAAY;AACrC,QAAI,aAAc;AAClB,mBAAe,IAAI,QAAc,CAAA,YAAW;AAC1C,mBAAa,MAAM;AACjB,aAAK,kBAAkB,EAAE,QAAQ,OAAO;MAC1C,CAAC;IACH,CAAC,EAAE,QAAQ,MAAM;AACf,qBAAe;AACf,UAAI,WAAW,SAAS,GAAG;AACzB,2BAAmB;MACrB;IACF,CAAC;EACH;AAEA,QAAM,2BAA2B,YAA2B;AAC1D,QAAI,WAAW,SAAS,KAAK,CAAC,cAAc;AAC1C,yBAAmB;IACrB;AAIA,QAAI,WAAW;AACf,WAAO,UAAU;AACf,YAAM;AACN,iBAAW;IACb;EACF;AAUA,QAAM,iBAAiB,QAAQ,4BAA4B;AAC3D,MAAI,kBAAkB,WAAW,YAAY,GAAG;AAC9C,QAAI;AACF,YAAM,QAAQ,aAAa,cAAc,MAAM,EAC5C,MAAM,IAAI,EACV,IAAI,CAAA,SAAQ,KAAK,KAAK,CAAC,EACvB,OAAO,OAAO;AACjB,iBAAW,MAAM,IAAI,CAAA,UAAS;QAC5B,KAAM,KAAK,MAAM,IAAI,EAAsB;QAC3C;MACF,EAAE;AACF,mBAAa,SAAS,GAAG,EAAE,GAAG,OAAO;IACvC,QAAQ;AAGN,iBAAW,CAAC;AACZ,mBAAa;IACf;EACF;AAEA,QAAM,qBAAqB,oBAAI,IAG7B;AACF,QAAM,uBAAuB,oBAAI,IAG/B;AAGF,QAAM,kBAAkB,OAAO,UAAsC;AACnE,QAAI;AACF,YAAM;QACJ;QACA,KAAK,UAAU;UACb,MAAM;UACN,MAAM;UACN;UACA;QACF,CAAC;MACH;IACF,QAAQ;IAER;EACF;AAEA,QAAM,mBAAmB,OAAO,UAAkC;AAChE,QAAI;AACF,YAAM,aAAa,KAAK,UAAU,KAAK;AACvC,YAAM,UAAU,iBAAiB,UAAU;AAG3C,UAAI,CAAC,WAAW,oBAAoB,GAAG;AACrC,cAAM,UAAU,sBAAsB,UAAU;MAClD;IACF,QAAQ;IAER;EACF;AAGA,QAAM,cAAc,CAAC,QAAuC;AAC1D,QAAI,cAAc,eAAe,SAAS;AACxC,UAAI;AACF,qBAAa,KAAK,KAAK,UAAU,GAAG,CAAC;MACvC,QAAQ;MAER;IACF;EACF;AAEA,QAAM,OAAO,CAAC,UAA6B;AACzC,UAAM,MAAM,EAAE;AACd,UAAM,OAAO,KAAK,UAAU,EAAE,GAAG,OAAO,IAAI,CAAC;AAC7C,aAAS,KAAK,EAAE,KAAK,KAAK,CAAC;AAC3B,kBAAc,GAAG,IAAI;;AACrB,uBAAmB;AACnB,QAAI,cAAc,eAAe,SAAS;AACxC,UAAI;AACF,qBAAa,KAAK,IAAI;MACxB,QAAQ;MAGR;IACF;EACF;AAEA,QAAM,SAAS,CAAC,IAAe,aAA2B;AACxD,eAAW,SAAS,UAAU;AAC5B,UAAI,MAAM,MAAM,YAAY,GAAG,eAAe,SAAS;AACrD,WAAG,KAAK,MAAM,IAAI;MACpB;IACF;EACF;AAGA,QAAM,uBAAuB,CAC3B,OACA,cACY;AACZ,QAAI,CAAC,aAAa,QAAS,QAAO;AAClC,UAAM,YAAY,YAAY,SAAS;AACvC,QAAI,mBAAmB,KAAK,IAAI,mBAAmB,SAAS,EAAG,QAAO;AACtE,WAAO,iBAAiB,YAAY,YAAY,SAAS;EAC3D;AAUA,QAAM,iBAAiB,QAAQ,OAAO,MAAM,KAAK,QAAQ,MAAM;AAC/D,QAAM,iBAAiB,QAAQ,OAAO,MAAM,KAAK,QAAQ,MAAM;AAC/D,QAAM,wBAAwB,MAAY;AACxC,QAAI,wBAAyB;AAC7B,8BAA0B;AAC1B,UAAM,UAA8C;MAClD,QAAQ;MACR,QAAQ;IACV;AACA,UAAM,QACJ,CAAC,QAA6B,QAC9B,CAAC,OAAgB,UAAoB,OAA0B;AAC7D,UAAI,aAAa,SAAS;AACxB,YAAI;AACF,gBAAM,MAAM,OAAO,aAAa,WAAW,WAAW;AACtD,gBAAM,OACJ,OAAO,UAAU,WACb,QACA,OAAO,KAAK,KAAmB,EAAE;YAC/B;UACF;AACN,gBAAM,WAAW,QAAQ,MAAM,IAAI,KAAK,QAAQ,SAAS,IAAI;AAC7D,gBAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,kBAAQ,MAAM,IAAI,MAAM,IAAI,KAAK;AACjC,qBAAW,QAAQ,OAAO;AACxB,kBAAM,UAAU,KAAK,QAAQ,QAAQ,EAAE;AACvC,gBAAI,SAAS;AACX,mBAAK;gBACH,MAAM;gBACN,QAAQ;gBACR;gBACA,MAAM;cACR,CAAC;YACH;UACF;QACF,QAAQ;QAER;MACF;AACA,aAAQ;QACN;QACA;QACA;MACF;IACF;AACF,YAAQ,OAAO,QAAQ;MACrB;MACA;IACF;AACA,YAAQ,OAAO,QAAQ;MACrB;MACA;IACF;EACF;AAGA,QAAM,gBAAgB,OACpB,KACA,OACkB;AAClB,YAAQ,IAAI,MAAM;MAChB,KAAK,SAAS;AACZ,cAAM,YAAY;AAClB,sBAAc;AACd,mBAAW,CAAC;AAGZ,qBAAa;AACb,aAAK,UAAU,cAAc,EAAE,EAAE,MAAM,MAAM;QAAC,CAAC;AAC/C,oBAAY,IAAI,gBAAgB;AAChC,2BAAmB;AACnB,aAAK,iBAAiB,GAAG;AACzB,aAAK,gBAAgB,SAAS;AAC9B,cAAM,aAAc,IAAsC;AAC1D,sBAAc;UACZ,SAAS,YAAY,WAAW;UAChC,OACE,YAAY,SACX,QAAQ;UACX,YACE,YAAY,cACZ,aAAa,QAAQ,wBAAwB;QACjD;AACA,YAAI,YAAY,SAAS;AACvB,gCAAsB;QACxB;AACA,cAAM,OAAmB;UACvB;UACA,mBAAmB,CAAA,eACjB,IAAI,QAAQ,CAAA,YAAW;AACrB,+BAAmB,IAAI,YAAY,OAAO;UAC5C,CAAC;UACH,qBAAqB,CAAA,eACnB,IAAI,QAAQ,CAAA,YAAW;AACrB,iCAAqB,IAAI,YAAY,OAAO;UAC9C,CAAC;UACH,qBAAqB,CAAC;UACtB,aAAa,UAAU;UACvB;UACA,WAAW,CAAA,UAAS;AAClB,kBAAM,QAAQ,MAAM,SAAS;AAC7B,gBAAI,CAAC,qBAAqB,OAAO,MAAM,SAAS,EAAG;AACnD,iBAAK;cACH,MAAM;cACN;cACA,WAAW,MAAM;cACjB,SAAS,MAAM;cACf,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;cAC5C,GAAI,MAAM,UAAU,SAChB,EAAE,OAAO,kBAAkB,MAAM,KAAK,EAAE,IACxC,CAAC;YACP,CAAC;UACH;QACF;AACA,8BAAsB,KAAK;AAC3B,YAAI;AACF,gBAAM,QAAQ,KAAe,IAAI;QACnC,SAAS,KAAK;AACZ,eAAK,EAAE,MAAM,SAAS,OAAO,eAAe,GAAG,EAAE,CAAC;QACpD,UAAA;AACE,6BAAmB;AACnB,eAAK,gBAAgB,SAAS;QAChC;AACA;MACF;MACA,KAAK,eAAe;AAClB,cAAM,WAAW,mBAAmB,IAAI,IAAI,UAAU;AACtD,YAAI,UAAU;AACZ,6BAAmB,OAAO,IAAI,UAAU;AACxC,mBAAS,EAAE,QAAQ,IAAI,QAAQ,SAAS,IAAI,QAAQ,CAAC;QACvD;AACA;MACF;MACA,KAAK,0BAA0B;AAC7B,cAAM,WAAW,qBAAqB,IAAI,IAAI,UAAU;AACxD,YAAI,UAAU;AACZ,+BAAqB,OAAO,IAAI,UAAU;AAC1C,mBAAS,EAAE,UAAU,IAAI,UAAU,QAAQ,IAAI,OAAO,CAAC;QACzD;AACA;MACF;MACA,KAAK;AACH,6BAAqB,KAAK,IAAI,IAAI;AAClC;MACF,KAAK;AACH,mBAAW,MAAM;AACjB;MACF,KAAK;AACH,eAAO,IAAI,IAAI,eAAe;AAC9B;MACF,KAAK;AACH,2BAAmB;AACnB,aAAK,gBAAgB,MAAM;AAC3B,sBAAc,IAAI,KAAM,UAAU;AAClC;MACF,KAAK,UAAU;AACb,2BAAmB;AACnB,aAAK,gBAAgB,MAAM;AAC3B,cAAM,OAAQ,MAAM,WAAW,KAAM,CAAC;AACtC,oBAAY,EAAE,MAAM,iBAAiB,KAAK,CAAC;AAC3C,sBAAc,IAAI,KAAM,QAAQ;AAChC;MACF;IACF;EACF;AAGA,OAAK,gBAAgB,MAAM;AAE3B,QAAM,MAAM,IAAI,gBAAgB,EAAE,MAAM,cAAc,MAAM,UAAU,CAAC;AAEvE,QAAM,OAAO,MAAY;AACvB,QAAI,QAAQ,QAAQ;AAClB,cAAQ,OAAO;AACf;IACF;AACA,QAAI,MAAM,MAAM,QAAQ,KAAK,CAAC,CAAC;AAC/B,eAAW,MAAM,QAAQ,KAAK,CAAC,GAAG,GAAI,EAAE,MAAM;EAChD;AAEA,QAAM,gBAAgB,CAAC,IAAe,MAAc,WAAyB;AAC3E,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,OAAO,MAAY;AACvB,YAAM,UAAU,GAAG,mBAAmB,KAAK,GAAG,eAAe;AAC7D,UAAI,WAAW,KAAK,IAAI,IAAI,SAAS,KAAO;AAG1C,aAAK,yBAAyB,EAAE,QAAQ,MAAM;AAC5C,cAAI;AACF,eAAG,MAAM,MAAM,MAAM;UACvB,UAAA;AACE,iBAAK;UACP;QACF,CAAC;AACD;MACF;AACA,iBAAW,MAAM,EAAE,EAAE,MAAM;IAC7B;AACA,SAAK;EACP;AAEA,MAAI,GAAG,aAAa,MAAM;AACxB,UAAM,OAAO,IAAI,QAAQ;AACzB,uBAAmB,OAAO,SAAS,YAAY,OAAO,KAAK,OAAO;AAClE,uBAAmB;AACnB,SAAK,gBAAgB,SAAS;AAC9B,WAAO;MACL,KAAK,UAAU;QACb,MAAM;QACN,MAAM;MACR,CAAC,IAAI;IACP;AACA,YAAQ,cAAc,gBAAgB;EACxC,CAAC;AAED,MAAI,GAAG,cAAc,CAAC,IAAe,QAA0B;AAC7D,UAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,kBAAkB;AACtD,QAAI,IAAI,aAAa,IAAI,oBAAoB,MAAM,eAAe;AAChE,SAAG,MAAM,MAAM,cAAc;AAC7B;IACF;AAKA,mBAAe;AAKf,gBAAY;MACV,MAAM;MACN,OAAO;MACP,SAAS;IACX,CAAC;AAED,OAAG,GAAG,WAAW,CAAC,QAAkC;AAClD,UAAI;AACJ,UAAI;AACF,cAAM,OACJ,OAAO,QAAQ,WAAW,MAAM,OAAO,KAAK,GAAG,EAAE,SAAS,MAAM;AAClE,iBAAS,KAAK,MAAM,IAAI;MAC1B,SAAS,KAAK;AACZ,oBAAY;UACV,MAAM;UACN,OAAO,yBAA0B,IAAc,OAAO;QACxD,CAAC;AACD;MACF;AACA,WAAK,cAAc,QAAQ,EAAE;IAC/B,CAAC;AAED,OAAG,GAAG,SAAS,MAAM;AAKnB,UAAI,iBAAiB,IAAI;AACvB,uBAAe;MACjB;IACF,CAAC;AAED,OAAG,GAAG,SAAS,MAAM;IAErB,CAAC;EACH,CAAC;AAGD,UAAQ,GAAG,qBAAqB,CAAA,QAAO;AACrC,SAAK,EAAE,MAAM,SAAS,OAAO,eAAe,GAAG,EAAE,CAAC;EACpD,CAAC;AACD,UAAQ,GAAG,sBAAsB,CAAA,QAAO;AACtC,SAAK,EAAE,MAAM,SAAS,OAAO,eAAe,GAAG,EAAE,CAAC;EACpD,CAAC;AAED,QAAM,IAAI,QAAc,CAAA,YAAW;AACjC,QAAI,IAAI,QAAQ,KAAK,MAAM;AACzB,cAAQ;AACR;IACF;AACA,QAAI,GAAG,aAAa,MAAM,QAAQ,CAAC;EACrC,CAAC;AAED,SAAO;IACL,MAAM;IACN,OAAO,MACL,IAAI,QAAc,CAAA,YAAW;AAC3B,UAAI,MAAM,MAAM,QAAQ,CAAC;IAC3B,CAAC;EACL;AACF;AAEA,SAAS,eAAe,KAAuB;AAC7C,MAAI,eAAe,OAAO;AACxB,WAAO,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,SAAS,OAAO,IAAI,MAAM;EAClE;AACA,SAAO;AACT;;;ACxqBA,SAAS,kBAAkB;AAC3B,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,oBAAiC;;;ACQnC,IAAM,oBAAoB;AAE1B,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AACF,GAGW;AACT,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iDAcwC,SAAS;AAAA;AAAA,0EAEgB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBpF;AAEO,SAAS,6BAA6B;AAAA,EAC3C;AAAA,EACA;AACF,GAOW;AACT,QAAM,QAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,WAAW;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,aAAW,QAAQ,OAAO;AACxB,UAAM,KAAK,OAAO,KAAK,IAAI,EAAE;AAC7B,QAAI,KAAK,YAAa,OAAM,KAAK,KAAK,WAAW;AACjD,UAAM;AAAA,MACJ,mBAAmB,KAAK,UAAU,KAAK,eAAe,CAAC,CAAC,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AACF,GAGY;AACV,SAAO,QAAQ,SAAS,WAAW;AACrC;;;ADpFA,SAAS,MAAM,OAAOC,UAAS,UAAAC,eAAc;AAmB7C,YAAY,oBAAoB;AAOhC,IAAM,mBAAuE;AAAA,EAC3E,OAAO;AAAA,EACP,YAAY;AACd;AAEA,SAAS,aAAa,YAAuD;AAC3E,SAAO,iBAAiB,UAAU,KAAK;AACzC;AAEA,IAAM,OAAO,UAAU,KAAK,MAAM,CAAC,CAAC;AACpC,IAAM,UAAU,KAAK;AACrB,IAAM,iBAAiB,KAAK;AAC5B,IAAI,CAAC,SAAS;AACZ,YAAU,6BAA6B;AACzC;AACA,IAAI,CAAC,gBAAgB;AACnB,YAAU,sCAAsC;AAClD;AACA,IAAM,eAAe,KAAK,gBAAgB;AAG1C,IAAM,WAAW;AAIjB,IAAM,cAA0C,EAAE,IAAI,OAAU;AAEhE,MAAM,UAAwB;AAAA,EAC5B,YAAY;AAAA,EACZ;AAAA,EACA,SAAS;AAAA,EACT,UAAU,MAAO,YAAY,KAAK,EAAE,UAAU,YAAY,GAAG,IAAI,CAAC;AACpE,CAAC;AAID,eAAe,QAAQ,OAAqB,MAAiC;AAC3E,QAAM,OAAa,SAAO,KAAK,KAAK,GAAkB;AAKtD,MACE,OAAO,MAAM,mBAAmB,YAChC,MAAM,eAAe,SAAS,GAC9B;AACA,gBAAY,KAAK,MAAM;AAAA,EACzB;AAmBA,QAAM,aAAsC,CAAC;AAC7C,MAAI;AACJ,MAAI;AACJ,MAAI,MAAM,SAAS,MAAM,MAAM,SAAS,GAAG;AACzC,UAAM,aAAa,WAAW;AAC9B,YAAQ,MAAM,eAAe;AAAA,MAC3B;AAAA,MACA,OAAO,MAAM;AAAA,MACb;AAAA,MACA,mBAAmB,KAAK;AAAA,IAC1B,CAAC;AACD,eAAW,eAAe,IAAI;AAAA,MAC5B,SAAS;AAAA,MACT,SAAS;AAAA,MACT,MAAM,CAAC,GAAG,YAAY,oBAAoB;AAAA,MAC1C,KAAK;AAAA,QACH,cAAc,KAAK;AAAA,UACjB,MAAM,MAAM,IAAI,QAAM;AAAA,YACpB,MAAM,EAAE;AAAA,YACR,aAAa,EAAE;AAAA,YACf,aAAa,EAAE;AAAA,UACjB,EAAE;AAAA,QACJ;AAAA,QACA,gBAAgB,oBAAoB,MAAM,IAAI;AAAA,QAC9C,kBAAkB;AAAA,MACpB;AAAA,IACF;AAEA,kBAAc,GAAG,OAAO,IAAI,iBAAiB;AAC7C,UAAMC;AAAA,MACJ;AAAA,MACA,mBAAmB,EAAE,WAAW,MAAM,MAAM,WAAW,CAAC;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAuC,CAAC;AAC9C,MAAI,OAAO,KAAK,UAAU,EAAE,SAAS,EAAG,aAAY,cAAc;AAElE,QAAM,aAAaF,SAAQ,qBACvBA,SAAQ,uBAAuB,oCAC/BA,SAAQ;AACZ,MAAI,YAAY;AACd,gBAAY,wBAAwB;AACpC,gBAAY,iBAAiB;AAC7B,gBAAY,kBAAkB;AAAA,MAC5B,qBAAqB;AAAA,QACnB,MAAMA,SAAQ,6BAA6B;AAAA,QAC3C,UAAU;AAAA,QACV,SAAS;AAAA,QACT,UAAU;AAAA,QACV,qBAAqB;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACA,QAAM,8BACJ,OAAO,YAAY,mBAAmB;AAExC,QAAM,QAAQ,IAAI,SAAS,MAAM;AAAA,IAC/B,GAAIA,SAAQ,gBAAgB,EAAE,QAAQA,SAAQ,cAAc,IAAI,CAAC;AAAA,IACjE,GAAI,CAAC,+BAA+B,aAChC,EAAE,SAAS,WAAW,IACtB,CAAC;AAAA,IACL,KAAK,OAAO;AAAA,MACV,OAAO,QAAQA,QAAO,EAAE;AAAA,QACtB,CAAC,UAAqC,OAAO,MAAM,CAAC,MAAM;AAAA,MAC5D;AAAA,IACF;AAAA,IACA,GAAI,OAAO,KAAK,WAAW,EAAE,SAAS,IAAI,EAAE,QAAQ,YAAY,IAAI,CAAC;AAAA,EACvE,CAAC;AAED,QAAM,gBAAgB;AAAA,IACpB,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC5C,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,IAClB,GAAI,MAAM,kBACN,EAAE,sBAAsB,MAAM,gBAAgB,IAC9C,CAAC;AAAA,IACL,eAAe,MAAM,YAAY,SAAS;AAAA,EAC5C;AACA,QAAM,SAAS,YAAY,KACvB,MAAM,aAAa,YAAY,IAAI,aAAa,IAChD,MAAM,YAAY,aAAa;AAEnC,OAAK,EAAE,MAAM,eAAe,CAAC;AAE7B,QAAM,cAAc,mBAAmB;AAAA,IACrC,MAAM,MAAM;AAAA,IACZ,cAAc,MAAM;AAAA,IACpB,QAAQ,MAAM;AAAA;AAAA,IAEd,gBACE,eAAe,MAAM,SAAS,MAAM,MAAM,SAAS,IAC/C,6BAA6B;AAAA,MAC3B,OAAO,MAAM;AAAA,MACb;AAAA,IACF,CAAC,IACD;AAAA,EACR,CAAC;AACD,MAAI;AACJ,QAAM,aAAa,oBAAI,IAAoB;AAC3C,QAAM,kBAAkB,oBAAI,IAAoB;AAEhD,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,OAAO,YAAY,aAAa;AAAA,MACvD,QAAQ,KAAK;AAAA,IACf,CAAC;AACD,qBAAiB,SAAS,QAAqC;AAC7D,UAAI,KAAK,YAAY,QAAS;AAC9B,UACE,MAAM,SAAS,oBACf,OAAO,MAAM,cAAc,UAC3B;AACA,oBAAY,KAAK,MAAM;AAEvB,aAAK,EAAE,MAAM,iBAAiB,UAAU,MAAM,UAAU,CAAC;AAAA,MAC3D;AAEA,UACE,eACA,MAAM,MAAM,SAAS,uBACrB,OAAO,MAAM,KAAK,YAAY,YAC9B,mBAAmB,EAAE,SAAS,MAAM,KAAK,SAAS,YAAY,CAAC,GAC/D;AACA;AAAA,MACF;AACA,uBAAiB,OAAO;AAAA,QACtB,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,cAAc,OAAM,YAAY;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,SAAK,EAAE,MAAM,SAAS,OAAOG,gBAAe,GAAG,EAAE,CAAC;AAClD;AAAA,EACF,UAAE;AACA,WAAO,MAAM;AAAA,EACf;AAEA,OAAK;AAAA,IACH,MAAM;AAAA,IACN,cAAc,EAAE,SAAS,QAAQ,KAAK,OAAO;AAAA,IAC7C,YAAY,aAAa,aAAa;AAAA,EACxC,CAAC;AAED,OAAK,KAAK;AACZ;AAuBA,SAAS,yBAAyB,MAA0B;AAC1D,MACE,KAAK,WAAW,UAChB,KAAK,WAAW,QAChB,OAAO,KAAK,WAAW,UACvB;AACA,WAAO,KAAK,OAAO,UAAU,EAAE,OAAO,KAAK,MAAM,QAAQ,IAAI;AAAA,EAC/D;AACA,QAAM,SAAS,KAAK;AAIpB,MACE,OAAO,uBAAuB,UAC9B,OAAO,uBAAuB,MAC9B;AACA,WAAO,OAAO;AAAA,EAChB;AACA,SAAO,OAAO,WAAW;AAC3B;AAkBA,SAAS,iBACP,OACA,KAMM;AACN,MAAI,MAAM,SAAS,kBAAkB;AACnC,QAAI,MAAM,MAAO,KAAI,aAAa,SAAS,MAAM,KAAK,CAAC;AACvD,QAAI,KAAK;AAAA,MACP,MAAM;AAAA,MACN,cAAc,EAAE,SAAS,QAAQ,KAAK,OAAO;AAAA,MAC7C,OAAO,MAAM,QAAQ,SAAS,MAAM,KAAK,IAAI,aAAa;AAAA,IAC5D,CAAC;AACD;AAAA,EACF;AACA,MAAI,MAAM,SAAS,eAAe;AAChC,QAAI,KAAK;AAAA,MACP,MAAM;AAAA,MACN,OAAO,MAAM,OAAO,WAAW;AAAA,IACjC,CAAC;AACD;AAAA,EACF;AACA,MAAI,MAAM,SAAS,SAAS;AAC1B,QAAI,KAAK,EAAE,MAAM,SAAS,OAAO,MAAM,WAAW,cAAc,CAAC;AACjE;AAAA,EACF;AACA,MAAI,CAAC,MAAM,KAAM;AACjB,QAAM,OAAO,MAAM;AACnB,QAAM,KAAK,KAAK,MAAM,WAAW;AAEjC,MAAI,KAAK,SAAS,mBAAmB,OAAO,KAAK,SAAS,UAAU;AASlE,QAAI,CAAC,IAAI,WAAW,IAAI,EAAE,GAAG;AAC3B,UAAI,KAAK,EAAE,MAAM,cAAc,GAAG,CAAC;AACnC,UAAI,WAAW,IAAI,IAAI,EAAE;AAAA,IAC3B;AACA,UAAM,OAAO,IAAI,WAAW,IAAI,EAAE,KAAK;AACvC,UAAM,OAAO,KAAK;AAClB,QAAI,KAAK,SAAS,KAAK,QAAQ;AAC7B,UAAI,KAAK,EAAE,MAAM,cAAc,IAAI,OAAO,KAAK,MAAM,KAAK,MAAM,EAAE,CAAC;AACnE,UAAI,WAAW,IAAI,IAAI,IAAI;AAAA,IAC7B;AACA,QAAI,MAAM,SAAS,iBAAkB,KAAI,KAAK,EAAE,MAAM,YAAY,GAAG,CAAC;AACtE;AAAA,EACF;AAEA,MAAI,KAAK,SAAS,eAAe,OAAO,KAAK,SAAS,UAAU;AAC9D,QAAI,CAAC,IAAI,gBAAgB,IAAI,EAAE,GAAG;AAChC,UAAI,KAAK,EAAE,MAAM,mBAAmB,GAAG,CAAC;AACxC,UAAI,gBAAgB,IAAI,IAAI,EAAE;AAAA,IAChC;AACA,UAAM,OAAO,IAAI,gBAAgB,IAAI,EAAE,KAAK;AAC5C,UAAM,OAAO,KAAK;AAClB,QAAI,KAAK,SAAS,KAAK,QAAQ;AAC7B,UAAI,KAAK,EAAE,MAAM,mBAAmB,IAAI,OAAO,KAAK,MAAM,KAAK,MAAM,EAAE,CAAC;AACxE,UAAI,gBAAgB,IAAI,IAAI,IAAI;AAAA,IAClC;AACA,QAAI,MAAM,SAAS;AACjB,UAAI,KAAK,EAAE,MAAM,iBAAiB,GAAG,CAAC;AACxC;AAAA,EACF;AAEA,MAAI,KAAK,SAAS,qBAAqB;AACrC,UAAM,aAAa;AACnB,QAAI,MAAM,SAAS,gBAAgB;AACjC,UAAI,KAAK;AAAA,QACP,MAAM;AAAA,QACN,YAAY;AAAA,QACZ,UAAU,aAAa,UAAU;AAAA,QACjC;AAAA,QACA,OAAO,KAAK,UAAU,EAAE,SAAS,KAAK,WAAW,GAAG,CAAC;AAAA,QACrD,kBAAkB;AAAA,MACpB,CAAC;AAAA,IACH,WAAW,MAAM,SAAS,kBAAkB;AAC1C,UAAI,KAAK;AAAA,QACP,MAAM;AAAA,QACN,YAAY;AAAA,QACZ,UAAU,aAAa,UAAU;AAAA,QACjC,QAAQ;AAAA,UACN,UAAU,KAAK,aAAa;AAAA,UAC5B,QAAQ,KAAK,qBAAqB;AAAA,UAClC,QAAQ,KAAK,UAAU;AAAA,QACzB;AAAA,MACF,CAAC;AAAA,IACH;AACA;AAAA,EACF;AAEA,MAAI,KAAK,SAAS,iBAAiB;AACjC,UAAM,aAAa,KAAK,WAAW;AACnC,QAAI,MAAM,SAAS,gBAAgB;AACjC,UAAI,KAAK;AAAA,QACP,MAAM;AAAA,QACN,YAAY;AAAA,QACZ,UAAU,KAAK,QAAQ;AAAA,QACvB,GAAI,aAAa,CAAC,IAAI,EAAE,YAAY,KAAK,QAAQ,UAAU;AAAA,QAC3D,OAAO,KAAK,UAAU,KAAK,aAAa,CAAC,CAAC;AAAA,QAC1C,kBAAkB,CAAC;AAAA,MACrB,CAAC;AAAA,IACH,WAAW,MAAM,SAAS,kBAAkB;AAC1C,UAAI,KAAK;AAAA,QACP,MAAM;AAAA,QACN,YAAY;AAAA,QACZ,UAAU,KAAK,QAAQ;AAAA,QACvB,QAAQ,yBAAyB,IAAI;AAAA,MACvC,CAAC;AAAA,IACH;AACA;AAAA,EACF;AAEA,MAAI,KAAK,SAAS,cAAc;AAC9B,UAAM,aAAa;AACnB,QAAI,MAAM,SAAS,gBAAgB;AACjC,UAAI,KAAK;AAAA,QACP,MAAM;AAAA,QACN,YAAY;AAAA,QACZ,UAAU,aAAa,UAAU;AAAA,QACjC;AAAA,QACA,OAAO,KAAK,UAAU,EAAE,OAAO,KAAK,SAAS,GAAG,CAAC;AAAA,QACjD,kBAAkB;AAAA,MACpB,CAAC;AAAA,IACH,WAAW,MAAM,SAAS,kBAAkB;AAC1C,UAAI,KAAK;AAAA,QACP,MAAM;AAAA,QACN,YAAY;AAAA,QACZ,UAAU,aAAa,UAAU;AAAA,QACjC,QAAQ,KAAK,UAAU;AAAA,MACzB,CAAC;AAAA,IACH;AACA;AAAA,EACF;AAEA,MAAI,KAAK,SAAS,iBAAiB,MAAM,SAAS,kBAAkB;AAClE,eAAW,UAAU,KAAK,WAAW,CAAC,GAAG;AACvC,UAAI,KAAK;AAAA,QACP,MAAM;AAAA,QACN,OACE,OAAO,SAAS,QACZ,WACA,OAAO,SAAS,WACd,WACA;AAAA,QACR,MAAM,OAAO;AAAA,MACf,CAAC;AAAA,IACH;AACA;AAAA,EACF;AAEA,MAAI,KAAK,SAAS,WAAW,MAAM,SAAS,kBAAkB;AAC5D,QAAI,KAAK;AAAA,MACP,MAAM;AAAA,MACN,OAAQ,KAA8B,WAAW;AAAA,IACnD,CAAC;AACD;AAAA,EACF;AACF;AAEA,SAAS,SAAS,OAAwD;AACxE,QAAM,QAAQ,MAAM,gBAAgB;AACpC,QAAM,YAAY,MAAM,uBAAuB;AAC/C,SAAO;AAAA,IACL,aAAa;AAAA,MACX,OAAO;AAAA,MACP,SAAS,KAAK,IAAI,GAAG,QAAQ,SAAS;AAAA,MACtC;AAAA,MACA,YAAY;AAAA,IACd;AAAA,IACA,cAAc;AAAA,MACZ,OAAO,MAAM,iBAAiB;AAAA,MAC9B,MAAM,MAAM,iBAAiB;AAAA,IAC/B;AAAA,EACF;AACF;AAEA,SAAS,eAAwC;AAC/C,SAAO;AAAA,IACL,aAAa,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,EAAE;AAAA,IACjE,cAAc,EAAE,OAAO,GAAG,MAAM,EAAE;AAAA,EACpC;AACF;AAEA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOW;AACT,QAAM,SAAmB,CAAC;AAQ1B,MAAI,cAAc;AAChB,WAAO;AAAA,MACL;AAAA;AAAA;AAAA,EAEK,YAAY;AAAA;AAAA,IAEnB;AAAA,EACF;AACA,MAAI,UAAU,OAAO,SAAS,GAAG;AAC/B,UAAM,QAAkB,CAAC,qBAAqB;AAC9C,eAAW,SAAS,QAAQ;AAC1B,YAAM,KAAK,IAAI,OAAO,MAAM,IAAI,IAAI,MAAM,aAAa,IAAI,MAAM,OAAO;AAAA,IAC1E;AACA,WAAO,KAAK,MAAM,KAAK,IAAI,CAAC;AAAA,EAC9B;AACA,MAAI,eAAgB,QAAO,KAAK,cAAc;AAC9C,SAAO,KAAK,eAAe;AAAA,EAAmB,IAAI;AAAA,mBAAsB,IAAI;AAC5E,SAAO,OAAO,KAAK,MAAM;AAC3B;AASA,eAAe,eAAe;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAO6C;AAC3C,QAAM,YAAY,IAAI,IAAI,MAAM,IAAI,OAAK,EAAE,IAAI,CAAC;AAEhD,QAAM,SAAS,aAAa,OAAO,KAAK,QAAQ;AAC9C,QAAI;AACF,UACE,IAAI,WAAW,UACf,IAAI,QAAQ,OACZ,IAAI,QAAQ,kBAAkB,UAAU,UAAU,IAClD;AACA,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,kCAAkC,CAAC,CAAC;AACpE;AAAA,MACF;AACA,YAAM,SAAmB,CAAC;AAC1B,uBAAiB,SAAS,KAAK;AAC7B,eAAO,KAAK,KAAe;AAAA,MAC7B;AACA,YAAM,OAAO,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM;AAClD,YAAM,EAAE,WAAW,UAAU,MAAM,IAAI,KAAK,MAAM,IAAI;AAMtD,UAAI,CAAC,UAAU,IAAI,QAAQ,GAAG;AAC5B,YAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,YAAI;AAAA,UACF,KAAK,UAAU,EAAE,OAAO,SAAS,QAAQ,qBAAqB,CAAC;AAAA,QACjE;AACA;AAAA,MACF;AAEA,WAAK;AAAA,QACH,MAAM;AAAA,QACN,YAAY;AAAA,QACZ;AAAA,QACA,OAAO,KAAK,UAAU,SAAS,CAAC,CAAC;AAAA,QACjC,kBAAkB;AAAA,MACpB,CAAC;AAED,YAAM,EAAE,QAAQ,QAAQ,IAAI,MAAM,kBAAkB,SAAS;AAC7D,WAAK;AAAA,QACH,MAAM;AAAA,QACN,YAAY;AAAA,QACZ;AAAA,QACA,QAAQ,UAAU;AAAA,QAClB,SAAS,CAAC,CAAC;AAAA,MACb,CAAC;AAED,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,QAAQ,OAAO,CAAC,CAAC;AAAA,IAC5C,SAAS,OAAO;AACd,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI;AAAA,QACF,KAAK,UAAU;AAAA,UACb,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAC9D,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,IAAI;AAAA,IAAc,aACtB,OAAO,OAAO,GAAG,aAAa,MAAM,QAAQ,CAAC;AAAA,EAC/C;AACA,QAAM,UAAU,OAAO,QAAQ;AAC/B,MAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,SAAO;AAAA,IACL,MAAM,QAAQ;AAAA,IACd,OAAO,MAAM,YAAY,MAAM;AAAA,EACjC;AACF;AAEA,SAAS,YAAY,QAAsB;AACzC,MAAI;AACF,WAAO,MAAM;AAAA,EACf,QAAQ;AAAA,EAAC;AACX;AAEA,SAAS,UAAUC,OAIjB;AACA,QAAM,MAIF,CAAC;AACL,WAAS,IAAI,GAAG,IAAIA,MAAK,QAAQ,KAAK;AACpC,QAAIA,MAAK,CAAC,MAAM,eAAe,IAAI,IAAIA,MAAK,QAAQ;AAClD,UAAI,UAAUA,MAAK,EAAE,CAAC;AAAA,IACxB,WAAWA,MAAK,CAAC,MAAM,wBAAwB,IAAI,IAAIA,MAAK,QAAQ;AAClE,UAAI,iBAAiBA,MAAK,EAAE,CAAC;AAAA,IAC/B,WAAWA,MAAK,CAAC,MAAM,qBAAqB,IAAI,IAAIA,MAAK,QAAQ;AAC/D,UAAI,eAAeA,MAAK,EAAE,CAAC;AAAA,IAC7B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAASD,gBAAe,KAAuB;AAC7C,MAAI,eAAe,OAAO;AACxB,WAAO,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,SAAS,OAAO,IAAI,MAAM;AAAA,EAClE;AACA,SAAO;AACT;AAEA,SAAS,UAAU,SAAwB;AACzC,EAAAF,QAAO,MAAM,KAAK,UAAU,EAAE,MAAM,gBAAgB,QAAQ,CAAC,IAAI,IAAI;AACrE,UAAQ,KAAK,CAAC;AAChB;","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 => {\n if (wss.address() != null) {\n resolve();\n return;\n }\n wss.on('listening', () => resolve());\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 apiBaseUrl = procEnv.AI_GATEWAY_API_KEY\n ? procEnv.AI_GATEWAY_BASE_URL || 'https://ai-gateway.vercel.sh/v1'\n : 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"]}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/codex-harness.ts
2
2
  import { randomBytes } from "crypto";
3
3
  import { readFile } from "fs/promises";
4
+ import path from "path";
4
5
  import { fileURLToPath } from "url";
5
6
  import {
6
7
  commonTool,
@@ -81,13 +82,6 @@ var startMessageSchema = harnessV1BridgeStartBaseSchema.extend({
81
82
  instructions: z.string().optional(),
82
83
  reasoningEffort: z.enum(["low", "medium", "high"]).optional(),
83
84
  webSearch: z.boolean().optional(),
84
- skills: z.array(
85
- z.object({
86
- name: z.string(),
87
- description: z.string(),
88
- content: z.string()
89
- })
90
- ).optional(),
91
85
  // Resume signal. When supplied, the bridge calls
92
86
  // `codex.resumeThread(resumeThreadId, …)` instead of starting a fresh thread.
93
87
  // The host sources the id from lifecycle state `data` cached from a prior
@@ -210,7 +204,6 @@ function createCodex(settings = {}) {
210
204
  channel: attachChannel,
211
205
  // The live bridge was spawned by another process; no process handle.
212
206
  proc: void 0,
213
- skills: startOpts.skills,
214
207
  model: settings.model ?? DEFAULT_CODEX_MODEL,
215
208
  reasoningEffort: settings.reasoningEffort,
216
209
  webSearch: settings.webSearch,
@@ -241,10 +234,19 @@ function createCodex(settings = {}) {
241
234
  }
242
235
  const port = resolveBridgePort(sandboxSession, settings.port);
243
236
  const token = randomBytes(32).toString("hex");
237
+ const codexSkillSetup = startOpts.skills && startOpts.skills.length > 0 ? await writeSkills({
238
+ sandbox: session,
239
+ skills: startOpts.skills,
240
+ abortSignal: startOpts.abortSignal
241
+ }) : void 0;
244
242
  const env = {
245
243
  ...resolveCodexEnv(settings.auth),
246
244
  BRIDGE_CHANNEL_TOKEN: token,
247
245
  BRIDGE_WS_PORT: String(port),
246
+ ...codexSkillSetup ? {
247
+ HOME: codexSkillSetup.homeDir,
248
+ CODEX_HOME: codexSkillSetup.codexHomeDir
249
+ } : {},
248
250
  ...respawnStrategy === "replay" ? { BRIDGE_REPLAY_FROM_DISK: "1" } : {}
249
251
  };
250
252
  if (respawnStrategy === void 0) {
@@ -283,7 +285,6 @@ function createCodex(settings = {}) {
283
285
  sessionId: startOpts.sessionId,
284
286
  channel,
285
287
  proc,
286
- skills: startOpts.skills,
287
288
  model: settings.model ?? DEFAULT_CODEX_MODEL,
288
289
  reasoningEffort: settings.reasoningEffort,
289
290
  webSearch: settings.webSearch,
@@ -325,6 +326,99 @@ async function readBridgeAsset(name) {
325
326
  }
326
327
  throw lastErr ?? new Error(`bridge asset not found: ${name}`);
327
328
  }
329
+ async function writeSkills({
330
+ sandbox,
331
+ skills,
332
+ abortSignal
333
+ }) {
334
+ for (const skill of skills) {
335
+ safeCodexSkillName(skill.name);
336
+ for (const file of skill.files ?? []) {
337
+ safeCodexSkillFilePath({
338
+ skillName: skill.name,
339
+ filePath: file.path
340
+ });
341
+ }
342
+ }
343
+ const homeDir = await resolveSandboxHomeDir({ sandbox, abortSignal });
344
+ const codexHomeDir = path.posix.join(homeDir, ".codex");
345
+ await sandbox.run({
346
+ command: `mkdir -p ${shellQuote(codexHomeDir)}`,
347
+ abortSignal
348
+ });
349
+ const rootDir = path.posix.join(homeDir, ".agents", "skills");
350
+ await sandbox.run({
351
+ command: `mkdir -p ${shellQuote(rootDir)}`,
352
+ abortSignal
353
+ });
354
+ for (const skill of skills) {
355
+ const name = safeCodexSkillName(skill.name);
356
+ const skillDir = path.posix.join(rootDir, name);
357
+ const content = `---
358
+ name: ${skill.name}
359
+ description: ${skill.description}
360
+ ---
361
+
362
+ ${skill.content}`;
363
+ await sandbox.writeTextFile({
364
+ path: path.posix.join(skillDir, "SKILL.md"),
365
+ content,
366
+ abortSignal
367
+ });
368
+ for (const file of skill.files ?? []) {
369
+ const filePath = safeCodexSkillFilePath({
370
+ skillName: skill.name,
371
+ filePath: file.path
372
+ });
373
+ await sandbox.writeTextFile({
374
+ path: path.posix.join(skillDir, filePath),
375
+ content: file.content,
376
+ abortSignal
377
+ });
378
+ }
379
+ }
380
+ return {
381
+ homeDir,
382
+ codexHomeDir
383
+ };
384
+ }
385
+ async function resolveSandboxHomeDir({
386
+ sandbox,
387
+ abortSignal
388
+ }) {
389
+ const result = await sandbox.run({
390
+ command: 'printf "%s" "$HOME"',
391
+ abortSignal
392
+ });
393
+ const homeDir = result.stdout.trim();
394
+ if (result.exitCode !== 0 || !homeDir || !path.posix.isAbsolute(homeDir)) {
395
+ throw new Error(
396
+ `Unable to resolve sandbox HOME directory: ${result.stderr || result.stdout}`
397
+ );
398
+ }
399
+ return homeDir;
400
+ }
401
+ function safeCodexSkillName(name) {
402
+ if (!/^[A-Za-z0-9._-]+$/.test(name) || name === "." || name === "..") {
403
+ throw new Error(`Invalid Codex skill name: ${name}`);
404
+ }
405
+ return name;
406
+ }
407
+ function safeCodexSkillFilePath({
408
+ skillName,
409
+ filePath
410
+ }) {
411
+ const normalized = path.posix.normalize(filePath);
412
+ if (normalized === "." || normalized.startsWith("../") || path.posix.isAbsolute(normalized)) {
413
+ throw new Error(
414
+ `Invalid Codex skill file path for ${skillName}: ${filePath}`
415
+ );
416
+ }
417
+ return normalized;
418
+ }
419
+ function shellQuote(value) {
420
+ return `'${value.replace(/'/g, `'\\''`)}'`;
421
+ }
328
422
  async function waitForBridgeReady({
329
423
  proc,
330
424
  timeoutMs,
@@ -433,7 +527,6 @@ function createSession({
433
527
  sessionId,
434
528
  channel,
435
529
  proc,
436
- skills,
437
530
  model,
438
531
  reasoningEffort,
439
532
  webSearch,
@@ -591,13 +684,6 @@ function createSession({
591
684
  reasoningEffort,
592
685
  webSearch,
593
686
  ...permissionMode ? { permissionMode } : {},
594
- ...skills && skills.length > 0 ? {
595
- skills: skills.map((s) => ({
596
- name: s.name,
597
- description: s.description,
598
- content: s.content
599
- }))
600
- } : {},
601
687
  ...pendingResumeThreadId ? { resumeThreadId: pendingResumeThreadId } : {},
602
688
  ...debug ? { debug } : {}
603
689
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/codex-harness.ts","../src/codex-auth.ts","../src/codex-bridge-protocol.ts","../src/index.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { fileURLToPath } from 'node:url';\nimport {\n commonTool,\n HarnessCapabilityUnsupportedError,\n harnessV1DiagnosticFromBridgeFrame,\n type HarnessV1,\n type HarnessV1Bootstrap,\n type HarnessV1DebugConfig,\n type HarnessV1BuiltinTool,\n type HarnessV1ContinueTurnState,\n type HarnessV1Prompt,\n type HarnessV1PromptControl,\n type HarnessV1ResumeSessionState,\n type HarnessV1NetworkSandboxSession,\n type HarnessV1PermissionMode,\n type HarnessV1Session,\n type HarnessV1Skill,\n type HarnessV1StreamPart,\n} from '@ai-sdk/harness';\nimport { classifyDiskLog, SandboxChannel } from '@ai-sdk/harness/utils';\nimport {\n safeParseJSON,\n type Experimental_SandboxProcess,\n} from '@ai-sdk/provider-utils';\nimport { WebSocket } from 'ws';\nimport { z } from 'zod';\nimport { resolveCodexEnv, type CodexAuthOptions } from './codex-auth';\nimport {\n bridgeReadySchema,\n outboundMessageSchema,\n type InboundMessage,\n type OutboundMessage,\n} from './codex-bridge-protocol';\n\ntype CodexChannel = SandboxChannel<OutboundMessage, InboundMessage>;\ntype CodexRespawnStrategy = 'replay' | 'rerun';\n\n/*\n * The model the adapter pins when the consumer configures none. The Codex SDK\n * does not report the model it resolves to at runtime (no model field on any\n * event), and exposes no default-model constant, so we pin the latest\n * codex-specialized model available for the bundled `@openai/codex@0.130.0`\n * (published 2026-05-08): `gpt-5.3-codex` (released 2026-02). Keep this in sync\n * when bumping the codex SDK/binary. Passing it explicitly makes the resolved\n * model deterministic and the telemetry (`gen_ai.request.model`) accurate.\n */\nconst DEFAULT_CODEX_MODEL = 'gpt-5.3-codex';\n\nexport type CodexHarnessSettings = {\n readonly auth?: CodexAuthOptions;\n /**\n * OpenAI model id the underlying `codex` CLI should use. Leaving this unset\n * pins the adapter default (`DEFAULT_CODEX_MODEL`).\n */\n readonly model?: string;\n /**\n * Reasoning effort for reasoning-capable models. Leaving this unset\n * defers to the CLI's default.\n */\n readonly reasoningEffort?: 'low' | 'medium' | 'high';\n /**\n * When `true`, allow the underlying runtime to use live web search.\n */\n readonly webSearch?: boolean;\n /**\n * Override the port the bridge binds inside the sandbox. By default the\n * adapter uses the first port the sandbox declares via `sandbox.ports`.\n * Only set this if the sandbox declares multiple ports and the first one\n * is reserved for something else.\n */\n readonly port?: number;\n /** Maximum milliseconds to wait for the bridge to advertise its port. Defaults to 120000. */\n readonly startupTimeoutMs?: number;\n};\n\n/*\n * Every native tool the Codex CLI can invoke as a model-callable tool,\n * declared as a `ToolSet` keyed by what the bridge emits as `toolName` on\n * the wire (`commonName ?? nativeName`). Schemas reflect the `ThreadItem`\n * union in `@openai/codex-sdk`'s `dist/index.d.ts`.\n *\n * Codex's other native operations (`apply_patch`, todo planning) surface\n * only as side-effect events (`file_change`, `todo_list`) and are not\n * model-callable tools — they don't appear here.\n */\nconst CODEX_BUILTIN_TOOLS = {\n bash: commonTool('bash', {\n nativeName: 'shell',\n toolUseKind: 'bash',\n description: 'Execute a shell command',\n inputSchema: z.object({ command: z.string() }),\n }),\n webSearch: commonTool('webSearch', {\n nativeName: 'web_search',\n toolUseKind: 'readonly',\n description: 'Search the web',\n inputSchema: z.object({ query: z.string() }),\n }),\n} as const satisfies Record<string, HarnessV1BuiltinTool<any, any>>;\n\n/*\n * Bootstrap lives in /tmp because it's pure derived state — the harness can\n * reinstall the CLI and bridge files on any fresh sandbox from the recipe.\n * Persistence comes from the sandbox provider's snapshot, not the path.\n *\n * The session work dir (`startOpts.sessionWorkDir`) and the bridge-state dir\n * derived from `sandboxSession.defaultWorkingDirectory` both live under the sandbox's\n * default working directory — the provider's persistent mount — so the\n * workdir's contents (the codex CLI shim and any files the agent edits) and\n * the bridge state files survive both detach -> attach and\n * stop -> snapshot -> resume cycles.\n */\nconst BOOTSTRAP_DIR = '/tmp/harness/codex';\n\n/**\n * Live bridge coordinates returned by `doDetach()` and `doSuspendTurn()`. A\n * future process uses them to reopen a socket to the still-running bridge\n * (`attach`) instead of re-spawning it. Absent on a `doStop()` payload.\n */\nconst bridgeCoordsSchema = z.object({\n port: z.number(),\n token: z.string(),\n lastSeenEventId: z.number(),\n sandboxId: z.string().optional(),\n});\n\n/**\n * Schema for the adapter-specific lifecycle `data` payload Codex produces.\n * `threadId` is what `codex.resumeThread(...)` requires for the replay/rerun\n * rungs; the sandbox lookup is handled separately via\n * `provider.resumeSession({ sessionId })`. `bridge` carries live coordinates\n * for cross-process `attach` (present on `doDetach()` and `doSuspendTurn()`\n * payloads).\n */\nconst codexResumeStateSchema = z.object({\n threadId: z.string().optional(),\n bridge: bridgeCoordsSchema.optional(),\n});\n\ntype CodexBridgeCoords = z.infer<typeof bridgeCoordsSchema>;\n\nexport function createCodex(\n settings: CodexHarnessSettings = {},\n): HarnessV1<typeof CODEX_BUILTIN_TOOLS> {\n let cachedBootstrap: HarnessV1Bootstrap | undefined;\n\n return {\n specificationVersion: 'harness-v1',\n harnessId: 'codex',\n builtinTools: CODEX_BUILTIN_TOOLS,\n supportsBuiltinToolApprovals: false,\n lifecycleStateSchema: codexResumeStateSchema,\n getBootstrap: async () => {\n if (cachedBootstrap != null) return cachedBootstrap;\n const [pkg, lock, bridge, hostToolMcp] = await Promise.all([\n readBridgeAsset('package.json'),\n readBridgeAsset('pnpm-lock.yaml'),\n readBridgeAsset('index.mjs'),\n readBridgeAsset('host-tool-mcp.mjs'),\n ]);\n cachedBootstrap = {\n harnessId: 'codex',\n bootstrapDir: BOOTSTRAP_DIR,\n files: [\n { path: `${BOOTSTRAP_DIR}/package.json`, content: pkg },\n { path: `${BOOTSTRAP_DIR}/pnpm-lock.yaml`, content: lock },\n { path: `${BOOTSTRAP_DIR}/bridge.mjs`, content: bridge },\n {\n path: `${BOOTSTRAP_DIR}/host-tool-mcp.mjs`,\n content: hostToolMcp,\n },\n ],\n commands: [\n { command: `mkdir -p ${BOOTSTRAP_DIR}` },\n {\n command: `pnpm --dir ${BOOTSTRAP_DIR} install --frozen-lockfile --store-dir ${BOOTSTRAP_DIR}/.pnpm-store`,\n },\n ],\n };\n return cachedBootstrap;\n },\n doStart: async startOpts => {\n if (\n startOpts.permissionMode != null &&\n startOpts.permissionMode !== 'allow-all'\n ) {\n throw new HarnessCapabilityUnsupportedError({\n message:\n \"Harness 'codex' does not support built-in tool approval requests; use permissionMode: 'allow-all'.\",\n harnessId: 'codex',\n });\n }\n const sandboxSession = startOpts.sandboxSession;\n const session = sandboxSession.restricted();\n const sandboxId = sandboxSession.id;\n const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;\n const isResume = lifecycleState != null;\n const isContinue = startOpts.continueFrom != null;\n const resumeData =\n isResume && typeof lifecycleState?.data === 'object'\n ? (lifecycleState.data as {\n threadId?: unknown;\n bridge?: CodexBridgeCoords;\n })\n : undefined;\n const resumeThreadId = resumeData?.threadId;\n const resumeThreadIdString =\n typeof resumeThreadId === 'string' && resumeThreadId.length > 0\n ? resumeThreadId\n : undefined;\n const coords = resumeData?.bridge;\n\n const workDir = startOpts.sessionWorkDir;\n const sessionDataDir = `${sandboxSession.defaultWorkingDirectory}/.agent-runs/${startOpts.sessionId}`;\n const bridgeStateDir = `${sessionDataDir}/bridge`;\n const timeoutMs = settings.startupTimeoutMs ?? 120_000;\n\n // Normalize each forwarded bridge diagnostics frame into the general\n // `HarnessV1Diagnostic` and report it. The adapter does no telemetry work\n // beyond this transport→emission mapping.\n const report = startOpts.observability?.report;\n const onDiagnostic = report\n ? (frame: Parameters<typeof harnessV1DiagnosticFromBridgeFrame>[0]) =>\n report(\n harnessV1DiagnosticFromBridgeFrame(frame, {\n sessionId: startOpts.sessionId,\n timestamp: Date.now(),\n }),\n )\n : undefined;\n\n /*\n * Rung 1 — ATTACH. With live coordinates, reopen a socket to the\n * still-running bridge. Parked between-turn sessions just attach and wait\n * for the next `start`; suspended in-flight turns request replay of\n * everything past the persisted cursor. No spawn, no fresh token. If the\n * bridge is gone the open throws and we fall through to a spawn-based\n * recovery.\n */\n if (coords) {\n try {\n const attachUrl =\n (await sandboxSession.getPortUrl({\n port: coords.port,\n protocol: 'ws',\n })) + `?agent_bridge_token=${encodeURIComponent(coords.token)}`;\n const attachChannel: CodexChannel = new SandboxChannel({\n connect: () => openWebSocket(attachUrl),\n outboundSchema: outboundMessageSchema,\n initialLastSeenEventId: coords.lastSeenEventId,\n onDiagnostic,\n });\n await attachChannel.open(isContinue ? { resume: true } : undefined);\n return createSession({\n sessionId: startOpts.sessionId,\n channel: attachChannel,\n // The live bridge was spawned by another process; no process handle.\n proc: undefined,\n skills: startOpts.skills,\n model: settings.model ?? DEFAULT_CODEX_MODEL,\n reasoningEffort: settings.reasoningEffort,\n webSearch: settings.webSearch,\n resumeThreadId: resumeThreadIdString,\n isResume: true,\n seedResumeThreadOnFirstPrompt: false,\n rerunContinue: false,\n bridgePort: coords.port,\n bridgeToken: coords.token,\n sandboxId,\n debug: startOpts.observability?.debug,\n permissionMode: startOpts.permissionMode,\n });\n } catch {\n // Bridge no longer reachable — recover by respawning below.\n }\n }\n\n /*\n * Rungs 2/3 — REPLAY vs RERUN. Respawn the bridge. `replay` is only sound\n * for `continueFrom`: those coordinates include the cursor the on-disk\n * log is replayed *from*. `resumeFrom` is a between-turn resume; even when\n * it carries bridge coordinates, replaying the previous turn would\n * re-deliver stale events into the next turn. Those resumes always `rerun`\n * via `codex.resumeThread(threadId)` when attach is unavailable.\n */\n let respawnStrategy: CodexRespawnStrategy | undefined = isResume\n ? 'rerun'\n : undefined;\n if (coords && isContinue) {\n const logRaw = await Promise.resolve(\n session.readTextFile({\n path: `${bridgeStateDir}/event-log.ndjson`,\n abortSignal: startOpts.abortSignal,\n }),\n ).catch(() => null);\n if ((await classifyDiskLog(logRaw)) === 'replay') {\n respawnStrategy = 'replay';\n }\n }\n\n const port = resolveBridgePort(sandboxSession, settings.port);\n const token = randomBytes(32).toString('hex');\n const env = {\n ...resolveCodexEnv(settings.auth),\n BRIDGE_CHANNEL_TOKEN: token,\n BRIDGE_WS_PORT: String(port),\n ...(respawnStrategy === 'replay'\n ? { BRIDGE_REPLAY_FROM_DISK: '1' }\n : {}),\n };\n\n if (respawnStrategy === undefined) {\n await session.run({\n command: `mkdir -p ${workDir} ${bridgeStateDir}`,\n abortSignal: startOpts.abortSignal,\n });\n }\n\n const proc = await session.spawn({\n command: `node ${BOOTSTRAP_DIR}/bridge.mjs --workdir ${workDir} --bridge-state-dir ${bridgeStateDir} --bootstrap-dir ${BOOTSTRAP_DIR}`,\n env,\n abortSignal: startOpts.abortSignal,\n });\n\n const { port: boundPort } = await waitForBridgeReady({\n proc,\n timeoutMs,\n abortSignal: startOpts.abortSignal,\n });\n\n const wsUrl =\n (await sandboxSession.getPortUrl({\n port: boundPort,\n protocol: 'ws',\n })) + `?agent_bridge_token=${encodeURIComponent(token)}`;\n\n const channel: CodexChannel = new SandboxChannel({\n connect: () => openWebSocket(wsUrl),\n outboundSchema: outboundMessageSchema,\n onDiagnostic,\n // In replay mode the respawned bridge reloaded the finished turn from\n // disk; seed the cursor and resume so it streams the tail (incl.\n // `finish`).\n ...(respawnStrategy === 'replay'\n ? { initialLastSeenEventId: coords?.lastSeenEventId ?? 0 }\n : {}),\n });\n await channel.open(\n respawnStrategy === 'replay' ? { resume: true } : undefined,\n );\n\n return createSession({\n sessionId: startOpts.sessionId,\n channel,\n proc,\n skills: startOpts.skills,\n model: settings.model ?? DEFAULT_CODEX_MODEL,\n reasoningEffort: settings.reasoningEffort,\n webSearch: settings.webSearch,\n resumeThreadId: resumeThreadIdString,\n isResume: respawnStrategy !== undefined,\n seedResumeThreadOnFirstPrompt: respawnStrategy !== undefined,\n rerunContinue: respawnStrategy === 'rerun',\n bridgePort: boundPort,\n bridgeToken: token,\n sandboxId,\n debug: startOpts.observability?.debug,\n permissionMode: startOpts.permissionMode,\n });\n },\n };\n}\n\nfunction resolveBridgePort(\n sandboxSession: HarnessV1NetworkSandboxSession,\n override: number | undefined,\n): number {\n if (override !== undefined) return override;\n if (sandboxSession.ports.length > 0) return sandboxSession.ports[0];\n throw new HarnessCapabilityUnsupportedError({\n harnessId: 'codex',\n message:\n 'The codex harness needs a TCP port exposed by the sandbox. ' +\n 'Create the sandbox with `ports: [<port>]` or pass `createCodex({ port })`.',\n });\n}\n\nasync function readBridgeAsset(name: string): Promise<string> {\n const candidates = [\n new URL(`./bridge/${name}`, import.meta.url),\n new URL(`../bridge/${name}`, import.meta.url),\n ];\n let lastErr: unknown;\n for (const url of candidates) {\n try {\n return await readFile(fileURLToPath(url), 'utf8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== 'ENOENT') throw err;\n lastErr = err;\n }\n }\n throw lastErr ?? new Error(`bridge asset not found: ${name}`);\n}\n\nasync function waitForBridgeReady({\n proc,\n timeoutMs,\n abortSignal,\n}: {\n proc: Experimental_SandboxProcess;\n timeoutMs: number;\n abortSignal: AbortSignal | undefined;\n}): Promise<{ port: number }> {\n const reader = proc.stdout.pipeThrough(new TextDecoderStream()).getReader();\n\n const decoder = lineDecoder();\n\n const deadline = Date.now() + timeoutMs;\n try {\n while (true) {\n if (abortSignal?.aborted) {\n await proc.kill();\n throw abortSignal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n await proc.kill();\n throw new Error('codex bridge did not become ready in time.');\n }\n const { value, done } = (await Promise.race([\n reader.read(),\n new Promise(resolve =>\n setTimeout(\n () => resolve({ value: undefined, done: false }),\n remaining,\n ),\n ),\n ])) as ReadableStreamReadResult<string>;\n if (done) {\n throw new Error('codex bridge exited before becoming ready.');\n }\n if (value === undefined) continue;\n for (const line of decoder.push(value)) {\n const parsed = await safeParseJSON({\n text: line,\n schema: bridgeReadySchema,\n });\n if (parsed.success) return { port: parsed.value.port };\n }\n }\n } finally {\n reader.releaseLock();\n void drainRest(proc.stdout);\n /*\n * Bridge stderr is the only diagnostic channel for what happens\n * inside the sandbox once the bridge is running (uncaught\n * exceptions, Codex SDK errors, network failures). Forward it\n * line-by-line to the host console so a mid-turn bridge crash can\n * be inspected from `pnpm dev` logs without redeploying. The\n * bridge itself writes nothing to stderr in steady state, so this\n * is silent on the happy path.\n */\n void forwardBridgeStderr(proc.stderr);\n }\n}\n\nfunction lineDecoder() {\n let buffer = '';\n return {\n push(chunk: string): string[] {\n buffer += chunk;\n const lines: string[] = [];\n let nl: number;\n while ((nl = buffer.indexOf('\\n')) !== -1) {\n const raw = buffer.slice(0, nl);\n buffer = buffer.slice(nl + 1);\n const line = raw.replace(/\\r$/, '').trim();\n if (line.length > 0) lines.push(line);\n }\n return lines;\n },\n };\n}\n\nasync function forwardBridgeStderr(\n stream: ReadableStream<Uint8Array>,\n): Promise<void> {\n try {\n const reader = stream.pipeThrough(new TextDecoderStream()).getReader();\n while (true) {\n const { value, done } = await reader.read();\n if (done) return;\n if (value) {\n const trimmed = value.endsWith('\\n') ? value.slice(0, -1) : value;\n if (trimmed.length > 0) {\n // eslint-disable-next-line no-console\n console.log(`[bridge stderr] ${trimmed}`);\n }\n }\n }\n } catch {\n // Reader errors are non-fatal — best-effort diagnostic only.\n }\n}\n\nasync function drainRest(stream: ReadableStream<Uint8Array>): Promise<void> {\n try {\n const reader = stream.pipeThrough(new TextDecoderStream()).getReader();\n while (true) {\n const { done } = await reader.read();\n if (done) return;\n }\n } catch {}\n}\n\nfunction openWebSocket(url: string): Promise<WebSocket> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url);\n const onOpen = () => {\n ws.off('error', onError);\n resolve(ws);\n };\n const onError = (err: Error) => {\n ws.off('open', onOpen);\n reject(err);\n };\n ws.once('open', onOpen);\n ws.once('error', onError);\n });\n}\n\nfunction createSession({\n sessionId,\n channel,\n proc,\n skills,\n model,\n reasoningEffort,\n webSearch,\n resumeThreadId,\n isResume,\n seedResumeThreadOnFirstPrompt,\n rerunContinue,\n bridgePort,\n bridgeToken,\n sandboxId,\n debug,\n permissionMode,\n}: {\n sessionId: string;\n channel: CodexChannel;\n /** Undefined on `attach` — the live bridge was spawned by another process. */\n proc: Experimental_SandboxProcess | undefined;\n skills: ReadonlyArray<HarnessV1Skill> | undefined;\n model: string | undefined;\n reasoningEffort: 'low' | 'medium' | 'high' | undefined;\n webSearch: boolean | undefined;\n resumeThreadId: string | undefined;\n isResume: boolean;\n seedResumeThreadOnFirstPrompt: boolean;\n rerunContinue: boolean;\n bridgePort: number;\n bridgeToken: string;\n sandboxId: string;\n debug: HarnessV1DebugConfig | undefined;\n permissionMode: HarnessV1PermissionMode | undefined;\n}): HarnessV1Session {\n let stopped = false;\n let stopPromise: Promise<void> | undefined;\n /*\n * Send the persisted threadId on the first prompt only when the bridge was\n * respawned (rerun/replay) so it takes the `codex.resumeThread(...)` branch.\n * An `attach`ed bridge already holds its threadState in memory and continues\n * on its own, so it needs no seed.\n */\n let pendingResumeThreadId = seedResumeThreadOnFirstPrompt\n ? resumeThreadId\n : undefined;\n /*\n * Instructions are prepended to the first user message of a fresh session\n * only. A resumed session (attach/replay/rerun) already carried them in its\n * original first message (preserved in the persisted thread), so it starts\n * \"applied\".\n */\n let instructionsApplied = isResume;\n\n /*\n * Latest codex thread id, cached from the bridge's `bridge-thread`\n * announcements. Seeded from lifecycle state so `doDetach()` and `doStop()`\n * can include a thread id even before this process has run a turn.\n */\n let latestThreadId = resumeThreadId;\n channel.on('bridge-thread', msg => {\n latestThreadId = msg.threadId;\n });\n\n /*\n * Wire the channel into one turn's worth of events and return the control\n * surface. Shared by `doPromptTurn` (which sends a `start` afterwards) and\n * `doContinueTurn` (which attaches to an already-running/replayed turn, or sends\n * a rerun `start`). The only difference between the two entry points is the\n * `start` message, not the listener/abort/settle plumbing.\n */\n const wireTurn = (turnOpts: {\n emit: (event: HarnessV1StreamPart) => void;\n abortSignal?: AbortSignal;\n }): HarnessV1PromptControl => {\n let pendingResolve: (() => void) | undefined;\n let pendingReject: ((err: unknown) => void) | undefined;\n const done = new Promise<void>((resolve, reject) => {\n pendingResolve = resolve;\n pendingReject = reject;\n });\n\n const unsubs: Array<() => void> = [];\n const forward = (event: HarnessV1StreamPart) => {\n try {\n turnOpts.emit(event);\n } catch {}\n };\n\n const eventTypes = [\n 'stream-start',\n 'text-start',\n 'text-delta',\n 'text-end',\n 'reasoning-start',\n 'reasoning-delta',\n 'reasoning-end',\n 'tool-call',\n 'tool-approval-request',\n 'tool-result',\n 'file-change',\n 'finish-step',\n 'raw',\n ] as const;\n let isSettled = false;\n const settleSuccess = () => {\n if (isSettled) return;\n isSettled = true;\n for (const u of unsubs) u();\n pendingResolve!();\n };\n const settleError = (err: unknown) => {\n if (isSettled) return;\n isSettled = true;\n for (const u of unsubs) u();\n pendingReject!(err);\n };\n\n for (const type of eventTypes) {\n unsubs.push(\n channel.on(type, msg => {\n forward(msg);\n }),\n );\n }\n unsubs.push(\n channel.on('finish', msg => {\n forward(msg);\n settleSuccess();\n }),\n );\n unsubs.push(\n channel.on('error', msg => {\n forward(msg);\n settleError(msg.error);\n }),\n );\n\n /*\n * A `'suspended'` close is a graceful slice-boundary freeze the host\n * initiated (`doSuspendTurn`): the turn keeps running in the bridge and its\n * tail is replayed to the next process, so wind this turn down cleanly\n * rather than failing it. Any other close mid-turn is an unexpected drop.\n */\n const onClose = (_code?: number, reason?: string) => {\n if (isSettled) return;\n if (reason === 'suspended') {\n settleSuccess();\n return;\n }\n settleError(new Error('codex bridge closed before the turn finished.'));\n };\n channel.onClose(onClose);\n\n const onAbort = () => {\n if (isSettled) return;\n try {\n channel.send({ type: 'abort' });\n } catch {}\n settleError(\n turnOpts.abortSignal?.reason ??\n new DOMException('Aborted', 'AbortError'),\n );\n };\n if (turnOpts.abortSignal) {\n if (turnOpts.abortSignal.aborted) {\n onAbort();\n } else {\n turnOpts.abortSignal.addEventListener('abort', onAbort, {\n once: true,\n });\n }\n }\n\n return {\n submitToolResult: async input => {\n channel.send({\n type: 'tool-result',\n toolCallId: input.toolCallId,\n output: input.output,\n isError: input.isError,\n });\n },\n submitToolApproval: async input => {\n channel.send({\n type: 'tool-approval-response',\n approvalId: input.approvalId,\n approved: input.approved,\n reason: input.reason,\n });\n },\n submitUserMessage: async text => {\n channel.send({ type: 'user-message', text });\n },\n done,\n };\n };\n\n return {\n sessionId,\n isResume,\n modelId: model,\n doPromptTurn: async promptOpts => {\n const control = wireTurn({\n emit: promptOpts.emit,\n abortSignal: promptOpts.abortSignal,\n });\n\n const applyInstructions =\n !instructionsApplied && !!promptOpts.instructions;\n instructionsApplied = true;\n\n const startMessage = {\n type: 'start' as const,\n prompt: extractUserText(promptOpts.prompt),\n ...(applyInstructions ? { instructions: promptOpts.instructions } : {}),\n tools: (promptOpts.tools ?? []).map(t => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n model,\n reasoningEffort,\n webSearch,\n ...(permissionMode ? { permissionMode } : {}),\n ...(skills && skills.length > 0\n ? {\n skills: skills.map(s => ({\n name: s.name,\n description: s.description,\n content: s.content,\n })),\n }\n : {}),\n ...(pendingResumeThreadId\n ? { resumeThreadId: pendingResumeThreadId }\n : {}),\n ...(debug ? { debug } : {}),\n };\n pendingResumeThreadId = undefined;\n channel.send(startMessage);\n\n return control;\n },\n doContinueTurn: async continueOpts => {\n const control = wireTurn({\n emit: continueOpts.emit,\n abortSignal: continueOpts.abortSignal,\n });\n\n /*\n * attach / replay: the still-running (or disk-replayed) turn streams into\n * the wired listeners — `doStart` opened the channel with `{ resume: true }`\n * so the bridge replays everything past the persisted cursor (including a\n * `finish` if the turn ended during the gap). No `start` is sent: issuing\n * one would clear the bridge's replay log and begin a new turn. Lossless.\n *\n * rerun: the bridge was respawned with no in-flight turn to attach to, so\n * re-drive codex's own thread via `resumeThreadId`. Lossy — work in flight\n * at the interruption is recomputed. This is the rare bridge-died\n * fallback; the common slice path is `attach`.\n */\n if (rerunContinue) {\n const threadId = pendingResumeThreadId ?? latestThreadId;\n pendingResumeThreadId = undefined;\n channel.send({\n type: 'start' as const,\n /*\n * A continuation nudge rather than an empty prompt: `resumeThreadId`\n * rehydrates the prior thread, and this is the new user turn that\n * drives it forward. Keeping it non-empty avoids handing the runtime\n * an empty user message (and mirrors the claude-code adapter, where an\n * empty text block trips the Anthropic API's `cache_control` rule).\n */\n prompt: 'Continue.',\n tools: (continueOpts.tools ?? []).map(t => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n model,\n reasoningEffort,\n webSearch,\n ...(permissionMode ? { permissionMode } : {}),\n ...(threadId ? { resumeThreadId: threadId } : {}),\n ...(debug ? { debug } : {}),\n });\n }\n\n return control;\n },\n doCompact: async () => {\n /*\n * Codex compacts its context automatically inside the core turn loop\n * (~90% of the model context window), but the `codex exec` transport this\n * adapter drives exposes no manual compaction trigger and emits no\n * compaction event. Manual `compact()` is therefore unsupported; Codex's\n * own auto-compaction continues to run regardless.\n */\n throw new HarnessCapabilityUnsupportedError({\n message:\n \"Harness 'codex' does not support manual compaction; Codex auto-compacts its context internally.\",\n harnessId: 'codex',\n });\n },\n doDetach: async () => {\n if (stopped) {\n throw new Error(\n `codex session ${sessionId} is already stopped; cannot detach.`,\n );\n }\n stopped = true;\n const lastSeenEventId = await channel.suspend();\n const payload: HarnessV1ResumeSessionState = {\n type: 'resume-session',\n harnessId: 'codex',\n specificationVersion: 'harness-v1',\n data: {\n ...(latestThreadId ? { threadId: latestThreadId } : {}),\n bridge: {\n port: bridgePort,\n token: bridgeToken,\n lastSeenEventId,\n sandboxId,\n },\n },\n };\n return payload;\n },\n doDestroy: async () => {\n if (stopped) return stopPromise;\n stopped = true;\n stopPromise = (async () => {\n // Tell the channel we are tearing down so the bridge's post-shutdown\n // socket close finalises instead of triggering a reconnect.\n channel.beginClose();\n try {\n if (!channel.isClosed()) {\n channel.send({ type: 'shutdown' });\n }\n } catch {}\n let stopTimer: ReturnType<typeof setTimeout> | undefined;\n try {\n if (proc) {\n await Promise.race([\n proc.wait(),\n new Promise<void>(resolve => {\n stopTimer = setTimeout(resolve, 5000);\n stopTimer.unref?.();\n }),\n ]);\n }\n } finally {\n if (stopTimer) clearTimeout(stopTimer);\n try {\n await proc?.kill();\n } catch {}\n channel.close();\n }\n })();\n return stopPromise;\n },\n doStop: async () => {\n if (stopped) {\n throw new Error(\n `codex session ${sessionId} is already stopped; cannot stop.`,\n );\n }\n stopped = true;\n /*\n * If the bridge's channel already closed (e.g. mid-turn WS drop)\n * there is no one to ack a `detach` message. Synthesize an empty\n * payload — the workdir is still captured by the sandbox snapshot\n * during the subsequent `sandboxSession.stop()`, so the next turn can\n * resume the filesystem state. The trade-off: we lose\n * `threadId`, so the codex CLI starts a fresh thread on the\n * preserved workdir rather than resuming the prior conversation\n * inside Codex's runtime. Ability to continue beats throwing.\n */\n // Tell the channel we are tearing down so the bridge's post-detach\n // socket close finalises instead of triggering a reconnect.\n channel.beginClose();\n const data: unknown = channel.isClosed()\n ? {}\n : await new Promise<unknown>((resolve, reject) => {\n const timer = setTimeout(() => {\n unsub();\n reject(\n new Error(\n `codex session ${sessionId} did not reply to detach within 5s.`,\n ),\n );\n }, 5000);\n timer.unref?.();\n const unsub = channel.on('bridge-detach', msg => {\n clearTimeout(timer);\n unsub();\n resolve(msg.data);\n });\n try {\n channel.send({ type: 'detach' });\n } catch (err) {\n clearTimeout(timer);\n unsub();\n reject(err);\n }\n });\n\n let stopTimer: ReturnType<typeof setTimeout> | undefined;\n try {\n if (proc) {\n await Promise.race([\n proc.wait(),\n new Promise<void>(resolve => {\n stopTimer = setTimeout(resolve, 5000);\n stopTimer.unref?.();\n }),\n ]);\n }\n } finally {\n if (stopTimer) clearTimeout(stopTimer);\n try {\n await proc?.kill();\n } catch {}\n channel.close();\n }\n\n const payload: HarnessV1ResumeSessionState = {\n type: 'resume-session',\n harnessId: 'codex',\n specificationVersion: 'harness-v1',\n data: (data ?? {}) as HarnessV1ResumeSessionState['data'],\n };\n return payload;\n },\n doSuspendTurn: async () => {\n if (stopped) {\n throw new Error(\n `codex session ${sessionId} is stopped; cannot suspend.`,\n );\n }\n stopped = true;\n /*\n * Gracefully freeze the active turn at a precise cursor. `channel.suspend`\n * stops processing inbound frames (the cursor stops advancing exactly at\n * the last delivered event), drains what was already dispatched, then\n * closes the host socket with reason `'suspended'` — which `wireTurn`'s\n * `onClose` treats as a clean turn end. The bridge keeps the turn running\n * and accumulates events past the cursor for the next slice to replay. The\n * sandbox process is deliberately left alive (no `shutdown`/`detach`).\n */\n const lastSeenEventId = await channel.suspend();\n const payload: HarnessV1ContinueTurnState = {\n type: 'continue-turn',\n harnessId: 'codex',\n specificationVersion: 'harness-v1',\n data: {\n ...(latestThreadId ? { threadId: latestThreadId } : {}),\n bridge: {\n port: bridgePort,\n token: bridgeToken,\n lastSeenEventId,\n sandboxId,\n },\n },\n };\n return payload;\n },\n };\n}\n\n/*\n * Reduce a `HarnessV1Prompt` to the plain user text the bridge forwards\n * to the Codex SDK. File and image parts on the message are not yet\n * supported by the underlying runtime — throw rather than silently drop\n * them so callers learn about the gap instead of seeing mysteriously\n * truncated prompts.\n */\nfunction extractUserText(prompt: HarnessV1Prompt): string {\n if (typeof prompt === 'string') return prompt;\n const { content } = prompt;\n if (typeof content === 'string') return content;\n const parts: string[] = [];\n for (const part of content) {\n if (part.type !== 'text') {\n throw new HarnessCapabilityUnsupportedError({\n harnessId: 'codex',\n message: `The codex harness does not yet support user message parts of type '${part.type}'. Pass a string or a user message whose content contains only text parts.`,\n });\n }\n parts.push(part.text);\n }\n return parts.join('\\n\\n');\n}\n","export type CodexAuthOptions = {\n readonly openaiCompatible?: {\n readonly apiKey?: string;\n readonly baseUrl?: string;\n readonly modelProviderName?: string;\n readonly queryParamsJson?: string;\n };\n readonly openai?: {\n readonly apiKey?: string;\n readonly baseUrl?: string;\n readonly organization?: string;\n readonly project?: string;\n };\n readonly gateway?: {\n readonly apiKey?: string;\n readonly baseUrl?: string;\n };\n};\n\n/**\n * Resolve the environment-variable blob the codex bridge needs. Precedence:\n *\n * 1. Explicit `auth.openaiCompatible` — pin to a custom OpenAI-compatible endpoint.\n * 2. Explicit `auth.openai` — pin to direct OpenAI auth.\n * 3. Explicit `auth.gateway` — pin to Vercel AI Gateway.\n * 4. Auto-detect from the host process env: gateway first\n * (`AI_GATEWAY_API_KEY`), then `CODEX_API_KEY` / `OPENAI_API_KEY`.\n */\nexport function resolveCodexEnv(\n auth: CodexAuthOptions | undefined,\n processEnv: Record<string, string | undefined> = process.env,\n): Record<string, string> {\n if (auth?.openaiCompatible) {\n return pickOpenAICompatible(auth.openaiCompatible, processEnv);\n }\n if (auth?.openai) {\n return pickOpenAI({ explicit: auth.openai, processEnv });\n }\n if (auth?.gateway) {\n return pickGateway(auth.gateway, processEnv);\n }\n if (processEnv.AI_GATEWAY_API_KEY) {\n return pickGateway({}, processEnv);\n }\n return pickOpenAI({ processEnv });\n}\n\nfunction pickOpenAICompatible(\n explicit: NonNullable<CodexAuthOptions['openaiCompatible']>,\n processEnv: Record<string, string | undefined>,\n): Record<string, string> {\n const env: Record<string, string> = {};\n const apiKey =\n explicit.apiKey ?? processEnv.OPENAI_API_KEY ?? processEnv.CODEX_API_KEY;\n if (apiKey) env.CODEX_API_KEY = apiKey;\n if (explicit.baseUrl) env.OPENAI_BASE_URL = explicit.baseUrl;\n if (explicit.modelProviderName)\n env.CODEX_MODEL_PROVIDER_NAME = explicit.modelProviderName;\n if (explicit.queryParamsJson)\n env.OPENAI_QUERY_PARAMS_JSON = explicit.queryParamsJson;\n return env;\n}\n\nfunction pickOpenAI({\n explicit,\n processEnv,\n}: {\n explicit?: NonNullable<CodexAuthOptions['openai']>;\n processEnv: Record<string, string | undefined>;\n}): Record<string, string> {\n const env: Record<string, string> = {};\n const apiKey =\n explicit?.apiKey ?? processEnv.OPENAI_API_KEY ?? processEnv.CODEX_API_KEY;\n if (apiKey) env.CODEX_API_KEY = apiKey;\n const baseUrl = explicit?.baseUrl ?? processEnv.OPENAI_BASE_URL;\n if (baseUrl) env.OPENAI_BASE_URL = baseUrl;\n const organization = explicit?.organization ?? processEnv.OPENAI_ORGANIZATION;\n if (organization) env.OPENAI_ORGANIZATION = organization;\n const project = explicit?.project ?? processEnv.OPENAI_PROJECT;\n if (project) env.OPENAI_PROJECT = project;\n return env;\n}\n\nfunction pickGateway(\n explicit: NonNullable<CodexAuthOptions['gateway']>,\n processEnv: Record<string, string | undefined>,\n): Record<string, string> {\n const apiKey = explicit.apiKey ?? processEnv.AI_GATEWAY_API_KEY;\n const baseUrl =\n explicit.baseUrl ??\n processEnv.AI_GATEWAY_BASE_URL ??\n 'https://ai-gateway.vercel.sh/v1';\n const env: Record<string, string> = {};\n if (apiKey) {\n env.AI_GATEWAY_API_KEY = apiKey;\n env.CODEX_API_KEY = apiKey;\n }\n env.AI_GATEWAY_BASE_URL = baseUrl;\n return env;\n}\n","import {\n harnessV1BridgeInboundCommandSchemas,\n harnessV1BridgeOutboundMessageSchema,\n harnessV1BridgeReadySchema,\n harnessV1BridgeStartBaseSchema,\n} from '@ai-sdk/harness';\nimport { z } from 'zod/v4';\n\n/*\n * Codex's bridge wire protocol. The outbound events (including `file-change`\n * and the `bridge-thread` resume coordinate), transport frames, shared inbound\n * commands, and `bridge-ready` line all come from the shared `@ai-sdk/harness`\n * protocol — the only Codex-specific piece is the `start` payload.\n */\n\nexport const outboundMessageSchema = harnessV1BridgeOutboundMessageSchema;\nexport type OutboundMessage = z.infer<typeof outboundMessageSchema>;\n\nexport const startMessageSchema = harnessV1BridgeStartBaseSchema.extend({\n instructions: z.string().optional(),\n reasoningEffort: z.enum(['low', 'medium', 'high']).optional(),\n webSearch: z.boolean().optional(),\n skills: z\n .array(\n z.object({\n name: z.string(),\n description: z.string(),\n content: z.string(),\n }),\n )\n .optional(),\n // Resume signal. When supplied, the bridge calls\n // `codex.resumeThread(resumeThreadId, …)` instead of starting a fresh thread.\n // The host sources the id from lifecycle state `data` cached from a prior\n // `agent.detach`.\n resumeThreadId: z.string().optional(),\n});\n\nexport type StartMessage = z.infer<typeof startMessageSchema>;\n\nexport const inboundMessageSchema = z.discriminatedUnion('type', [\n startMessageSchema,\n ...harnessV1BridgeInboundCommandSchemas,\n]);\nexport type InboundMessage = z.infer<typeof inboundMessageSchema>;\n\nexport const bridgeReadySchema = harnessV1BridgeReadySchema;\nexport type BridgeReady = z.infer<typeof bridgeReadySchema>;\n","import { createCodex } from './codex-harness';\n\n/**\n * Default `codex` harness instance with no overrides — suitable for the\n * common case where the underlying `codex` CLI's defaults are fine.\n * Equivalent to `createCodex()`.\n */\nexport const codex = createCodex();\n\nexport { createCodex } from './codex-harness';\nexport type { CodexHarnessSettings } from './codex-harness';\nexport type { CodexAuthOptions } from './codex-auth';\n"],"mappings":"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","names":["z","z"]}
1
+ {"version":3,"sources":["../src/codex-harness.ts","../src/codex-auth.ts","../src/codex-bridge-protocol.ts","../src/index.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n commonTool,\n HarnessCapabilityUnsupportedError,\n harnessV1DiagnosticFromBridgeFrame,\n type HarnessV1,\n type HarnessV1Bootstrap,\n type HarnessV1DebugConfig,\n type HarnessV1BuiltinTool,\n type HarnessV1ContinueTurnState,\n type HarnessV1Prompt,\n type HarnessV1PromptControl,\n type HarnessV1ResumeSessionState,\n type HarnessV1NetworkSandboxSession,\n type HarnessV1PermissionMode,\n type HarnessV1Session,\n type HarnessV1Skill,\n type HarnessV1StreamPart,\n} from '@ai-sdk/harness';\nimport { classifyDiskLog, SandboxChannel } from '@ai-sdk/harness/utils';\nimport {\n safeParseJSON,\n type Experimental_SandboxProcess,\n type Experimental_SandboxSession,\n} from '@ai-sdk/provider-utils';\nimport { WebSocket } from 'ws';\nimport { z } from 'zod';\nimport { resolveCodexEnv, type CodexAuthOptions } from './codex-auth';\nimport {\n bridgeReadySchema,\n outboundMessageSchema,\n type InboundMessage,\n type OutboundMessage,\n} from './codex-bridge-protocol';\n\ntype CodexChannel = SandboxChannel<OutboundMessage, InboundMessage>;\ntype CodexRespawnStrategy = 'replay' | 'rerun';\n\ntype WriteSkillsResult = {\n readonly homeDir: string;\n readonly codexHomeDir: string;\n};\n\n/*\n * The model the adapter pins when the consumer configures none. The Codex SDK\n * does not report the model it resolves to at runtime (no model field on any\n * event), and exposes no default-model constant, so we pin the latest\n * codex-specialized model available for the bundled `@openai/codex@0.130.0`\n * (published 2026-05-08): `gpt-5.3-codex` (released 2026-02). Keep this in sync\n * when bumping the codex SDK/binary. Passing it explicitly makes the resolved\n * model deterministic and the telemetry (`gen_ai.request.model`) accurate.\n */\nconst DEFAULT_CODEX_MODEL = 'gpt-5.3-codex';\n\nexport type CodexHarnessSettings = {\n readonly auth?: CodexAuthOptions;\n /**\n * OpenAI model id the underlying `codex` CLI should use. Leaving this unset\n * pins the adapter default (`DEFAULT_CODEX_MODEL`).\n */\n readonly model?: string;\n /**\n * Reasoning effort for reasoning-capable models. Leaving this unset\n * defers to the CLI's default.\n */\n readonly reasoningEffort?: 'low' | 'medium' | 'high';\n /**\n * When `true`, allow the underlying runtime to use live web search.\n */\n readonly webSearch?: boolean;\n /**\n * Override the port the bridge binds inside the sandbox. By default the\n * adapter uses the first port the sandbox declares via `sandbox.ports`.\n * Only set this if the sandbox declares multiple ports and the first one\n * is reserved for something else.\n */\n readonly port?: number;\n /** Maximum milliseconds to wait for the bridge to advertise its port. Defaults to 120000. */\n readonly startupTimeoutMs?: number;\n};\n\n/*\n * Every native tool the Codex CLI can invoke as a model-callable tool,\n * declared as a `ToolSet` keyed by what the bridge emits as `toolName` on\n * the wire (`commonName ?? nativeName`). Schemas reflect the `ThreadItem`\n * union in `@openai/codex-sdk`'s `dist/index.d.ts`.\n *\n * Codex's other native operations (`apply_patch`, todo planning) surface\n * only as side-effect events (`file_change`, `todo_list`) and are not\n * model-callable tools — they don't appear here.\n */\nconst CODEX_BUILTIN_TOOLS = {\n bash: commonTool('bash', {\n nativeName: 'shell',\n toolUseKind: 'bash',\n description: 'Execute a shell command',\n inputSchema: z.object({ command: z.string() }),\n }),\n webSearch: commonTool('webSearch', {\n nativeName: 'web_search',\n toolUseKind: 'readonly',\n description: 'Search the web',\n inputSchema: z.object({ query: z.string() }),\n }),\n} as const satisfies Record<string, HarnessV1BuiltinTool<any, any>>;\n\n/*\n * Bootstrap lives in /tmp because it's pure derived state — the harness can\n * reinstall the CLI and bridge files on any fresh sandbox from the recipe.\n * Persistence comes from the sandbox provider's snapshot, not the path.\n *\n * The session work dir (`startOpts.sessionWorkDir`) and the bridge-state dir\n * derived from `sandboxSession.defaultWorkingDirectory` both live under the sandbox's\n * default working directory — the provider's persistent mount — so the\n * workdir's contents (the codex CLI shim and any files the agent edits) and\n * the bridge state files survive both detach -> attach and\n * stop -> snapshot -> resume cycles.\n */\nconst BOOTSTRAP_DIR = '/tmp/harness/codex';\n\n/**\n * Live bridge coordinates returned by `doDetach()` and `doSuspendTurn()`. A\n * future process uses them to reopen a socket to the still-running bridge\n * (`attach`) instead of re-spawning it. Absent on a `doStop()` payload.\n */\nconst bridgeCoordsSchema = z.object({\n port: z.number(),\n token: z.string(),\n lastSeenEventId: z.number(),\n sandboxId: z.string().optional(),\n});\n\n/**\n * Schema for the adapter-specific lifecycle `data` payload Codex produces.\n * `threadId` is what `codex.resumeThread(...)` requires for the replay/rerun\n * rungs; the sandbox lookup is handled separately via\n * `provider.resumeSession({ sessionId })`. `bridge` carries live coordinates\n * for cross-process `attach` (present on `doDetach()` and `doSuspendTurn()`\n * payloads).\n */\nconst codexResumeStateSchema = z.object({\n threadId: z.string().optional(),\n bridge: bridgeCoordsSchema.optional(),\n});\n\ntype CodexBridgeCoords = z.infer<typeof bridgeCoordsSchema>;\n\nexport function createCodex(\n settings: CodexHarnessSettings = {},\n): HarnessV1<typeof CODEX_BUILTIN_TOOLS> {\n let cachedBootstrap: HarnessV1Bootstrap | undefined;\n\n return {\n specificationVersion: 'harness-v1',\n harnessId: 'codex',\n builtinTools: CODEX_BUILTIN_TOOLS,\n supportsBuiltinToolApprovals: false,\n lifecycleStateSchema: codexResumeStateSchema,\n getBootstrap: async () => {\n if (cachedBootstrap != null) return cachedBootstrap;\n const [pkg, lock, bridge, hostToolMcp] = await Promise.all([\n readBridgeAsset('package.json'),\n readBridgeAsset('pnpm-lock.yaml'),\n readBridgeAsset('index.mjs'),\n readBridgeAsset('host-tool-mcp.mjs'),\n ]);\n cachedBootstrap = {\n harnessId: 'codex',\n bootstrapDir: BOOTSTRAP_DIR,\n files: [\n { path: `${BOOTSTRAP_DIR}/package.json`, content: pkg },\n { path: `${BOOTSTRAP_DIR}/pnpm-lock.yaml`, content: lock },\n { path: `${BOOTSTRAP_DIR}/bridge.mjs`, content: bridge },\n {\n path: `${BOOTSTRAP_DIR}/host-tool-mcp.mjs`,\n content: hostToolMcp,\n },\n ],\n commands: [\n { command: `mkdir -p ${BOOTSTRAP_DIR}` },\n {\n command: `pnpm --dir ${BOOTSTRAP_DIR} install --frozen-lockfile --store-dir ${BOOTSTRAP_DIR}/.pnpm-store`,\n },\n ],\n };\n return cachedBootstrap;\n },\n doStart: async startOpts => {\n if (\n startOpts.permissionMode != null &&\n startOpts.permissionMode !== 'allow-all'\n ) {\n throw new HarnessCapabilityUnsupportedError({\n message:\n \"Harness 'codex' does not support built-in tool approval requests; use permissionMode: 'allow-all'.\",\n harnessId: 'codex',\n });\n }\n const sandboxSession = startOpts.sandboxSession;\n const session = sandboxSession.restricted();\n const sandboxId = sandboxSession.id;\n const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;\n const isResume = lifecycleState != null;\n const isContinue = startOpts.continueFrom != null;\n const resumeData =\n isResume && typeof lifecycleState?.data === 'object'\n ? (lifecycleState.data as {\n threadId?: unknown;\n bridge?: CodexBridgeCoords;\n })\n : undefined;\n const resumeThreadId = resumeData?.threadId;\n const resumeThreadIdString =\n typeof resumeThreadId === 'string' && resumeThreadId.length > 0\n ? resumeThreadId\n : undefined;\n const coords = resumeData?.bridge;\n\n const workDir = startOpts.sessionWorkDir;\n const sessionDataDir = `${sandboxSession.defaultWorkingDirectory}/.agent-runs/${startOpts.sessionId}`;\n const bridgeStateDir = `${sessionDataDir}/bridge`;\n const timeoutMs = settings.startupTimeoutMs ?? 120_000;\n\n // Normalize each forwarded bridge diagnostics frame into the general\n // `HarnessV1Diagnostic` and report it. The adapter does no telemetry work\n // beyond this transport→emission mapping.\n const report = startOpts.observability?.report;\n const onDiagnostic = report\n ? (frame: Parameters<typeof harnessV1DiagnosticFromBridgeFrame>[0]) =>\n report(\n harnessV1DiagnosticFromBridgeFrame(frame, {\n sessionId: startOpts.sessionId,\n timestamp: Date.now(),\n }),\n )\n : undefined;\n\n /*\n * Rung 1 — ATTACH. With live coordinates, reopen a socket to the\n * still-running bridge. Parked between-turn sessions just attach and wait\n * for the next `start`; suspended in-flight turns request replay of\n * everything past the persisted cursor. No spawn, no fresh token. If the\n * bridge is gone the open throws and we fall through to a spawn-based\n * recovery.\n */\n if (coords) {\n try {\n const attachUrl =\n (await sandboxSession.getPortUrl({\n port: coords.port,\n protocol: 'ws',\n })) + `?agent_bridge_token=${encodeURIComponent(coords.token)}`;\n const attachChannel: CodexChannel = new SandboxChannel({\n connect: () => openWebSocket(attachUrl),\n outboundSchema: outboundMessageSchema,\n initialLastSeenEventId: coords.lastSeenEventId,\n onDiagnostic,\n });\n await attachChannel.open(isContinue ? { resume: true } : undefined);\n return createSession({\n sessionId: startOpts.sessionId,\n channel: attachChannel,\n // The live bridge was spawned by another process; no process handle.\n proc: undefined,\n model: settings.model ?? DEFAULT_CODEX_MODEL,\n reasoningEffort: settings.reasoningEffort,\n webSearch: settings.webSearch,\n resumeThreadId: resumeThreadIdString,\n isResume: true,\n seedResumeThreadOnFirstPrompt: false,\n rerunContinue: false,\n bridgePort: coords.port,\n bridgeToken: coords.token,\n sandboxId,\n debug: startOpts.observability?.debug,\n permissionMode: startOpts.permissionMode,\n });\n } catch {\n // Bridge no longer reachable — recover by respawning below.\n }\n }\n\n /*\n * Rungs 2/3 — REPLAY vs RERUN. Respawn the bridge. `replay` is only sound\n * for `continueFrom`: those coordinates include the cursor the on-disk\n * log is replayed *from*. `resumeFrom` is a between-turn resume; even when\n * it carries bridge coordinates, replaying the previous turn would\n * re-deliver stale events into the next turn. Those resumes always `rerun`\n * via `codex.resumeThread(threadId)` when attach is unavailable.\n */\n let respawnStrategy: CodexRespawnStrategy | undefined = isResume\n ? 'rerun'\n : undefined;\n if (coords && isContinue) {\n const logRaw = await Promise.resolve(\n session.readTextFile({\n path: `${bridgeStateDir}/event-log.ndjson`,\n abortSignal: startOpts.abortSignal,\n }),\n ).catch(() => null);\n if ((await classifyDiskLog(logRaw)) === 'replay') {\n respawnStrategy = 'replay';\n }\n }\n\n const port = resolveBridgePort(sandboxSession, settings.port);\n const token = randomBytes(32).toString('hex');\n const codexSkillSetup =\n startOpts.skills && startOpts.skills.length > 0\n ? await writeSkills({\n sandbox: session,\n skills: startOpts.skills,\n abortSignal: startOpts.abortSignal,\n })\n : undefined;\n const env = {\n ...resolveCodexEnv(settings.auth),\n BRIDGE_CHANNEL_TOKEN: token,\n BRIDGE_WS_PORT: String(port),\n ...(codexSkillSetup\n ? {\n HOME: codexSkillSetup.homeDir,\n CODEX_HOME: codexSkillSetup.codexHomeDir,\n }\n : {}),\n ...(respawnStrategy === 'replay'\n ? { BRIDGE_REPLAY_FROM_DISK: '1' }\n : {}),\n };\n\n if (respawnStrategy === undefined) {\n await session.run({\n command: `mkdir -p ${workDir} ${bridgeStateDir}`,\n abortSignal: startOpts.abortSignal,\n });\n }\n\n const proc = await session.spawn({\n command: `node ${BOOTSTRAP_DIR}/bridge.mjs --workdir ${workDir} --bridge-state-dir ${bridgeStateDir} --bootstrap-dir ${BOOTSTRAP_DIR}`,\n env,\n abortSignal: startOpts.abortSignal,\n });\n\n const { port: boundPort } = await waitForBridgeReady({\n proc,\n timeoutMs,\n abortSignal: startOpts.abortSignal,\n });\n\n const wsUrl =\n (await sandboxSession.getPortUrl({\n port: boundPort,\n protocol: 'ws',\n })) + `?agent_bridge_token=${encodeURIComponent(token)}`;\n\n const channel: CodexChannel = new SandboxChannel({\n connect: () => openWebSocket(wsUrl),\n outboundSchema: outboundMessageSchema,\n onDiagnostic,\n // In replay mode the respawned bridge reloaded the finished turn from\n // disk; seed the cursor and resume so it streams the tail (incl.\n // `finish`).\n ...(respawnStrategy === 'replay'\n ? { initialLastSeenEventId: coords?.lastSeenEventId ?? 0 }\n : {}),\n });\n await channel.open(\n respawnStrategy === 'replay' ? { resume: true } : undefined,\n );\n\n return createSession({\n sessionId: startOpts.sessionId,\n channel,\n proc,\n model: settings.model ?? DEFAULT_CODEX_MODEL,\n reasoningEffort: settings.reasoningEffort,\n webSearch: settings.webSearch,\n resumeThreadId: resumeThreadIdString,\n isResume: respawnStrategy !== undefined,\n seedResumeThreadOnFirstPrompt: respawnStrategy !== undefined,\n rerunContinue: respawnStrategy === 'rerun',\n bridgePort: boundPort,\n bridgeToken: token,\n sandboxId,\n debug: startOpts.observability?.debug,\n permissionMode: startOpts.permissionMode,\n });\n },\n };\n}\n\nfunction resolveBridgePort(\n sandboxSession: HarnessV1NetworkSandboxSession,\n override: number | undefined,\n): number {\n if (override !== undefined) return override;\n if (sandboxSession.ports.length > 0) return sandboxSession.ports[0];\n throw new HarnessCapabilityUnsupportedError({\n harnessId: 'codex',\n message:\n 'The codex harness needs a TCP port exposed by the sandbox. ' +\n 'Create the sandbox with `ports: [<port>]` or pass `createCodex({ port })`.',\n });\n}\n\nasync function readBridgeAsset(name: string): Promise<string> {\n const candidates = [\n new URL(`./bridge/${name}`, import.meta.url),\n new URL(`../bridge/${name}`, import.meta.url),\n ];\n let lastErr: unknown;\n for (const url of candidates) {\n try {\n return await readFile(fileURLToPath(url), 'utf8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== 'ENOENT') throw err;\n lastErr = err;\n }\n }\n throw lastErr ?? new Error(`bridge asset not found: ${name}`);\n}\n\nasync function writeSkills({\n sandbox,\n skills,\n abortSignal,\n}: {\n sandbox: Experimental_SandboxSession;\n skills: ReadonlyArray<HarnessV1Skill>;\n abortSignal?: AbortSignal;\n}): Promise<WriteSkillsResult> {\n for (const skill of skills) {\n safeCodexSkillName(skill.name);\n for (const file of skill.files ?? []) {\n safeCodexSkillFilePath({\n skillName: skill.name,\n filePath: file.path,\n });\n }\n }\n\n const homeDir = await resolveSandboxHomeDir({ sandbox, abortSignal });\n const codexHomeDir = path.posix.join(homeDir, '.codex');\n await sandbox.run({\n command: `mkdir -p ${shellQuote(codexHomeDir)}`,\n abortSignal,\n });\n\n const rootDir = path.posix.join(homeDir, '.agents', 'skills');\n await sandbox.run({\n command: `mkdir -p ${shellQuote(rootDir)}`,\n abortSignal,\n });\n\n for (const skill of skills) {\n const name = safeCodexSkillName(skill.name);\n const skillDir = path.posix.join(rootDir, name);\n const content = `---\\nname: ${skill.name}\\ndescription: ${skill.description}\\n---\\n\\n${skill.content}`;\n\n await sandbox.writeTextFile({\n path: path.posix.join(skillDir, 'SKILL.md'),\n content,\n abortSignal,\n });\n\n for (const file of skill.files ?? []) {\n const filePath = safeCodexSkillFilePath({\n skillName: skill.name,\n filePath: file.path,\n });\n await sandbox.writeTextFile({\n path: path.posix.join(skillDir, filePath),\n content: file.content,\n abortSignal,\n });\n }\n }\n\n return {\n homeDir,\n codexHomeDir,\n };\n}\n\nasync function resolveSandboxHomeDir({\n sandbox,\n abortSignal,\n}: {\n sandbox: Experimental_SandboxSession;\n abortSignal?: AbortSignal;\n}): Promise<string> {\n const result = await sandbox.run({\n command: 'printf \"%s\" \"$HOME\"',\n abortSignal,\n });\n const homeDir = result.stdout.trim();\n if (result.exitCode !== 0 || !homeDir || !path.posix.isAbsolute(homeDir)) {\n throw new Error(\n `Unable to resolve sandbox HOME directory: ${result.stderr || result.stdout}`,\n );\n }\n return homeDir;\n}\n\nfunction safeCodexSkillName(name: string): string {\n if (!/^[A-Za-z0-9._-]+$/.test(name) || name === '.' || name === '..') {\n throw new Error(`Invalid Codex skill name: ${name}`);\n }\n return name;\n}\n\nfunction safeCodexSkillFilePath({\n skillName,\n filePath,\n}: {\n skillName: string;\n filePath: string;\n}): string {\n const normalized = path.posix.normalize(filePath);\n if (\n normalized === '.' ||\n normalized.startsWith('../') ||\n path.posix.isAbsolute(normalized)\n ) {\n throw new Error(\n `Invalid Codex skill file path for ${skillName}: ${filePath}`,\n );\n }\n return normalized;\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nasync function waitForBridgeReady({\n proc,\n timeoutMs,\n abortSignal,\n}: {\n proc: Experimental_SandboxProcess;\n timeoutMs: number;\n abortSignal: AbortSignal | undefined;\n}): Promise<{ port: number }> {\n const reader = proc.stdout.pipeThrough(new TextDecoderStream()).getReader();\n\n const decoder = lineDecoder();\n\n const deadline = Date.now() + timeoutMs;\n try {\n while (true) {\n if (abortSignal?.aborted) {\n await proc.kill();\n throw abortSignal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n await proc.kill();\n throw new Error('codex bridge did not become ready in time.');\n }\n const { value, done } = (await Promise.race([\n reader.read(),\n new Promise(resolve =>\n setTimeout(\n () => resolve({ value: undefined, done: false }),\n remaining,\n ),\n ),\n ])) as ReadableStreamReadResult<string>;\n if (done) {\n throw new Error('codex bridge exited before becoming ready.');\n }\n if (value === undefined) continue;\n for (const line of decoder.push(value)) {\n const parsed = await safeParseJSON({\n text: line,\n schema: bridgeReadySchema,\n });\n if (parsed.success) return { port: parsed.value.port };\n }\n }\n } finally {\n reader.releaseLock();\n void drainRest(proc.stdout);\n /*\n * Bridge stderr is the only diagnostic channel for what happens\n * inside the sandbox once the bridge is running (uncaught\n * exceptions, Codex SDK errors, network failures). Forward it\n * line-by-line to the host console so a mid-turn bridge crash can\n * be inspected from `pnpm dev` logs without redeploying. The\n * bridge itself writes nothing to stderr in steady state, so this\n * is silent on the happy path.\n */\n void forwardBridgeStderr(proc.stderr);\n }\n}\n\nfunction lineDecoder() {\n let buffer = '';\n return {\n push(chunk: string): string[] {\n buffer += chunk;\n const lines: string[] = [];\n let nl: number;\n while ((nl = buffer.indexOf('\\n')) !== -1) {\n const raw = buffer.slice(0, nl);\n buffer = buffer.slice(nl + 1);\n const line = raw.replace(/\\r$/, '').trim();\n if (line.length > 0) lines.push(line);\n }\n return lines;\n },\n };\n}\n\nasync function forwardBridgeStderr(\n stream: ReadableStream<Uint8Array>,\n): Promise<void> {\n try {\n const reader = stream.pipeThrough(new TextDecoderStream()).getReader();\n while (true) {\n const { value, done } = await reader.read();\n if (done) return;\n if (value) {\n const trimmed = value.endsWith('\\n') ? value.slice(0, -1) : value;\n if (trimmed.length > 0) {\n // eslint-disable-next-line no-console\n console.log(`[bridge stderr] ${trimmed}`);\n }\n }\n }\n } catch {\n // Reader errors are non-fatal — best-effort diagnostic only.\n }\n}\n\nasync function drainRest(stream: ReadableStream<Uint8Array>): Promise<void> {\n try {\n const reader = stream.pipeThrough(new TextDecoderStream()).getReader();\n while (true) {\n const { done } = await reader.read();\n if (done) return;\n }\n } catch {}\n}\n\nfunction openWebSocket(url: string): Promise<WebSocket> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(url);\n const onOpen = () => {\n ws.off('error', onError);\n resolve(ws);\n };\n const onError = (err: Error) => {\n ws.off('open', onOpen);\n reject(err);\n };\n ws.once('open', onOpen);\n ws.once('error', onError);\n });\n}\n\nfunction createSession({\n sessionId,\n channel,\n proc,\n model,\n reasoningEffort,\n webSearch,\n resumeThreadId,\n isResume,\n seedResumeThreadOnFirstPrompt,\n rerunContinue,\n bridgePort,\n bridgeToken,\n sandboxId,\n debug,\n permissionMode,\n}: {\n sessionId: string;\n channel: CodexChannel;\n /** Undefined on `attach` — the live bridge was spawned by another process. */\n proc: Experimental_SandboxProcess | undefined;\n model: string | undefined;\n reasoningEffort: 'low' | 'medium' | 'high' | undefined;\n webSearch: boolean | undefined;\n resumeThreadId: string | undefined;\n isResume: boolean;\n seedResumeThreadOnFirstPrompt: boolean;\n rerunContinue: boolean;\n bridgePort: number;\n bridgeToken: string;\n sandboxId: string;\n debug: HarnessV1DebugConfig | undefined;\n permissionMode: HarnessV1PermissionMode | undefined;\n}): HarnessV1Session {\n let stopped = false;\n let stopPromise: Promise<void> | undefined;\n /*\n * Send the persisted threadId on the first prompt only when the bridge was\n * respawned (rerun/replay) so it takes the `codex.resumeThread(...)` branch.\n * An `attach`ed bridge already holds its threadState in memory and continues\n * on its own, so it needs no seed.\n */\n let pendingResumeThreadId = seedResumeThreadOnFirstPrompt\n ? resumeThreadId\n : undefined;\n /*\n * Instructions are prepended to the first user message of a fresh session\n * only. A resumed session (attach/replay/rerun) already carried them in its\n * original first message (preserved in the persisted thread), so it starts\n * \"applied\".\n */\n let instructionsApplied = isResume;\n\n /*\n * Latest codex thread id, cached from the bridge's `bridge-thread`\n * announcements. Seeded from lifecycle state so `doDetach()` and `doStop()`\n * can include a thread id even before this process has run a turn.\n */\n let latestThreadId = resumeThreadId;\n channel.on('bridge-thread', msg => {\n latestThreadId = msg.threadId;\n });\n\n /*\n * Wire the channel into one turn's worth of events and return the control\n * surface. Shared by `doPromptTurn` (which sends a `start` afterwards) and\n * `doContinueTurn` (which attaches to an already-running/replayed turn, or sends\n * a rerun `start`). The only difference between the two entry points is the\n * `start` message, not the listener/abort/settle plumbing.\n */\n const wireTurn = (turnOpts: {\n emit: (event: HarnessV1StreamPart) => void;\n abortSignal?: AbortSignal;\n }): HarnessV1PromptControl => {\n let pendingResolve: (() => void) | undefined;\n let pendingReject: ((err: unknown) => void) | undefined;\n const done = new Promise<void>((resolve, reject) => {\n pendingResolve = resolve;\n pendingReject = reject;\n });\n\n const unsubs: Array<() => void> = [];\n const forward = (event: HarnessV1StreamPart) => {\n try {\n turnOpts.emit(event);\n } catch {}\n };\n\n const eventTypes = [\n 'stream-start',\n 'text-start',\n 'text-delta',\n 'text-end',\n 'reasoning-start',\n 'reasoning-delta',\n 'reasoning-end',\n 'tool-call',\n 'tool-approval-request',\n 'tool-result',\n 'file-change',\n 'finish-step',\n 'raw',\n ] as const;\n let isSettled = false;\n const settleSuccess = () => {\n if (isSettled) return;\n isSettled = true;\n for (const u of unsubs) u();\n pendingResolve!();\n };\n const settleError = (err: unknown) => {\n if (isSettled) return;\n isSettled = true;\n for (const u of unsubs) u();\n pendingReject!(err);\n };\n\n for (const type of eventTypes) {\n unsubs.push(\n channel.on(type, msg => {\n forward(msg);\n }),\n );\n }\n unsubs.push(\n channel.on('finish', msg => {\n forward(msg);\n settleSuccess();\n }),\n );\n unsubs.push(\n channel.on('error', msg => {\n forward(msg);\n settleError(msg.error);\n }),\n );\n\n /*\n * A `'suspended'` close is a graceful slice-boundary freeze the host\n * initiated (`doSuspendTurn`): the turn keeps running in the bridge and its\n * tail is replayed to the next process, so wind this turn down cleanly\n * rather than failing it. Any other close mid-turn is an unexpected drop.\n */\n const onClose = (_code?: number, reason?: string) => {\n if (isSettled) return;\n if (reason === 'suspended') {\n settleSuccess();\n return;\n }\n settleError(new Error('codex bridge closed before the turn finished.'));\n };\n channel.onClose(onClose);\n\n const onAbort = () => {\n if (isSettled) return;\n try {\n channel.send({ type: 'abort' });\n } catch {}\n settleError(\n turnOpts.abortSignal?.reason ??\n new DOMException('Aborted', 'AbortError'),\n );\n };\n if (turnOpts.abortSignal) {\n if (turnOpts.abortSignal.aborted) {\n onAbort();\n } else {\n turnOpts.abortSignal.addEventListener('abort', onAbort, {\n once: true,\n });\n }\n }\n\n return {\n submitToolResult: async input => {\n channel.send({\n type: 'tool-result',\n toolCallId: input.toolCallId,\n output: input.output,\n isError: input.isError,\n });\n },\n submitToolApproval: async input => {\n channel.send({\n type: 'tool-approval-response',\n approvalId: input.approvalId,\n approved: input.approved,\n reason: input.reason,\n });\n },\n submitUserMessage: async text => {\n channel.send({ type: 'user-message', text });\n },\n done,\n };\n };\n\n return {\n sessionId,\n isResume,\n modelId: model,\n doPromptTurn: async promptOpts => {\n const control = wireTurn({\n emit: promptOpts.emit,\n abortSignal: promptOpts.abortSignal,\n });\n\n const applyInstructions =\n !instructionsApplied && !!promptOpts.instructions;\n instructionsApplied = true;\n\n const startMessage = {\n type: 'start' as const,\n prompt: extractUserText(promptOpts.prompt),\n ...(applyInstructions ? { instructions: promptOpts.instructions } : {}),\n tools: (promptOpts.tools ?? []).map(t => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n model,\n reasoningEffort,\n webSearch,\n ...(permissionMode ? { permissionMode } : {}),\n ...(pendingResumeThreadId\n ? { resumeThreadId: pendingResumeThreadId }\n : {}),\n ...(debug ? { debug } : {}),\n };\n pendingResumeThreadId = undefined;\n channel.send(startMessage);\n\n return control;\n },\n doContinueTurn: async continueOpts => {\n const control = wireTurn({\n emit: continueOpts.emit,\n abortSignal: continueOpts.abortSignal,\n });\n\n /*\n * attach / replay: the still-running (or disk-replayed) turn streams into\n * the wired listeners — `doStart` opened the channel with `{ resume: true }`\n * so the bridge replays everything past the persisted cursor (including a\n * `finish` if the turn ended during the gap). No `start` is sent: issuing\n * one would clear the bridge's replay log and begin a new turn. Lossless.\n *\n * rerun: the bridge was respawned with no in-flight turn to attach to, so\n * re-drive codex's own thread via `resumeThreadId`. Lossy — work in flight\n * at the interruption is recomputed. This is the rare bridge-died\n * fallback; the common slice path is `attach`.\n */\n if (rerunContinue) {\n const threadId = pendingResumeThreadId ?? latestThreadId;\n pendingResumeThreadId = undefined;\n channel.send({\n type: 'start' as const,\n /*\n * A continuation nudge rather than an empty prompt: `resumeThreadId`\n * rehydrates the prior thread, and this is the new user turn that\n * drives it forward. Keeping it non-empty avoids handing the runtime\n * an empty user message (and mirrors the claude-code adapter, where an\n * empty text block trips the Anthropic API's `cache_control` rule).\n */\n prompt: 'Continue.',\n tools: (continueOpts.tools ?? []).map(t => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n model,\n reasoningEffort,\n webSearch,\n ...(permissionMode ? { permissionMode } : {}),\n ...(threadId ? { resumeThreadId: threadId } : {}),\n ...(debug ? { debug } : {}),\n });\n }\n\n return control;\n },\n doCompact: async () => {\n /*\n * Codex compacts its context automatically inside the core turn loop\n * (~90% of the model context window), but the `codex exec` transport this\n * adapter drives exposes no manual compaction trigger and emits no\n * compaction event. Manual `compact()` is therefore unsupported; Codex's\n * own auto-compaction continues to run regardless.\n */\n throw new HarnessCapabilityUnsupportedError({\n message:\n \"Harness 'codex' does not support manual compaction; Codex auto-compacts its context internally.\",\n harnessId: 'codex',\n });\n },\n doDetach: async () => {\n if (stopped) {\n throw new Error(\n `codex session ${sessionId} is already stopped; cannot detach.`,\n );\n }\n stopped = true;\n const lastSeenEventId = await channel.suspend();\n const payload: HarnessV1ResumeSessionState = {\n type: 'resume-session',\n harnessId: 'codex',\n specificationVersion: 'harness-v1',\n data: {\n ...(latestThreadId ? { threadId: latestThreadId } : {}),\n bridge: {\n port: bridgePort,\n token: bridgeToken,\n lastSeenEventId,\n sandboxId,\n },\n },\n };\n return payload;\n },\n doDestroy: async () => {\n if (stopped) return stopPromise;\n stopped = true;\n stopPromise = (async () => {\n // Tell the channel we are tearing down so the bridge's post-shutdown\n // socket close finalises instead of triggering a reconnect.\n channel.beginClose();\n try {\n if (!channel.isClosed()) {\n channel.send({ type: 'shutdown' });\n }\n } catch {}\n let stopTimer: ReturnType<typeof setTimeout> | undefined;\n try {\n if (proc) {\n await Promise.race([\n proc.wait(),\n new Promise<void>(resolve => {\n stopTimer = setTimeout(resolve, 5000);\n stopTimer.unref?.();\n }),\n ]);\n }\n } finally {\n if (stopTimer) clearTimeout(stopTimer);\n try {\n await proc?.kill();\n } catch {}\n channel.close();\n }\n })();\n return stopPromise;\n },\n doStop: async () => {\n if (stopped) {\n throw new Error(\n `codex session ${sessionId} is already stopped; cannot stop.`,\n );\n }\n stopped = true;\n /*\n * If the bridge's channel already closed (e.g. mid-turn WS drop)\n * there is no one to ack a `detach` message. Synthesize an empty\n * payload — the workdir is still captured by the sandbox snapshot\n * during the subsequent `sandboxSession.stop()`, so the next turn can\n * resume the filesystem state. The trade-off: we lose\n * `threadId`, so the codex CLI starts a fresh thread on the\n * preserved workdir rather than resuming the prior conversation\n * inside Codex's runtime. Ability to continue beats throwing.\n */\n // Tell the channel we are tearing down so the bridge's post-detach\n // socket close finalises instead of triggering a reconnect.\n channel.beginClose();\n const data: unknown = channel.isClosed()\n ? {}\n : await new Promise<unknown>((resolve, reject) => {\n const timer = setTimeout(() => {\n unsub();\n reject(\n new Error(\n `codex session ${sessionId} did not reply to detach within 5s.`,\n ),\n );\n }, 5000);\n timer.unref?.();\n const unsub = channel.on('bridge-detach', msg => {\n clearTimeout(timer);\n unsub();\n resolve(msg.data);\n });\n try {\n channel.send({ type: 'detach' });\n } catch (err) {\n clearTimeout(timer);\n unsub();\n reject(err);\n }\n });\n\n let stopTimer: ReturnType<typeof setTimeout> | undefined;\n try {\n if (proc) {\n await Promise.race([\n proc.wait(),\n new Promise<void>(resolve => {\n stopTimer = setTimeout(resolve, 5000);\n stopTimer.unref?.();\n }),\n ]);\n }\n } finally {\n if (stopTimer) clearTimeout(stopTimer);\n try {\n await proc?.kill();\n } catch {}\n channel.close();\n }\n\n const payload: HarnessV1ResumeSessionState = {\n type: 'resume-session',\n harnessId: 'codex',\n specificationVersion: 'harness-v1',\n data: (data ?? {}) as HarnessV1ResumeSessionState['data'],\n };\n return payload;\n },\n doSuspendTurn: async () => {\n if (stopped) {\n throw new Error(\n `codex session ${sessionId} is stopped; cannot suspend.`,\n );\n }\n stopped = true;\n /*\n * Gracefully freeze the active turn at a precise cursor. `channel.suspend`\n * stops processing inbound frames (the cursor stops advancing exactly at\n * the last delivered event), drains what was already dispatched, then\n * closes the host socket with reason `'suspended'` — which `wireTurn`'s\n * `onClose` treats as a clean turn end. The bridge keeps the turn running\n * and accumulates events past the cursor for the next slice to replay. The\n * sandbox process is deliberately left alive (no `shutdown`/`detach`).\n */\n const lastSeenEventId = await channel.suspend();\n const payload: HarnessV1ContinueTurnState = {\n type: 'continue-turn',\n harnessId: 'codex',\n specificationVersion: 'harness-v1',\n data: {\n ...(latestThreadId ? { threadId: latestThreadId } : {}),\n bridge: {\n port: bridgePort,\n token: bridgeToken,\n lastSeenEventId,\n sandboxId,\n },\n },\n };\n return payload;\n },\n };\n}\n\n/*\n * Reduce a `HarnessV1Prompt` to the plain user text the bridge forwards\n * to the Codex SDK. File and image parts on the message are not yet\n * supported by the underlying runtime — throw rather than silently drop\n * them so callers learn about the gap instead of seeing mysteriously\n * truncated prompts.\n */\nfunction extractUserText(prompt: HarnessV1Prompt): string {\n if (typeof prompt === 'string') return prompt;\n const { content } = prompt;\n if (typeof content === 'string') return content;\n const parts: string[] = [];\n for (const part of content) {\n if (part.type !== 'text') {\n throw new HarnessCapabilityUnsupportedError({\n harnessId: 'codex',\n message: `The codex harness does not yet support user message parts of type '${part.type}'. Pass a string or a user message whose content contains only text parts.`,\n });\n }\n parts.push(part.text);\n }\n return parts.join('\\n\\n');\n}\n","export type CodexAuthOptions = {\n readonly openaiCompatible?: {\n readonly apiKey?: string;\n readonly baseUrl?: string;\n readonly modelProviderName?: string;\n readonly queryParamsJson?: string;\n };\n readonly openai?: {\n readonly apiKey?: string;\n readonly baseUrl?: string;\n readonly organization?: string;\n readonly project?: string;\n };\n readonly gateway?: {\n readonly apiKey?: string;\n readonly baseUrl?: string;\n };\n};\n\n/**\n * Resolve the environment-variable blob the codex bridge needs. Precedence:\n *\n * 1. Explicit `auth.openaiCompatible` — pin to a custom OpenAI-compatible endpoint.\n * 2. Explicit `auth.openai` — pin to direct OpenAI auth.\n * 3. Explicit `auth.gateway` — pin to Vercel AI Gateway.\n * 4. Auto-detect from the host process env: gateway first\n * (`AI_GATEWAY_API_KEY`), then `CODEX_API_KEY` / `OPENAI_API_KEY`.\n */\nexport function resolveCodexEnv(\n auth: CodexAuthOptions | undefined,\n processEnv: Record<string, string | undefined> = process.env,\n): Record<string, string> {\n if (auth?.openaiCompatible) {\n return pickOpenAICompatible(auth.openaiCompatible, processEnv);\n }\n if (auth?.openai) {\n return pickOpenAI({ explicit: auth.openai, processEnv });\n }\n if (auth?.gateway) {\n return pickGateway(auth.gateway, processEnv);\n }\n if (processEnv.AI_GATEWAY_API_KEY) {\n return pickGateway({}, processEnv);\n }\n return pickOpenAI({ processEnv });\n}\n\nfunction pickOpenAICompatible(\n explicit: NonNullable<CodexAuthOptions['openaiCompatible']>,\n processEnv: Record<string, string | undefined>,\n): Record<string, string> {\n const env: Record<string, string> = {};\n const apiKey =\n explicit.apiKey ?? processEnv.OPENAI_API_KEY ?? processEnv.CODEX_API_KEY;\n if (apiKey) env.CODEX_API_KEY = apiKey;\n if (explicit.baseUrl) env.OPENAI_BASE_URL = explicit.baseUrl;\n if (explicit.modelProviderName)\n env.CODEX_MODEL_PROVIDER_NAME = explicit.modelProviderName;\n if (explicit.queryParamsJson)\n env.OPENAI_QUERY_PARAMS_JSON = explicit.queryParamsJson;\n return env;\n}\n\nfunction pickOpenAI({\n explicit,\n processEnv,\n}: {\n explicit?: NonNullable<CodexAuthOptions['openai']>;\n processEnv: Record<string, string | undefined>;\n}): Record<string, string> {\n const env: Record<string, string> = {};\n const apiKey =\n explicit?.apiKey ?? processEnv.OPENAI_API_KEY ?? processEnv.CODEX_API_KEY;\n if (apiKey) env.CODEX_API_KEY = apiKey;\n const baseUrl = explicit?.baseUrl ?? processEnv.OPENAI_BASE_URL;\n if (baseUrl) env.OPENAI_BASE_URL = baseUrl;\n const organization = explicit?.organization ?? processEnv.OPENAI_ORGANIZATION;\n if (organization) env.OPENAI_ORGANIZATION = organization;\n const project = explicit?.project ?? processEnv.OPENAI_PROJECT;\n if (project) env.OPENAI_PROJECT = project;\n return env;\n}\n\nfunction pickGateway(\n explicit: NonNullable<CodexAuthOptions['gateway']>,\n processEnv: Record<string, string | undefined>,\n): Record<string, string> {\n const apiKey = explicit.apiKey ?? processEnv.AI_GATEWAY_API_KEY;\n const baseUrl =\n explicit.baseUrl ??\n processEnv.AI_GATEWAY_BASE_URL ??\n 'https://ai-gateway.vercel.sh/v1';\n const env: Record<string, string> = {};\n if (apiKey) {\n env.AI_GATEWAY_API_KEY = apiKey;\n env.CODEX_API_KEY = apiKey;\n }\n env.AI_GATEWAY_BASE_URL = baseUrl;\n return env;\n}\n","import {\n harnessV1BridgeInboundCommandSchemas,\n harnessV1BridgeOutboundMessageSchema,\n harnessV1BridgeReadySchema,\n harnessV1BridgeStartBaseSchema,\n} from '@ai-sdk/harness';\nimport { z } from 'zod/v4';\n\n/*\n * Codex's bridge wire protocol. The outbound events (including `file-change`\n * and the `bridge-thread` resume coordinate), transport frames, shared inbound\n * commands, and `bridge-ready` line all come from the shared `@ai-sdk/harness`\n * protocol — the only Codex-specific piece is the `start` payload.\n */\n\nexport const outboundMessageSchema = harnessV1BridgeOutboundMessageSchema;\nexport type OutboundMessage = z.infer<typeof outboundMessageSchema>;\n\nexport const startMessageSchema = harnessV1BridgeStartBaseSchema.extend({\n instructions: z.string().optional(),\n reasoningEffort: z.enum(['low', 'medium', 'high']).optional(),\n webSearch: z.boolean().optional(),\n // Resume signal. When supplied, the bridge calls\n // `codex.resumeThread(resumeThreadId, …)` instead of starting a fresh thread.\n // The host sources the id from lifecycle state `data` cached from a prior\n // `agent.detach`.\n resumeThreadId: z.string().optional(),\n});\n\nexport type StartMessage = z.infer<typeof startMessageSchema>;\n\nexport const inboundMessageSchema = z.discriminatedUnion('type', [\n startMessageSchema,\n ...harnessV1BridgeInboundCommandSchemas,\n]);\nexport type InboundMessage = z.infer<typeof inboundMessageSchema>;\n\nexport const bridgeReadySchema = harnessV1BridgeReadySchema;\nexport type BridgeReady = z.infer<typeof bridgeReadySchema>;\n","import { createCodex } from './codex-harness';\n\n/**\n * Default `codex` harness instance with no overrides — suitable for the\n * common case where the underlying `codex` CLI's defaults are fine.\n * Equivalent to `createCodex()`.\n */\nexport const codex = createCodex();\n\nexport { createCodex } from './codex-harness';\nexport type { CodexHarnessSettings } from './codex-harness';\nexport type { CodexAuthOptions } from './codex-auth';\n"],"mappings":"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","names":["z","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/harness-codex",
3
- "version": "1.0.0-canary.1",
3
+ "version": "1.0.0-canary.3",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -28,14 +28,12 @@
28
28
  "dependencies": {
29
29
  "ws": "^8.20.1",
30
30
  "zod": "3.25.76",
31
- "@ai-sdk/harness": "1.0.0-canary.5",
32
- "@ai-sdk/provider-utils": "5.0.0-canary.47"
33
- },
34
- "peerDependencies": {
35
- "@openai/codex-sdk": "^0.130.0"
31
+ "@ai-sdk/harness": "1.0.0-canary.7",
32
+ "@ai-sdk/provider-utils": "5.0.0-canary.48"
36
33
  },
37
34
  "devDependencies": {
38
35
  "@modelcontextprotocol/sdk": "1.29.0",
36
+ "@openai/codex-sdk": "0.130.0",
39
37
  "@types/node": "22.19.19",
40
38
  "@types/ws": "^8.5.13",
41
39
  "tsup": "^8.5.1",
@@ -210,7 +210,6 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
210
210
  const userMessage = composeUserMessage({
211
211
  text: start.prompt,
212
212
  instructions: start.instructions,
213
- skills: start.skills,
214
213
  // Temporary workaround for upstream codex MCP-tool bug — see ./cli-relay.ts
215
214
  toolUsageBlock:
216
215
  cliShimPath && start.tools && start.tools.length > 0
@@ -522,14 +521,10 @@ function defaultUsage(): Record<string, unknown> {
522
521
  function composeUserMessage({
523
522
  text,
524
523
  instructions,
525
- skills,
526
524
  toolUsageBlock,
527
525
  }: {
528
526
  text: string;
529
527
  instructions: string | undefined;
530
- skills:
531
- | ReadonlyArray<{ name: string; description: string; content: string }>
532
- | undefined;
533
528
  toolUsageBlock: string | undefined;
534
529
  }): string {
535
530
  const blocks: string[] = [];
@@ -548,13 +543,6 @@ function composeUserMessage({
548
543
  '</session-instructions>',
549
544
  );
550
545
  }
551
- if (skills && skills.length > 0) {
552
- const lines: string[] = ['## Available skills'];
553
- for (const skill of skills) {
554
- lines.push('', `### ${skill.name}`, skill.description, '', skill.content);
555
- }
556
- blocks.push(lines.join('\n'));
557
- }
558
546
  if (toolUsageBlock) blocks.push(toolUsageBlock);
559
547
  blocks.push(instructions ? `<user-message>\n${text}\n</user-message>` : text);
560
548
  return blocks.join('\n\n');
@@ -20,15 +20,6 @@ export const startMessageSchema = harnessV1BridgeStartBaseSchema.extend({
20
20
  instructions: z.string().optional(),
21
21
  reasoningEffort: z.enum(['low', 'medium', 'high']).optional(),
22
22
  webSearch: z.boolean().optional(),
23
- skills: z
24
- .array(
25
- z.object({
26
- name: z.string(),
27
- description: z.string(),
28
- content: z.string(),
29
- }),
30
- )
31
- .optional(),
32
23
  // Resume signal. When supplied, the bridge calls
33
24
  // `codex.resumeThread(resumeThreadId, …)` instead of starting a fresh thread.
34
25
  // The host sources the id from lifecycle state `data` cached from a prior
@@ -1,5 +1,6 @@
1
1
  import { randomBytes } from 'node:crypto';
2
2
  import { readFile } from 'node:fs/promises';
3
+ import path from 'node:path';
3
4
  import { fileURLToPath } from 'node:url';
4
5
  import {
5
6
  commonTool,
@@ -23,6 +24,7 @@ import { classifyDiskLog, SandboxChannel } from '@ai-sdk/harness/utils';
23
24
  import {
24
25
  safeParseJSON,
25
26
  type Experimental_SandboxProcess,
27
+ type Experimental_SandboxSession,
26
28
  } from '@ai-sdk/provider-utils';
27
29
  import { WebSocket } from 'ws';
28
30
  import { z } from 'zod';
@@ -37,6 +39,11 @@ import {
37
39
  type CodexChannel = SandboxChannel<OutboundMessage, InboundMessage>;
38
40
  type CodexRespawnStrategy = 'replay' | 'rerun';
39
41
 
42
+ type WriteSkillsResult = {
43
+ readonly homeDir: string;
44
+ readonly codexHomeDir: string;
45
+ };
46
+
40
47
  /*
41
48
  * The model the adapter pins when the consumer configures none. The Codex SDK
42
49
  * does not report the model it resolves to at runtime (no model field on any
@@ -258,7 +265,6 @@ export function createCodex(
258
265
  channel: attachChannel,
259
266
  // The live bridge was spawned by another process; no process handle.
260
267
  proc: undefined,
261
- skills: startOpts.skills,
262
268
  model: settings.model ?? DEFAULT_CODEX_MODEL,
263
269
  reasoningEffort: settings.reasoningEffort,
264
270
  webSearch: settings.webSearch,
@@ -302,10 +308,24 @@ export function createCodex(
302
308
 
303
309
  const port = resolveBridgePort(sandboxSession, settings.port);
304
310
  const token = randomBytes(32).toString('hex');
311
+ const codexSkillSetup =
312
+ startOpts.skills && startOpts.skills.length > 0
313
+ ? await writeSkills({
314
+ sandbox: session,
315
+ skills: startOpts.skills,
316
+ abortSignal: startOpts.abortSignal,
317
+ })
318
+ : undefined;
305
319
  const env = {
306
320
  ...resolveCodexEnv(settings.auth),
307
321
  BRIDGE_CHANNEL_TOKEN: token,
308
322
  BRIDGE_WS_PORT: String(port),
323
+ ...(codexSkillSetup
324
+ ? {
325
+ HOME: codexSkillSetup.homeDir,
326
+ CODEX_HOME: codexSkillSetup.codexHomeDir,
327
+ }
328
+ : {}),
309
329
  ...(respawnStrategy === 'replay'
310
330
  ? { BRIDGE_REPLAY_FROM_DISK: '1' }
311
331
  : {}),
@@ -355,7 +375,6 @@ export function createCodex(
355
375
  sessionId: startOpts.sessionId,
356
376
  channel,
357
377
  proc,
358
- skills: startOpts.skills,
359
378
  model: settings.model ?? DEFAULT_CODEX_MODEL,
360
379
  reasoningEffort: settings.reasoningEffort,
361
380
  webSearch: settings.webSearch,
@@ -405,6 +424,119 @@ async function readBridgeAsset(name: string): Promise<string> {
405
424
  throw lastErr ?? new Error(`bridge asset not found: ${name}`);
406
425
  }
407
426
 
427
+ async function writeSkills({
428
+ sandbox,
429
+ skills,
430
+ abortSignal,
431
+ }: {
432
+ sandbox: Experimental_SandboxSession;
433
+ skills: ReadonlyArray<HarnessV1Skill>;
434
+ abortSignal?: AbortSignal;
435
+ }): Promise<WriteSkillsResult> {
436
+ for (const skill of skills) {
437
+ safeCodexSkillName(skill.name);
438
+ for (const file of skill.files ?? []) {
439
+ safeCodexSkillFilePath({
440
+ skillName: skill.name,
441
+ filePath: file.path,
442
+ });
443
+ }
444
+ }
445
+
446
+ const homeDir = await resolveSandboxHomeDir({ sandbox, abortSignal });
447
+ const codexHomeDir = path.posix.join(homeDir, '.codex');
448
+ await sandbox.run({
449
+ command: `mkdir -p ${shellQuote(codexHomeDir)}`,
450
+ abortSignal,
451
+ });
452
+
453
+ const rootDir = path.posix.join(homeDir, '.agents', 'skills');
454
+ await sandbox.run({
455
+ command: `mkdir -p ${shellQuote(rootDir)}`,
456
+ abortSignal,
457
+ });
458
+
459
+ for (const skill of skills) {
460
+ const name = safeCodexSkillName(skill.name);
461
+ const skillDir = path.posix.join(rootDir, name);
462
+ const content = `---\nname: ${skill.name}\ndescription: ${skill.description}\n---\n\n${skill.content}`;
463
+
464
+ await sandbox.writeTextFile({
465
+ path: path.posix.join(skillDir, 'SKILL.md'),
466
+ content,
467
+ abortSignal,
468
+ });
469
+
470
+ for (const file of skill.files ?? []) {
471
+ const filePath = safeCodexSkillFilePath({
472
+ skillName: skill.name,
473
+ filePath: file.path,
474
+ });
475
+ await sandbox.writeTextFile({
476
+ path: path.posix.join(skillDir, filePath),
477
+ content: file.content,
478
+ abortSignal,
479
+ });
480
+ }
481
+ }
482
+
483
+ return {
484
+ homeDir,
485
+ codexHomeDir,
486
+ };
487
+ }
488
+
489
+ async function resolveSandboxHomeDir({
490
+ sandbox,
491
+ abortSignal,
492
+ }: {
493
+ sandbox: Experimental_SandboxSession;
494
+ abortSignal?: AbortSignal;
495
+ }): Promise<string> {
496
+ const result = await sandbox.run({
497
+ command: 'printf "%s" "$HOME"',
498
+ abortSignal,
499
+ });
500
+ const homeDir = result.stdout.trim();
501
+ if (result.exitCode !== 0 || !homeDir || !path.posix.isAbsolute(homeDir)) {
502
+ throw new Error(
503
+ `Unable to resolve sandbox HOME directory: ${result.stderr || result.stdout}`,
504
+ );
505
+ }
506
+ return homeDir;
507
+ }
508
+
509
+ function safeCodexSkillName(name: string): string {
510
+ if (!/^[A-Za-z0-9._-]+$/.test(name) || name === '.' || name === '..') {
511
+ throw new Error(`Invalid Codex skill name: ${name}`);
512
+ }
513
+ return name;
514
+ }
515
+
516
+ function safeCodexSkillFilePath({
517
+ skillName,
518
+ filePath,
519
+ }: {
520
+ skillName: string;
521
+ filePath: string;
522
+ }): string {
523
+ const normalized = path.posix.normalize(filePath);
524
+ if (
525
+ normalized === '.' ||
526
+ normalized.startsWith('../') ||
527
+ path.posix.isAbsolute(normalized)
528
+ ) {
529
+ throw new Error(
530
+ `Invalid Codex skill file path for ${skillName}: ${filePath}`,
531
+ );
532
+ }
533
+ return normalized;
534
+ }
535
+
536
+ function shellQuote(value: string): string {
537
+ return `'${value.replace(/'/g, `'\\''`)}'`;
538
+ }
539
+
408
540
  async function waitForBridgeReady({
409
541
  proc,
410
542
  timeoutMs,
@@ -536,7 +668,6 @@ function createSession({
536
668
  sessionId,
537
669
  channel,
538
670
  proc,
539
- skills,
540
671
  model,
541
672
  reasoningEffort,
542
673
  webSearch,
@@ -554,7 +685,6 @@ function createSession({
554
685
  channel: CodexChannel;
555
686
  /** Undefined on `attach` — the live bridge was spawned by another process. */
556
687
  proc: Experimental_SandboxProcess | undefined;
557
- skills: ReadonlyArray<HarnessV1Skill> | undefined;
558
688
  model: string | undefined;
559
689
  reasoningEffort: 'low' | 'medium' | 'high' | undefined;
560
690
  webSearch: boolean | undefined;
@@ -758,15 +888,6 @@ function createSession({
758
888
  reasoningEffort,
759
889
  webSearch,
760
890
  ...(permissionMode ? { permissionMode } : {}),
761
- ...(skills && skills.length > 0
762
- ? {
763
- skills: skills.map(s => ({
764
- name: s.name,
765
- description: s.description,
766
- content: s.content,
767
- })),
768
- }
769
- : {}),
770
891
  ...(pendingResumeThreadId
771
892
  ? { resumeThreadId: pendingResumeThreadId }
772
893
  : {}),