@byok-sdk/client 0.14.0 → 0.15.0

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.
Files changed (46) hide show
  1. package/README.md +15 -0
  2. package/dist/adapters/codex/codex-adapter.d.ts +2 -0
  3. package/dist/adapters/codex/process-runner.d.ts +16 -17
  4. package/dist/adapters/detect-outcome.d.ts +18 -0
  5. package/dist/adapters/index.d.ts +1 -1
  6. package/dist/adapters/index.js +189 -50
  7. package/dist/adapters/index.js.map +1 -1
  8. package/dist/bin/byok-agent-memory-mcp.js +29 -0
  9. package/dist/bin/byok-agent-memory-mcp.js.map +1 -1
  10. package/dist/bin/byok-agent-message-mcp.js +29 -0
  11. package/dist/bin/byok-agent-message-mcp.js.map +1 -1
  12. package/dist/bin/byok-agent-team-mcp.js +29 -0
  13. package/dist/bin/byok-agent-team-mcp.js.map +1 -1
  14. package/dist/bin/byok-agent.js +1832 -932
  15. package/dist/bin/byok-agent.js.map +1 -1
  16. package/dist/bin/byok-approval-mcp.js +29 -0
  17. package/dist/bin/byok-approval-mcp.js.map +1 -1
  18. package/dist/bin/byok-mcp-env.d.ts +2 -0
  19. package/dist/bin/byok-mcp-env.js +36 -0
  20. package/dist/bin/byok-mcp-env.js.map +1 -0
  21. package/dist/bin/commands/doctor.d.ts +3 -0
  22. package/dist/bin/mcp-env-launcher.d.ts +5 -0
  23. package/dist/bin/runtime-probe.d.ts +6 -7
  24. package/dist/daemon/admission-wait.d.ts +2 -0
  25. package/dist/daemon/agent-egress-spool.d.ts +1 -0
  26. package/dist/daemon/agent-message-outbox.d.ts +15 -3
  27. package/dist/daemon/artifact-read.d.ts +7 -0
  28. package/dist/daemon/connection-manager.d.ts +18 -77
  29. package/dist/daemon/create-daemon.d.ts +11 -1
  30. package/dist/daemon/runtime-start.d.ts +3 -0
  31. package/dist/daemon/store.d.ts +2 -0
  32. package/dist/daemon/task-runner.d.ts +23 -0
  33. package/dist/daemon/terminal-commit-queue.d.ts +21 -0
  34. package/dist/daemon/terminal-identity.d.ts +6 -0
  35. package/dist/diagnostics/device-doctor.d.ts +46 -0
  36. package/dist/diagnostics/diagnostics.d.ts +3 -80
  37. package/dist/diagnostics/types.d.ts +82 -0
  38. package/dist/index.d.ts +3 -1
  39. package/dist/index.js +1942 -423
  40. package/dist/index.js.map +1 -1
  41. package/dist/runtime-detection.d.ts +3 -0
  42. package/dist/runtime-failure.d.ts +6 -0
  43. package/dist/sdk-reserved-helper-host.d.ts +1 -1
  44. package/dist/types.d.ts +20 -10
  45. package/dist/util/durable-jsonl.d.ts +12 -0
  46. package/package.json +4 -4
package/README.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @byok-sdk/client
2
2
 
3
+ ## Diagnostics and recovery integration
4
+
5
+ Use the SDK-maintained [downstream guide](https://github.com/Ancienttwo/byok-sdk/blob/main/docs/agent-diagnostics-integration.md)
6
+ to build diagnostic UI and local repair flows. `byok-agent doctor --json` is
7
+ the current read-only CLI entrypoint; `--fix --yes` only quarantines confirmed-corrupt
8
+ operational health state with the daemon stopped. It does not repair an Agent
9
+ or rebuild its journal. The guide includes integration limits and acceptance
10
+ scenarios; qualify them against the exact SDK artifact shipped by your product.
11
+
12
+ The public `diagnoseDevice` API accepts the host's actual adapters.
13
+ `repairDeviceEnrollmentMetadata` (or the named CLI `--repair restore-enrollment-metadata`
14
+ action) restores missing/valid-stale non-secret enrollment metadata from its
15
+ existing OS authority, with confirmation, exact expected tenant/device and
16
+ exclusive store ownership. It does not renew credentials or prove Agent readiness.
17
+
3
18
  ## Exact provider-profile admission
4
19
 
5
20
  When `DaemonConfig.piByokLauncher` is configured, the daemon advertises the
@@ -1,7 +1,9 @@
1
+ import { type SdkHelperHostConfig } from '../../sdk-reserved-helper-host';
1
2
  import { type RuntimeAdapter, type RuntimeDetectResult, type RuntimeAdapterPrepareInput, type RuntimeAdapterPrepareResult } from '../../types';
2
3
  import { type ResolvedBin } from './resolve-bin';
3
4
  import { type SpawnFn } from './process-runner';
4
5
  export interface CodexAdapterOptions {
6
+ sdkHelperHost?: SdkHelperHostConfig;
5
7
  /** Override bin resolution — tests substitute the fake-codex fixture script. */
6
8
  resolveBin?: () => ResolvedBin;
7
9
  /** Override process spawning — tests substitute a fake spawn. */
@@ -1,3 +1,4 @@
1
+ import { RuntimeExecutionFailure } from '../../runtime-failure';
1
2
  import { spawn } from 'node:child_process';
2
3
  export type SpawnFn = typeof spawn;
3
4
  /**
@@ -14,11 +15,13 @@ export interface CodexRawEvent {
14
15
  export interface CodexProcessOptions {
15
16
  command: string;
16
17
  args: string[];
18
+ instruction?: string;
17
19
  cwd: string;
18
20
  env: NodeJS.ProcessEnv;
19
21
  spawnFn?: SpawnFn;
20
22
  /** Called once per parsed JSONL line, in arrival order. */
21
23
  onEvent: (evt: CodexRawEvent) => void;
24
+ onFailure?: (error: RuntimeExecutionFailure) => void;
22
25
  /**
23
26
  * DI seam scoped to ADOPTION only (`../process-tree.ts`'s
24
27
  * `adoptOwnedProcessTree`), so the win32 job-object branch is exercisable
@@ -31,6 +34,9 @@ export interface CodexProcessOptions {
31
34
  assign(pid: number): Promise<void>;
32
35
  };
33
36
  }
37
+ export declare const CODEX_MAX_FRAME_BYTES: number;
38
+ export declare const CODEX_MAX_DEFERRED_BYTES: number;
39
+ export declare const CODEX_MAX_STDERR_BYTES: number;
34
40
  /**
35
41
  * Spawns and streams ONE `codex exec` / `codex exec resume` invocation — i.e.
36
42
  * exactly one turn.
@@ -38,32 +44,24 @@ export interface CodexProcessOptions {
38
44
  * Unlike pi (a single long-lived RPC server process for a whole session's
39
45
  * lifetime — see `../pi/rpc-client.ts`), `codex exec` is a one-shot batch
40
46
  * process per turn with no persistent request/response channel: it takes its
41
- * prompt as an argv positional, streams JSONL to stdout for the one turn
47
+ * prompt from stdin with the documented `-` positional, streams JSONL to stdout for the one turn
42
48
  * it's running, and exits. `../codex-adapter.ts`'s `CodexSession` constructs
43
49
  * a fresh `CodexProcessRunner` for every turn (the initial `start()` and
44
50
  * every later `followUp()`), forwarding each one's lines into the same
45
51
  * long-lived event queue.
46
52
  *
47
- * stdin is deliberately never piped to the child (`stdio: ['ignore', 'pipe',
48
- * 'pipe']`): `codex exec --help` documents that a piped, non-TTY stdin is
49
- * read and appended to the prompt as a `<stdin>` block even when a prompt was
50
- * ALSO given as an argv positional, and empirically every single real
51
- * invocation made while building this adapter logged "Reading additional
52
- * input from stdin..." on stderr regardless of whether a prompt argument was
53
- * given. Leaving `stdio: ['pipe', ...]` open for stdin and never closing it
54
- * risks codex blocking on that read forever — exactly the hang class this
55
- * task was built to avoid (the pi adapter's own `agent_end`/`agent_settled`
56
- * mismatch left a task stuck `Running` forever in the M0/M1 GLM run).
57
- * `'ignore'` presents immediate EOF instead, which was verified live with a
58
- * dedicated Node `child_process` probe before this was written: no hang,
59
- * clean completion at normal model latency. This adapter never needs to
60
- * SEND codex anything over stdin — there is no in-band steer/approval
61
- * protocol (see `../codex-adapter.ts`'s `steer`/`resolveApproval`).
53
+ * Prompt stdin is closed with EOF immediately after writing. This is a one-shot
54
+ * input channel; steer and approvals are not multiplexed over it.
62
55
  */
63
56
  export declare class CodexProcessRunner {
64
57
  private readonly child;
65
58
  private readonly onEvent;
66
- private buffer;
59
+ private readonly frameChunks;
60
+ private frameBytes;
61
+ private deferredBytes;
62
+ private stderrBytes;
63
+ private transportFailure;
64
+ private readonly onFailure;
67
65
  private readonly stderrRing;
68
66
  private closed;
69
67
  private exitCode;
@@ -133,6 +131,7 @@ export declare class CodexProcessRunner {
133
131
  * plus an empty stderr tail would bury the only reason anyone can act on.
134
132
  */
135
133
  buildExitError(context: string): Error;
134
+ private failTransport;
136
135
  private onData;
137
136
  private parseLine;
138
137
  private onStderr;
@@ -0,0 +1,18 @@
1
+ import type { RuntimeDetectResult } from '../types';
2
+ type ProbeFailure = Exclude<RuntimeDetectResult, {
3
+ kind: 'available';
4
+ }>;
5
+ type VersionProbeResult = ProbeFailure | {
6
+ kind: 'available';
7
+ stdout: string;
8
+ stderr: string;
9
+ };
10
+ /** OS/process codes only. Arbitrary messages, streams and resolver paths never escape. */
11
+ export declare function classifyDetectError(error: unknown): ProbeFailure;
12
+ /**
13
+ * This probe owns both the deadline and the version child. A killed child is
14
+ * not by itself timeout evidence (execFile also kills on output overflow).
15
+ * Resolve only at execFile completion; SIGKILL bounds a TERM-ignoring probe.
16
+ */
17
+ export declare function probeRuntimeVersion(command: string, timeoutMs: number): Promise<VersionProbeResult>;
18
+ export {};
@@ -1,6 +1,6 @@
1
1
  export type { RuntimeAdapter, RuntimeAdapterDescriptor, RuntimeAdapterPrepareInput, RuntimeAdapterPrepareResult, RuntimeAdapterRejectedOperation, RuntimeAdapterPreparedOperation, PreparedRuntimeOperation, RuntimeOperationManifest, RuntimeOperationStartInput, RuntimeCapabilities, RuntimeDetectResult, } from '../types';
2
2
  export type { RuntimeEnvironmentRequirements } from '../daemon/environment';
3
- export { RuntimeDisposalFailure, RuntimeExecutionFailure } from '../runtime-failure';
3
+ export { RuntimeDisposalFailure, RuntimeExecutionFailure, RuntimeStartupDisposalFailure } from '../runtime-failure';
4
4
  export type { RuntimeDisposalFailureInput, RuntimeDisposalStage, RuntimeExecutionFailureInput, RuntimeFailureCategory, RuntimeFailurePhase, RuntimeRetryDisposition, } from '../runtime-failure';
5
5
  export { PiAdapter } from './pi/pi-adapter';
6
6
  export type { PiAdapterOptions, PiByokLauncherConfig } from './pi/pi-adapter';
@@ -5,6 +5,7 @@ import path5, { isAbsolute } from 'path';
5
5
  import { promisify } from 'util';
6
6
  import { fileURLToPath } from 'url';
7
7
  import 'readline';
8
+ import { randomBytes } from 'crypto';
8
9
 
9
10
  var __defProp = Object.defineProperty;
10
11
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -181,6 +182,48 @@ function isRuntimeExecutionFailure(value) {
181
182
  const candidate = value;
182
183
  return candidate[RUNTIME_EXECUTION_FAILURE_BRAND] === true && isRuntimeFailurePhase(candidate.phase) && isRuntimeFailureCategory(candidate.category) && isRuntimeRetryDisposition(candidate.retry) && typeof candidate.message === "string" && candidate.message.length > 0;
183
184
  }
185
+ var RuntimeStartupDisposalFailure = class extends Error {
186
+ retryDisposal;
187
+ constructor(retryDisposal, options) {
188
+ super("runtime startup failed and process ownership remains quarantined", options);
189
+ this.name = "RuntimeStartupDisposalFailure";
190
+ this.retryDisposal = retryDisposal;
191
+ Object.defineProperty(this, /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeStartupDisposalFailure/v1"), { value: true });
192
+ Object.freeze(this);
193
+ }
194
+ };
195
+ function classifyDetectError(error) {
196
+ const code = error !== null && typeof error === "object" ? error.code : void 0;
197
+ if (code === "ENOENT") return { kind: "not-found" };
198
+ if (code === "EACCES" || code === "EPERM" || code === "ENOEXEC") return { kind: "not-executable" };
199
+ return { kind: "probe-failed" };
200
+ }
201
+ async function probeRuntimeVersion(command, timeoutMs) {
202
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) throw new TypeError("invalid runtime probe timeout");
203
+ return new Promise((resolve) => {
204
+ let timer;
205
+ let timedOut = false;
206
+ try {
207
+ const child = execFile(command, ["--version"], { encoding: "utf8" }, (error, stdout, stderr) => {
208
+ if (timer) clearTimeout(timer);
209
+ if (error?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") resolve({ kind: "probe-failed" });
210
+ else if (timedOut) resolve({ kind: "timeout" });
211
+ else if (error) resolve(classifyDetectError(error));
212
+ else resolve({ kind: "available", stdout, stderr });
213
+ });
214
+ timer = setTimeout(() => {
215
+ timedOut = true;
216
+ child.stdout?.destroy();
217
+ child.stderr?.destroy();
218
+ child.kill("SIGKILL");
219
+ }, timeoutMs);
220
+ timer.unref?.();
221
+ } catch (error) {
222
+ if (timer) clearTimeout(timer);
223
+ resolve(classifyDetectError(error));
224
+ }
225
+ });
226
+ }
184
227
 
185
228
  // src/types.ts
186
229
  function frozenStrings(values) {
@@ -1231,12 +1274,13 @@ var PiAdapter = class {
1231
1274
  async detect() {
1232
1275
  try {
1233
1276
  const bin = this.resolveBin();
1234
- const { stdout, stderr } = await execFileAsync(bin.command, ["--version"], { timeout: DETECT_TIMEOUT_MS });
1235
- const version = stdout.trim() || stderr.trim();
1277
+ const probe = await probeRuntimeVersion(bin.command, DETECT_TIMEOUT_MS);
1278
+ if (probe.kind !== "available") return probe;
1279
+ const version = probe.stdout.trim() || probe.stderr.trim();
1236
1280
  const authPresent = PROVIDER_CREDENTIAL_ENV_NAMES.some((name) => process.env[name] !== void 0);
1237
- return { present: true, version, authPresent };
1238
- } catch {
1239
- return { present: false };
1281
+ return { kind: "available", version, authPresent };
1282
+ } catch (error) {
1283
+ return classifyDetectError(error);
1240
1284
  }
1241
1285
  }
1242
1286
  async prepare(input) {
@@ -1657,7 +1701,8 @@ var DIST_SCRIPT_BY_KIND = Object.freeze({
1657
1701
  "agent-message-mcp": "byok-agent-message-mcp.js",
1658
1702
  "agent-memory-mcp": "byok-agent-memory-mcp.js",
1659
1703
  "approval-mcp": "byok-approval-mcp.js",
1660
- "agent-team-mcp": "byok-agent-team-mcp.js"
1704
+ "agent-team-mcp": "byok-agent-team-mcp.js",
1705
+ "mcp-env": "byok-mcp-env.js"
1661
1706
  });
1662
1707
  function assertExecutable(executable) {
1663
1708
  if (!path5.isAbsolute(executable) || /[\u0000\r\n]/u.test(executable)) {
@@ -1675,8 +1720,11 @@ function resolveSdkReservedHelperBin(kind, host) {
1675
1720
  source: "self-executable"
1676
1721
  });
1677
1722
  }
1723
+ const entryDir = path5.dirname(fileURLToPath(import.meta.url));
1724
+ const entryKind = path5.basename(entryDir);
1725
+ const distDir = entryKind === "adapters" || entryKind === "bin" ? path5.dirname(entryDir) : entryDir;
1678
1726
  const script = path5.join(
1679
- path5.dirname(fileURLToPath(import.meta.url)),
1727
+ distDir,
1680
1728
  "bin",
1681
1729
  DIST_SCRIPT_BY_KIND[kind]
1682
1730
  );
@@ -2290,14 +2338,15 @@ var ClaudeAdapter = class {
2290
2338
  environmentRequirements: { credentialNames: [] }
2291
2339
  });
2292
2340
  async detect() {
2293
- const bin = this.resolveBin();
2294
2341
  try {
2295
- const { stdout } = await execFileAsync2(bin.command, ["--version"], { timeout: DETECT_TIMEOUT_MS2 });
2296
- const version = stdout.trim();
2342
+ const bin = this.resolveBin();
2343
+ const probe = await probeRuntimeVersion(bin.command, DETECT_TIMEOUT_MS2);
2344
+ if (probe.kind !== "available") return probe;
2345
+ const version = probe.stdout.trim();
2297
2346
  const authPresent = await this.probeAuthPresent(bin.command);
2298
- return { present: true, version, authPresent };
2299
- } catch {
2300
- return { present: false };
2347
+ return { kind: "available", version, authPresent };
2348
+ } catch (error) {
2349
+ return classifyDetectError(error);
2301
2350
  }
2302
2351
  }
2303
2352
  async prepare(input) {
@@ -2916,10 +2965,18 @@ function unmappedFrameKey(evt) {
2916
2965
  return evt.type;
2917
2966
  }
2918
2967
  var STDERR_RING_CAPACITY3 = 20;
2968
+ var CODEX_MAX_FRAME_BYTES = 1024 * 1024;
2969
+ var CODEX_MAX_DEFERRED_BYTES = 4 * 1024 * 1024;
2970
+ var CODEX_MAX_STDERR_BYTES = 64 * 1024;
2919
2971
  var CodexProcessRunner = class {
2920
2972
  child;
2921
2973
  onEvent;
2922
- buffer = "";
2974
+ frameChunks = [];
2975
+ frameBytes = 0;
2976
+ deferredBytes = 0;
2977
+ stderrBytes = 0;
2978
+ transportFailure;
2979
+ onFailure;
2923
2980
  stderrRing = [];
2924
2981
  closed = false;
2925
2982
  exitCode = null;
@@ -2942,11 +2999,12 @@ var CodexProcessRunner = class {
2942
2999
  deferredEvents = [];
2943
3000
  constructor(options) {
2944
3001
  this.onEvent = options.onEvent;
3002
+ this.onFailure = options.onFailure;
2945
3003
  const spawnFn = options.spawnFn ?? spawn;
2946
3004
  this.child = spawnFn(options.command, options.args, withOwnedProcessTree({
2947
3005
  cwd: options.cwd,
2948
3006
  env: options.env,
2949
- stdio: ["ignore", "pipe", "pipe"]
3007
+ stdio: [options.instruction === void 0 ? "ignore" : "pipe", "pipe", "pipe"]
2950
3008
  }));
2951
3009
  this.closedPromise = new Promise((resolve) => {
2952
3010
  this.resolveClosed = resolve;
@@ -2954,7 +3012,6 @@ var CodexProcessRunner = class {
2954
3012
  this.adopted = this.adoptOwnedTree(options);
2955
3013
  this.adopted.catch(() => {
2956
3014
  });
2957
- this.child.stdout.setEncoding("utf8");
2958
3015
  this.child.stdout.on("data", (chunk) => this.onData(chunk));
2959
3016
  this.child.stderr.setEncoding("utf8");
2960
3017
  this.child.stderr.on("data", (chunk) => this.onStderr(chunk));
@@ -2963,6 +3020,12 @@ var CodexProcessRunner = class {
2963
3020
  this.exitSignal = signal;
2964
3021
  this.finishClosing();
2965
3022
  });
3023
+ if (options.instruction !== void 0) {
3024
+ this.child.stdin.on("error", () => {
3025
+ this.failTransport("codex prompt stdin failed before delivery");
3026
+ });
3027
+ this.child.stdin.end(options.instruction, "utf8");
3028
+ }
2966
3029
  this.child.on("error", () => {
2967
3030
  this.finishClosing();
2968
3031
  });
@@ -3048,12 +3111,19 @@ var CodexProcessRunner = class {
3048
3111
  throw failure;
3049
3112
  }
3050
3113
  this.adoption = "adopted";
3114
+ this.deferredBytes = 0;
3051
3115
  for (const evt of this.deferredEvents.splice(0)) this.onEvent(evt);
3052
3116
  }
3053
3117
  /** Arrival-order delivery, held back until the tree is backstopped (see `deferredEvents`). */
3054
- deliver(evt) {
3118
+ deliver(evt, bytes) {
3119
+ if (this.transportFailure) return;
3055
3120
  if (this.adoption === "failed") return;
3056
3121
  if (this.adoption === "pending") {
3122
+ if (this.deferredBytes + bytes > CODEX_MAX_DEFERRED_BYTES) {
3123
+ this.failTransport("codex pre-adoption event buffer exceeded its byte budget");
3124
+ return;
3125
+ }
3126
+ this.deferredBytes += bytes;
3057
3127
  this.deferredEvents.push(evt);
3058
3128
  return;
3059
3129
  }
@@ -3070,6 +3140,7 @@ var CodexProcessRunner = class {
3070
3140
  * plus an empty stderr tail would bury the only reason anyone can act on.
3071
3141
  */
3072
3142
  buildExitError(context) {
3143
+ if (this.transportFailure) return this.transportFailure;
3073
3144
  if (this.adoptionFailure !== void 0) return this.adoptionFailure;
3074
3145
  const parts = [`${context} (exit code=${this.exitCode}, signal=${this.exitSignal})`];
3075
3146
  if (this.stderrRing.length > 0) {
@@ -3077,18 +3148,45 @@ var CodexProcessRunner = class {
3077
3148
  }
3078
3149
  return new Error(parts.join("; "));
3079
3150
  }
3080
- onData(chunk) {
3081
- this.buffer += chunk;
3082
- let newlineIndex = this.buffer.indexOf("\n");
3083
- while (newlineIndex !== -1) {
3084
- let line = this.buffer.slice(0, newlineIndex);
3085
- this.buffer = this.buffer.slice(newlineIndex + 1);
3086
- if (line.endsWith("\r")) line = line.slice(0, -1);
3087
- if (line.length > 0) this.parseLine(line);
3088
- newlineIndex = this.buffer.indexOf("\n");
3089
- }
3151
+ failTransport(reason) {
3152
+ if (this.transportFailure) return;
3153
+ this.transportFailure = new RuntimeExecutionFailure({
3154
+ phase: "run",
3155
+ category: "infrastructure",
3156
+ retry: "non-retryable",
3157
+ reason
3158
+ });
3159
+ this.frameChunks.length = 0;
3160
+ this.frameBytes = 0;
3161
+ this.deferredEvents.length = 0;
3162
+ this.deferredBytes = 0;
3163
+ this.onFailure?.(this.transportFailure);
3164
+ this.kill();
3090
3165
  }
3091
- parseLine(line) {
3166
+ onData(chunk) {
3167
+ if (this.transportFailure) return;
3168
+ let offset = 0;
3169
+ while (offset < chunk.length) {
3170
+ const newline = chunk.indexOf(10, offset);
3171
+ const end = newline === -1 ? chunk.length : newline;
3172
+ const bytes = end - offset;
3173
+ if (this.frameBytes + bytes > CODEX_MAX_FRAME_BYTES) {
3174
+ this.failTransport("codex raw JSONL frame exceeded its byte budget");
3175
+ return;
3176
+ }
3177
+ if (bytes > 0) this.frameChunks.push(Buffer.from(chunk.subarray(offset, end)));
3178
+ this.frameBytes += bytes;
3179
+ if (newline === -1) return;
3180
+ const frame = Buffer.concat(this.frameChunks, this.frameBytes);
3181
+ this.frameChunks.length = 0;
3182
+ this.frameBytes = 0;
3183
+ const line = frame.toString("utf8").replace(/\r$/, "");
3184
+ if (line.length > 0) this.parseLine(line, frame.length);
3185
+ if (this.transportFailure) return;
3186
+ offset = newline + 1;
3187
+ }
3188
+ }
3189
+ parseLine(line, bytes) {
3092
3190
  let parsed;
3093
3191
  try {
3094
3192
  parsed = JSON.parse(line);
@@ -3098,14 +3196,18 @@ var CodexProcessRunner = class {
3098
3196
  if (!parsed || typeof parsed !== "object" || typeof parsed.type !== "string") {
3099
3197
  return;
3100
3198
  }
3101
- this.deliver(parsed);
3199
+ this.deliver(parsed, bytes);
3102
3200
  }
3103
3201
  onStderr(chunk) {
3104
- for (const rawLine of chunk.split("\n")) {
3202
+ const bounded = Buffer.from(chunk).subarray(-CODEX_MAX_STDERR_BYTES).toString("utf8");
3203
+ for (const rawLine of bounded.split("\n")) {
3105
3204
  const line = rawLine.trim();
3106
3205
  if (line.length === 0) continue;
3107
3206
  this.stderrRing.push(line);
3108
- if (this.stderrRing.length > STDERR_RING_CAPACITY3) this.stderrRing.shift();
3207
+ this.stderrBytes += Buffer.byteLength(line);
3208
+ while (this.stderrRing.length > STDERR_RING_CAPACITY3 || this.stderrBytes > CODEX_MAX_STDERR_BYTES) {
3209
+ this.stderrBytes -= Buffer.byteLength(this.stderrRing.shift());
3210
+ }
3109
3211
  }
3110
3212
  }
3111
3213
  };
@@ -3138,14 +3240,15 @@ var CodexAdapter = class {
3138
3240
  environmentRequirements: { credentialNames: [] }
3139
3241
  });
3140
3242
  async detect() {
3141
- const bin = this.resolveBin();
3142
3243
  try {
3143
- const versionResult = await execFileAsync3(bin.command, ["--version"], { timeout: DETECT_TIMEOUT_MS3 });
3144
- const version = versionResult.stdout.trim() || versionResult.stderr.trim();
3244
+ const bin = this.resolveBin();
3245
+ const probe = await probeRuntimeVersion(bin.command, DETECT_TIMEOUT_MS3);
3246
+ if (probe.kind !== "available") return probe;
3247
+ const version = probe.stdout.trim() || probe.stderr.trim();
3145
3248
  const authPresent = await this.probeAuthPresent(bin);
3146
- return { present: true, version, authPresent };
3147
- } catch {
3148
- return { present: false };
3249
+ return { kind: "available", version, authPresent };
3250
+ } catch (error) {
3251
+ return classifyDetectError(error);
3149
3252
  }
3150
3253
  }
3151
3254
  /**
@@ -3288,7 +3391,7 @@ ${withStreams.stderr ?? ""}`);
3288
3391
  reason: "prepared codex operation received a manifest with different runtime selection"
3289
3392
  });
3290
3393
  }
3291
- const mcpConfigArgs = codexMcpConfigArgs(startInput.mcpServers, preparedMcpGrants);
3394
+ const mcpConfigArgs = codexMcpConfigArgs(startInput.mcpServers, runtimeEnv, this.options.sdkHelperHost, preparedMcpGrants);
3292
3395
  const { sessionRef, runner } = await runCodexTurn({
3293
3396
  command,
3294
3397
  resumeRef: startInput.manifest.sessionRef,
@@ -3304,6 +3407,7 @@ ${withStreams.stderr ?? ""}`);
3304
3407
  expectedSessionRef: startInput.manifest.sessionRef,
3305
3408
  preparedGit: startInput.manifest.workspace.workspaceId !== void 0,
3306
3409
  failurePhase: "start",
3410
+ signal: startInput.signal,
3307
3411
  terminal
3308
3412
  });
3309
3413
  return new CodexSession({
@@ -3311,6 +3415,7 @@ ${withStreams.stderr ?? ""}`);
3311
3415
  command,
3312
3416
  workspaceDir,
3313
3417
  env: startInput.env,
3418
+ mcpEnvironment: Object.fromEntries(Object.entries(runtimeEnv).filter(([key]) => key.startsWith("BYOK_MCP_PAYLOAD_"))),
3314
3419
  spawnFn: this.options.spawnFn,
3315
3420
  queue,
3316
3421
  recordUnmapped,
@@ -3363,16 +3468,18 @@ function codexMcpToolApprovalArgs(name, tools) {
3363
3468
  }
3364
3469
  return args;
3365
3470
  }
3366
- function codexMcpConfigArgs(servers, grants = []) {
3471
+ function codexMcpConfigArgs(servers, env, helperHost, grants = []) {
3367
3472
  if (servers === void 0 || Object.keys(servers).length === 0) return [];
3368
3473
  const grantedTools = new Map(grants.map((grant) => [grant.server, grant.tools]));
3369
3474
  const args = ["--ignore-user-config"];
3370
3475
  for (const [name, server] of Object.entries(servers).sort(([left], [right]) => left.localeCompare(right))) {
3371
- args.push("-c", `mcp_servers.${name}.command=${JSON.stringify(server.command)}`);
3372
- if (server.args !== void 0) args.push("-c", `mcp_servers.${name}.args=${JSON.stringify([...server.args])}`);
3373
- for (const [key, value] of Object.entries(server.env ?? {}).sort(([left], [right]) => left.localeCompare(right))) {
3374
- args.push("-c", `mcp_servers.${name}.env.${key}=${JSON.stringify(value)}`);
3375
- }
3476
+ const key = `BYOK_MCP_PAYLOAD_${randomBytes(16).toString("hex").toUpperCase()}`;
3477
+ env[key] = JSON.stringify(server);
3478
+ const helper = resolveSdkReservedHelperBin("mcp-env", helperHost);
3479
+ args.push("-c", `mcp_servers.${name}.command=${JSON.stringify(helper.command)}`);
3480
+ args.push("-c", `mcp_servers.${name}.args=${JSON.stringify([...helper.args])}`);
3481
+ args.push("-c", `mcp_servers.${name}.env.BYOK_MCP_ENV_KEY=${JSON.stringify(key)}`);
3482
+ args.push("-c", `mcp_servers.${name}.env_vars=${JSON.stringify([key])}`);
3376
3483
  const granted = grantedTools.get(name);
3377
3484
  if (granted !== void 0) args.push(...codexMcpToolApprovalArgs(name, granted));
3378
3485
  }
@@ -3400,7 +3507,7 @@ function buildArgv(resumeRef, policyArgs, instruction, modelId, preparedGit = fa
3400
3507
  ...modelId ? ["--model", modelId] : [],
3401
3508
  ...preparedGit ? [] : ["--skip-git-repo-check"],
3402
3509
  ...policyArgs,
3403
- instruction
3510
+ "-"
3404
3511
  ];
3405
3512
  }
3406
3513
  async function runCodexTurn(params) {
@@ -3424,9 +3531,18 @@ async function runCodexTurn(params) {
3424
3531
  runner = new CodexProcessRunner({
3425
3532
  command: params.command,
3426
3533
  args: argv,
3534
+ instruction: params.instruction,
3427
3535
  cwd: params.cwd,
3428
3536
  env: params.env,
3429
3537
  spawnFn: params.spawnFn,
3538
+ onFailure: (failure) => {
3539
+ if (!firstLineSettled) {
3540
+ firstLineSettled = true;
3541
+ rejectFirstLine(failure);
3542
+ }
3543
+ params.terminal.failure = failure;
3544
+ params.queue.end();
3545
+ },
3430
3546
  onEvent: (evt) => {
3431
3547
  if (!firstLineSettled) {
3432
3548
  firstLineSettled = true;
@@ -3493,6 +3609,16 @@ async function runCodexTurn(params) {
3493
3609
  params.queue.end();
3494
3610
  });
3495
3611
  let sessionRef;
3612
+ const abortStartup = () => rejectFirstLine(new RuntimeExecutionFailure({
3613
+ phase: params.failurePhase,
3614
+ category: "infrastructure",
3615
+ retry: "non-retryable",
3616
+ reason: "codex startup cancelled or exceeded its deadline"
3617
+ }));
3618
+ const startupTimer = setTimeout(abortStartup, 3e4);
3619
+ startupTimer.unref?.();
3620
+ params.signal?.addEventListener("abort", abortStartup, { once: true });
3621
+ if (params.signal?.aborted) abortStartup();
3496
3622
  try {
3497
3623
  sessionRef = await new Promise((resolve, reject) => {
3498
3624
  let settled = false;
@@ -3524,20 +3650,31 @@ async function runCodexTurn(params) {
3524
3650
  });
3525
3651
  });
3526
3652
  } catch (err) {
3527
- runner.kill();
3653
+ await disposeFailedStartup(runner, err);
3528
3654
  throw err;
3655
+ } finally {
3656
+ clearTimeout(startupTimer);
3657
+ params.signal?.removeEventListener("abort", abortStartup);
3529
3658
  }
3530
3659
  if (params.expectedSessionRef !== void 0 && sessionRef !== params.expectedSessionRef) {
3531
- runner.kill();
3532
- throw new RuntimeExecutionFailure({
3660
+ const failure = new RuntimeExecutionFailure({
3533
3661
  phase: params.failurePhase,
3534
3662
  category: "authority",
3535
3663
  retry: "non-retryable",
3536
3664
  reason: `codex exec resume echoed a different thread id than requested (requested ${params.expectedSessionRef}, got ${sessionRef})`
3537
3665
  });
3666
+ await disposeFailedStartup(runner, failure);
3667
+ throw failure;
3538
3668
  }
3539
3669
  return { sessionRef, runner };
3540
3670
  }
3671
+ async function disposeFailedStartup(runner, cause) {
3672
+ try {
3673
+ await runner.dispose();
3674
+ } catch (disposalFailure) {
3675
+ throw new RuntimeStartupDisposalFailure(() => runner.dispose(), { cause: new AggregateError([cause, disposalFailure]) });
3676
+ }
3677
+ }
3541
3678
  var CodexSession = class {
3542
3679
  /**
3543
3680
  * NOT `readonly` (cross-model review finding): `followUp()` below
@@ -3573,6 +3710,7 @@ var CodexSession = class {
3573
3710
  * recomputed from anything a later turn supplies, so a follow-up can never
3574
3711
  * widen this session's MCP authority.
3575
3712
  */
3713
+ mcpEnvironment;
3576
3714
  mcpConfigArgs;
3577
3715
  terminal;
3578
3716
  currentRunner;
@@ -3592,6 +3730,7 @@ var CodexSession = class {
3592
3730
  this.modelId = options.modelId;
3593
3731
  this.terminal = options.terminal;
3594
3732
  this.mcpConfigArgs = options.mcpConfigArgs;
3733
+ this.mcpEnvironment = Object.freeze({ ...options.mcpEnvironment });
3595
3734
  this.currentRunner = options.initialRunner;
3596
3735
  this.ownedRunners.add(options.initialRunner);
3597
3736
  void this.forgetRunnerOnceClosed(options.initialRunner);
@@ -3704,7 +3843,7 @@ var CodexSession = class {
3704
3843
  // frozen MCP config replayed verbatim — see `mcpConfigArgs`.
3705
3844
  policyArgs: [...mapping.args, ...this.mcpConfigArgs],
3706
3845
  cwd: this.workspaceDir,
3707
- env: withoutProviderCredentials(this.env),
3846
+ env: { ...withoutProviderCredentials(this.env), ...this.mcpEnvironment },
3708
3847
  spawnFn: this.spawnFn,
3709
3848
  workspaceDir: this.workspaceDir,
3710
3849
  queue: this.queue,
@@ -3803,6 +3942,6 @@ function subscriptionModel2(selection) {
3803
3942
  return selection.modelId;
3804
3943
  }
3805
3944
 
3806
- export { ClaudeAdapter, CodexAdapter, PI_PACKAGE_NAME, PiAdapter, RuntimeDisposalFailure, RuntimeExecutionFailure };
3945
+ export { ClaudeAdapter, CodexAdapter, PI_PACKAGE_NAME, PiAdapter, RuntimeDisposalFailure, RuntimeExecutionFailure, RuntimeStartupDisposalFailure };
3807
3946
  //# sourceMappingURL=index.js.map
3808
3947
  //# sourceMappingURL=index.js.map