@byok-sdk/client 0.12.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 +93 -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 +433 -33
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/pi/events.d.ts +1 -1
- package/dist/adapters/pi/pi-adapter.d.ts +3 -0
- 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/agent-home.d.ts +74 -0
- package/dist/agent-memory/index.d.ts +1 -1
- package/dist/agent-memory/index.js.map +1 -1
- 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 +9571 -8272
- 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 +66 -224
- package/dist/daemon/control-protocol.d.ts +10 -1
- package/dist/daemon/create-daemon.d.ts +58 -13
- 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 +28 -70
- package/dist/daemon/observer.d.ts +1 -1
- package/dist/daemon/task-runner.d.ts +31 -0
- package/dist/daemon/team-workspace.d.ts +9 -0
- package/dist/daemon/url.d.ts +10 -12
- package/dist/diagnostics/support-bundle.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1334 -696
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
- package/dist/daemon/ws-transport.d.ts +0 -139
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",
|
|
@@ -958,7 +1188,11 @@ function validatePiByokLauncherConfig(launcher) {
|
|
|
958
1188
|
"--macos-keychain-path",
|
|
959
1189
|
"--secret-service-prefix",
|
|
960
1190
|
"--provider",
|
|
961
|
-
"--model"
|
|
1191
|
+
"--model",
|
|
1192
|
+
"--profile-revision",
|
|
1193
|
+
"--profile-hash",
|
|
1194
|
+
"--required-capabilities",
|
|
1195
|
+
"--validate-only"
|
|
962
1196
|
]);
|
|
963
1197
|
const conflicting = launcher.args?.find((arg) => reserved.has(arg));
|
|
964
1198
|
if (conflicting !== void 0) {
|
|
@@ -1020,11 +1254,17 @@ var PiAdapter = class {
|
|
|
1020
1254
|
const bin = this.resolveBin();
|
|
1021
1255
|
const extensions = (this.options.resolveExtensions ?? resolvePiExtensions)();
|
|
1022
1256
|
const selection = input.offer.dispatchSelection;
|
|
1023
|
-
const pinnedSelection = selection === void 0 ? void 0 : Object.freeze({
|
|
1257
|
+
const pinnedSelection = selection === void 0 ? void 0 : selection.lane === "byok-profile" ? Object.freeze({
|
|
1258
|
+
...selection,
|
|
1259
|
+
providerProfile: Object.freeze({
|
|
1260
|
+
...selection.providerProfile,
|
|
1261
|
+
requiredCapabilities: Object.freeze([...selection.providerProfile.requiredCapabilities])
|
|
1262
|
+
})
|
|
1263
|
+
}) : Object.freeze({ ...selection });
|
|
1024
1264
|
let command = bin.command;
|
|
1025
1265
|
let launcherArgs;
|
|
1026
1266
|
if (pinnedSelection !== void 0) {
|
|
1027
|
-
if (pinnedSelection.lane !== "byok" || pinnedSelection.runtimeId !== "pi") {
|
|
1267
|
+
if (pinnedSelection.lane !== "byok" && pinnedSelection.lane !== "byok-profile" || pinnedSelection.runtimeId !== "pi") {
|
|
1028
1268
|
return { kind: "reject", reason: `pi adapter cannot execute ${pinnedSelection.lane} selection for runtime ${pinnedSelection.runtimeId}`, retryable: false };
|
|
1029
1269
|
}
|
|
1030
1270
|
const launcher = this.options.byokLauncher;
|
|
@@ -1032,6 +1272,21 @@ var PiAdapter = class {
|
|
|
1032
1272
|
return { kind: "reject", reason: "pi BYOK selection requires a configured credential-custody launcher", retryable: false };
|
|
1033
1273
|
}
|
|
1034
1274
|
command = launcher.command;
|
|
1275
|
+
const providerProfile = pinnedSelection.lane === "byok-profile" ? pinnedSelection.providerProfile : void 0;
|
|
1276
|
+
if (providerProfile !== void 0) {
|
|
1277
|
+
try {
|
|
1278
|
+
await (this.options.validateProviderProfileBinding ?? validateProviderProfileBindingWithLauncher)(
|
|
1279
|
+
providerProfile,
|
|
1280
|
+
launcher
|
|
1281
|
+
);
|
|
1282
|
+
} catch (error) {
|
|
1283
|
+
return {
|
|
1284
|
+
kind: "reject",
|
|
1285
|
+
reason: `provider profile admission failed: ${errorMessage(error)}`,
|
|
1286
|
+
retryable: false
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1035
1290
|
launcherArgs = [
|
|
1036
1291
|
...launcher.args ?? [],
|
|
1037
1292
|
"--pi-bin",
|
|
@@ -1043,9 +1298,19 @@ var PiAdapter = class {
|
|
|
1043
1298
|
...launcher.macosKeychainPath !== void 0 ? ["--macos-keychain-path", launcher.macosKeychainPath] : [],
|
|
1044
1299
|
...launcher.secretServicePrefix ? ["--secret-service-prefix", launcher.secretServicePrefix] : [],
|
|
1045
1300
|
"--provider",
|
|
1046
|
-
pinnedSelection.providerId,
|
|
1301
|
+
providerProfile?.profileRef ?? (pinnedSelection.lane === "byok" ? pinnedSelection.providerId : ""),
|
|
1047
1302
|
"--model",
|
|
1048
|
-
pinnedSelection.modelId
|
|
1303
|
+
providerProfile?.modelId ?? (pinnedSelection.lane === "byok" ? pinnedSelection.modelId : ""),
|
|
1304
|
+
...providerProfile === void 0 ? [] : [
|
|
1305
|
+
"--profile-revision",
|
|
1306
|
+
providerProfile.profileRevision,
|
|
1307
|
+
"--profile-hash",
|
|
1308
|
+
providerProfile.profileHash,
|
|
1309
|
+
"--required-capabilities",
|
|
1310
|
+
JSON.stringify(providerProfile.requiredCapabilities),
|
|
1311
|
+
"--validate-only",
|
|
1312
|
+
"false"
|
|
1313
|
+
]
|
|
1049
1314
|
];
|
|
1050
1315
|
}
|
|
1051
1316
|
return {
|
|
@@ -1188,8 +1453,42 @@ var PiAdapter = class {
|
|
|
1188
1453
|
return (this.options.resolveBin ?? resolvePiBin)();
|
|
1189
1454
|
}
|
|
1190
1455
|
};
|
|
1456
|
+
async function validateProviderProfileBindingWithLauncher(binding, launcher) {
|
|
1457
|
+
await execFileAsync(launcher.command, [
|
|
1458
|
+
...launcher.args ?? [],
|
|
1459
|
+
"--pi-bin",
|
|
1460
|
+
process.execPath,
|
|
1461
|
+
"--profile-db",
|
|
1462
|
+
launcher.profileDbPath,
|
|
1463
|
+
"--session-dir",
|
|
1464
|
+
launcher.sessionDir,
|
|
1465
|
+
"--provider",
|
|
1466
|
+
binding.profileRef,
|
|
1467
|
+
"--model",
|
|
1468
|
+
binding.modelId,
|
|
1469
|
+
"--profile-revision",
|
|
1470
|
+
binding.profileRevision,
|
|
1471
|
+
"--profile-hash",
|
|
1472
|
+
binding.profileHash,
|
|
1473
|
+
"--required-capabilities",
|
|
1474
|
+
JSON.stringify(binding.requiredCapabilities),
|
|
1475
|
+
"--validate-only",
|
|
1476
|
+
"true",
|
|
1477
|
+
...launcher.macosKeychainPath !== void 0 ? ["--macos-keychain-path", launcher.macosKeychainPath] : [],
|
|
1478
|
+
...launcher.secretServicePrefix ? ["--secret-service-prefix", launcher.secretServicePrefix] : []
|
|
1479
|
+
], { timeout: DETECT_TIMEOUT_MS });
|
|
1480
|
+
}
|
|
1191
1481
|
function sameDispatchSelection(left, right) {
|
|
1192
1482
|
if (left === void 0 || right === void 0) return left === right;
|
|
1483
|
+
if (left.lane === "byok-profile" || right.lane === "byok-profile") {
|
|
1484
|
+
if (left.lane !== "byok-profile" || right.lane !== "byok-profile") return false;
|
|
1485
|
+
if (left.runtimeId !== right.runtimeId) return false;
|
|
1486
|
+
const leftProfile = left.providerProfile;
|
|
1487
|
+
const rightProfile = right.providerProfile;
|
|
1488
|
+
return leftProfile.profileRef === rightProfile.profileRef && leftProfile.profileRevision === rightProfile.profileRevision && leftProfile.profileHash === rightProfile.profileHash && leftProfile.modelId === rightProfile.modelId && leftProfile.requiredCapabilities.length === rightProfile.requiredCapabilities.length && leftProfile.requiredCapabilities.every(
|
|
1489
|
+
(capability, index) => capability === rightProfile.requiredCapabilities[index]
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1193
1492
|
return left.lane === right.lane && left.runtimeId === right.runtimeId && left.providerId === right.providerId && left.modelId === right.modelId;
|
|
1194
1493
|
}
|
|
1195
1494
|
async function resolveAuthoritativeSessionId(rpc) {
|
|
@@ -1684,6 +1983,10 @@ var ClaudeProcessClient = class {
|
|
|
1684
1983
|
closedPromise;
|
|
1685
1984
|
resolveClosed;
|
|
1686
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;
|
|
1687
1990
|
stderrRing = [];
|
|
1688
1991
|
unmappedFrameCounts = /* @__PURE__ */ new Map();
|
|
1689
1992
|
sessionId;
|
|
@@ -1698,6 +2001,9 @@ var ClaudeProcessClient = class {
|
|
|
1698
2001
|
this.closedPromise = new Promise((resolve) => {
|
|
1699
2002
|
this.resolveClosed = resolve;
|
|
1700
2003
|
});
|
|
2004
|
+
this.adopted = this.adoptOwnedTree(options);
|
|
2005
|
+
this.adopted.catch(() => {
|
|
2006
|
+
});
|
|
1701
2007
|
this.child.stdout.setEncoding("utf8");
|
|
1702
2008
|
this.child.stdout.on("data", (chunk) => this.onData(chunk));
|
|
1703
2009
|
this.child.stderr.setEncoding("utf8");
|
|
@@ -1741,9 +2047,10 @@ var ClaudeProcessClient = class {
|
|
|
1741
2047
|
* same id; if the process already closed before init ever arrived,
|
|
1742
2048
|
* every call rejects with that same exit error.
|
|
1743
2049
|
*/
|
|
1744
|
-
waitForInit() {
|
|
1745
|
-
|
|
1746
|
-
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");
|
|
1747
2054
|
return new Promise((resolve, reject) => {
|
|
1748
2055
|
this.initWaiter = { resolve, reject };
|
|
1749
2056
|
});
|
|
@@ -1807,6 +2114,32 @@ var ClaudeProcessClient = class {
|
|
|
1807
2114
|
label: "claude"
|
|
1808
2115
|
};
|
|
1809
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
|
+
}
|
|
1810
2143
|
onData(chunk) {
|
|
1811
2144
|
this.buffer += chunk;
|
|
1812
2145
|
let newlineIndex = this.buffer.indexOf("\n");
|
|
@@ -1855,9 +2188,10 @@ var ClaudeProcessClient = class {
|
|
|
1855
2188
|
onClosed(err) {
|
|
1856
2189
|
if (this.closed) return;
|
|
1857
2190
|
this.closed = true;
|
|
1858
|
-
this.
|
|
2191
|
+
const terminal = this.adoptionFailure ?? err;
|
|
2192
|
+
this.exitError = terminal;
|
|
1859
2193
|
this.resolveClosed();
|
|
1860
|
-
this.initWaiter?.reject(
|
|
2194
|
+
this.initWaiter?.reject(terminal);
|
|
1861
2195
|
this.initWaiter = void 0;
|
|
1862
2196
|
this.eventQueue.end();
|
|
1863
2197
|
}
|
|
@@ -2593,6 +2927,19 @@ var CodexProcessRunner = class {
|
|
|
2593
2927
|
closedPromise;
|
|
2594
2928
|
resolveClosed;
|
|
2595
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 = [];
|
|
2596
2943
|
constructor(options) {
|
|
2597
2944
|
this.onEvent = options.onEvent;
|
|
2598
2945
|
const spawnFn = options.spawnFn ?? spawn;
|
|
@@ -2604,6 +2951,9 @@ var CodexProcessRunner = class {
|
|
|
2604
2951
|
this.closedPromise = new Promise((resolve) => {
|
|
2605
2952
|
this.resolveClosed = resolve;
|
|
2606
2953
|
});
|
|
2954
|
+
this.adopted = this.adoptOwnedTree(options);
|
|
2955
|
+
this.adopted.catch(() => {
|
|
2956
|
+
});
|
|
2607
2957
|
this.child.stdout.setEncoding("utf8");
|
|
2608
2958
|
this.child.stdout.on("data", (chunk) => this.onData(chunk));
|
|
2609
2959
|
this.child.stderr.setEncoding("utf8");
|
|
@@ -2669,8 +3019,58 @@ var CodexProcessRunner = class {
|
|
|
2669
3019
|
label: "codex"
|
|
2670
3020
|
};
|
|
2671
3021
|
}
|
|
2672
|
-
/**
|
|
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
|
+
*/
|
|
2673
3072
|
buildExitError(context) {
|
|
3073
|
+
if (this.adoptionFailure !== void 0) return this.adoptionFailure;
|
|
2674
3074
|
const parts = [`${context} (exit code=${this.exitCode}, signal=${this.exitSignal})`];
|
|
2675
3075
|
if (this.stderrRing.length > 0) {
|
|
2676
3076
|
parts.push(`stderr: ${this.stderrRing.join(" | ")}`);
|
|
@@ -2698,7 +3098,7 @@ var CodexProcessRunner = class {
|
|
|
2698
3098
|
if (!parsed || typeof parsed !== "object" || typeof parsed.type !== "string") {
|
|
2699
3099
|
return;
|
|
2700
3100
|
}
|
|
2701
|
-
this.
|
|
3101
|
+
this.deliver(parsed);
|
|
2702
3102
|
}
|
|
2703
3103
|
onStderr(chunk) {
|
|
2704
3104
|
for (const rawLine of chunk.split("\n")) {
|