@byok-sdk/client 0.13.0 → 0.14.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 +80 -0
- package/dist/adapters/claude/process-client.d.ts +24 -0
- package/dist/adapters/codex/process-runner.d.ts +46 -1
- package/dist/adapters/index.js +359 -28
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/pi/events.d.ts +1 -1
- package/dist/adapters/pi/rpc-client.d.ts +47 -1
- package/dist/adapters/pi/subagents-policy-extension.js +1 -1
- package/dist/adapters/pi/subagents-policy-extension.js.map +1 -1
- package/dist/adapters/pi/team-interaction-extension.d.ts +24 -0
- package/dist/adapters/pi/team-interaction-extension.js +81 -0
- package/dist/adapters/pi/team-interaction-extension.js.map +1 -0
- package/dist/adapters/process-tree.d.ts +74 -4
- package/dist/adapters/provider-credential-environment.d.ts +1 -1
- package/dist/adapters/win32-job-object.d.ts +101 -0
- package/dist/bin/byok-agent-memory-mcp.js.map +1 -1
- package/dist/bin/byok-agent-message-mcp.js.map +1 -1
- package/dist/bin/byok-agent-team-mcp.js.map +1 -1
- package/dist/bin/byok-agent.js +9305 -7988
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/bin/byok-approval-mcp.js.map +1 -1
- package/dist/bin/commands/team-pi-relay.d.ts +24 -0
- package/dist/bin/commands/team-relay.d.ts +11 -0
- package/dist/bin/team-codex-relay.d.ts +32 -0
- package/dist/bin/team-notification-relay.d.ts +40 -0
- package/dist/bin/team-pi-session.d.ts +59 -0
- package/dist/daemon/agent-egress-policy.d.ts +8 -0
- package/dist/daemon/connection-manager.d.ts +6 -100
- package/dist/daemon/control-protocol.d.ts +2 -0
- package/dist/daemon/create-daemon.d.ts +32 -0
- package/dist/daemon/event-spill.d.ts +90 -0
- package/dist/daemon/journal/journal.d.ts +15 -3
- package/dist/daemon/journal/sqlite-journal.d.ts +7 -2
- package/dist/daemon/long-poll-transport.d.ts +2 -50
- package/dist/daemon/task-runner.d.ts +13 -0
- package/dist/daemon/team-workspace.d.ts +9 -0
- package/dist/index.js +897 -241
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
package/dist/adapters/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execFile, spawn } from 'child_process';
|
|
1
|
+
import { execFile, spawn, spawnSync } from 'child_process';
|
|
2
2
|
import { promises, existsSync, readFileSync, realpathSync } from 'fs';
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import path5, { isAbsolute } from 'path';
|
|
@@ -6,6 +6,131 @@ import { promisify } from 'util';
|
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import 'readline';
|
|
8
8
|
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
+
var __esm = (fn, res) => function __init() {
|
|
12
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
+
};
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/adapters/win32-job-object.ts
|
|
20
|
+
var win32_job_object_exports = {};
|
|
21
|
+
__export(win32_job_object_exports, {
|
|
22
|
+
Win32JobObjectFailure: () => Win32JobObjectFailure,
|
|
23
|
+
assignOwnedProcessToJob: () => assignOwnedProcessToJob,
|
|
24
|
+
isWin32JobObjectFailure: () => isWin32JobObjectFailure,
|
|
25
|
+
loadBindings: () => loadBindings
|
|
26
|
+
});
|
|
27
|
+
function isWin32JobObjectFailure(value) {
|
|
28
|
+
return value instanceof Error && value.name === "Win32JobObjectFailure";
|
|
29
|
+
}
|
|
30
|
+
function isNullHandle(value) {
|
|
31
|
+
return value === null || value === void 0 || value === 0n || value === 0;
|
|
32
|
+
}
|
|
33
|
+
async function loadBindings() {
|
|
34
|
+
if (cachedBindings !== void 0) return cachedBindings;
|
|
35
|
+
let koffi;
|
|
36
|
+
try {
|
|
37
|
+
const imported = await import('koffi');
|
|
38
|
+
koffi = imported.default ?? imported;
|
|
39
|
+
} catch (cause) {
|
|
40
|
+
throw new Win32JobObjectFailure({
|
|
41
|
+
step: "loadKoffi",
|
|
42
|
+
reason: "win32 runtime process ownership requires the koffi optional dependency, which could not be loaded"
|
|
43
|
+
}, { cause });
|
|
44
|
+
}
|
|
45
|
+
const kernel32 = koffi.load("kernel32.dll");
|
|
46
|
+
const PVOID = koffi.pointer("void");
|
|
47
|
+
const bind = (name, result, args) => kernel32.func("__stdcall", name, result, args);
|
|
48
|
+
cachedBindings = {
|
|
49
|
+
createJobObjectW: bind("CreateJobObjectW", PVOID, [PVOID, "str16"]),
|
|
50
|
+
setInformationJobObject: bind("SetInformationJobObject", "int", [PVOID, "int", PVOID, "uint32"]),
|
|
51
|
+
openProcess: bind("OpenProcess", PVOID, ["uint32", "int", "uint32"]),
|
|
52
|
+
assignProcessToJobObject: bind("AssignProcessToJobObject", "int", [PVOID, PVOID]),
|
|
53
|
+
closeHandle: bind("CloseHandle", "int", [PVOID]),
|
|
54
|
+
getLastError: bind("GetLastError", "uint32", [])
|
|
55
|
+
};
|
|
56
|
+
return cachedBindings;
|
|
57
|
+
}
|
|
58
|
+
function killOnCloseLimitInformation() {
|
|
59
|
+
const information = Buffer.alloc(JOBOBJECT_EXTENDED_LIMIT_SIZE);
|
|
60
|
+
information.writeUInt32LE(JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, JOBOBJECT_EXTENDED_LIMIT_FLAGS_OFFSET);
|
|
61
|
+
return information;
|
|
62
|
+
}
|
|
63
|
+
function daemonJob(bindings) {
|
|
64
|
+
const existing = jobsByBindings.get(bindings);
|
|
65
|
+
if (existing !== void 0) return existing;
|
|
66
|
+
const job = bindings.createJobObjectW(null, null);
|
|
67
|
+
if (isNullHandle(job)) {
|
|
68
|
+
throw new Win32JobObjectFailure({
|
|
69
|
+
step: "CreateJobObjectW",
|
|
70
|
+
reason: "win32 runtime process ownership could not create the daemon kill-on-close job object",
|
|
71
|
+
win32Code: bindings.getLastError()
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const information = killOnCloseLimitInformation();
|
|
75
|
+
if (bindings.setInformationJobObject(job, JobObjectExtendedLimitInformation, information, information.length) === 0) {
|
|
76
|
+
const win32Code = bindings.getLastError();
|
|
77
|
+
bindings.closeHandle(job);
|
|
78
|
+
throw new Win32JobObjectFailure({
|
|
79
|
+
step: "SetInformationJobObject",
|
|
80
|
+
reason: "win32 runtime process ownership could not make the daemon job object kill-on-close",
|
|
81
|
+
win32Code
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
jobsByBindings.set(bindings, job);
|
|
85
|
+
return job;
|
|
86
|
+
}
|
|
87
|
+
async function assignOwnedProcessToJob(pid, options) {
|
|
88
|
+
const bindings = options?.bindings ?? await loadBindings();
|
|
89
|
+
const job = daemonJob(bindings);
|
|
90
|
+
const processHandle = bindings.openProcess(OWNED_PROCESS_ACCESS, 0, pid);
|
|
91
|
+
if (isNullHandle(processHandle)) {
|
|
92
|
+
throw new Win32JobObjectFailure({
|
|
93
|
+
step: "OpenProcess",
|
|
94
|
+
reason: `win32 runtime process ownership could not open process ${pid} for job assignment`,
|
|
95
|
+
win32Code: bindings.getLastError()
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
if (bindings.assignProcessToJobObject(job, processHandle) === 0) {
|
|
100
|
+
throw new Win32JobObjectFailure({
|
|
101
|
+
step: "AssignProcessToJobObject",
|
|
102
|
+
reason: `win32 runtime process ownership could not assign process ${pid} to the daemon kill-on-close job object`,
|
|
103
|
+
win32Code: bindings.getLastError()
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
} finally {
|
|
107
|
+
bindings.closeHandle(processHandle);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
var JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, JobObjectExtendedLimitInformation, JOBOBJECT_EXTENDED_LIMIT_SIZE, JOBOBJECT_EXTENDED_LIMIT_FLAGS_OFFSET, PROCESS_SET_QUOTA, PROCESS_TERMINATE, OWNED_PROCESS_ACCESS, Win32JobObjectFailure, cachedBindings, jobsByBindings;
|
|
111
|
+
var init_win32_job_object = __esm({
|
|
112
|
+
"src/adapters/win32-job-object.ts"() {
|
|
113
|
+
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 8192;
|
|
114
|
+
JobObjectExtendedLimitInformation = 9;
|
|
115
|
+
JOBOBJECT_EXTENDED_LIMIT_SIZE = 144;
|
|
116
|
+
JOBOBJECT_EXTENDED_LIMIT_FLAGS_OFFSET = 16;
|
|
117
|
+
PROCESS_SET_QUOTA = 256;
|
|
118
|
+
PROCESS_TERMINATE = 1;
|
|
119
|
+
OWNED_PROCESS_ACCESS = PROCESS_SET_QUOTA | PROCESS_TERMINATE;
|
|
120
|
+
Win32JobObjectFailure = class extends Error {
|
|
121
|
+
step;
|
|
122
|
+
win32Code;
|
|
123
|
+
constructor(input, options) {
|
|
124
|
+
super(input.reason, options);
|
|
125
|
+
this.name = "Win32JobObjectFailure";
|
|
126
|
+
this.step = input.step;
|
|
127
|
+
this.win32Code = input.win32Code;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
jobsByBindings = /* @__PURE__ */ new WeakMap();
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
9
134
|
// src/runtime-failure.ts
|
|
10
135
|
var RUNTIME_EXECUTION_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeExecutionFailure/v1");
|
|
11
136
|
var RUNTIME_DISPOSAL_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeDisposalFailure/v1");
|
|
@@ -436,6 +561,55 @@ function withOwnedProcessTree(options) {
|
|
|
436
561
|
...process.platform === "win32" ? { windowsHide: true } : { detached: true }
|
|
437
562
|
};
|
|
438
563
|
}
|
|
564
|
+
var hostExitTargets = /* @__PURE__ */ new Map();
|
|
565
|
+
var hostExitHandlerInstalled = false;
|
|
566
|
+
function terminateOwnedTreesForHostExit() {
|
|
567
|
+
for (const target of hostExitTargets.values()) {
|
|
568
|
+
try {
|
|
569
|
+
if (target.platform === "win32") {
|
|
570
|
+
target.spawnSyncFn("taskkill", ["/PID", String(target.pid), "/T", "/F"], {
|
|
571
|
+
stdio: "ignore",
|
|
572
|
+
windowsHide: true
|
|
573
|
+
});
|
|
574
|
+
} else {
|
|
575
|
+
target.kill(-target.pid, "SIGKILL");
|
|
576
|
+
}
|
|
577
|
+
} catch {
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
hostExitTargets.clear();
|
|
581
|
+
}
|
|
582
|
+
function ensureHostExitHandler() {
|
|
583
|
+
if (hostExitHandlerInstalled) return;
|
|
584
|
+
hostExitHandlerInstalled = true;
|
|
585
|
+
process.prependListener("exit", terminateOwnedTreesForHostExit);
|
|
586
|
+
}
|
|
587
|
+
var defaultJobObject = {
|
|
588
|
+
async assign(pid) {
|
|
589
|
+
const { assignOwnedProcessToJob: assignOwnedProcessToJob2 } = await Promise.resolve().then(() => (init_win32_job_object(), win32_job_object_exports));
|
|
590
|
+
await assignOwnedProcessToJob2(pid);
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
async function adoptOwnedProcessTree(options) {
|
|
594
|
+
const pid = positivePid(options.child, options.label);
|
|
595
|
+
if (pid === void 0) return;
|
|
596
|
+
const platform = options.platform ?? process.platform;
|
|
597
|
+
ensureHostExitHandler();
|
|
598
|
+
hostExitTargets.set(options.child, {
|
|
599
|
+
pid,
|
|
600
|
+
label: options.label,
|
|
601
|
+
platform,
|
|
602
|
+
kill: options.killFn ?? defaultKill,
|
|
603
|
+
spawnSyncFn: options.spawnSyncFn ?? spawnSync
|
|
604
|
+
});
|
|
605
|
+
const forget = () => {
|
|
606
|
+
hostExitTargets.delete(options.child);
|
|
607
|
+
};
|
|
608
|
+
options.child.once("close", forget);
|
|
609
|
+
options.child.once("exit", forget);
|
|
610
|
+
if (platform !== "win32") return;
|
|
611
|
+
await (options.jobObject ?? defaultJobObject).assign(pid);
|
|
612
|
+
}
|
|
439
613
|
function positivePid(child, label) {
|
|
440
614
|
const pid = child.pid;
|
|
441
615
|
if (pid === void 0) return void 0;
|
|
@@ -647,21 +821,8 @@ async function disposeOwnedProcessTree(options) {
|
|
|
647
821
|
var STDERR_RING_CAPACITY = 20;
|
|
648
822
|
var DIALOG_UI_METHODS = /* @__PURE__ */ new Set(["select", "confirm", "input", "editor"]);
|
|
649
823
|
var PiRpcClient = class {
|
|
650
|
-
child;
|
|
651
|
-
buffer = "";
|
|
652
|
-
nextId = 1;
|
|
653
|
-
pending = /* @__PURE__ */ new Map();
|
|
654
|
-
eventQueue = new AsyncQueue();
|
|
655
|
-
closed = false;
|
|
656
|
-
exitError;
|
|
657
|
-
closedPromise;
|
|
658
|
-
resolveClosed;
|
|
659
|
-
disposalAttempt;
|
|
660
|
-
/** Bounded tail of recent stderr lines — pi discarded this entirely before (nothing ever read `child.stderr`), which is exactly why finding #1 (`Error: Unknown option: --session-id`, exit 1) had to be root-caused by hand instead of reading it off a thrown error. See `buildExitError`. */
|
|
661
|
-
stderrRing = [];
|
|
662
|
-
/** Count of pi RPC message types `PiSession` (pi-adapter.ts) has told us have no `AgentEvent` mapping and aren't routine bookkeeping — see `recordUnmappedFrame`. */
|
|
663
|
-
unmappedFrameCounts = /* @__PURE__ */ new Map();
|
|
664
824
|
constructor(options) {
|
|
825
|
+
this.options = options;
|
|
665
826
|
const spawnFn = options.spawnFn ?? spawn;
|
|
666
827
|
this.child = spawnFn(options.command, options.args, withOwnedProcessTree({
|
|
667
828
|
cwd: options.cwd,
|
|
@@ -671,6 +832,9 @@ var PiRpcClient = class {
|
|
|
671
832
|
this.closedPromise = new Promise((resolve) => {
|
|
672
833
|
this.resolveClosed = resolve;
|
|
673
834
|
});
|
|
835
|
+
this.adopted = this.adoptOwnedTree(options);
|
|
836
|
+
this.adopted.catch(() => {
|
|
837
|
+
});
|
|
674
838
|
this.child.stdout.setEncoding("utf8");
|
|
675
839
|
this.child.stdout.on("data", (chunk) => this.onData(chunk));
|
|
676
840
|
this.child.stderr.setEncoding("utf8");
|
|
@@ -682,10 +846,37 @@ var PiRpcClient = class {
|
|
|
682
846
|
this.onClosed(err);
|
|
683
847
|
});
|
|
684
848
|
}
|
|
685
|
-
|
|
686
|
-
|
|
849
|
+
options;
|
|
850
|
+
child;
|
|
851
|
+
buffer = "";
|
|
852
|
+
nextId = 1;
|
|
853
|
+
pending = /* @__PURE__ */ new Map();
|
|
854
|
+
eventQueue = new AsyncQueue();
|
|
855
|
+
closed = false;
|
|
856
|
+
exitError;
|
|
857
|
+
closedPromise;
|
|
858
|
+
resolveClosed;
|
|
859
|
+
disposalAttempt;
|
|
860
|
+
/** Resolves once this tree is backstopped (see `adoptOwnedProcessTree`); rejects with the adoption failure, having already terminated the tree. */
|
|
861
|
+
adopted;
|
|
862
|
+
/** Set before the fail-closed termination starts, so it — not the exit code of the kill we ourselves requested — becomes this client's exit error. */
|
|
863
|
+
adoptionFailure;
|
|
864
|
+
/** Bounded tail of recent stderr lines — pi discarded this entirely before (nothing ever read `child.stderr`), which is exactly why finding #1 (`Error: Unknown option: --session-id`, exit 1) had to be root-caused by hand instead of reading it off a thrown error. See `buildExitError`. */
|
|
865
|
+
stderrRing = [];
|
|
866
|
+
/** Count of pi RPC message types `PiSession` (pi-adapter.ts) has told us have no `AgentEvent` mapping and aren't routine bookkeeping — see `recordUnmappedFrame`. */
|
|
867
|
+
unmappedFrameCounts = /* @__PURE__ */ new Map();
|
|
868
|
+
/**
|
|
869
|
+
* Send a command, resolved with its correlated `response` message.
|
|
870
|
+
*
|
|
871
|
+
* Adoption is awaited FIRST — this is `PiAdapter.start()`'s first awaited
|
|
872
|
+
* operation, so an unbackstopped tree never receives the initial prompt and
|
|
873
|
+
* never yields a session. The wait costs one settled-promise turn once the
|
|
874
|
+
* tree is adopted; every later command sees it already resolved.
|
|
875
|
+
*/
|
|
876
|
+
async send(command) {
|
|
877
|
+
await this.adopted;
|
|
687
878
|
if (this.closed) {
|
|
688
|
-
|
|
879
|
+
throw this.exitError ?? new Error("pi process is closed");
|
|
689
880
|
}
|
|
690
881
|
const id = command.id ?? `req-${this.nextId++}`;
|
|
691
882
|
const full = { ...command, id };
|
|
@@ -758,6 +949,32 @@ var PiRpcClient = class {
|
|
|
758
949
|
label: "pi"
|
|
759
950
|
};
|
|
760
951
|
}
|
|
952
|
+
/**
|
|
953
|
+
* Backstop this tree, or tear it down. Adoption failure is a start-time
|
|
954
|
+
* precondition, not a degraded mode: the child is terminated through the one
|
|
955
|
+
* disposal authority and the failure is re-thrown, which is what makes the
|
|
956
|
+
* first `send()` — and therefore `PiAdapter.start()` — fail before any
|
|
957
|
+
* session is published. Both cleanup attempts are best-effort because the
|
|
958
|
+
* adoption failure, not a terminator's own complaint, is the reason to report.
|
|
959
|
+
*/
|
|
960
|
+
async adoptOwnedTree(options) {
|
|
961
|
+
try {
|
|
962
|
+
await adoptOwnedProcessTree({
|
|
963
|
+
child: this.child,
|
|
964
|
+
label: "pi",
|
|
965
|
+
platform: options.platform,
|
|
966
|
+
jobObject: options.jobObject
|
|
967
|
+
});
|
|
968
|
+
} catch (cause) {
|
|
969
|
+
const failure = cause instanceof Error ? cause : new Error(String(cause));
|
|
970
|
+
this.adoptionFailure = failure;
|
|
971
|
+
await requestOwnedProcessTreeTermination(this.processTreeOptions()).catch(() => {
|
|
972
|
+
});
|
|
973
|
+
await disposeOwnedProcessTree(this.processTreeOptions()).catch(() => {
|
|
974
|
+
});
|
|
975
|
+
throw failure;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
761
978
|
onData(chunk) {
|
|
762
979
|
this.buffer += chunk;
|
|
763
980
|
let newlineIndex = this.buffer.indexOf("\n");
|
|
@@ -776,6 +993,7 @@ var PiRpcClient = class {
|
|
|
776
993
|
} catch {
|
|
777
994
|
return;
|
|
778
995
|
}
|
|
996
|
+
this.options.onFrame?.(msg);
|
|
779
997
|
if (msg.type === "response" && typeof msg.id === "string" && this.pending.has(msg.id)) {
|
|
780
998
|
const waiter = this.pending.get(msg.id);
|
|
781
999
|
this.pending.delete(msg.id);
|
|
@@ -783,11 +1001,20 @@ var PiRpcClient = class {
|
|
|
783
1001
|
return;
|
|
784
1002
|
}
|
|
785
1003
|
if (msg.type === "extension_ui_request" && typeof msg.id === "string" && typeof msg.method === "string") {
|
|
786
|
-
this.
|
|
1004
|
+
if (this.options.extensionUi?.mode === "hold") this.options.extensionUi.onRequest(msg);
|
|
1005
|
+
else this.respondToExtensionUiRequest(msg.id, msg.method);
|
|
787
1006
|
return;
|
|
788
1007
|
}
|
|
789
1008
|
this.eventQueue.push(msg);
|
|
790
1009
|
}
|
|
1010
|
+
/** Write an explicit host-owned response; completion is a transport receipt only. */
|
|
1011
|
+
async respondExtensionUi(response) {
|
|
1012
|
+
if (this.options.extensionUi?.mode !== "hold") throw new Error("Pi interactions are not host-owned");
|
|
1013
|
+
await this.adopted;
|
|
1014
|
+
if (this.closed) throw new Error("Pi process is closed");
|
|
1015
|
+
await new Promise((resolve, reject) => this.child.stdin.write(`${JSON.stringify({ type: "extension_ui_response", ...response })}
|
|
1016
|
+
`, (error) => error ? reject(error) : resolve()));
|
|
1017
|
+
}
|
|
791
1018
|
/**
|
|
792
1019
|
* Answer pi's extension-UI blocking protocol headlessly (rpc.md's
|
|
793
1020
|
* "Extension UI Protocol"). Fail-closed policy, stated explicitly because
|
|
@@ -841,9 +1068,10 @@ var PiRpcClient = class {
|
|
|
841
1068
|
onClosed(err) {
|
|
842
1069
|
if (this.closed) return;
|
|
843
1070
|
this.closed = true;
|
|
844
|
-
this.
|
|
1071
|
+
const terminal = this.adoptionFailure ?? err;
|
|
1072
|
+
this.exitError = terminal;
|
|
845
1073
|
this.resolveClosed();
|
|
846
|
-
for (const [, waiter] of this.pending) waiter.reject(
|
|
1074
|
+
for (const [, waiter] of this.pending) waiter.reject(terminal);
|
|
847
1075
|
this.pending.clear();
|
|
848
1076
|
this.eventQueue.end();
|
|
849
1077
|
}
|
|
@@ -877,6 +1105,8 @@ var PROVIDER_CREDENTIAL_ENV_DENY_NAMES = [
|
|
|
877
1105
|
"TOGETHER_API_KEY",
|
|
878
1106
|
"BASETEN_API_KEY",
|
|
879
1107
|
"KIMI_API_KEY",
|
|
1108
|
+
"HF_TOKEN",
|
|
1109
|
+
"MOONSHOT_API_KEY",
|
|
880
1110
|
"MINIMAX_API_KEY",
|
|
881
1111
|
"MINIMAX_CN_API_KEY",
|
|
882
1112
|
"QWEN_TOKEN_PLAN_API_KEY",
|
|
@@ -1753,6 +1983,10 @@ var ClaudeProcessClient = class {
|
|
|
1753
1983
|
closedPromise;
|
|
1754
1984
|
resolveClosed;
|
|
1755
1985
|
disposalAttempt;
|
|
1986
|
+
/** Resolves once this tree is backstopped (see `adoptOwnedProcessTree`); rejects with the adoption failure, having already terminated the tree. */
|
|
1987
|
+
adopted;
|
|
1988
|
+
/** Set before the fail-closed termination starts, so it — not the exit code of the kill we ourselves requested — becomes this client's exit error. */
|
|
1989
|
+
adoptionFailure;
|
|
1756
1990
|
stderrRing = [];
|
|
1757
1991
|
unmappedFrameCounts = /* @__PURE__ */ new Map();
|
|
1758
1992
|
sessionId;
|
|
@@ -1767,6 +2001,9 @@ var ClaudeProcessClient = class {
|
|
|
1767
2001
|
this.closedPromise = new Promise((resolve) => {
|
|
1768
2002
|
this.resolveClosed = resolve;
|
|
1769
2003
|
});
|
|
2004
|
+
this.adopted = this.adoptOwnedTree(options);
|
|
2005
|
+
this.adopted.catch(() => {
|
|
2006
|
+
});
|
|
1770
2007
|
this.child.stdout.setEncoding("utf8");
|
|
1771
2008
|
this.child.stdout.on("data", (chunk) => this.onData(chunk));
|
|
1772
2009
|
this.child.stderr.setEncoding("utf8");
|
|
@@ -1810,9 +2047,10 @@ var ClaudeProcessClient = class {
|
|
|
1810
2047
|
* same id; if the process already closed before init ever arrived,
|
|
1811
2048
|
* every call rejects with that same exit error.
|
|
1812
2049
|
*/
|
|
1813
|
-
waitForInit() {
|
|
1814
|
-
|
|
1815
|
-
if (this.
|
|
2050
|
+
async waitForInit() {
|
|
2051
|
+
await this.adopted;
|
|
2052
|
+
if (this.sessionId !== void 0) return this.sessionId;
|
|
2053
|
+
if (this.closed) throw this.exitError ?? new Error("claude process is closed");
|
|
1816
2054
|
return new Promise((resolve, reject) => {
|
|
1817
2055
|
this.initWaiter = { resolve, reject };
|
|
1818
2056
|
});
|
|
@@ -1876,6 +2114,32 @@ var ClaudeProcessClient = class {
|
|
|
1876
2114
|
label: "claude"
|
|
1877
2115
|
};
|
|
1878
2116
|
}
|
|
2117
|
+
/**
|
|
2118
|
+
* Backstop this tree, or tear it down. Adoption failure is a start-time
|
|
2119
|
+
* precondition, not a degraded mode: the child is terminated through the one
|
|
2120
|
+
* disposal authority and the failure is re-thrown, which is what makes
|
|
2121
|
+
* `waitForInit()` — and therefore `ClaudeAdapter.start()` — fail before any
|
|
2122
|
+
* session is published. Both cleanup attempts are best-effort because the
|
|
2123
|
+
* adoption failure, not a terminator's own complaint, is the reason to report.
|
|
2124
|
+
*/
|
|
2125
|
+
async adoptOwnedTree(options) {
|
|
2126
|
+
try {
|
|
2127
|
+
await adoptOwnedProcessTree({
|
|
2128
|
+
child: this.child,
|
|
2129
|
+
label: "claude",
|
|
2130
|
+
platform: options.platform,
|
|
2131
|
+
jobObject: options.jobObject
|
|
2132
|
+
});
|
|
2133
|
+
} catch (cause) {
|
|
2134
|
+
const failure = cause instanceof Error ? cause : new Error(String(cause));
|
|
2135
|
+
this.adoptionFailure = failure;
|
|
2136
|
+
await requestOwnedProcessTreeTermination(this.processTreeOptions()).catch(() => {
|
|
2137
|
+
});
|
|
2138
|
+
await disposeOwnedProcessTree(this.processTreeOptions()).catch(() => {
|
|
2139
|
+
});
|
|
2140
|
+
throw failure;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
1879
2143
|
onData(chunk) {
|
|
1880
2144
|
this.buffer += chunk;
|
|
1881
2145
|
let newlineIndex = this.buffer.indexOf("\n");
|
|
@@ -1924,9 +2188,10 @@ var ClaudeProcessClient = class {
|
|
|
1924
2188
|
onClosed(err) {
|
|
1925
2189
|
if (this.closed) return;
|
|
1926
2190
|
this.closed = true;
|
|
1927
|
-
this.
|
|
2191
|
+
const terminal = this.adoptionFailure ?? err;
|
|
2192
|
+
this.exitError = terminal;
|
|
1928
2193
|
this.resolveClosed();
|
|
1929
|
-
this.initWaiter?.reject(
|
|
2194
|
+
this.initWaiter?.reject(terminal);
|
|
1930
2195
|
this.initWaiter = void 0;
|
|
1931
2196
|
this.eventQueue.end();
|
|
1932
2197
|
}
|
|
@@ -2662,6 +2927,19 @@ var CodexProcessRunner = class {
|
|
|
2662
2927
|
closedPromise;
|
|
2663
2928
|
resolveClosed;
|
|
2664
2929
|
disposalAttempt;
|
|
2930
|
+
/** Resolves once this tree is backstopped (see `adoptOwnedProcessTree`); rejects with the adoption failure, having already terminated the tree. */
|
|
2931
|
+
adopted;
|
|
2932
|
+
/** Set before the fail-closed termination starts; `buildExitError` reports it instead of the exit status of the kill we ourselves requested. */
|
|
2933
|
+
adoptionFailure;
|
|
2934
|
+
adoption = "pending";
|
|
2935
|
+
/**
|
|
2936
|
+
* Lines parsed before adoption settled. Unlike pi and claude, this runner has
|
|
2937
|
+
* no first awaited operation of its own to gate on — its caller reads the
|
|
2938
|
+
* FIRST event as the authoritative thread id. Holding events until the tree
|
|
2939
|
+
* is backstopped is what keeps that caller from publishing a session for a
|
|
2940
|
+
* tree the job object never took.
|
|
2941
|
+
*/
|
|
2942
|
+
deferredEvents = [];
|
|
2665
2943
|
constructor(options) {
|
|
2666
2944
|
this.onEvent = options.onEvent;
|
|
2667
2945
|
const spawnFn = options.spawnFn ?? spawn;
|
|
@@ -2673,6 +2951,9 @@ var CodexProcessRunner = class {
|
|
|
2673
2951
|
this.closedPromise = new Promise((resolve) => {
|
|
2674
2952
|
this.resolveClosed = resolve;
|
|
2675
2953
|
});
|
|
2954
|
+
this.adopted = this.adoptOwnedTree(options);
|
|
2955
|
+
this.adopted.catch(() => {
|
|
2956
|
+
});
|
|
2676
2957
|
this.child.stdout.setEncoding("utf8");
|
|
2677
2958
|
this.child.stdout.on("data", (chunk) => this.onData(chunk));
|
|
2678
2959
|
this.child.stderr.setEncoding("utf8");
|
|
@@ -2738,8 +3019,58 @@ var CodexProcessRunner = class {
|
|
|
2738
3019
|
label: "codex"
|
|
2739
3020
|
};
|
|
2740
3021
|
}
|
|
2741
|
-
/**
|
|
3022
|
+
/**
|
|
3023
|
+
* Backstop this tree, or tear it down. Adoption failure is a start-time
|
|
3024
|
+
* precondition, not a degraded mode: the child is terminated through the one
|
|
3025
|
+
* disposal authority, every parsed line is dropped instead of delivered, and
|
|
3026
|
+
* the resulting close makes the caller's own `waitClosed()` race reject with
|
|
3027
|
+
* the adoption failure (`buildExitError`) before a thread id is published.
|
|
3028
|
+
* Both cleanup attempts are best-effort because the adoption failure, not a
|
|
3029
|
+
* terminator's own complaint, is the reason to report.
|
|
3030
|
+
*/
|
|
3031
|
+
async adoptOwnedTree(options) {
|
|
3032
|
+
try {
|
|
3033
|
+
await adoptOwnedProcessTree({
|
|
3034
|
+
child: this.child,
|
|
3035
|
+
label: "codex",
|
|
3036
|
+
platform: options.platform,
|
|
3037
|
+
jobObject: options.jobObject
|
|
3038
|
+
});
|
|
3039
|
+
} catch (cause) {
|
|
3040
|
+
const failure = cause instanceof Error ? cause : new Error(String(cause));
|
|
3041
|
+
this.adoptionFailure = failure;
|
|
3042
|
+
this.adoption = "failed";
|
|
3043
|
+
this.deferredEvents.length = 0;
|
|
3044
|
+
await requestOwnedProcessTreeTermination(this.processTreeOptions()).catch(() => {
|
|
3045
|
+
});
|
|
3046
|
+
await disposeOwnedProcessTree(this.processTreeOptions()).catch(() => {
|
|
3047
|
+
});
|
|
3048
|
+
throw failure;
|
|
3049
|
+
}
|
|
3050
|
+
this.adoption = "adopted";
|
|
3051
|
+
for (const evt of this.deferredEvents.splice(0)) this.onEvent(evt);
|
|
3052
|
+
}
|
|
3053
|
+
/** Arrival-order delivery, held back until the tree is backstopped (see `deferredEvents`). */
|
|
3054
|
+
deliver(evt) {
|
|
3055
|
+
if (this.adoption === "failed") return;
|
|
3056
|
+
if (this.adoption === "pending") {
|
|
3057
|
+
this.deferredEvents.push(evt);
|
|
3058
|
+
return;
|
|
3059
|
+
}
|
|
3060
|
+
this.onEvent(evt);
|
|
3061
|
+
}
|
|
3062
|
+
/**
|
|
3063
|
+
* Builds a descriptive error folding in the exit code/signal and the stderr
|
|
3064
|
+
* tail — mirrors `PiRpcClient.buildExitError`'s reasoning: a post-mortem on a
|
|
3065
|
+
* failed start/resume should never need separately re-running codex by hand
|
|
3066
|
+
* with a raw JSONL logger to learn why.
|
|
3067
|
+
*
|
|
3068
|
+
* A tree this runner could not backstop is the one exception: that process
|
|
3069
|
+
* exited because THIS runner killed it, so `exit code=null, signal=SIGKILL`
|
|
3070
|
+
* plus an empty stderr tail would bury the only reason anyone can act on.
|
|
3071
|
+
*/
|
|
2742
3072
|
buildExitError(context) {
|
|
3073
|
+
if (this.adoptionFailure !== void 0) return this.adoptionFailure;
|
|
2743
3074
|
const parts = [`${context} (exit code=${this.exitCode}, signal=${this.exitSignal})`];
|
|
2744
3075
|
if (this.stderrRing.length > 0) {
|
|
2745
3076
|
parts.push(`stderr: ${this.stderrRing.join(" | ")}`);
|
|
@@ -2767,7 +3098,7 @@ var CodexProcessRunner = class {
|
|
|
2767
3098
|
if (!parsed || typeof parsed !== "object" || typeof parsed.type !== "string") {
|
|
2768
3099
|
return;
|
|
2769
3100
|
}
|
|
2770
|
-
this.
|
|
3101
|
+
this.deliver(parsed);
|
|
2771
3102
|
}
|
|
2772
3103
|
onStderr(chunk) {
|
|
2773
3104
|
for (const rawLine of chunk.split("\n")) {
|