@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.
- package/README.md +15 -0
- package/dist/adapters/codex/codex-adapter.d.ts +2 -0
- package/dist/adapters/codex/process-runner.d.ts +16 -17
- package/dist/adapters/detect-outcome.d.ts +18 -0
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +189 -50
- package/dist/adapters/index.js.map +1 -1
- package/dist/bin/byok-agent-memory-mcp.js +29 -0
- package/dist/bin/byok-agent-memory-mcp.js.map +1 -1
- package/dist/bin/byok-agent-message-mcp.js +29 -0
- package/dist/bin/byok-agent-message-mcp.js.map +1 -1
- package/dist/bin/byok-agent-team-mcp.js +29 -0
- package/dist/bin/byok-agent-team-mcp.js.map +1 -1
- package/dist/bin/byok-agent.js +1832 -932
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/bin/byok-approval-mcp.js +29 -0
- package/dist/bin/byok-approval-mcp.js.map +1 -1
- package/dist/bin/byok-mcp-env.d.ts +2 -0
- package/dist/bin/byok-mcp-env.js +36 -0
- package/dist/bin/byok-mcp-env.js.map +1 -0
- package/dist/bin/commands/doctor.d.ts +3 -0
- package/dist/bin/mcp-env-launcher.d.ts +5 -0
- package/dist/bin/runtime-probe.d.ts +6 -7
- package/dist/daemon/admission-wait.d.ts +2 -0
- package/dist/daemon/agent-egress-spool.d.ts +1 -0
- package/dist/daemon/agent-message-outbox.d.ts +15 -3
- package/dist/daemon/artifact-read.d.ts +7 -0
- package/dist/daemon/connection-manager.d.ts +18 -77
- package/dist/daemon/create-daemon.d.ts +11 -1
- package/dist/daemon/runtime-start.d.ts +3 -0
- package/dist/daemon/store.d.ts +2 -0
- package/dist/daemon/task-runner.d.ts +23 -0
- package/dist/daemon/terminal-commit-queue.d.ts +21 -0
- package/dist/daemon/terminal-identity.d.ts +6 -0
- package/dist/diagnostics/device-doctor.d.ts +46 -0
- package/dist/diagnostics/diagnostics.d.ts +3 -80
- package/dist/diagnostics/types.d.ts +82 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1942 -423
- package/dist/index.js.map +1 -1
- package/dist/runtime-detection.d.ts +3 -0
- package/dist/runtime-failure.d.ts +6 -0
- package/dist/sdk-reserved-helper-host.d.ts +1 -1
- package/dist/types.d.ts +20 -10
- package/dist/util/durable-jsonl.d.ts +12 -0
- 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
|
|
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
|
|
48
|
-
*
|
|
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
|
|
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 {};
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/adapters/index.js
CHANGED
|
@@ -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
|
|
1235
|
-
|
|
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 {
|
|
1238
|
-
} catch {
|
|
1239
|
-
return
|
|
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
|
-
|
|
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
|
|
2296
|
-
const
|
|
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 {
|
|
2299
|
-
} catch {
|
|
2300
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
3081
|
-
this.
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3144
|
-
const
|
|
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 {
|
|
3147
|
-
} catch {
|
|
3148
|
-
return
|
|
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
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|