@byok-sdk/client 0.3.0 → 0.4.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 +13 -0
- package/dist/adapters/claude/claude-adapter.d.ts +4 -20
- package/dist/adapters/claude/events.d.ts +3 -0
- package/dist/adapters/claude/process-client.d.ts +9 -1
- package/dist/adapters/codex/codex-adapter.d.ts +4 -16
- package/dist/adapters/codex/process-runner.d.ts +4 -1
- package/dist/adapters/index.d.ts +3 -1
- package/dist/adapters/index.js +923 -258
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/pi/pi-adapter.d.ts +3 -16
- package/dist/adapters/pi/rpc-client.d.ts +9 -1
- package/dist/adapters/process-tree.d.ts +19 -0
- package/dist/bin/audit-log.d.ts +12 -0
- package/dist/bin/byok-agent.js +1293 -484
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/bin/byok-approval-mcp.js.map +1 -1
- package/dist/bin/commands/workspaces.d.ts +11 -0
- package/dist/bin/format.d.ts +13 -0
- package/dist/bin/runtime-probe.d.ts +1 -1
- package/dist/bin/tasks-view.d.ts +13 -0
- package/dist/daemon/approvals.d.ts +2 -2
- package/dist/daemon/connection-manager.d.ts +4 -2
- package/dist/daemon/control-server.d.ts +18 -1
- package/dist/daemon/create-daemon.d.ts +2 -2
- package/dist/daemon/daemon-owner.d.ts +4 -2
- package/dist/daemon/environment.d.ts +9 -9
- package/dist/daemon/git-workspace.d.ts +21 -0
- package/dist/daemon/observer.d.ts +13 -0
- package/dist/daemon/presence-publisher.d.ts +29 -0
- package/dist/daemon/runtime-capabilities.d.ts +1 -1
- package/dist/daemon/task-runner.d.ts +27 -34
- package/dist/daemon/ws-transport.d.ts +3 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1254 -432
- package/dist/index.js.map +1 -1
- package/dist/runtime-failure.d.ts +64 -0
- package/dist/types.d.ts +100 -73
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { promises, mkdirSync, existsSync, renameSync, writeFileSync, chmodSync,
|
|
|
4
4
|
import path17, { join, isAbsolute } from 'path';
|
|
5
5
|
import os5 from 'os';
|
|
6
6
|
import { parseDeviceAssertionEnvelope, tenantId, DeviceProofProtectedClaimsSchema, deviceProofSigningInput, DEVICE_PROOF_SCHEMA_ID, SKILL_PACK_MAX_BYTES, hasCapability, parseSkillPackManifest, checkSkillPackManifest, skillPackContentHashInput, checkSkillPackFileContent, SKILL_PACK_ENTRY_PATH, checkSkillPackEntry, isSkillPackPathSafe, DEVICE_PROOF_HEADER, contentHash, TRUTH_RECORD_KINDS, nonceSigningBytes, DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_MAX_TTL_MS, CONTENT_HASH_PATTERN, CapabilityDeclarationSchema, DeviceAssertionClaimsSchema, deviceAssertionSigningInput, DEVICE_ASSERTION_SCHEMA_ID } from '@byok-sdk/core';
|
|
7
|
-
import { BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, partitionAgentEvents, BYOK_SKILL_PACKS_PATH, byokSkillPackFilePath, BYOK_RECORDS_PATH, byokRecordPath, TASK_TRANSITIONS, ToolsetIdSchema, encodeEnvelope, createEnvelope, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema, RESULT_DOCUMENT_MAX_BYTES, PROTOCOL_VERSION, decodeEnvelope, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, BYOK_EVENTS_PATH, parseMessage, UnknownMessageTypeError, BYOK_WS_PATH } from '@byok-sdk/protocol';
|
|
7
|
+
import { BYOK_PAIR_PATH, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, partitionAgentEvents, BYOK_SKILL_PACKS_PATH, byokSkillPackFilePath, BYOK_RECORDS_PATH, byokRecordPath, TASK_TRANSITIONS, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, encodeEnvelope, createEnvelope, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema, RESULT_DOCUMENT_MAX_BYTES, PROTOCOL_VERSION, decodeEnvelope, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, BYOK_EVENTS_PATH, parseMessage, UnknownMessageTypeError, BYOK_WS_PATH } from '@byok-sdk/protocol';
|
|
8
8
|
import { promisify } from 'util';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
10
|
import 'readline';
|
|
@@ -13,6 +13,52 @@ import { WebSocket } from 'ws';
|
|
|
13
13
|
import { createRequire } from 'module';
|
|
14
14
|
|
|
15
15
|
// src/types.ts
|
|
16
|
+
function frozenStrings(values) {
|
|
17
|
+
return values === void 0 ? void 0 : Object.freeze([...values]);
|
|
18
|
+
}
|
|
19
|
+
function frozenPolicy(policy) {
|
|
20
|
+
const allowTools = policy.allowTools === void 0 ? void 0 : Object.freeze([...policy.allowTools]);
|
|
21
|
+
const denyTools = policy.denyTools === void 0 ? void 0 : Object.freeze([...policy.denyTools]);
|
|
22
|
+
return Object.freeze({
|
|
23
|
+
mode: policy.mode,
|
|
24
|
+
...allowTools === void 0 ? {} : { allowTools },
|
|
25
|
+
...denyTools === void 0 ? {} : { denyTools },
|
|
26
|
+
...policy.workspaceRoot === void 0 ? {} : { workspaceRoot: policy.workspaceRoot },
|
|
27
|
+
...policy.network === void 0 ? {} : { network: policy.network }
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function freezeRuntimeAdapterDescriptor(descriptor) {
|
|
31
|
+
const baseNames = frozenStrings(descriptor.environmentRequirements.baseNames);
|
|
32
|
+
const credentialNames = frozenStrings(descriptor.environmentRequirements.credentialNames);
|
|
33
|
+
return Object.freeze({
|
|
34
|
+
id: descriptor.id,
|
|
35
|
+
supportsDispatchSelection: descriptor.supportsDispatchSelection === true,
|
|
36
|
+
capabilities: Object.freeze({
|
|
37
|
+
steer: descriptor.capabilities.steer === true,
|
|
38
|
+
resume: descriptor.capabilities.resume === true,
|
|
39
|
+
approvalInteractive: descriptor.capabilities.approvalInteractive === true,
|
|
40
|
+
...descriptor.capabilities.mcpToolsets === void 0 ? {} : { mcpToolsets: descriptor.capabilities.mcpToolsets === true },
|
|
41
|
+
permissionModes: Object.freeze([...descriptor.capabilities.permissionModes])
|
|
42
|
+
}),
|
|
43
|
+
environmentRequirements: Object.freeze({
|
|
44
|
+
...baseNames === void 0 ? {} : { baseNames },
|
|
45
|
+
...credentialNames === void 0 ? {} : { credentialNames }
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function sealRuntimeOperationManifest(manifest) {
|
|
50
|
+
return Object.freeze({
|
|
51
|
+
taskId: manifest.taskId,
|
|
52
|
+
runtimeId: manifest.runtimeId,
|
|
53
|
+
descriptor: freezeRuntimeAdapterDescriptor(manifest.descriptor),
|
|
54
|
+
policy: frozenPolicy(manifest.policy),
|
|
55
|
+
requiredToolsetIds: Object.freeze([...manifest.requiredToolsetIds]),
|
|
56
|
+
...manifest.dispatchSelection === void 0 ? {} : { dispatchSelection: Object.freeze({ ...manifest.dispatchSelection }) },
|
|
57
|
+
...manifest.sessionRef === void 0 ? {} : { sessionRef: manifest.sessionRef },
|
|
58
|
+
workspace: Object.freeze({ ...manifest.workspace }),
|
|
59
|
+
forwardedEnvironmentNames: Object.freeze([...manifest.forwardedEnvironmentNames])
|
|
60
|
+
});
|
|
61
|
+
}
|
|
16
62
|
var PolicyUnsupportedError = class extends Error {
|
|
17
63
|
constructor(message) {
|
|
18
64
|
super(message);
|
|
@@ -20,7 +66,7 @@ var PolicyUnsupportedError = class extends Error {
|
|
|
20
66
|
}
|
|
21
67
|
};
|
|
22
68
|
var SteerUnsupportedError = class extends Error {
|
|
23
|
-
/** The `RuntimeAdapter.id` that cannot steer (e.g. `claude`, `codex`). */
|
|
69
|
+
/** The `RuntimeAdapter.descriptor.id` that cannot steer (e.g. `claude`, `codex`). */
|
|
24
70
|
runtimeId;
|
|
25
71
|
constructor(runtimeId, message) {
|
|
26
72
|
super(message);
|
|
@@ -28,6 +74,90 @@ var SteerUnsupportedError = class extends Error {
|
|
|
28
74
|
this.runtimeId = runtimeId;
|
|
29
75
|
}
|
|
30
76
|
};
|
|
77
|
+
|
|
78
|
+
// src/runtime-failure.ts
|
|
79
|
+
var RUNTIME_EXECUTION_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeExecutionFailure/v1");
|
|
80
|
+
var RUNTIME_DISPOSAL_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeDisposalFailure/v1");
|
|
81
|
+
var RuntimeDisposalFailure = class extends Error {
|
|
82
|
+
stage;
|
|
83
|
+
constructor(input, options) {
|
|
84
|
+
if (!isRuntimeDisposalStage(input.stage) || typeof input.reason !== "string" || input.reason.length === 0) {
|
|
85
|
+
throw new TypeError("invalid RuntimeDisposalFailure input");
|
|
86
|
+
}
|
|
87
|
+
super(input.reason, options);
|
|
88
|
+
this.name = "RuntimeDisposalFailure";
|
|
89
|
+
this.stage = input.stage;
|
|
90
|
+
Object.defineProperty(this, RUNTIME_DISPOSAL_FAILURE_BRAND, { value: true });
|
|
91
|
+
Object.freeze(this);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
function isRuntimeDisposalStage(value) {
|
|
95
|
+
return value === "signal" || value === "quiescence" || value === "cleanup";
|
|
96
|
+
}
|
|
97
|
+
function isRuntimeDisposalFailure(value) {
|
|
98
|
+
if (typeof value !== "object" || value === null) return false;
|
|
99
|
+
const candidate = value;
|
|
100
|
+
return candidate[RUNTIME_DISPOSAL_FAILURE_BRAND] === true && isRuntimeDisposalStage(candidate.stage) && typeof candidate.message === "string" && candidate.message.length > 0;
|
|
101
|
+
}
|
|
102
|
+
var RuntimeExecutionFailure = class extends Error {
|
|
103
|
+
phase;
|
|
104
|
+
category;
|
|
105
|
+
retry;
|
|
106
|
+
constructor(input, options) {
|
|
107
|
+
if (!isRuntimeFailurePhase(input.phase) || !isRuntimeFailureCategory(input.category) || !isRuntimeRetryDisposition(input.retry) || typeof input.reason !== "string" || input.reason.length === 0) {
|
|
108
|
+
throw new TypeError("invalid RuntimeExecutionFailure input");
|
|
109
|
+
}
|
|
110
|
+
super(input.reason, options);
|
|
111
|
+
this.name = "RuntimeExecutionFailure";
|
|
112
|
+
this.phase = input.phase;
|
|
113
|
+
this.category = input.category;
|
|
114
|
+
this.retry = input.retry;
|
|
115
|
+
Object.defineProperty(this, RUNTIME_EXECUTION_FAILURE_BRAND, { value: true });
|
|
116
|
+
Object.freeze(this);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
function isRuntimeFailurePhase(value) {
|
|
120
|
+
return value === "start" || value === "run";
|
|
121
|
+
}
|
|
122
|
+
function isRuntimeFailureCategory(value) {
|
|
123
|
+
return value === "semantic" || value === "infrastructure" || value === "authority";
|
|
124
|
+
}
|
|
125
|
+
function isRuntimeRetryDisposition(value) {
|
|
126
|
+
return value === "retryable" || value === "non-retryable";
|
|
127
|
+
}
|
|
128
|
+
function isRuntimeExecutionFailure(value) {
|
|
129
|
+
if (typeof value !== "object" || value === null) return false;
|
|
130
|
+
const candidate = value;
|
|
131
|
+
return candidate[RUNTIME_EXECUTION_FAILURE_BRAND] === true && isRuntimeFailurePhase(candidate.phase) && isRuntimeFailureCategory(candidate.category) && isRuntimeRetryDisposition(candidate.retry) && typeof candidate.message === "string" && candidate.message.length > 0;
|
|
132
|
+
}
|
|
133
|
+
function retryableFromDisposition(disposition) {
|
|
134
|
+
switch (disposition) {
|
|
135
|
+
case "retryable":
|
|
136
|
+
return true;
|
|
137
|
+
case "non-retryable":
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function projectRuntimeExecutionFailure(failure) {
|
|
142
|
+
return {
|
|
143
|
+
reason: failure.message,
|
|
144
|
+
retryable: retryableFromDisposition(failure.retry)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
var RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON = Object.freeze({
|
|
148
|
+
start: "runtime adapter contract violation during start",
|
|
149
|
+
run: "runtime adapter contract violation during run"
|
|
150
|
+
});
|
|
151
|
+
function projectRuntimeBoundaryFailure(value, expectedPhase) {
|
|
152
|
+
if (isRuntimeExecutionFailure(value) && value.phase === expectedPhase) {
|
|
153
|
+
return { ...projectRuntimeExecutionFailure(value), contractViolation: false };
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
reason: RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON[expectedPhase],
|
|
157
|
+
retryable: false,
|
|
158
|
+
contractViolation: true
|
|
159
|
+
};
|
|
160
|
+
}
|
|
31
161
|
var GitWorkspaceError = class extends Error {
|
|
32
162
|
constructor(category, message = category) {
|
|
33
163
|
super(message);
|
|
@@ -914,6 +1044,155 @@ var AsyncQueue = class {
|
|
|
914
1044
|
};
|
|
915
1045
|
}
|
|
916
1046
|
};
|
|
1047
|
+
var DEFAULT_TERM_GRACE_MS = 750;
|
|
1048
|
+
var DEFAULT_KILL_GRACE_MS = 2e3;
|
|
1049
|
+
var POLL_MS = 20;
|
|
1050
|
+
var terminationRequested = /* @__PURE__ */ new WeakSet();
|
|
1051
|
+
var terminationRequestFailed = /* @__PURE__ */ new WeakSet();
|
|
1052
|
+
function withOwnedProcessTree(options) {
|
|
1053
|
+
return {
|
|
1054
|
+
...options,
|
|
1055
|
+
...process.platform === "win32" ? { windowsHide: true } : { detached: true }
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
function positivePid(child, label) {
|
|
1059
|
+
const pid = child.pid;
|
|
1060
|
+
if (pid === void 0) return void 0;
|
|
1061
|
+
if (!Number.isSafeInteger(pid) || pid <= 0 || pid === process.pid) {
|
|
1062
|
+
throw new RuntimeDisposalFailure({
|
|
1063
|
+
stage: "signal",
|
|
1064
|
+
reason: `${label} runtime process has an unsafe owned pid`
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
return pid;
|
|
1068
|
+
}
|
|
1069
|
+
function groupExists(pid, label) {
|
|
1070
|
+
try {
|
|
1071
|
+
process.kill(-pid, 0);
|
|
1072
|
+
return true;
|
|
1073
|
+
} catch (cause) {
|
|
1074
|
+
const code = cause.code;
|
|
1075
|
+
if (code === "ESRCH") return false;
|
|
1076
|
+
if (code === "EPERM") return true;
|
|
1077
|
+
throw new RuntimeDisposalFailure({
|
|
1078
|
+
stage: "quiescence",
|
|
1079
|
+
reason: `${label} runtime process-group state could not be verified`
|
|
1080
|
+
}, { cause });
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
function signalGroup(pid, signal, label) {
|
|
1084
|
+
try {
|
|
1085
|
+
process.kill(-pid, signal);
|
|
1086
|
+
} catch (cause) {
|
|
1087
|
+
const code = cause.code;
|
|
1088
|
+
if (code === "ESRCH" || code === "EPERM") return;
|
|
1089
|
+
throw new RuntimeDisposalFailure({
|
|
1090
|
+
stage: "signal",
|
|
1091
|
+
reason: `${label} runtime process group ${pid} could not receive ${signal} (${code ?? "unknown"})`
|
|
1092
|
+
}, { cause });
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
async function waitUntil(predicate, timeoutMs) {
|
|
1096
|
+
const deadline = Date.now() + timeoutMs;
|
|
1097
|
+
while (predicate()) {
|
|
1098
|
+
if (Date.now() >= deadline) return false;
|
|
1099
|
+
await new Promise((resolve) => {
|
|
1100
|
+
setTimeout(resolve, POLL_MS);
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
return true;
|
|
1104
|
+
}
|
|
1105
|
+
async function waitWithDeadline(promise, timeoutMs) {
|
|
1106
|
+
return new Promise((resolve) => {
|
|
1107
|
+
let settled = false;
|
|
1108
|
+
const timer = setTimeout(() => {
|
|
1109
|
+
if (!settled) {
|
|
1110
|
+
settled = true;
|
|
1111
|
+
resolve(false);
|
|
1112
|
+
}
|
|
1113
|
+
}, timeoutMs);
|
|
1114
|
+
void promise.then(
|
|
1115
|
+
() => {
|
|
1116
|
+
if (!settled) {
|
|
1117
|
+
settled = true;
|
|
1118
|
+
clearTimeout(timer);
|
|
1119
|
+
resolve(true);
|
|
1120
|
+
}
|
|
1121
|
+
},
|
|
1122
|
+
() => {
|
|
1123
|
+
if (!settled) {
|
|
1124
|
+
settled = true;
|
|
1125
|
+
clearTimeout(timer);
|
|
1126
|
+
resolve(false);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
);
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
function requestOwnedProcessTreeTermination(options) {
|
|
1133
|
+
if (options.isClosed()) return;
|
|
1134
|
+
const pid = positivePid(options.child, options.label);
|
|
1135
|
+
if (pid === void 0) return;
|
|
1136
|
+
if (process.platform === "win32") {
|
|
1137
|
+
const result = spawnSync("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true });
|
|
1138
|
+
if (result.error) {
|
|
1139
|
+
throw new RuntimeDisposalFailure({
|
|
1140
|
+
stage: "signal",
|
|
1141
|
+
reason: `${options.label} runtime process tree could not be terminated`
|
|
1142
|
+
}, { cause: result.error });
|
|
1143
|
+
}
|
|
1144
|
+
terminationRequested.add(options.child);
|
|
1145
|
+
if (result.status !== 0) terminationRequestFailed.add(options.child);
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
signalGroup(pid, "SIGTERM", options.label);
|
|
1149
|
+
terminationRequested.add(options.child);
|
|
1150
|
+
}
|
|
1151
|
+
async function disposeOwnedProcessTree(options) {
|
|
1152
|
+
const pid = positivePid(options.child, options.label);
|
|
1153
|
+
const termGraceMs = options.termGraceMs ?? DEFAULT_TERM_GRACE_MS;
|
|
1154
|
+
const killGraceMs = options.killGraceMs ?? DEFAULT_KILL_GRACE_MS;
|
|
1155
|
+
if (pid === void 0) {
|
|
1156
|
+
if (await waitWithDeadline(options.waitClosed(), killGraceMs)) return;
|
|
1157
|
+
throw new RuntimeDisposalFailure({
|
|
1158
|
+
stage: "quiescence",
|
|
1159
|
+
reason: `${options.label} runtime process did not settle after spawn failure`
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
if (process.platform === "win32") {
|
|
1163
|
+
if (!options.isClosed() && !terminationRequested.has(options.child)) requestOwnedProcessTreeTermination(options);
|
|
1164
|
+
if (await waitWithDeadline(options.waitClosed(), killGraceMs)) return;
|
|
1165
|
+
if (terminationRequestFailed.has(options.child)) {
|
|
1166
|
+
throw new RuntimeDisposalFailure({
|
|
1167
|
+
stage: "signal",
|
|
1168
|
+
reason: `${options.label} runtime process tree could not be terminated`
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
throw new RuntimeDisposalFailure({
|
|
1172
|
+
stage: "quiescence",
|
|
1173
|
+
reason: `${options.label} runtime process tree did not close before the disposal deadline`
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
if (groupExists(pid, options.label) && !terminationRequested.has(options.child)) {
|
|
1177
|
+
signalGroup(pid, "SIGTERM", options.label);
|
|
1178
|
+
terminationRequested.add(options.child);
|
|
1179
|
+
}
|
|
1180
|
+
if (!await waitUntil(() => groupExists(pid, options.label), termGraceMs)) {
|
|
1181
|
+
signalGroup(pid, "SIGKILL", options.label);
|
|
1182
|
+
if (!await waitUntil(() => groupExists(pid, options.label), killGraceMs)) {
|
|
1183
|
+
throw new RuntimeDisposalFailure({
|
|
1184
|
+
stage: "quiescence",
|
|
1185
|
+
reason: `${options.label} runtime process group remained live after SIGKILL`
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
if (!await waitWithDeadline(options.waitClosed(), killGraceMs)) {
|
|
1190
|
+
throw new RuntimeDisposalFailure({
|
|
1191
|
+
stage: "quiescence",
|
|
1192
|
+
reason: `${options.label} runtime root did not emit close after its process group exited`
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
917
1196
|
|
|
918
1197
|
// src/adapters/pi/rpc-client.ts
|
|
919
1198
|
var STDERR_RING_CAPACITY = 20;
|
|
@@ -926,16 +1205,22 @@ var PiRpcClient = class {
|
|
|
926
1205
|
eventQueue = new AsyncQueue();
|
|
927
1206
|
closed = false;
|
|
928
1207
|
exitError;
|
|
1208
|
+
closedPromise;
|
|
1209
|
+
resolveClosed;
|
|
1210
|
+
disposalAttempt;
|
|
929
1211
|
/** 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`. */
|
|
930
1212
|
stderrRing = [];
|
|
931
1213
|
/** Count of pi RPC message types `PiSession` (pi-adapter.ts) has told us have no `AgentEvent` mapping and aren't routine bookkeeping — see `recordUnmappedFrame`. */
|
|
932
1214
|
unmappedFrameCounts = /* @__PURE__ */ new Map();
|
|
933
1215
|
constructor(options) {
|
|
934
1216
|
const spawnFn = options.spawnFn ?? spawn;
|
|
935
|
-
this.child = spawnFn(options.command, options.args, {
|
|
1217
|
+
this.child = spawnFn(options.command, options.args, withOwnedProcessTree({
|
|
936
1218
|
cwd: options.cwd,
|
|
937
1219
|
env: options.env,
|
|
938
1220
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1221
|
+
}));
|
|
1222
|
+
this.closedPromise = new Promise((resolve) => {
|
|
1223
|
+
this.resolveClosed = resolve;
|
|
939
1224
|
});
|
|
940
1225
|
this.child.stdout.setEncoding("utf8");
|
|
941
1226
|
this.child.stdout.on("data", (chunk) => this.onData(chunk));
|
|
@@ -970,6 +1255,10 @@ var PiRpcClient = class {
|
|
|
970
1255
|
get events() {
|
|
971
1256
|
return this.eventQueue;
|
|
972
1257
|
}
|
|
1258
|
+
/** Local transport diagnostic retained when the process closes; consumers must classify it explicitly. */
|
|
1259
|
+
get terminalError() {
|
|
1260
|
+
return this.exitError;
|
|
1261
|
+
}
|
|
973
1262
|
/**
|
|
974
1263
|
* Record a pi RPC message `type` that `PiSession` (pi-adapter.ts) decided
|
|
975
1264
|
* has no `AgentEvent` mapping and isn't routine bookkeeping (see
|
|
@@ -988,15 +1277,30 @@ var PiRpcClient = class {
|
|
|
988
1277
|
);
|
|
989
1278
|
}
|
|
990
1279
|
}
|
|
991
|
-
/**
|
|
1280
|
+
/** Immediate process-tree termination request. `dispose()` is the settlement receipt. */
|
|
992
1281
|
kill() {
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1282
|
+
requestOwnedProcessTreeTermination(this.processTreeOptions());
|
|
1283
|
+
}
|
|
1284
|
+
waitClosed() {
|
|
1285
|
+
return this.closedPromise;
|
|
1286
|
+
}
|
|
1287
|
+
dispose() {
|
|
1288
|
+
if (!this.disposalAttempt) {
|
|
1289
|
+
const attempt = disposeOwnedProcessTree(this.processTreeOptions());
|
|
1290
|
+
this.disposalAttempt = attempt.catch((error) => {
|
|
1291
|
+
this.disposalAttempt = void 0;
|
|
1292
|
+
throw error;
|
|
1293
|
+
});
|
|
999
1294
|
}
|
|
1295
|
+
return this.disposalAttempt;
|
|
1296
|
+
}
|
|
1297
|
+
processTreeOptions() {
|
|
1298
|
+
return {
|
|
1299
|
+
child: this.child,
|
|
1300
|
+
waitClosed: () => this.closedPromise,
|
|
1301
|
+
isClosed: () => this.closed,
|
|
1302
|
+
label: "pi"
|
|
1303
|
+
};
|
|
1000
1304
|
}
|
|
1001
1305
|
onData(chunk) {
|
|
1002
1306
|
this.buffer += chunk;
|
|
@@ -1082,6 +1386,7 @@ var PiRpcClient = class {
|
|
|
1082
1386
|
if (this.closed) return;
|
|
1083
1387
|
this.closed = true;
|
|
1084
1388
|
this.exitError = err;
|
|
1389
|
+
this.resolveClosed();
|
|
1085
1390
|
for (const [, waiter] of this.pending) waiter.reject(err);
|
|
1086
1391
|
this.pending.clear();
|
|
1087
1392
|
this.eventQueue.end();
|
|
@@ -1152,8 +1457,17 @@ var PiAdapter = class {
|
|
|
1152
1457
|
this.options = options;
|
|
1153
1458
|
}
|
|
1154
1459
|
options;
|
|
1155
|
-
|
|
1156
|
-
|
|
1460
|
+
descriptor = freezeRuntimeAdapterDescriptor({
|
|
1461
|
+
id: "pi",
|
|
1462
|
+
supportsDispatchSelection: true,
|
|
1463
|
+
capabilities: {
|
|
1464
|
+
steer: true,
|
|
1465
|
+
resume: true,
|
|
1466
|
+
approvalInteractive: false,
|
|
1467
|
+
permissionModes: ["auto", "readonly"]
|
|
1468
|
+
},
|
|
1469
|
+
environmentRequirements: { credentialNames: PROVIDER_CREDENTIAL_ENV_NAMES }
|
|
1470
|
+
});
|
|
1157
1471
|
async detect() {
|
|
1158
1472
|
try {
|
|
1159
1473
|
const bin = this.resolveBin();
|
|
@@ -1165,49 +1479,26 @@ var PiAdapter = class {
|
|
|
1165
1479
|
return { present: false };
|
|
1166
1480
|
}
|
|
1167
1481
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
}
|
|
1171
|
-
/**
|
|
1172
|
-
* M5: pi authenticates to its ~30 supported providers via env-var API
|
|
1173
|
-
* keys — `detect()`'s own `authPresent` probe above checks this identical
|
|
1174
|
-
* list — so these MUST keep flowing into pi's spawned process or pi auth
|
|
1175
|
-
* breaks entirely. `KNOWN_PROVIDER_ENV_VARS` above is the single source
|
|
1176
|
-
* of truth, reused here rather than duplicated. No `baseNames`: nothing
|
|
1177
|
-
* in this adapter or `rpc-client.ts` reads a pi-specific config-discovery
|
|
1178
|
-
* variable beyond the platform baseline (`daemon/environment.ts`).
|
|
1179
|
-
*/
|
|
1180
|
-
environmentRequirements() {
|
|
1181
|
-
return { credentialNames: PROVIDER_CREDENTIAL_ENV_NAMES };
|
|
1182
|
-
}
|
|
1183
|
-
async start(task, ctx) {
|
|
1184
|
-
if (typeof task.instruction !== "string") {
|
|
1185
|
-
throw new PolicyUnsupportedError("pi adapter only supports string instructions in M0 (no blob-ref fetch yet)");
|
|
1186
|
-
}
|
|
1187
|
-
const mapping = mapPermissionPolicyToPiArgs(ctx.policy);
|
|
1482
|
+
async prepare(input) {
|
|
1483
|
+
const mapping = mapPermissionPolicyToPiArgs(input.policy);
|
|
1188
1484
|
if (!mapping.ok) {
|
|
1189
|
-
|
|
1485
|
+
return { kind: "reject", reason: mapping.reason ?? "policy rejected by pi adapter", retryable: false };
|
|
1190
1486
|
}
|
|
1191
1487
|
const bin = this.resolveBin();
|
|
1192
|
-
const
|
|
1193
|
-
const
|
|
1194
|
-
const selection = task.dispatchSelection;
|
|
1488
|
+
const selection = input.offer.dispatchSelection;
|
|
1489
|
+
const pinnedSelection = selection === void 0 ? void 0 : Object.freeze({ ...selection });
|
|
1195
1490
|
let command = bin.command;
|
|
1196
|
-
let
|
|
1197
|
-
if (
|
|
1198
|
-
if (
|
|
1199
|
-
|
|
1200
|
-
`pi adapter cannot execute ${selection.lane} selection for runtime ${selection.runtimeId}`
|
|
1201
|
-
);
|
|
1491
|
+
let launcherArgs;
|
|
1492
|
+
if (pinnedSelection !== void 0) {
|
|
1493
|
+
if (pinnedSelection.lane !== "byok" || pinnedSelection.runtimeId !== "pi") {
|
|
1494
|
+
return { kind: "reject", reason: `pi adapter cannot execute ${pinnedSelection.lane} selection for runtime ${pinnedSelection.runtimeId}`, retryable: false };
|
|
1202
1495
|
}
|
|
1203
1496
|
const launcher = this.options.byokLauncher;
|
|
1204
1497
|
if (launcher === void 0) {
|
|
1205
|
-
|
|
1206
|
-
"pi BYOK selection requires a configured credential-custody launcher"
|
|
1207
|
-
);
|
|
1498
|
+
return { kind: "reject", reason: "pi BYOK selection requires a configured credential-custody launcher", retryable: false };
|
|
1208
1499
|
}
|
|
1209
1500
|
command = launcher.command;
|
|
1210
|
-
|
|
1501
|
+
launcherArgs = [
|
|
1211
1502
|
...launcher.args ?? [],
|
|
1212
1503
|
"--pi-bin",
|
|
1213
1504
|
bin.command,
|
|
@@ -1217,62 +1508,136 @@ var PiAdapter = class {
|
|
|
1217
1508
|
launcher.sessionDir,
|
|
1218
1509
|
...launcher.secretServicePrefix ? ["--secret-service-prefix", launcher.secretServicePrefix] : [],
|
|
1219
1510
|
"--provider",
|
|
1220
|
-
|
|
1511
|
+
pinnedSelection.providerId,
|
|
1221
1512
|
"--model",
|
|
1222
|
-
|
|
1223
|
-
"--",
|
|
1224
|
-
...piArgs
|
|
1513
|
+
pinnedSelection.modelId
|
|
1225
1514
|
];
|
|
1226
1515
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1516
|
+
return {
|
|
1517
|
+
kind: "prepared",
|
|
1518
|
+
operation: {
|
|
1519
|
+
start: async (startInput) => {
|
|
1520
|
+
const manifestSelection = startInput.manifest.dispatchSelection;
|
|
1521
|
+
if (!sameDispatchSelection(manifestSelection, pinnedSelection)) {
|
|
1522
|
+
throw new RuntimeExecutionFailure({
|
|
1523
|
+
phase: "start",
|
|
1524
|
+
category: "authority",
|
|
1525
|
+
retry: "non-retryable",
|
|
1526
|
+
reason: "prepared pi operation received a manifest with different runtime selection"
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
if (typeof startInput.instruction !== "string") {
|
|
1530
|
+
throw new RuntimeExecutionFailure({
|
|
1531
|
+
phase: "start",
|
|
1532
|
+
category: "authority",
|
|
1533
|
+
retry: "non-retryable",
|
|
1534
|
+
reason: "prepared pi operation requires a resolved string instruction"
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
const resumeSessionId = startInput.manifest.sessionRef;
|
|
1538
|
+
const piArgs = ["--mode", "rpc", ...resumeSessionId ? ["--session", resumeSessionId] : [], ...mapping.args];
|
|
1539
|
+
const args = launcherArgs === void 0 ? piArgs : [...launcherArgs, "--", ...piArgs];
|
|
1540
|
+
let rpc;
|
|
1541
|
+
try {
|
|
1542
|
+
rpc = new PiRpcClient({
|
|
1543
|
+
command,
|
|
1544
|
+
args,
|
|
1545
|
+
cwd: startInput.manifest.workspace.workspaceDir,
|
|
1546
|
+
env: manifestSelection === void 0 ? startInput.env : withoutProviderCredentials(startInput.env),
|
|
1547
|
+
spawnFn: this.options.spawnFn
|
|
1548
|
+
});
|
|
1549
|
+
} catch (cause) {
|
|
1550
|
+
throw new RuntimeExecutionFailure({
|
|
1551
|
+
phase: "start",
|
|
1552
|
+
category: "infrastructure",
|
|
1553
|
+
retry: "retryable",
|
|
1554
|
+
reason: "pi runtime process could not be spawned"
|
|
1555
|
+
}, { cause });
|
|
1556
|
+
}
|
|
1557
|
+
let response;
|
|
1558
|
+
try {
|
|
1559
|
+
response = await rpc.send({ type: "prompt", message: startInput.instruction });
|
|
1560
|
+
} catch (cause) {
|
|
1561
|
+
rpc.kill();
|
|
1562
|
+
throw new RuntimeExecutionFailure({
|
|
1563
|
+
phase: "start",
|
|
1564
|
+
category: "infrastructure",
|
|
1565
|
+
retry: "retryable",
|
|
1566
|
+
reason: `pi initial prompt transport failed: ${errorMessage(cause)}`
|
|
1567
|
+
}, { cause });
|
|
1568
|
+
}
|
|
1569
|
+
if (response.success === false) {
|
|
1570
|
+
rpc.kill();
|
|
1571
|
+
throw new RuntimeExecutionFailure({
|
|
1572
|
+
phase: "start",
|
|
1573
|
+
category: "semantic",
|
|
1574
|
+
retry: "non-retryable",
|
|
1575
|
+
reason: typeof response.error === "string" ? response.error : "pi rejected the initial prompt"
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
let sessionRef;
|
|
1579
|
+
try {
|
|
1580
|
+
sessionRef = await resolveAuthoritativeSessionId(rpc);
|
|
1581
|
+
} catch (err) {
|
|
1582
|
+
rpc.kill();
|
|
1583
|
+
throw err;
|
|
1584
|
+
}
|
|
1585
|
+
if (resumeSessionId !== void 0 && sessionRef !== resumeSessionId) {
|
|
1586
|
+
rpc.kill();
|
|
1587
|
+
throw new RuntimeExecutionFailure({
|
|
1588
|
+
phase: "start",
|
|
1589
|
+
category: "authority",
|
|
1590
|
+
retry: "non-retryable",
|
|
1591
|
+
reason: "pi resumed a different authoritative session than requested"
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
return new PiSession(sessionRef, rpc, manifestSelection);
|
|
1595
|
+
}
|
|
1248
1596
|
}
|
|
1249
|
-
}
|
|
1250
|
-
return new PiSession(sessionRef, rpc, selection);
|
|
1597
|
+
};
|
|
1251
1598
|
}
|
|
1252
1599
|
resolveBin() {
|
|
1253
1600
|
return (this.options.resolveBin ?? resolvePiBin)();
|
|
1254
1601
|
}
|
|
1255
1602
|
};
|
|
1256
|
-
|
|
1603
|
+
function sameDispatchSelection(left, right) {
|
|
1604
|
+
if (left === void 0 || right === void 0) return left === right;
|
|
1605
|
+
return left.lane === right.lane && left.runtimeId === right.runtimeId && left.providerId === right.providerId && left.modelId === right.modelId;
|
|
1606
|
+
}
|
|
1607
|
+
async function resolveAuthoritativeSessionId(rpc) {
|
|
1257
1608
|
let state;
|
|
1258
1609
|
try {
|
|
1259
1610
|
state = await rpc.send({ type: "get_state" });
|
|
1260
1611
|
} catch (err) {
|
|
1261
|
-
|
|
1612
|
+
if (isRuntimeExecutionFailure(err)) throw err;
|
|
1613
|
+
throw new RuntimeExecutionFailure({
|
|
1614
|
+
phase: "start",
|
|
1615
|
+
category: "infrastructure",
|
|
1616
|
+
retry: "retryable",
|
|
1617
|
+
reason: `pi transport ended before yielding an authoritative session id: ${errorMessage(err)}`
|
|
1618
|
+
}, {
|
|
1262
1619
|
cause: err
|
|
1263
1620
|
});
|
|
1264
1621
|
}
|
|
1265
1622
|
if (state.success === false) {
|
|
1266
1623
|
const reason = typeof state.error === "string" ? state.error : "get_state reported failure";
|
|
1267
|
-
throw new
|
|
1624
|
+
throw new RuntimeExecutionFailure({
|
|
1625
|
+
phase: "start",
|
|
1626
|
+
category: "authority",
|
|
1627
|
+
retry: "non-retryable",
|
|
1628
|
+
reason: `pi did not yield an authoritative session id: ${reason}`
|
|
1629
|
+
});
|
|
1268
1630
|
}
|
|
1269
1631
|
const data = state.data;
|
|
1270
1632
|
if (typeof data?.sessionId === "string" && data.sessionId.length > 0) {
|
|
1271
1633
|
return data.sessionId;
|
|
1272
1634
|
}
|
|
1273
|
-
throw new
|
|
1274
|
-
|
|
1275
|
-
|
|
1635
|
+
throw new RuntimeExecutionFailure({
|
|
1636
|
+
phase: "start",
|
|
1637
|
+
category: "authority",
|
|
1638
|
+
retry: "non-retryable",
|
|
1639
|
+
reason: "pi get_state reported no authoritative session id"
|
|
1640
|
+
});
|
|
1276
1641
|
}
|
|
1277
1642
|
var PiSession = class {
|
|
1278
1643
|
constructor(sessionRef, rpc, selection) {
|
|
@@ -1288,12 +1653,40 @@ var PiSession = class {
|
|
|
1288
1653
|
return {
|
|
1289
1654
|
[Symbol.asyncIterator]() {
|
|
1290
1655
|
const inner = rpc.events[Symbol.asyncIterator]();
|
|
1656
|
+
let terminalFailure;
|
|
1291
1657
|
return {
|
|
1292
1658
|
async next() {
|
|
1293
1659
|
for (; ; ) {
|
|
1294
|
-
|
|
1295
|
-
|
|
1660
|
+
if (terminalFailure) throw terminalFailure;
|
|
1661
|
+
let result;
|
|
1662
|
+
try {
|
|
1663
|
+
result = await inner.next();
|
|
1664
|
+
} catch (cause) {
|
|
1665
|
+
throw new RuntimeExecutionFailure({
|
|
1666
|
+
phase: "run",
|
|
1667
|
+
category: "infrastructure",
|
|
1668
|
+
retry: "retryable",
|
|
1669
|
+
reason: "pi runtime event transport failed"
|
|
1670
|
+
}, { cause });
|
|
1671
|
+
}
|
|
1672
|
+
const { value, done } = result;
|
|
1673
|
+
if (done) {
|
|
1674
|
+
throw new RuntimeExecutionFailure({
|
|
1675
|
+
phase: "run",
|
|
1676
|
+
category: "infrastructure",
|
|
1677
|
+
retry: "retryable",
|
|
1678
|
+
reason: "pi runtime process ended before agent_settled"
|
|
1679
|
+
}, { cause: rpc.terminalError });
|
|
1680
|
+
}
|
|
1296
1681
|
const mapped = mapPiMessageToAgentEvent(value);
|
|
1682
|
+
if (value.type === "auto_retry_end" && value.success === false) {
|
|
1683
|
+
terminalFailure = new RuntimeExecutionFailure({
|
|
1684
|
+
phase: "run",
|
|
1685
|
+
category: "semantic",
|
|
1686
|
+
retry: "non-retryable",
|
|
1687
|
+
reason: "pi exhausted its native retry policy"
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1297
1690
|
if (mapped) return { value: mapped, done: false };
|
|
1298
1691
|
if (!ROUTINE_PI_EVENT_TYPES.has(value.type)) {
|
|
1299
1692
|
rpc.recordUnmappedFrame(value.type);
|
|
@@ -1323,7 +1716,7 @@ var PiSession = class {
|
|
|
1323
1716
|
await this.rpc.send({ type: "abort" });
|
|
1324
1717
|
}
|
|
1325
1718
|
async close() {
|
|
1326
|
-
this.rpc.
|
|
1719
|
+
await this.rpc.dispose();
|
|
1327
1720
|
}
|
|
1328
1721
|
async resolveApproval() {
|
|
1329
1722
|
throw new Error("pi adapter does not support approval resume: pi never emits needs_approval in M0/M1");
|
|
@@ -1535,7 +1928,15 @@ function mapResult(msg) {
|
|
|
1535
1928
|
diagnostic ? `claude result frame had a missing/invalid is_error flag (got ${JSON.stringify(msg.is_error)}) \u2014 treating as failure, fail-closed; diagnostic content on the frame: ${truncateResultDiagnostic(diagnostic)}` : `claude result frame had a missing/invalid is_error flag (got ${JSON.stringify(msg.is_error)}) \u2014 treating as failure, fail-closed`
|
|
1536
1929
|
);
|
|
1537
1930
|
const events = usageEvent ? [usageEvent, { type: "error", message }] : [{ type: "error", message }];
|
|
1538
|
-
return {
|
|
1931
|
+
return {
|
|
1932
|
+
events,
|
|
1933
|
+
terminalFailure: new RuntimeExecutionFailure({
|
|
1934
|
+
phase: "run",
|
|
1935
|
+
category: msg.is_error === true ? "semantic" : "authority",
|
|
1936
|
+
retry: "non-retryable",
|
|
1937
|
+
reason: msg.is_error === true ? "claude reported terminal task failure" : "claude emitted a malformed terminal result frame"
|
|
1938
|
+
})
|
|
1939
|
+
};
|
|
1539
1940
|
}
|
|
1540
1941
|
var RESULT_DIAGNOSTIC_MAX_CHARS = 2e3;
|
|
1541
1942
|
function truncateResultDiagnostic(text) {
|
|
@@ -1587,16 +1988,22 @@ var ClaudeProcessClient = class {
|
|
|
1587
1988
|
eventQueue = new AsyncQueue();
|
|
1588
1989
|
closed = false;
|
|
1589
1990
|
exitError;
|
|
1991
|
+
closedPromise;
|
|
1992
|
+
resolveClosed;
|
|
1993
|
+
disposalAttempt;
|
|
1590
1994
|
stderrRing = [];
|
|
1591
1995
|
unmappedFrameCounts = /* @__PURE__ */ new Map();
|
|
1592
1996
|
sessionId;
|
|
1593
1997
|
initWaiter;
|
|
1594
1998
|
constructor(options) {
|
|
1595
1999
|
const spawnFn = options.spawnFn ?? spawn;
|
|
1596
|
-
this.child = spawnFn(options.command, options.args, {
|
|
2000
|
+
this.child = spawnFn(options.command, options.args, withOwnedProcessTree({
|
|
1597
2001
|
cwd: options.cwd,
|
|
1598
2002
|
env: options.env,
|
|
1599
2003
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2004
|
+
}));
|
|
2005
|
+
this.closedPromise = new Promise((resolve) => {
|
|
2006
|
+
this.resolveClosed = resolve;
|
|
1600
2007
|
});
|
|
1601
2008
|
this.child.stdout.setEncoding("utf8");
|
|
1602
2009
|
this.child.stdout.on("data", (chunk) => this.onData(chunk));
|
|
@@ -1652,6 +2059,10 @@ var ClaudeProcessClient = class {
|
|
|
1652
2059
|
get events() {
|
|
1653
2060
|
return this.eventQueue;
|
|
1654
2061
|
}
|
|
2062
|
+
/** Local transport diagnostic retained when the process closes; consumers classify it at the session boundary. */
|
|
2063
|
+
get terminalError() {
|
|
2064
|
+
return this.exitError;
|
|
2065
|
+
}
|
|
1655
2066
|
/**
|
|
1656
2067
|
* Record a claude stream-json frame/subtype/content-block label that
|
|
1657
2068
|
* `ClaudeSession`'s event iterator (`../claude-adapter.ts`) decided has
|
|
@@ -1671,15 +2082,30 @@ var ClaudeProcessClient = class {
|
|
|
1671
2082
|
);
|
|
1672
2083
|
}
|
|
1673
2084
|
}
|
|
1674
|
-
/**
|
|
2085
|
+
/** Immediate process-tree termination request. `dispose()` is the settlement receipt. */
|
|
1675
2086
|
kill() {
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
2087
|
+
requestOwnedProcessTreeTermination(this.processTreeOptions());
|
|
2088
|
+
}
|
|
2089
|
+
waitClosed() {
|
|
2090
|
+
return this.closedPromise;
|
|
2091
|
+
}
|
|
2092
|
+
dispose() {
|
|
2093
|
+
if (!this.disposalAttempt) {
|
|
2094
|
+
const attempt = disposeOwnedProcessTree(this.processTreeOptions());
|
|
2095
|
+
this.disposalAttempt = attempt.catch((error) => {
|
|
2096
|
+
this.disposalAttempt = void 0;
|
|
2097
|
+
throw error;
|
|
2098
|
+
});
|
|
1682
2099
|
}
|
|
2100
|
+
return this.disposalAttempt;
|
|
2101
|
+
}
|
|
2102
|
+
processTreeOptions() {
|
|
2103
|
+
return {
|
|
2104
|
+
child: this.child,
|
|
2105
|
+
waitClosed: () => this.closedPromise,
|
|
2106
|
+
isClosed: () => this.closed,
|
|
2107
|
+
label: "claude"
|
|
2108
|
+
};
|
|
1683
2109
|
}
|
|
1684
2110
|
onData(chunk) {
|
|
1685
2111
|
this.buffer += chunk;
|
|
@@ -1730,6 +2156,7 @@ var ClaudeProcessClient = class {
|
|
|
1730
2156
|
if (this.closed) return;
|
|
1731
2157
|
this.closed = true;
|
|
1732
2158
|
this.exitError = err;
|
|
2159
|
+
this.resolveClosed();
|
|
1733
2160
|
this.initWaiter?.reject(err);
|
|
1734
2161
|
this.initWaiter = void 0;
|
|
1735
2162
|
this.eventQueue.end();
|
|
@@ -1741,18 +2168,37 @@ var APPROVAL_TOOL_NAME = "approval_prompt";
|
|
|
1741
2168
|
var APPROVAL_MCP_SERVER_NAME = "byokapproval";
|
|
1742
2169
|
var execFileAsync2 = promisify(execFile);
|
|
1743
2170
|
var DETECT_TIMEOUT_MS2 = 5e3;
|
|
2171
|
+
function errorMessage2(err) {
|
|
2172
|
+
return err instanceof Error ? err.message : String(err);
|
|
2173
|
+
}
|
|
1744
2174
|
async function cleanupMcpConfigDir(dir) {
|
|
1745
2175
|
if (!dir) return;
|
|
1746
|
-
|
|
1747
|
-
|
|
2176
|
+
try {
|
|
2177
|
+
await promises.rm(dir, { recursive: true, force: true });
|
|
2178
|
+
} catch (cause) {
|
|
2179
|
+
throw new RuntimeDisposalFailure({
|
|
2180
|
+
stage: "cleanup",
|
|
2181
|
+
reason: "claude task-scoped MCP configuration could not be removed"
|
|
2182
|
+
}, { cause });
|
|
2183
|
+
}
|
|
1748
2184
|
}
|
|
1749
2185
|
var ClaudeAdapter = class {
|
|
1750
2186
|
constructor(options = {}) {
|
|
1751
2187
|
this.options = options;
|
|
1752
2188
|
}
|
|
1753
2189
|
options;
|
|
1754
|
-
|
|
1755
|
-
|
|
2190
|
+
descriptor = freezeRuntimeAdapterDescriptor({
|
|
2191
|
+
id: "claude",
|
|
2192
|
+
supportsDispatchSelection: true,
|
|
2193
|
+
capabilities: {
|
|
2194
|
+
steer: false,
|
|
2195
|
+
resume: true,
|
|
2196
|
+
approvalInteractive: true,
|
|
2197
|
+
mcpToolsets: true,
|
|
2198
|
+
permissionModes: ["auto", "readonly", "plan", "confirm"]
|
|
2199
|
+
},
|
|
2200
|
+
environmentRequirements: { credentialNames: [] }
|
|
2201
|
+
});
|
|
1756
2202
|
async detect() {
|
|
1757
2203
|
const bin = this.resolveBin();
|
|
1758
2204
|
try {
|
|
@@ -1764,54 +2210,73 @@ var ClaudeAdapter = class {
|
|
|
1764
2210
|
return { present: false };
|
|
1765
2211
|
}
|
|
1766
2212
|
}
|
|
1767
|
-
|
|
2213
|
+
async prepare(input) {
|
|
2214
|
+
const mapping = mapPermissionPolicyToClaudeArgs(input.policy);
|
|
2215
|
+
if (!mapping.ok) return { kind: "reject", reason: mapping.reason ?? "policy rejected by claude adapter", retryable: false };
|
|
2216
|
+
let modelId;
|
|
2217
|
+
try {
|
|
2218
|
+
modelId = subscriptionModel(input.offer.dispatchSelection, "claude");
|
|
2219
|
+
} catch (error) {
|
|
2220
|
+
return { kind: "reject", reason: error instanceof Error ? error.message : String(error), retryable: false };
|
|
2221
|
+
}
|
|
2222
|
+
if (mapping.needsApprovalMcp && Object.prototype.hasOwnProperty.call(input.mcpServers ?? {}, APPROVAL_MCP_SERVER_NAME)) {
|
|
2223
|
+
return { kind: "reject", reason: `MCP server name "${APPROVAL_MCP_SERVER_NAME}" is reserved by the claude approval channel`, retryable: false };
|
|
2224
|
+
}
|
|
2225
|
+
let bin;
|
|
2226
|
+
try {
|
|
2227
|
+
bin = this.resolveBin();
|
|
2228
|
+
} catch (error) {
|
|
2229
|
+
return { kind: "reject", reason: error instanceof Error ? error.message : String(error), retryable: true };
|
|
2230
|
+
}
|
|
2231
|
+
let approvalMcpBin;
|
|
2232
|
+
if (mapping.needsApprovalMcp) {
|
|
2233
|
+
try {
|
|
2234
|
+
approvalMcpBin = (this.options.resolveApprovalMcpBin ?? resolveApprovalMcpBin)();
|
|
2235
|
+
} catch (error) {
|
|
2236
|
+
return { kind: "reject", reason: error instanceof Error ? error.message : String(error), retryable: true };
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
1768
2239
|
return {
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
permissionModes: ["auto", "readonly", "plan", "confirm"]
|
|
2240
|
+
kind: "prepared",
|
|
2241
|
+
operation: {
|
|
2242
|
+
start: (startInput) => this.startPrepared(startInput, mapping, modelId, bin, approvalMcpBin)
|
|
2243
|
+
}
|
|
1774
2244
|
};
|
|
1775
2245
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
return { credentialNames: [] };
|
|
1791
|
-
}
|
|
1792
|
-
async start(task, ctx) {
|
|
1793
|
-
if (typeof task.instruction !== "string") {
|
|
1794
|
-
throw new PolicyUnsupportedError("claude adapter only supports string instructions in M2 (no blob-ref fetch yet)");
|
|
1795
|
-
}
|
|
1796
|
-
const mapping = mapPermissionPolicyToClaudeArgs(ctx.policy);
|
|
1797
|
-
if (!mapping.ok) {
|
|
1798
|
-
throw new PolicyUnsupportedError(mapping.reason ?? "policy rejected by claude adapter");
|
|
2246
|
+
async startPrepared(startInput, initialMapping, modelId, bin, approvalMcpBin) {
|
|
2247
|
+
if (!initialMapping.ok) throw new RuntimeExecutionFailure({
|
|
2248
|
+
phase: "start",
|
|
2249
|
+
category: "authority",
|
|
2250
|
+
retry: "non-retryable",
|
|
2251
|
+
reason: "prepared claude permission mapping was invalid"
|
|
2252
|
+
});
|
|
2253
|
+
if (typeof startInput.instruction !== "string") {
|
|
2254
|
+
throw new RuntimeExecutionFailure({
|
|
2255
|
+
phase: "start",
|
|
2256
|
+
category: "authority",
|
|
2257
|
+
retry: "non-retryable",
|
|
2258
|
+
reason: "prepared claude operation requires a resolved string instruction"
|
|
2259
|
+
});
|
|
1799
2260
|
}
|
|
1800
|
-
const
|
|
2261
|
+
const mapping = { ...initialMapping, args: [...initialMapping.args] };
|
|
1801
2262
|
let mcpConfigDir;
|
|
1802
|
-
const taskMcpServers =
|
|
2263
|
+
const taskMcpServers = startInput.mcpServers ?? {};
|
|
1803
2264
|
const needsMcpConfig = mapping.needsApprovalMcp || Object.keys(taskMcpServers).length > 0;
|
|
1804
2265
|
if (mapping.needsApprovalMcp) {
|
|
1805
|
-
if (!
|
|
1806
|
-
throw new
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
`MCP server name "${APPROVAL_MCP_SERVER_NAME}" is reserved by the claude approval channel`
|
|
1813
|
-
);
|
|
2266
|
+
if (!startInput.approvalChannel) {
|
|
2267
|
+
throw new RuntimeExecutionFailure({
|
|
2268
|
+
phase: "start",
|
|
2269
|
+
category: "authority",
|
|
2270
|
+
retry: "non-retryable",
|
|
2271
|
+
reason: 'claude adapter requires policy.mode "confirm" to be started with an approval channel'
|
|
2272
|
+
});
|
|
1814
2273
|
}
|
|
2274
|
+
if (!approvalMcpBin) throw new RuntimeExecutionFailure({
|
|
2275
|
+
phase: "start",
|
|
2276
|
+
category: "authority",
|
|
2277
|
+
retry: "non-retryable",
|
|
2278
|
+
reason: "prepared claude approval MCP binary was not resolved"
|
|
2279
|
+
});
|
|
1815
2280
|
}
|
|
1816
2281
|
if (needsMcpConfig) {
|
|
1817
2282
|
mcpConfigDir = await promises.mkdtemp(path17.join(os5.tmpdir(), "byok-mcp-"));
|
|
@@ -1820,12 +2285,23 @@ var ClaudeAdapter = class {
|
|
|
1820
2285
|
const mcpConfigPath = path17.join(mcpConfigDir, "mcp-config.json");
|
|
1821
2286
|
const mcpServers = { ...taskMcpServers };
|
|
1822
2287
|
if (mapping.needsApprovalMcp) {
|
|
1823
|
-
const approvalChannel =
|
|
1824
|
-
if (!approvalChannel) throw new
|
|
1825
|
-
|
|
2288
|
+
const approvalChannel = startInput.approvalChannel;
|
|
2289
|
+
if (!approvalChannel) throw new RuntimeExecutionFailure({
|
|
2290
|
+
phase: "start",
|
|
2291
|
+
category: "authority",
|
|
2292
|
+
retry: "non-retryable",
|
|
2293
|
+
reason: "prepared claude approval channel was not available"
|
|
2294
|
+
});
|
|
2295
|
+
const preparedApprovalMcpBin = approvalMcpBin;
|
|
2296
|
+
if (!preparedApprovalMcpBin) throw new RuntimeExecutionFailure({
|
|
2297
|
+
phase: "start",
|
|
2298
|
+
category: "authority",
|
|
2299
|
+
retry: "non-retryable",
|
|
2300
|
+
reason: "prepared claude approval MCP binary was not resolved"
|
|
2301
|
+
});
|
|
1826
2302
|
mcpServers[APPROVAL_MCP_SERVER_NAME] = {
|
|
1827
|
-
command:
|
|
1828
|
-
args:
|
|
2303
|
+
command: preparedApprovalMcpBin.command,
|
|
2304
|
+
args: preparedApprovalMcpBin.args,
|
|
1829
2305
|
env: {
|
|
1830
2306
|
BYOK_STORE_DIR: approvalChannel.storeDir,
|
|
1831
2307
|
BYOK_PRODUCT_ID: approvalChannel.productId,
|
|
@@ -1845,8 +2321,26 @@ var ClaudeAdapter = class {
|
|
|
1845
2321
|
"--strict-mcp-config"
|
|
1846
2322
|
];
|
|
1847
2323
|
}
|
|
1848
|
-
const
|
|
1849
|
-
|
|
2324
|
+
const resumeSessionId = startInput.manifest.sessionRef;
|
|
2325
|
+
let manifestModelId;
|
|
2326
|
+
try {
|
|
2327
|
+
manifestModelId = subscriptionModel(startInput.manifest.dispatchSelection, "claude");
|
|
2328
|
+
} catch (cause) {
|
|
2329
|
+
throw new RuntimeExecutionFailure({
|
|
2330
|
+
phase: "start",
|
|
2331
|
+
category: "authority",
|
|
2332
|
+
retry: "non-retryable",
|
|
2333
|
+
reason: "prepared claude operation received an invalid runtime selection manifest"
|
|
2334
|
+
}, { cause });
|
|
2335
|
+
}
|
|
2336
|
+
if (manifestModelId !== modelId) {
|
|
2337
|
+
throw new RuntimeExecutionFailure({
|
|
2338
|
+
phase: "start",
|
|
2339
|
+
category: "authority",
|
|
2340
|
+
retry: "non-retryable",
|
|
2341
|
+
reason: "prepared claude operation received a manifest with different runtime selection"
|
|
2342
|
+
});
|
|
2343
|
+
}
|
|
1850
2344
|
const args = [
|
|
1851
2345
|
"-p",
|
|
1852
2346
|
"--input-format",
|
|
@@ -1858,40 +2352,71 @@ var ClaudeAdapter = class {
|
|
|
1858
2352
|
// "Error: When using --print, --output-format=stream-json requires
|
|
1859
2353
|
// --verbose", before spawning any model call.
|
|
1860
2354
|
"--verbose",
|
|
1861
|
-
...
|
|
2355
|
+
...manifestModelId ? ["--model", manifestModelId] : [],
|
|
1862
2356
|
...resumeSessionId ? ["--resume", resumeSessionId] : [],
|
|
1863
2357
|
...mapping.args
|
|
1864
2358
|
];
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
2359
|
+
let client;
|
|
2360
|
+
try {
|
|
2361
|
+
client = new ClaudeProcessClient({
|
|
2362
|
+
command: bin.command,
|
|
2363
|
+
args,
|
|
2364
|
+
cwd: startInput.manifest.workspace.workspaceDir,
|
|
2365
|
+
env: withoutProviderCredentials(startInput.env),
|
|
2366
|
+
spawnFn: this.options.spawnFn
|
|
2367
|
+
});
|
|
2368
|
+
} catch (cause) {
|
|
2369
|
+
await cleanupMcpConfigDir(mcpConfigDir);
|
|
2370
|
+
throw new RuntimeExecutionFailure({
|
|
2371
|
+
phase: "start",
|
|
2372
|
+
category: "infrastructure",
|
|
2373
|
+
retry: "retryable",
|
|
2374
|
+
reason: "claude runtime process could not be spawned"
|
|
2375
|
+
}, { cause });
|
|
2376
|
+
}
|
|
2377
|
+
try {
|
|
2378
|
+
client.writeUserMessage(startInput.instruction);
|
|
2379
|
+
} catch (cause) {
|
|
2380
|
+
client.kill();
|
|
2381
|
+
await cleanupMcpConfigDir(mcpConfigDir);
|
|
2382
|
+
throw new RuntimeExecutionFailure({
|
|
2383
|
+
phase: "start",
|
|
2384
|
+
category: "infrastructure",
|
|
2385
|
+
retry: "retryable",
|
|
2386
|
+
reason: "claude initial instruction transport failed"
|
|
2387
|
+
}, { cause });
|
|
2388
|
+
}
|
|
1873
2389
|
let sessionRef;
|
|
1874
2390
|
try {
|
|
1875
2391
|
sessionRef = await client.waitForInit();
|
|
1876
2392
|
} catch (err) {
|
|
1877
2393
|
client.kill();
|
|
1878
2394
|
await cleanupMcpConfigDir(mcpConfigDir);
|
|
1879
|
-
throw err;
|
|
2395
|
+
if (isRuntimeExecutionFailure(err)) throw err;
|
|
2396
|
+
throw new RuntimeExecutionFailure({
|
|
2397
|
+
phase: "start",
|
|
2398
|
+
category: "infrastructure",
|
|
2399
|
+
retry: "retryable",
|
|
2400
|
+
reason: `claude exited before yielding an authoritative session id: ${errorMessage2(err)}`
|
|
2401
|
+
}, { cause: err });
|
|
1880
2402
|
}
|
|
1881
2403
|
if (resumeSessionId !== void 0 && sessionRef !== resumeSessionId) {
|
|
1882
2404
|
client.kill();
|
|
1883
2405
|
await cleanupMcpConfigDir(mcpConfigDir);
|
|
1884
|
-
throw new
|
|
1885
|
-
|
|
1886
|
-
|
|
2406
|
+
throw new RuntimeExecutionFailure({
|
|
2407
|
+
phase: "start",
|
|
2408
|
+
category: "authority",
|
|
2409
|
+
retry: "non-retryable",
|
|
2410
|
+
reason: `claude --resume echoed a different session id than requested (requested ${resumeSessionId}, got ${sessionRef})`
|
|
2411
|
+
});
|
|
1887
2412
|
}
|
|
1888
2413
|
return new ClaudeSession(
|
|
1889
2414
|
sessionRef,
|
|
1890
2415
|
client,
|
|
1891
|
-
|
|
1892
|
-
|
|
2416
|
+
startInput.manifest.workspace.workspaceDir,
|
|
2417
|
+
startInput.approvalChannel,
|
|
1893
2418
|
mcpConfigDir,
|
|
1894
|
-
|
|
2419
|
+
manifestModelId
|
|
1895
2420
|
);
|
|
1896
2421
|
}
|
|
1897
2422
|
/**
|
|
@@ -1926,8 +2451,7 @@ var ClaudeAdapter = class {
|
|
|
1926
2451
|
return (this.options.resolveBin ?? resolveClaudeBin)();
|
|
1927
2452
|
}
|
|
1928
2453
|
};
|
|
1929
|
-
function subscriptionModel(
|
|
1930
|
-
const selection = task.dispatchSelection;
|
|
2454
|
+
function subscriptionModel(selection, runtimeId) {
|
|
1931
2455
|
if (selection === void 0) return void 0;
|
|
1932
2456
|
if (selection.lane !== "subscription" || selection.runtimeId !== runtimeId) {
|
|
1933
2457
|
throw new PolicyUnsupportedError(
|
|
@@ -1952,6 +2476,7 @@ var ClaudeSession = class {
|
|
|
1952
2476
|
mcpConfigDir;
|
|
1953
2477
|
modelId;
|
|
1954
2478
|
correlation = createToolUseCorrelation();
|
|
2479
|
+
closeAttempt;
|
|
1955
2480
|
get events() {
|
|
1956
2481
|
const client = this.client;
|
|
1957
2482
|
const correlation = this.correlation;
|
|
@@ -1960,17 +2485,40 @@ var ClaudeSession = class {
|
|
|
1960
2485
|
[Symbol.asyncIterator]() {
|
|
1961
2486
|
const inner = client.events[Symbol.asyncIterator]();
|
|
1962
2487
|
let pending = [];
|
|
2488
|
+
let terminalFailure;
|
|
1963
2489
|
let turnSettled = false;
|
|
1964
2490
|
return {
|
|
1965
2491
|
async next() {
|
|
1966
2492
|
for (; ; ) {
|
|
1967
2493
|
const buffered = pending.shift();
|
|
1968
2494
|
if (buffered) return { value: buffered, done: false };
|
|
1969
|
-
if (turnSettled)
|
|
1970
|
-
|
|
1971
|
-
|
|
2495
|
+
if (turnSettled) {
|
|
2496
|
+
if (terminalFailure) throw terminalFailure;
|
|
2497
|
+
return { value: void 0, done: true };
|
|
2498
|
+
}
|
|
2499
|
+
let raw;
|
|
2500
|
+
try {
|
|
2501
|
+
raw = await inner.next();
|
|
2502
|
+
} catch (cause) {
|
|
2503
|
+
throw new RuntimeExecutionFailure({
|
|
2504
|
+
phase: "run",
|
|
2505
|
+
category: "infrastructure",
|
|
2506
|
+
retry: "retryable",
|
|
2507
|
+
reason: "claude runtime event transport failed"
|
|
2508
|
+
}, { cause });
|
|
2509
|
+
}
|
|
2510
|
+
const { value, done } = raw;
|
|
2511
|
+
if (done) {
|
|
2512
|
+
throw new RuntimeExecutionFailure({
|
|
2513
|
+
phase: "run",
|
|
2514
|
+
category: "infrastructure",
|
|
2515
|
+
retry: "retryable",
|
|
2516
|
+
reason: "claude runtime process ended before a terminal result frame"
|
|
2517
|
+
}, { cause: client.terminalError });
|
|
2518
|
+
}
|
|
1972
2519
|
if (value.type === "result") turnSettled = true;
|
|
1973
2520
|
const mapped = mapClaudeMessageToAgentEvents(value, correlation, { workspaceDir });
|
|
2521
|
+
terminalFailure = mapped.terminalFailure ?? terminalFailure;
|
|
1974
2522
|
if (mapped.unmappedLabel) {
|
|
1975
2523
|
client.recordUnmappedFrame(mapped.unmappedLabel);
|
|
1976
2524
|
}
|
|
@@ -2001,7 +2549,7 @@ var ClaudeSession = class {
|
|
|
2001
2549
|
if (typeof task.instruction !== "string") {
|
|
2002
2550
|
throw new PolicyUnsupportedError("claude adapter only supports string instructions in M2 (no blob-ref fetch yet)");
|
|
2003
2551
|
}
|
|
2004
|
-
const requestedModel = subscriptionModel(task, "claude");
|
|
2552
|
+
const requestedModel = subscriptionModel(task.dispatchSelection, "claude");
|
|
2005
2553
|
if (requestedModel !== void 0 && requestedModel !== this.modelId) {
|
|
2006
2554
|
throw new PolicyUnsupportedError(
|
|
2007
2555
|
`claude persistent session cannot change model from ${this.modelId ?? "(legacy default)"} to ${requestedModel}`
|
|
@@ -2025,8 +2573,17 @@ var ClaudeSession = class {
|
|
|
2025
2573
|
this.client.kill();
|
|
2026
2574
|
}
|
|
2027
2575
|
async close() {
|
|
2028
|
-
this.
|
|
2029
|
-
|
|
2576
|
+
if (!this.closeAttempt) {
|
|
2577
|
+
const attempt = (async () => {
|
|
2578
|
+
await this.client.dispose();
|
|
2579
|
+
await cleanupMcpConfigDir(this.mcpConfigDir);
|
|
2580
|
+
})();
|
|
2581
|
+
this.closeAttempt = attempt.catch((error) => {
|
|
2582
|
+
this.closeAttempt = void 0;
|
|
2583
|
+
throw error;
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
await this.closeAttempt;
|
|
2030
2587
|
}
|
|
2031
2588
|
/**
|
|
2032
2589
|
* M4 Phase 3: routes into the out-of-band approval channel `start()`
|
|
@@ -2243,14 +2800,15 @@ var CodexProcessRunner = class {
|
|
|
2243
2800
|
exitSignal = null;
|
|
2244
2801
|
closedPromise;
|
|
2245
2802
|
resolveClosed;
|
|
2803
|
+
disposalAttempt;
|
|
2246
2804
|
constructor(options) {
|
|
2247
2805
|
this.onEvent = options.onEvent;
|
|
2248
2806
|
const spawnFn = options.spawnFn ?? spawn;
|
|
2249
|
-
this.child = spawnFn(options.command, options.args, {
|
|
2807
|
+
this.child = spawnFn(options.command, options.args, withOwnedProcessTree({
|
|
2250
2808
|
cwd: options.cwd,
|
|
2251
2809
|
env: options.env,
|
|
2252
2810
|
stdio: ["ignore", "pipe", "pipe"]
|
|
2253
|
-
});
|
|
2811
|
+
}));
|
|
2254
2812
|
this.closedPromise = new Promise((resolve) => {
|
|
2255
2813
|
this.resolveClosed = resolve;
|
|
2256
2814
|
});
|
|
@@ -2280,7 +2838,7 @@ var CodexProcessRunner = class {
|
|
|
2280
2838
|
return this.closed;
|
|
2281
2839
|
}
|
|
2282
2840
|
/**
|
|
2283
|
-
*
|
|
2841
|
+
* Immediate tree termination request. SIGTERM on POSIX: SIGINT was empirically confirmed
|
|
2284
2842
|
* to be silently ignored by `codex exec` (a real, direct test — a 60s
|
|
2285
2843
|
* shell `sleep` ran to full, unaffected completion despite SIGINT sent at
|
|
2286
2844
|
* t=4s) — a genuine, evidence-based correction to this task's own initial
|
|
@@ -2293,13 +2851,25 @@ var CodexProcessRunner = class {
|
|
|
2293
2851
|
* `../pi/rpc-client.ts`'s own cross-platform convention.
|
|
2294
2852
|
*/
|
|
2295
2853
|
kill() {
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
this.
|
|
2854
|
+
requestOwnedProcessTreeTermination(this.processTreeOptions());
|
|
2855
|
+
}
|
|
2856
|
+
dispose() {
|
|
2857
|
+
if (!this.disposalAttempt) {
|
|
2858
|
+
const attempt = disposeOwnedProcessTree(this.processTreeOptions());
|
|
2859
|
+
this.disposalAttempt = attempt.catch((error) => {
|
|
2860
|
+
this.disposalAttempt = void 0;
|
|
2861
|
+
throw error;
|
|
2862
|
+
});
|
|
2302
2863
|
}
|
|
2864
|
+
return this.disposalAttempt;
|
|
2865
|
+
}
|
|
2866
|
+
processTreeOptions() {
|
|
2867
|
+
return {
|
|
2868
|
+
child: this.child,
|
|
2869
|
+
waitClosed: () => this.closedPromise,
|
|
2870
|
+
isClosed: () => this.closed,
|
|
2871
|
+
label: "codex"
|
|
2872
|
+
};
|
|
2303
2873
|
}
|
|
2304
2874
|
/** Builds a descriptive error folding in the exit code/signal and the stderr tail — mirrors `PiRpcClient.buildExitError`'s reasoning: a post-mortem on a failed start/resume should never need separately re-running codex by hand with a raw JSONL logger to learn why. */
|
|
2305
2875
|
buildExitError(context) {
|
|
@@ -2350,8 +2920,17 @@ var CodexAdapter = class {
|
|
|
2350
2920
|
this.options = options;
|
|
2351
2921
|
}
|
|
2352
2922
|
options;
|
|
2353
|
-
|
|
2354
|
-
|
|
2923
|
+
descriptor = freezeRuntimeAdapterDescriptor({
|
|
2924
|
+
id: "codex",
|
|
2925
|
+
supportsDispatchSelection: true,
|
|
2926
|
+
capabilities: {
|
|
2927
|
+
steer: false,
|
|
2928
|
+
resume: true,
|
|
2929
|
+
approvalInteractive: false,
|
|
2930
|
+
permissionModes: ["auto", "readonly"]
|
|
2931
|
+
},
|
|
2932
|
+
environmentRequirements: { credentialNames: [] }
|
|
2933
|
+
});
|
|
2355
2934
|
async detect() {
|
|
2356
2935
|
const bin = this.resolveBin();
|
|
2357
2936
|
try {
|
|
@@ -2400,61 +2979,100 @@ ${result.stderr}`);
|
|
|
2400
2979
|
${withStreams.stderr ?? ""}`);
|
|
2401
2980
|
}
|
|
2402
2981
|
}
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
* API-key passthrough remains a separate, pending product decision. No
|
|
2412
|
-
* `baseNames` either: nothing in this adapter reads a codex-specific
|
|
2413
|
-
* config-discovery variable (e.g. `CODEX_HOME`) today.
|
|
2414
|
-
*/
|
|
2415
|
-
environmentRequirements() {
|
|
2416
|
-
return { credentialNames: [] };
|
|
2417
|
-
}
|
|
2418
|
-
async start(task, ctx) {
|
|
2419
|
-
if (typeof task.instruction !== "string") {
|
|
2420
|
-
throw new PolicyUnsupportedError("codex adapter only supports string instructions in M2 (no blob-ref fetch yet)");
|
|
2982
|
+
async prepare(input) {
|
|
2983
|
+
const mapping = mapPermissionPolicyToCodexArgs(input.policy);
|
|
2984
|
+
if (!mapping.ok) return { kind: "reject", reason: mapping.reason ?? "policy rejected by codex adapter", retryable: false };
|
|
2985
|
+
let modelId;
|
|
2986
|
+
try {
|
|
2987
|
+
modelId = subscriptionModel2(input.offer.dispatchSelection);
|
|
2988
|
+
} catch (error) {
|
|
2989
|
+
return { kind: "reject", reason: error instanceof Error ? error.message : String(error), retryable: false };
|
|
2421
2990
|
}
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2991
|
+
let command;
|
|
2992
|
+
try {
|
|
2993
|
+
command = this.resolveBin().command;
|
|
2994
|
+
} catch (error) {
|
|
2995
|
+
return { kind: "reject", reason: error instanceof Error ? error.message : String(error), retryable: true };
|
|
2996
|
+
}
|
|
2997
|
+
return {
|
|
2998
|
+
kind: "prepared",
|
|
2999
|
+
operation: {
|
|
3000
|
+
start: (startInput) => this.startPrepared(startInput, mapping.args, modelId, command)
|
|
3001
|
+
}
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
async startPrepared(startInput, policyArgs, modelId, command) {
|
|
3005
|
+
if (typeof startInput.instruction !== "string") {
|
|
3006
|
+
throw new RuntimeExecutionFailure({
|
|
3007
|
+
phase: "start",
|
|
3008
|
+
category: "authority",
|
|
3009
|
+
retry: "non-retryable",
|
|
3010
|
+
reason: "prepared codex operation requires a resolved string instruction"
|
|
3011
|
+
});
|
|
2425
3012
|
}
|
|
2426
|
-
const modelId = subscriptionModel2(task);
|
|
2427
|
-
const bin = this.resolveBin();
|
|
2428
3013
|
const queue = new AsyncQueue();
|
|
3014
|
+
const terminal = {};
|
|
2429
3015
|
const recordUnmapped = makeUnmappedFrameRecorder(/* @__PURE__ */ new Map());
|
|
2430
|
-
|
|
2431
|
-
|
|
3016
|
+
let workspaceDir;
|
|
3017
|
+
try {
|
|
3018
|
+
workspaceDir = await resolveRealWorkspaceDir(startInput.manifest.workspace.workspaceDir);
|
|
3019
|
+
} catch (cause) {
|
|
3020
|
+
throw new RuntimeExecutionFailure({
|
|
3021
|
+
phase: "start",
|
|
3022
|
+
category: "infrastructure",
|
|
3023
|
+
retry: "retryable",
|
|
3024
|
+
reason: "codex runtime workspace could not be resolved"
|
|
3025
|
+
}, { cause });
|
|
3026
|
+
}
|
|
3027
|
+
const runtimeEnv = withoutProviderCredentials(startInput.env);
|
|
3028
|
+
let manifestModelId;
|
|
3029
|
+
try {
|
|
3030
|
+
manifestModelId = subscriptionModel2(startInput.manifest.dispatchSelection);
|
|
3031
|
+
} catch (cause) {
|
|
3032
|
+
throw new RuntimeExecutionFailure({
|
|
3033
|
+
phase: "start",
|
|
3034
|
+
category: "authority",
|
|
3035
|
+
retry: "non-retryable",
|
|
3036
|
+
reason: "prepared codex operation received an invalid runtime selection manifest"
|
|
3037
|
+
}, { cause });
|
|
3038
|
+
}
|
|
3039
|
+
if (manifestModelId !== modelId) {
|
|
3040
|
+
throw new RuntimeExecutionFailure({
|
|
3041
|
+
phase: "start",
|
|
3042
|
+
category: "authority",
|
|
3043
|
+
retry: "non-retryable",
|
|
3044
|
+
reason: "prepared codex operation received a manifest with different runtime selection"
|
|
3045
|
+
});
|
|
3046
|
+
}
|
|
2432
3047
|
const { sessionRef, runner } = await runCodexTurn({
|
|
2433
|
-
command
|
|
2434
|
-
resumeRef:
|
|
2435
|
-
instruction:
|
|
2436
|
-
modelId,
|
|
2437
|
-
policyArgs:
|
|
2438
|
-
cwd:
|
|
3048
|
+
command,
|
|
3049
|
+
resumeRef: startInput.manifest.sessionRef,
|
|
3050
|
+
instruction: startInput.instruction,
|
|
3051
|
+
modelId: manifestModelId,
|
|
3052
|
+
policyArgs: [...policyArgs],
|
|
3053
|
+
cwd: startInput.manifest.workspace.workspaceDir,
|
|
2439
3054
|
env: runtimeEnv,
|
|
2440
3055
|
spawnFn: this.options.spawnFn,
|
|
2441
3056
|
workspaceDir,
|
|
2442
3057
|
queue,
|
|
2443
3058
|
recordUnmapped,
|
|
2444
|
-
expectedSessionRef:
|
|
2445
|
-
preparedGit:
|
|
3059
|
+
expectedSessionRef: startInput.manifest.sessionRef,
|
|
3060
|
+
preparedGit: startInput.manifest.workspace.workspaceId !== void 0,
|
|
3061
|
+
failurePhase: "start",
|
|
3062
|
+
terminal
|
|
2446
3063
|
});
|
|
2447
3064
|
return new CodexSession({
|
|
2448
3065
|
sessionRef,
|
|
2449
|
-
command
|
|
3066
|
+
command,
|
|
2450
3067
|
workspaceDir,
|
|
2451
|
-
env:
|
|
3068
|
+
env: startInput.env,
|
|
2452
3069
|
spawnFn: this.options.spawnFn,
|
|
2453
3070
|
queue,
|
|
2454
3071
|
recordUnmapped,
|
|
2455
3072
|
initialRunner: runner,
|
|
2456
|
-
preparedGit:
|
|
2457
|
-
modelId
|
|
3073
|
+
preparedGit: startInput.manifest.workspace.workspaceId !== void 0,
|
|
3074
|
+
modelId: manifestModelId,
|
|
3075
|
+
terminal
|
|
2458
3076
|
});
|
|
2459
3077
|
}
|
|
2460
3078
|
resolveBin() {
|
|
@@ -2502,37 +3120,69 @@ async function runCodexTurn(params) {
|
|
|
2502
3120
|
rejectFirstLine = reject;
|
|
2503
3121
|
});
|
|
2504
3122
|
let turnEnded = false;
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
if (
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
3123
|
+
let runner;
|
|
3124
|
+
try {
|
|
3125
|
+
runner = new CodexProcessRunner({
|
|
3126
|
+
command: params.command,
|
|
3127
|
+
args: argv,
|
|
3128
|
+
cwd: params.cwd,
|
|
3129
|
+
env: params.env,
|
|
3130
|
+
spawnFn: params.spawnFn,
|
|
3131
|
+
onEvent: (evt) => {
|
|
3132
|
+
if (!firstLineSettled) {
|
|
3133
|
+
firstLineSettled = true;
|
|
3134
|
+
if (evt.type === "thread.started" && typeof evt.thread_id === "string" && evt.thread_id.length > 0) {
|
|
3135
|
+
resolveFirstLine(evt.thread_id);
|
|
3136
|
+
} else {
|
|
3137
|
+
rejectFirstLine(
|
|
3138
|
+
new RuntimeExecutionFailure({
|
|
3139
|
+
phase: params.failurePhase,
|
|
3140
|
+
category: "authority",
|
|
3141
|
+
retry: "non-retryable",
|
|
3142
|
+
reason: `codex did not yield thread.started as its first event (got ${JSON.stringify(evt).slice(0, 200)})`
|
|
3143
|
+
})
|
|
3144
|
+
);
|
|
3145
|
+
}
|
|
3146
|
+
return;
|
|
3147
|
+
}
|
|
3148
|
+
const mapped = mapCodexEventToAgentEvents(evt, params.workspaceDir);
|
|
3149
|
+
for (const agentEvent of mapped) {
|
|
3150
|
+
if (agentEvent.type === "turn_end") turnEnded = true;
|
|
3151
|
+
params.queue.push(agentEvent);
|
|
3152
|
+
}
|
|
3153
|
+
if (evt.type === "turn.failed") {
|
|
3154
|
+
params.terminal.failure = new RuntimeExecutionFailure({
|
|
3155
|
+
phase: "run",
|
|
3156
|
+
category: "semantic",
|
|
3157
|
+
retry: "non-retryable",
|
|
3158
|
+
reason: "codex reported terminal task failure"
|
|
3159
|
+
});
|
|
3160
|
+
params.queue.end();
|
|
3161
|
+
}
|
|
3162
|
+
if (mapped.length === 0 && !isRoutineCodexEvent(evt)) {
|
|
3163
|
+
params.recordUnmapped(unmappedFrameKey(evt));
|
|
2520
3164
|
}
|
|
2521
|
-
return;
|
|
2522
|
-
}
|
|
2523
|
-
const mapped = mapCodexEventToAgentEvents(evt, params.workspaceDir);
|
|
2524
|
-
for (const agentEvent of mapped) {
|
|
2525
|
-
if (agentEvent.type === "turn_end") turnEnded = true;
|
|
2526
|
-
params.queue.push(agentEvent);
|
|
2527
|
-
}
|
|
2528
|
-
if (mapped.length === 0 && !isRoutineCodexEvent(evt)) {
|
|
2529
|
-
params.recordUnmapped(unmappedFrameKey(evt));
|
|
2530
3165
|
}
|
|
2531
|
-
}
|
|
2532
|
-
})
|
|
3166
|
+
});
|
|
3167
|
+
} catch (cause) {
|
|
3168
|
+
throw new RuntimeExecutionFailure({
|
|
3169
|
+
phase: params.failurePhase,
|
|
3170
|
+
category: "infrastructure",
|
|
3171
|
+
retry: "retryable",
|
|
3172
|
+
reason: "codex runtime process could not be spawned"
|
|
3173
|
+
}, { cause });
|
|
3174
|
+
}
|
|
3175
|
+
params.onRunnerCreated?.(runner);
|
|
2533
3176
|
void runner.waitClosed().then(() => {
|
|
2534
|
-
if (turnEnded) return;
|
|
2535
|
-
|
|
3177
|
+
if (turnEnded || params.terminal.failure) return;
|
|
3178
|
+
const cause = runner.buildExitError("codex exited without completing the turn");
|
|
3179
|
+
params.terminal.failure = new RuntimeExecutionFailure({
|
|
3180
|
+
phase: "run",
|
|
3181
|
+
category: "infrastructure",
|
|
3182
|
+
retry: "retryable",
|
|
3183
|
+
reason: cause.message
|
|
3184
|
+
}, { cause });
|
|
3185
|
+
params.queue.push({ type: "error", message: cause.message });
|
|
2536
3186
|
params.queue.end();
|
|
2537
3187
|
});
|
|
2538
3188
|
let sessionRef;
|
|
@@ -2556,7 +3206,13 @@ async function runCodexTurn(params) {
|
|
|
2556
3206
|
void runner.waitClosed().then(() => {
|
|
2557
3207
|
if (!settled) {
|
|
2558
3208
|
settled = true;
|
|
2559
|
-
|
|
3209
|
+
const cause = runner.buildExitError("codex exited before yielding an authoritative thread id");
|
|
3210
|
+
reject(new RuntimeExecutionFailure({
|
|
3211
|
+
phase: params.failurePhase,
|
|
3212
|
+
category: "infrastructure",
|
|
3213
|
+
retry: "retryable",
|
|
3214
|
+
reason: cause.message
|
|
3215
|
+
}, { cause }));
|
|
2560
3216
|
}
|
|
2561
3217
|
});
|
|
2562
3218
|
});
|
|
@@ -2566,9 +3222,12 @@ async function runCodexTurn(params) {
|
|
|
2566
3222
|
}
|
|
2567
3223
|
if (params.expectedSessionRef !== void 0 && sessionRef !== params.expectedSessionRef) {
|
|
2568
3224
|
runner.kill();
|
|
2569
|
-
throw new
|
|
2570
|
-
|
|
2571
|
-
|
|
3225
|
+
throw new RuntimeExecutionFailure({
|
|
3226
|
+
phase: params.failurePhase,
|
|
3227
|
+
category: "authority",
|
|
3228
|
+
retry: "non-retryable",
|
|
3229
|
+
reason: `codex exec resume echoed a different thread id than requested (requested ${params.expectedSessionRef}, got ${sessionRef})`
|
|
3230
|
+
});
|
|
2572
3231
|
}
|
|
2573
3232
|
return { sessionRef, runner };
|
|
2574
3233
|
}
|
|
@@ -2595,8 +3254,12 @@ var CodexSession = class {
|
|
|
2595
3254
|
recordUnmapped;
|
|
2596
3255
|
preparedGit;
|
|
2597
3256
|
modelId;
|
|
3257
|
+
terminal;
|
|
2598
3258
|
currentRunner;
|
|
3259
|
+
ownedRunners = /* @__PURE__ */ new Set();
|
|
3260
|
+
followUpAttempts = /* @__PURE__ */ new Set();
|
|
2599
3261
|
closed = false;
|
|
3262
|
+
closeAttempt;
|
|
2600
3263
|
constructor(options) {
|
|
2601
3264
|
this.sessionRef = options.sessionRef;
|
|
2602
3265
|
this.command = options.command;
|
|
@@ -2607,11 +3270,36 @@ var CodexSession = class {
|
|
|
2607
3270
|
this.recordUnmapped = options.recordUnmapped;
|
|
2608
3271
|
this.preparedGit = options.preparedGit;
|
|
2609
3272
|
this.modelId = options.modelId;
|
|
3273
|
+
this.terminal = options.terminal;
|
|
2610
3274
|
this.currentRunner = options.initialRunner;
|
|
3275
|
+
this.ownedRunners.add(options.initialRunner);
|
|
2611
3276
|
void this.forgetRunnerOnceClosed(options.initialRunner);
|
|
2612
3277
|
}
|
|
2613
3278
|
get events() {
|
|
2614
|
-
|
|
3279
|
+
const queue = this.queue;
|
|
3280
|
+
const session = this;
|
|
3281
|
+
return {
|
|
3282
|
+
[Symbol.asyncIterator]() {
|
|
3283
|
+
const inner = queue[Symbol.asyncIterator]();
|
|
3284
|
+
return {
|
|
3285
|
+
async next() {
|
|
3286
|
+
let result;
|
|
3287
|
+
try {
|
|
3288
|
+
result = await inner.next();
|
|
3289
|
+
} catch (cause) {
|
|
3290
|
+
throw new RuntimeExecutionFailure({
|
|
3291
|
+
phase: "run",
|
|
3292
|
+
category: "infrastructure",
|
|
3293
|
+
retry: "retryable",
|
|
3294
|
+
reason: "codex runtime event transport failed"
|
|
3295
|
+
}, { cause });
|
|
3296
|
+
}
|
|
3297
|
+
if (result.done && session.terminal.failure) throw session.terminal.failure;
|
|
3298
|
+
return result;
|
|
3299
|
+
}
|
|
3300
|
+
};
|
|
3301
|
+
}
|
|
3302
|
+
};
|
|
2615
3303
|
}
|
|
2616
3304
|
async forgetRunnerOnceClosed(runner) {
|
|
2617
3305
|
await runner.waitClosed();
|
|
@@ -2656,7 +3344,14 @@ var CodexSession = class {
|
|
|
2656
3344
|
* stale id even after codex had moved on) — it just can now only ever be
|
|
2657
3345
|
* the SAME id this call asked to resume, never a silently-different one.
|
|
2658
3346
|
*/
|
|
2659
|
-
|
|
3347
|
+
followUp(task) {
|
|
3348
|
+
const attempt = this.runFollowUp(task);
|
|
3349
|
+
this.followUpAttempts.add(attempt);
|
|
3350
|
+
void attempt.finally(() => this.followUpAttempts.delete(attempt)).catch(() => {
|
|
3351
|
+
});
|
|
3352
|
+
return attempt;
|
|
3353
|
+
}
|
|
3354
|
+
async runFollowUp(task) {
|
|
2660
3355
|
if (typeof task.instruction !== "string") {
|
|
2661
3356
|
throw new PolicyUnsupportedError("codex adapter only supports string instructions in M2 (no blob-ref fetch yet)");
|
|
2662
3357
|
}
|
|
@@ -2667,7 +3362,7 @@ var CodexSession = class {
|
|
|
2667
3362
|
if (!mapping.ok) {
|
|
2668
3363
|
throw new PolicyUnsupportedError(mapping.reason ?? "policy rejected by codex adapter");
|
|
2669
3364
|
}
|
|
2670
|
-
const requestedModel = subscriptionModel2(task);
|
|
3365
|
+
const requestedModel = subscriptionModel2(task.dispatchSelection);
|
|
2671
3366
|
if (requestedModel !== void 0 && requestedModel !== this.modelId) {
|
|
2672
3367
|
throw new PolicyUnsupportedError(
|
|
2673
3368
|
`codex persistent session cannot change model from ${this.modelId ?? "(legacy default)"} to ${requestedModel}`
|
|
@@ -2677,6 +3372,7 @@ var CodexSession = class {
|
|
|
2677
3372
|
const resumeRef = this.sessionRef;
|
|
2678
3373
|
let sessionRef;
|
|
2679
3374
|
let runner;
|
|
3375
|
+
const terminal = {};
|
|
2680
3376
|
try {
|
|
2681
3377
|
({ sessionRef, runner } = await runCodexTurn({
|
|
2682
3378
|
command: this.command,
|
|
@@ -2691,25 +3387,54 @@ var CodexSession = class {
|
|
|
2691
3387
|
queue: this.queue,
|
|
2692
3388
|
recordUnmapped: this.recordUnmapped,
|
|
2693
3389
|
expectedSessionRef: resumeRef,
|
|
2694
|
-
preparedGit: this.preparedGit
|
|
3390
|
+
preparedGit: this.preparedGit,
|
|
3391
|
+
failurePhase: "run",
|
|
3392
|
+
terminal,
|
|
3393
|
+
onRunnerCreated: (created) => {
|
|
3394
|
+
this.ownedRunners.add(created);
|
|
3395
|
+
void this.forgetRunnerOnceClosed(created);
|
|
3396
|
+
}
|
|
2695
3397
|
}));
|
|
2696
3398
|
} catch (err) {
|
|
2697
3399
|
this.queue.end();
|
|
3400
|
+
this.terminal = {
|
|
3401
|
+
failure: isRuntimeExecutionFailure(err) ? err : new RuntimeExecutionFailure({
|
|
3402
|
+
phase: "run",
|
|
3403
|
+
category: "authority",
|
|
3404
|
+
retry: "non-retryable",
|
|
3405
|
+
reason: "codex follow-up violated the runtime adapter contract"
|
|
3406
|
+
}, { cause: err })
|
|
3407
|
+
};
|
|
2698
3408
|
throw err;
|
|
2699
3409
|
}
|
|
3410
|
+
if (this.closed) {
|
|
3411
|
+
await runner.dispose();
|
|
3412
|
+
throw new Error("codex session closed while follow-up was starting");
|
|
3413
|
+
}
|
|
3414
|
+
this.terminal = terminal;
|
|
2700
3415
|
this.sessionRef = sessionRef;
|
|
2701
3416
|
this.currentRunner = runner;
|
|
2702
|
-
void this.forgetRunnerOnceClosed(runner);
|
|
2703
3417
|
}
|
|
2704
3418
|
/** Best-effort abort of the current turn. SIGTERM's the currently-running child, if any — see `process-runner.ts`'s `kill()` doc comment for why SIGTERM (not SIGINT) and why this is safe: the underlying codex thread survives and stays resumable, confirmed empirically. A no-op when no turn is currently in flight. */
|
|
2705
3419
|
async interrupt() {
|
|
2706
3420
|
this.currentRunner?.kill();
|
|
2707
3421
|
}
|
|
2708
3422
|
async close() {
|
|
2709
|
-
if (this.
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
3423
|
+
if (!this.closeAttempt) {
|
|
3424
|
+
this.closed = true;
|
|
3425
|
+
this.queue.end();
|
|
3426
|
+
const attempt = (async () => {
|
|
3427
|
+
const runners = [...this.ownedRunners];
|
|
3428
|
+
await Promise.all(runners.map((runner) => runner.dispose()));
|
|
3429
|
+
for (const runner of runners) this.ownedRunners.delete(runner);
|
|
3430
|
+
await Promise.allSettled([...this.followUpAttempts]);
|
|
3431
|
+
})();
|
|
3432
|
+
this.closeAttempt = attempt.catch((error) => {
|
|
3433
|
+
this.closeAttempt = void 0;
|
|
3434
|
+
throw error;
|
|
3435
|
+
});
|
|
3436
|
+
}
|
|
3437
|
+
await this.closeAttempt;
|
|
2713
3438
|
}
|
|
2714
3439
|
/**
|
|
2715
3440
|
* `codex exec` has no in-band channel to inject text into an already-
|
|
@@ -2745,8 +3470,7 @@ var CodexSession = class {
|
|
|
2745
3470
|
);
|
|
2746
3471
|
}
|
|
2747
3472
|
};
|
|
2748
|
-
function subscriptionModel2(
|
|
2749
|
-
const selection = task.dispatchSelection;
|
|
3473
|
+
function subscriptionModel2(selection) {
|
|
2750
3474
|
if (selection === void 0) return void 0;
|
|
2751
3475
|
if (selection.lane !== "subscription" || selection.runtimeId !== "codex") {
|
|
2752
3476
|
throw new PolicyUnsupportedError(
|
|
@@ -2811,7 +3535,7 @@ var ApprovalRegistry = class {
|
|
|
2811
3535
|
* `requestApproval` timeout and `finish()` fail-closed cleanup
|
|
2812
3536
|
* (`task-runner.ts`) — resolves a decision this device made on its own.
|
|
2813
3537
|
* The one exception, a server-sent wire `task.approve`/`task.reject`
|
|
2814
|
-
* relayed through `
|
|
3538
|
+
* relayed through `RuntimeOperationStartInput.approvalChannel.resolve`
|
|
2815
3539
|
* (`task-runner.ts`'s `handleOffer`), passes `'wire'` explicitly.
|
|
2816
3540
|
*/
|
|
2817
3541
|
resolve(approvalId, decision, reason, origin = "local") {
|
|
@@ -3442,7 +4166,10 @@ var PresencePublisher = class {
|
|
|
3442
4166
|
{
|
|
3443
4167
|
method: "PUT",
|
|
3444
4168
|
headers: { "content-type": "application/json" },
|
|
3445
|
-
body: JSON.stringify({
|
|
4169
|
+
body: JSON.stringify({
|
|
4170
|
+
level: "online",
|
|
4171
|
+
...this.opts.configuredToolsets === void 0 ? {} : { configuredToolsets: this.opts.configuredToolsets }
|
|
4172
|
+
})
|
|
3446
4173
|
},
|
|
3447
4174
|
this.opts.auth
|
|
3448
4175
|
);
|
|
@@ -3636,12 +4363,12 @@ var AnotherControlServerRunningError = class extends Error {
|
|
|
3636
4363
|
}
|
|
3637
4364
|
};
|
|
3638
4365
|
var MAX_HALF_OPEN_CONNECTIONS = 8;
|
|
3639
|
-
function
|
|
4366
|
+
function errorMessage3(err) {
|
|
3640
4367
|
return err instanceof Error ? err.message : String(err);
|
|
3641
4368
|
}
|
|
3642
4369
|
function toControlErrorShape(err) {
|
|
3643
4370
|
if (err instanceof ControlError) return { code: err.code, message: err.message };
|
|
3644
|
-
return { code: "internal_error", message:
|
|
4371
|
+
return { code: "internal_error", message: errorMessage3(err) };
|
|
3645
4372
|
}
|
|
3646
4373
|
function probeUnixSocketAlive(socketPath) {
|
|
3647
4374
|
return new Promise((resolve) => {
|
|
@@ -3848,12 +4575,19 @@ async function startControlServer(opts) {
|
|
|
3848
4575
|
}
|
|
3849
4576
|
throw err;
|
|
3850
4577
|
}
|
|
4578
|
+
let stopServingPromise;
|
|
4579
|
+
async function stopServing() {
|
|
4580
|
+
stopServingPromise ??= (async () => {
|
|
4581
|
+
for (const socket of sockets) socket.destroy();
|
|
4582
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
4583
|
+
})();
|
|
4584
|
+
await stopServingPromise;
|
|
4585
|
+
}
|
|
3851
4586
|
let closed = false;
|
|
3852
4587
|
async function close() {
|
|
3853
4588
|
if (closed) return;
|
|
3854
4589
|
closed = true;
|
|
3855
|
-
|
|
3856
|
-
await new Promise((resolve) => server.close(() => resolve()));
|
|
4590
|
+
await stopServing();
|
|
3857
4591
|
if (process.platform !== "win32") {
|
|
3858
4592
|
await promises.rm(endpoint, { force: true }).catch(() => {
|
|
3859
4593
|
});
|
|
@@ -3861,7 +4595,7 @@ async function startControlServer(opts) {
|
|
|
3861
4595
|
await promises.rm(tokenPath, { force: true }).catch(() => {
|
|
3862
4596
|
});
|
|
3863
4597
|
}
|
|
3864
|
-
return { endpoint, close };
|
|
4598
|
+
return { endpoint, stopServing, close };
|
|
3865
4599
|
}
|
|
3866
4600
|
function deterministicJitterMs(input) {
|
|
3867
4601
|
const ratio = input.ratio ?? 0.2;
|
|
@@ -4155,6 +4889,7 @@ var WsTransport = class {
|
|
|
4155
4889
|
deviceId: this.opts.deviceId,
|
|
4156
4890
|
productId: this.opts.productId,
|
|
4157
4891
|
runtimes: this.opts.runtimes,
|
|
4892
|
+
configuredToolsets: this.opts.configuredToolsets === void 0 ? void 0 : [...this.opts.configuredToolsets],
|
|
4158
4893
|
cursor: this.opts.getCursor?.()
|
|
4159
4894
|
});
|
|
4160
4895
|
socket.send(encodeEnvelope(hello));
|
|
@@ -4244,6 +4979,7 @@ var ConnectionManager = class {
|
|
|
4244
4979
|
productId: opts.productId,
|
|
4245
4980
|
capabilities: opts.capabilities,
|
|
4246
4981
|
runtimes: opts.runtimes,
|
|
4982
|
+
configuredToolsets: opts.configuredToolsets,
|
|
4247
4983
|
getCursor: () => this.cursor,
|
|
4248
4984
|
onEnvelope: (envelope) => this.deliver(envelope),
|
|
4249
4985
|
onStateChange: (state) => {
|
|
@@ -4366,7 +5102,7 @@ var ConnectionManager = class {
|
|
|
4366
5102
|
* already-delivered seqs (see its own doc comment) so the failed seq's own
|
|
4367
5103
|
* redelivery can get through — but that same frozen watermark also means
|
|
4368
5104
|
* every OTHER seq above it rides along on every re-poll too. Without this,
|
|
4369
|
-
* a seq already mid-flight (e.g. a `task.offer` whose
|
|
5105
|
+
* a seq already mid-flight (e.g. a `task.offer` whose prepared operation start()
|
|
4370
5106
|
* hasn't resolved yet) would be re-enqueued into `processingChain` on
|
|
4371
5107
|
* every such re-poll, piling up duplicate copies that — once the first
|
|
4372
5108
|
* finally resolves and the chain unwinds through them — run its handler
|
|
@@ -5205,6 +5941,12 @@ var RECLAIM_FILENAME = `${DAEMON_OWNER_FILENAME}.reclaim`;
|
|
|
5205
5941
|
var MAX_OWNER_BYTES = 4096;
|
|
5206
5942
|
var RECLAIM_MALFORMED_GRACE_MS = 3e4;
|
|
5207
5943
|
var SELF_PROCESS_STARTED_AT = new Date(Date.now() - process.uptime() * 1e3).toISOString();
|
|
5944
|
+
function endOwnershipProbe(socket, response) {
|
|
5945
|
+
socket.on("error", () => {
|
|
5946
|
+
});
|
|
5947
|
+
if (response === void 0) socket.end();
|
|
5948
|
+
else socket.end(response);
|
|
5949
|
+
}
|
|
5208
5950
|
var STORE_MUTEX_ID_PREFIX = "byok-store-mutex-v1:";
|
|
5209
5951
|
var STORE_MUTEX_PROBE_TIMEOUT_MS = 1e3;
|
|
5210
5952
|
var STORE_MUTEX_SOCKET_FILENAME = "mutex.sock";
|
|
@@ -5311,7 +6053,7 @@ async function portIsBound(port) {
|
|
|
5311
6053
|
});
|
|
5312
6054
|
}
|
|
5313
6055
|
async function createLivenessListener() {
|
|
5314
|
-
const server = createServer((socket) => socket
|
|
6056
|
+
const server = createServer((socket) => endOwnershipProbe(socket));
|
|
5315
6057
|
const port = await new Promise((resolve, reject) => {
|
|
5316
6058
|
server.once("error", reject);
|
|
5317
6059
|
server.listen({ host: "127.0.0.1", port: 0, exclusive: true }, () => {
|
|
@@ -5396,7 +6138,7 @@ async function acquireStoreMutex(canonicalStoreDir) {
|
|
|
5396
6138
|
}
|
|
5397
6139
|
await clearStaleStoreMutexSocket(endpoint, identity);
|
|
5398
6140
|
}
|
|
5399
|
-
const server = createServer((socket) => socket
|
|
6141
|
+
const server = createServer((socket) => endOwnershipProbe(socket, `${STORE_MUTEX_ID_PREFIX}${identity}
|
|
5400
6142
|
`));
|
|
5401
6143
|
try {
|
|
5402
6144
|
await new Promise((resolve, reject) => {
|
|
@@ -5538,7 +6280,7 @@ function toRuntimeInfoCapabilities(caps) {
|
|
|
5538
6280
|
resume: caps.resume,
|
|
5539
6281
|
approvalInteractive: caps.approvalInteractive,
|
|
5540
6282
|
...caps.mcpToolsets === void 0 ? {} : { mcpToolsets: caps.mcpToolsets },
|
|
5541
|
-
permissionModes: caps.permissionModes
|
|
6283
|
+
permissionModes: [...caps.permissionModes]
|
|
5542
6284
|
};
|
|
5543
6285
|
}
|
|
5544
6286
|
var CursorStore = class {
|
|
@@ -5778,6 +6520,9 @@ var DaemonObserver = class {
|
|
|
5778
6520
|
noteGitWorkspace(event) {
|
|
5779
6521
|
this.emit({ kind: "git-workspace", ts: nowIso(), ...event });
|
|
5780
6522
|
}
|
|
6523
|
+
noteRuntimeDisposalFailure(event) {
|
|
6524
|
+
this.emit({ kind: "runtime-disposal-failed", ts: nowIso(), ...event });
|
|
6525
|
+
}
|
|
5781
6526
|
/**
|
|
5782
6527
|
* Finding F4: wired from `TaskRunnerDeps.onApprovalDispatched`, called
|
|
5783
6528
|
* synchronously by `TaskRunner.dispatchApproval` BEFORE its own
|
|
@@ -7434,20 +8179,20 @@ function isKnownRuntimeId(id) {
|
|
|
7434
8179
|
var DEFAULT_RUNTIME_PREFERENCE = ["claude", "codex", "pi"];
|
|
7435
8180
|
function orderByPreference(candidates, preference) {
|
|
7436
8181
|
const rank = new Map(preference.map((id, index) => [id, index]));
|
|
7437
|
-
return [...candidates].sort((a, b) => (rank.get(a.id) ?? preference.length) - (rank.get(b.id) ?? preference.length));
|
|
8182
|
+
return [...candidates].sort((a, b) => (rank.get(a.descriptor.id) ?? preference.length) - (rank.get(b.descriptor.id) ?? preference.length));
|
|
7438
8183
|
}
|
|
7439
|
-
function adapterSupportsMode(
|
|
7440
|
-
return
|
|
8184
|
+
function adapterSupportsMode(descriptor, mode) {
|
|
8185
|
+
return descriptor.capabilities.permissionModes.includes(mode);
|
|
7441
8186
|
}
|
|
7442
|
-
function adapterSupportsMcpToolsets(
|
|
7443
|
-
return
|
|
8187
|
+
function adapterSupportsMcpToolsets(descriptor) {
|
|
8188
|
+
return descriptor.capabilities.mcpToolsets === true;
|
|
7444
8189
|
}
|
|
7445
8190
|
function withoutRequiredToolsets(payload) {
|
|
7446
8191
|
if (!("requiredToolsets" in payload)) return payload;
|
|
7447
8192
|
const { requiredToolsets, ...offer } = payload;
|
|
7448
8193
|
return offer;
|
|
7449
8194
|
}
|
|
7450
|
-
function
|
|
8195
|
+
function errorMessage4(err) {
|
|
7451
8196
|
return err instanceof Error ? err.message : String(err);
|
|
7452
8197
|
}
|
|
7453
8198
|
function raceSettleFirst(fn, timeoutMs) {
|
|
@@ -7500,7 +8245,7 @@ async function openArtifact(workspaceDir, name) {
|
|
|
7500
8245
|
try {
|
|
7501
8246
|
handle = await promises.open(candidate, constants.O_RDONLY | O_NOFOLLOW);
|
|
7502
8247
|
} catch (err) {
|
|
7503
|
-
return { ok: false, reason: `artifact "${name}" could not be opened: ${
|
|
8248
|
+
return { ok: false, reason: `artifact "${name}" could not be opened: ${errorMessage4(err)}` };
|
|
7504
8249
|
}
|
|
7505
8250
|
try {
|
|
7506
8251
|
const st = await handle.stat();
|
|
@@ -7512,7 +8257,7 @@ async function openArtifact(workspaceDir, name) {
|
|
|
7512
8257
|
} catch (err) {
|
|
7513
8258
|
await handle.close().catch(() => {
|
|
7514
8259
|
});
|
|
7515
|
-
return { ok: false, reason: `artifact "${name}" could not be verified: ${
|
|
8260
|
+
return { ok: false, reason: `artifact "${name}" could not be verified: ${errorMessage4(err)}` };
|
|
7516
8261
|
}
|
|
7517
8262
|
return { ok: true, handle };
|
|
7518
8263
|
}
|
|
@@ -7526,13 +8271,13 @@ var TaskRunner = class {
|
|
|
7526
8271
|
* Finding F4 (cancel lost during the offer-processing window): a
|
|
7527
8272
|
* `task.cancel` for a taskId that hasn't finished `handleOffer` yet (still
|
|
7528
8273
|
* awaiting adapter detection / instruction resolution / workspace setup /
|
|
7529
|
-
* `
|
|
8274
|
+
* prepared operation `start()`) has no `this.tasks` entry to land on — it used to be
|
|
7530
8275
|
* silently dropped, and the runtime session `handleOffer` was about to
|
|
7531
8276
|
* register would then run an unsupervised ("zombie") turn nobody asked
|
|
7532
8277
|
* for anymore. Recording the taskId here lets `handleOffer` consult it at
|
|
7533
8278
|
* the two points where it can still safely react (see its body): before
|
|
7534
8279
|
* claiming at all (decline instead of ever starting a session), and right
|
|
7535
|
-
* after
|
|
8280
|
+
* after the prepared operation resolves but before this task is registered as
|
|
7536
8281
|
* active (tear the just-started session down immediately, before its
|
|
7537
8282
|
* event loop ever pumps a single event). Consumed (deleted) at whichever
|
|
7538
8283
|
* checkpoint handles it; a cancel for a taskId that's already active,
|
|
@@ -7558,12 +8303,12 @@ var TaskRunner = class {
|
|
|
7558
8303
|
* checkpoint-2 cancel-teardown, or successful registration into
|
|
7559
8304
|
* `this.tasks`). Bounded eviction on `pendingCancelled` (below) must never
|
|
7560
8305
|
* remove an entry for a taskId in this set: doing so is exactly the bug —
|
|
7561
|
-
* block task A in `
|
|
8306
|
+
* block task A in prepared-operation `start()`, deliver A's own `task.cancel` (so
|
|
7562
8307
|
* `pendingCancelled` gets an entry for A while A is still in-flight),
|
|
7563
8308
|
* then deliver `MAX_TRACKED_TASK_IDS` more cancels for unrelated taskIds
|
|
7564
8309
|
* nobody ever offered — under naive oldest-wins eviction, A's entry (the
|
|
7565
8310
|
* single oldest) gets evicted purely because of unrelated churn, so when
|
|
7566
|
-
*
|
|
8311
|
+
* the prepared operation finally resolves, checkpoint 2 finds no cancel marker
|
|
7567
8312
|
* and the already-cancelled task starts a real session. See
|
|
7568
8313
|
* `evictPendingCancelled` below for the fix, and
|
|
7569
8314
|
* `task-runner-bounded-collections.test.ts` for a test mirroring this
|
|
@@ -7583,7 +8328,7 @@ var TaskRunner = class {
|
|
|
7583
8328
|
* explicitly relies on redelivered handlers being idempotent for exactly
|
|
7584
8329
|
* this reason). `handleOffer` must treat a redelivered offer for a taskId
|
|
7585
8330
|
* that's already active (`this.tasks`) or already finished (this set) as
|
|
7586
|
-
* a no-op — never a second `
|
|
8331
|
+
* a no-op — never a second prepared-operation `start()` call, which would orphan the
|
|
7587
8332
|
* first session.
|
|
7588
8333
|
*
|
|
7589
8334
|
* M3-B: unbounded otherwise — a long-lived daemon that's finished many
|
|
@@ -7652,10 +8397,9 @@ var TaskRunner = class {
|
|
|
7652
8397
|
this.stoppingOffers = true;
|
|
7653
8398
|
}
|
|
7654
8399
|
/**
|
|
7655
|
-
*
|
|
7656
|
-
*
|
|
7657
|
-
*
|
|
7658
|
-
* terminal message is sent either way) but reports `task.fail` rather than
|
|
8400
|
+
* Shutdown of every currently ACTIVE task for the control socket's
|
|
8401
|
+
* `shutdown` RPC. Soft interrupt remains bounded, but each task's
|
|
8402
|
+
* authoritative close receipt must settle successfully. Reports `task.fail` rather than
|
|
7659
8403
|
* `task.cancelled` — these tasks aren't ending because the SERVER
|
|
7660
8404
|
* cancelled them, they're ending because this device is shutting down.
|
|
7661
8405
|
* `retryable: true` throughout: nothing about the task/policy itself was
|
|
@@ -7709,28 +8453,13 @@ var TaskRunner = class {
|
|
|
7709
8453
|
* unconditionally, so a hung `interrupt()` (a misbehaving adapter) can
|
|
7710
8454
|
* never block `task.fail` from being sent at all.
|
|
7711
8455
|
*
|
|
7712
|
-
*
|
|
7713
|
-
*
|
|
7714
|
-
*
|
|
7715
|
-
*
|
|
7716
|
-
* far more here than it used to for the pre-existing graceful-shutdown-only
|
|
7717
|
-
* caller, since THAT path is additionally bounded by an outer deadline
|
|
7718
|
-
* (`SHUTDOWN_TASK_TEARDOWN_DEADLINE_MS`/`DaemonConfig.shutdownGraceMs`,
|
|
7719
|
-
* `create-daemon.ts`), while resource-limit enforcement fires during
|
|
7720
|
-
* ordinary operation with no such outer bound watching it). `close()` is
|
|
7721
|
-
* every adapter's harder teardown primitive — an actual process-level kill
|
|
7722
|
-
* (SIGTERM, or `taskkill /F` on Windows — see e.g.
|
|
7723
|
-
* `ClaudeProcessClient.kill()`/`PiRpcClient.kill()`) as opposed to pi's own
|
|
7724
|
-
* soft in-band `interrupt()` (an RPC `abort` message that leaves the
|
|
7725
|
-
* process alive and resumable) — so escalating to it is the closest thing
|
|
7726
|
-
* to a "hard kill" the `Session` interface exposes. `finish()` below calls
|
|
7727
|
-
* `session.close()` again regardless (documented idempotent) — this isn't
|
|
7728
|
-
* a substitute for that, only an earlier, bounded attempt at actually
|
|
7729
|
-
* stopping a stuck runtime before this method gives up and reports failure
|
|
7730
|
-
* anyway.
|
|
8456
|
+
* After the bounded soft interrupt, `finish()` always awaits the authoritative
|
|
8457
|
+
* `Session.close()` receipt. A failed receipt retains active/Git ownership;
|
|
8458
|
+
* shutdown surfaces the rejection while resource enforcement leaves local
|
|
8459
|
+
* evidence for a later retry.
|
|
7731
8460
|
*
|
|
7732
8461
|
* Re-checks task identity (`this.tasks.get(...) === active`) immediately
|
|
7733
|
-
* before sending `task.fail`: the interrupt
|
|
8462
|
+
* before sending `task.fail`: the interrupt race above has await
|
|
7734
8463
|
* points during which a DIFFERENT path (a racing `task.cancel`/
|
|
7735
8464
|
* `task.reject`, or the session completing normally on its own) may have
|
|
7736
8465
|
* already finished this exact task and sent its own terminal message.
|
|
@@ -7739,20 +8468,25 @@ var TaskRunner = class {
|
|
|
7739
8468
|
* identity-check guard for the same class of race.
|
|
7740
8469
|
*/
|
|
7741
8470
|
async teardownActiveTask(active, reason, retryable) {
|
|
8471
|
+
if (active.finalizationStarted) return this.finish(active.taskId);
|
|
8472
|
+
if (!this.reserveSemanticTerminal(active)) return active.semanticTerminalSettled ?? false;
|
|
7742
8473
|
active.beingTornDown = true;
|
|
7743
8474
|
await this.observeGit(active, "salvage");
|
|
7744
8475
|
const timeoutMs = this.deps.shutdownInterruptTimeoutMs ?? DEFAULT_SHUTDOWN_INTERRUPT_TIMEOUT_MS;
|
|
7745
|
-
|
|
7746
|
-
if (
|
|
7747
|
-
await raceSettleFirst(() => active.session.close(), timeoutMs);
|
|
7748
|
-
}
|
|
7749
|
-
if (this.tasks.get(active.taskId) !== active) return;
|
|
8476
|
+
await raceSettleFirst(() => active.session.interrupt(), timeoutMs);
|
|
8477
|
+
if (this.tasks.get(active.taskId) !== active) return true;
|
|
7750
8478
|
this.deps.send(createEnvelope("task.fail", { reason, retryable }, { taskId: active.taskId }));
|
|
7751
|
-
|
|
8479
|
+
return this.finish(active.taskId);
|
|
7752
8480
|
}
|
|
7753
8481
|
/** Graceful-shutdown caller of {@link teardownActiveTask} — see `shutdownActiveTasks`'s own doc comment. `retryable: true`: nothing about the task/policy itself was ever at fault, only this device's own availability right now. */
|
|
7754
8482
|
async shutdownTask(active, reason) {
|
|
7755
|
-
await this.teardownActiveTask(active, `daemon shutting down: ${reason}`, true);
|
|
8483
|
+
const disposed = await this.teardownActiveTask(active, `daemon shutting down: ${reason}`, true);
|
|
8484
|
+
if (!disposed) {
|
|
8485
|
+
throw new RuntimeDisposalFailure({
|
|
8486
|
+
stage: "quiescence",
|
|
8487
|
+
reason: `${active.adapter.descriptor.id} runtime ownership remains active after shutdown disposal failed`
|
|
8488
|
+
});
|
|
8489
|
+
}
|
|
7756
8490
|
}
|
|
7757
8491
|
/**
|
|
7758
8492
|
* M5 batch-3 (workstream 2): shared entry point for both resource-limit
|
|
@@ -7872,15 +8606,33 @@ var TaskRunner = class {
|
|
|
7872
8606
|
this.decline(taskId, resolvedMcp.reason, true);
|
|
7873
8607
|
return;
|
|
7874
8608
|
}
|
|
8609
|
+
const decision = computeEffectivePolicy(payload.policy, this.deps.permissionDefaults);
|
|
8610
|
+
if (!decision.ok) {
|
|
8611
|
+
this.decline(taskId, decision.reason ?? "policy rejected", false);
|
|
8612
|
+
return;
|
|
8613
|
+
}
|
|
8614
|
+
const offered = withoutRequiredToolsets(payload);
|
|
7875
8615
|
const requestedRuntime = payload.dispatchSelection?.runtimeId ?? payload.runtime;
|
|
7876
8616
|
const pick = await this.pickAdapter(requestedRuntime, payload.policy.mode, requiredToolsets !== void 0);
|
|
7877
8617
|
if (!pick.ok) {
|
|
7878
8618
|
this.decline(taskId, pick.reason, pick.retryable);
|
|
7879
8619
|
return;
|
|
7880
8620
|
}
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
8621
|
+
let prepared;
|
|
8622
|
+
try {
|
|
8623
|
+
prepared = await pick.adapter.prepare({
|
|
8624
|
+
offer: offered,
|
|
8625
|
+
policy: decision.policy,
|
|
8626
|
+
descriptor: pick.descriptor,
|
|
8627
|
+
requiredToolsetIds: requiredToolsets ?? [],
|
|
8628
|
+
...resolvedMcp?.ok ? { mcpServers: resolvedMcp.servers } : {}
|
|
8629
|
+
});
|
|
8630
|
+
} catch (error) {
|
|
8631
|
+
this.decline(taskId, `runtime preparation failed: ${errorMessage4(error)}`, true);
|
|
8632
|
+
return;
|
|
8633
|
+
}
|
|
8634
|
+
if (prepared.kind === "reject") {
|
|
8635
|
+
this.decline(taskId, prepared.reason, prepared.retryable);
|
|
7884
8636
|
return;
|
|
7885
8637
|
}
|
|
7886
8638
|
let known = void 0;
|
|
@@ -7915,6 +8667,7 @@ var TaskRunner = class {
|
|
|
7915
8667
|
}
|
|
7916
8668
|
} else {
|
|
7917
8669
|
workspaceDir = path17.join(this.deps.workspaceRoot, taskId);
|
|
8670
|
+
gitWorkspaceId = randomUUID();
|
|
7918
8671
|
}
|
|
7919
8672
|
try {
|
|
7920
8673
|
gitLease = await gitManager.acquireLease(workspaceDir, payload.sessionRef);
|
|
@@ -7930,6 +8683,26 @@ var TaskRunner = class {
|
|
|
7930
8683
|
this.decline(taskId, "workspace mode is unavailable", true);
|
|
7931
8684
|
return;
|
|
7932
8685
|
}
|
|
8686
|
+
const env = buildRuntimeEnv({
|
|
8687
|
+
ambient: process.env,
|
|
8688
|
+
requirements: pick.descriptor.environmentRequirements,
|
|
8689
|
+
locallyAllowedNames: this.deps.runtimeEnvironment?.[pick.descriptor.id]?.allow
|
|
8690
|
+
});
|
|
8691
|
+
const manifest = sealRuntimeOperationManifest({
|
|
8692
|
+
taskId,
|
|
8693
|
+
runtimeId: pick.descriptor.id,
|
|
8694
|
+
descriptor: pick.descriptor,
|
|
8695
|
+
policy: decision.policy,
|
|
8696
|
+
requiredToolsetIds: requiredToolsets ?? [],
|
|
8697
|
+
...offered.dispatchSelection === void 0 ? {} : { dispatchSelection: offered.dispatchSelection },
|
|
8698
|
+
...known === void 0 || payload.sessionRef === void 0 ? {} : { sessionRef: payload.sessionRef },
|
|
8699
|
+
workspace: {
|
|
8700
|
+
workspaceDir,
|
|
8701
|
+
...gitWorkspaceId === void 0 ? {} : { workspaceId: gitWorkspaceId },
|
|
8702
|
+
...gitBaseline === void 0 ? {} : { baseline: gitBaseline }
|
|
8703
|
+
},
|
|
8704
|
+
forwardedEnvironmentNames: Object.freeze(Object.keys(env).sort())
|
|
8705
|
+
});
|
|
7933
8706
|
this.deps.send(
|
|
7934
8707
|
createEnvelope(
|
|
7935
8708
|
"task.claim",
|
|
@@ -7945,7 +8718,7 @@ var TaskRunner = class {
|
|
|
7945
8718
|
// (the merely REQUESTED runtime): this is what closes the gap
|
|
7946
8719
|
// where an auto-selected task left the server never learning
|
|
7947
8720
|
// which runtime actually ran.
|
|
7948
|
-
runtime: isKnownRuntimeId(
|
|
8721
|
+
runtime: isKnownRuntimeId(manifest.descriptor.id) ? manifest.descriptor.id : void 0,
|
|
7949
8722
|
// S0/D-4 (`task.claim.capabilities`, docs/protocol.md §2.4): the
|
|
7950
8723
|
// selected adapter's own capability self-report, carried on the
|
|
7951
8724
|
// same message that establishes the task↔runtime binding. The
|
|
@@ -7962,7 +8735,7 @@ var TaskRunner = class {
|
|
|
7962
8735
|
// Gating them would silently strip a custom steer-capable
|
|
7963
8736
|
// adapter's own truth and leave the server fail-closing on it
|
|
7964
8737
|
// forever.
|
|
7965
|
-
capabilities: toRuntimeInfoCapabilities(
|
|
8738
|
+
capabilities: toRuntimeInfoCapabilities(manifest.descriptor.capabilities)
|
|
7966
8739
|
},
|
|
7967
8740
|
{ taskId }
|
|
7968
8741
|
)
|
|
@@ -7973,7 +8746,7 @@ var TaskRunner = class {
|
|
|
7973
8746
|
if (plainWorkspaceNeedsResolve) workspaceDir = await this.resolveWorkspaceDir(taskId, known?.workspaceDir);
|
|
7974
8747
|
} catch (err) {
|
|
7975
8748
|
gitLease?.release();
|
|
7976
|
-
await this.fail(taskId, `failed to resolve instruction blob: ${
|
|
8749
|
+
await this.fail(taskId, `failed to resolve instruction blob: ${errorMessage4(err)}`, true);
|
|
7977
8750
|
return;
|
|
7978
8751
|
}
|
|
7979
8752
|
if (this.deps.gitWorkspaceManager && gitLease) {
|
|
@@ -7982,7 +8755,7 @@ var TaskRunner = class {
|
|
|
7982
8755
|
if (gitExisting) {
|
|
7983
8756
|
observation = await this.deps.gitWorkspaceManager.validateExisting(workspaceDir);
|
|
7984
8757
|
} else {
|
|
7985
|
-
const workspaceId2 = randomUUID();
|
|
8758
|
+
const workspaceId2 = gitWorkspaceId ?? randomUUID();
|
|
7986
8759
|
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
7987
8760
|
gitWorkspaceId = workspaceId2;
|
|
7988
8761
|
await this.deps.gitWorkspaceStore?.upsert({
|
|
@@ -8033,30 +8806,11 @@ var TaskRunner = class {
|
|
|
8033
8806
|
return;
|
|
8034
8807
|
}
|
|
8035
8808
|
}
|
|
8036
|
-
const
|
|
8037
|
-
|
|
8038
|
-
|
|
8809
|
+
const startInput = {
|
|
8810
|
+
manifest,
|
|
8811
|
+
instruction: gitWorkspaceId ? prependGitWorkspaceGuidance(resolvedInstruction) : resolvedInstruction,
|
|
8812
|
+
env,
|
|
8039
8813
|
...resolvedMcp?.ok ? { mcpServers: resolvedMcp.servers } : {},
|
|
8040
|
-
...gitWorkspaceId ? { gitWorkspace: { workspaceId: gitWorkspaceId, baseline: gitBaseline } } : {},
|
|
8041
|
-
// M5: no longer `process.env` verbatim (see `environment.ts`'s own
|
|
8042
|
-
// module doc comment for the credential-leak gap that closed) —
|
|
8043
|
-
// built fresh per task from the SPECIFIC adapter `pickAdapter`
|
|
8044
|
-
// above already selected, so this always runs after adapter
|
|
8045
|
-
// selection: `pick.adapter.environmentRequirements?.()` (undefined
|
|
8046
|
-
// ⇒ platform baseline only, fail-closed) plus this device's own
|
|
8047
|
-
// `runtimeEnvironment` override, keyed by that same adapter's `id`.
|
|
8048
|
-
env: buildRuntimeEnv({
|
|
8049
|
-
ambient: process.env,
|
|
8050
|
-
requirements: pick.adapter.environmentRequirements?.(),
|
|
8051
|
-
locallyAllowedNames: this.deps.runtimeEnvironment?.[pick.adapter.id]?.allow
|
|
8052
|
-
}),
|
|
8053
|
-
// M4 Phase 3: adapter-agnostic and cheap to always populate — only an
|
|
8054
|
-
// adapter whose runtime genuinely supports an out-of-band approval
|
|
8055
|
-
// pause (claude, today) ever reads this. `resolve` is a closure over
|
|
8056
|
-
// `taskId` (not a pre-bound approvalId): it looks up whichever
|
|
8057
|
-
// approval is CURRENTLY pending for this task at call time, since one
|
|
8058
|
-
// task/session can face several approval requests, one at a time,
|
|
8059
|
-
// over its life. See `types.ts`'s `ApprovalChannel` doc comment.
|
|
8060
8814
|
approvalChannel: {
|
|
8061
8815
|
taskId,
|
|
8062
8816
|
storeDir: this.deps.storeDir,
|
|
@@ -8072,45 +8826,19 @@ var TaskRunner = class {
|
|
|
8072
8826
|
}
|
|
8073
8827
|
}
|
|
8074
8828
|
};
|
|
8075
|
-
const effectiveOffer = {
|
|
8076
|
-
...withoutRequiredToolsets(payload),
|
|
8077
|
-
instruction: gitWorkspaceId ? prependGitWorkspaceGuidance(resolvedInstruction) : resolvedInstruction,
|
|
8078
|
-
// Never forward a sessionRef this device has no recorded workspace
|
|
8079
|
-
// for (stale, from another device, or simply made up) — an adapter
|
|
8080
|
-
// that tries to resume an id it never minted fails outright (pi:
|
|
8081
|
-
// "No session found matching '<id>'", exit 1, empirically confirmed)
|
|
8082
|
-
// instead of silently starting fresh, so an unresolvable sessionRef
|
|
8083
|
-
// must look identical to "none supplied" by the time it reaches the
|
|
8084
|
-
// adapter, not get forwarded as a resume attempt doomed to fail.
|
|
8085
|
-
sessionRef: known ? payload.sessionRef : void 0
|
|
8086
|
-
};
|
|
8087
8829
|
let session;
|
|
8088
8830
|
try {
|
|
8089
|
-
session = await
|
|
8831
|
+
session = await prepared.operation.start(startInput);
|
|
8090
8832
|
} catch (err) {
|
|
8091
|
-
const
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
await this.fail(taskId, `adapter failed to start: ${errorMessage3(err)}`, retryable);
|
|
8095
|
-
return;
|
|
8096
|
-
}
|
|
8097
|
-
if (this.pendingCancelled.has(taskId)) {
|
|
8098
|
-
const reason = this.pendingCancelled.get(taskId);
|
|
8099
|
-
this.pendingCancelled.delete(taskId);
|
|
8100
|
-
try {
|
|
8101
|
-
await session.interrupt();
|
|
8102
|
-
} catch {
|
|
8103
|
-
}
|
|
8104
|
-
try {
|
|
8105
|
-
await session.close();
|
|
8106
|
-
} catch {
|
|
8833
|
+
const failure = projectRuntimeBoundaryFailure(err, "start");
|
|
8834
|
+
if (failure.contractViolation) {
|
|
8835
|
+
console.error("[byok/client] runtime adapter start() returned an untyped failure", err);
|
|
8107
8836
|
}
|
|
8837
|
+
await this.updateGitPhaseBestEffort(gitWorkspaceId, "failed", "repository-invalid");
|
|
8108
8838
|
gitLease?.release();
|
|
8109
|
-
await this.
|
|
8110
|
-
this.deps.send(createEnvelope("task.cancelled", { reason }, { taskId }));
|
|
8839
|
+
await this.fail(taskId, failure.reason, failure.retryable);
|
|
8111
8840
|
return;
|
|
8112
8841
|
}
|
|
8113
|
-
this.deps.send(createEnvelope("task.started", {}, { taskId }));
|
|
8114
8842
|
const active = {
|
|
8115
8843
|
taskId,
|
|
8116
8844
|
adapter: pick.adapter,
|
|
@@ -8127,6 +8855,21 @@ var TaskRunner = class {
|
|
|
8127
8855
|
approvalQueue: [],
|
|
8128
8856
|
outputBytesSoFar: 0
|
|
8129
8857
|
};
|
|
8858
|
+
if (this.pendingCancelled.has(taskId)) {
|
|
8859
|
+
const reason = this.pendingCancelled.get(taskId);
|
|
8860
|
+
this.pendingCancelled.delete(taskId);
|
|
8861
|
+
this.tasks.set(taskId, active);
|
|
8862
|
+
this.reserveSemanticTerminal(active);
|
|
8863
|
+
try {
|
|
8864
|
+
await session.interrupt();
|
|
8865
|
+
} catch {
|
|
8866
|
+
}
|
|
8867
|
+
await this.updateGitPhaseBestEffort(gitWorkspaceId, "cancelled");
|
|
8868
|
+
this.deps.send(createEnvelope("task.cancelled", { reason }, { taskId }));
|
|
8869
|
+
await this.finish(taskId);
|
|
8870
|
+
return;
|
|
8871
|
+
}
|
|
8872
|
+
this.deps.send(createEnvelope("task.started", {}, { taskId }));
|
|
8130
8873
|
this.tasks.set(taskId, active);
|
|
8131
8874
|
if (payload.limits?.maxDurationMs !== void 0) {
|
|
8132
8875
|
this.armMaxDurationTimer(active, payload.limits.maxDurationMs);
|
|
@@ -8184,6 +8927,7 @@ var TaskRunner = class {
|
|
|
8184
8927
|
async pump(active) {
|
|
8185
8928
|
try {
|
|
8186
8929
|
for await (const event of active.session.events) {
|
|
8930
|
+
if (this.tasks.get(active.taskId) !== active || active.beingTornDown) return;
|
|
8187
8931
|
if (this.tasks.get(active.taskId) !== active) return;
|
|
8188
8932
|
active.outputBytesSoFar += estimateEventBytes(event);
|
|
8189
8933
|
if (active.outputBytesSoFar > this.maxTaskOutputBytes) {
|
|
@@ -8206,7 +8950,7 @@ var TaskRunner = class {
|
|
|
8206
8950
|
try {
|
|
8207
8951
|
await active.session.resolveApproval(approved, reason);
|
|
8208
8952
|
} catch (err) {
|
|
8209
|
-
await this.fail(taskId, `failed to resume session after approval decision: ${
|
|
8953
|
+
await this.fail(taskId, `failed to resume session after approval decision: ${errorMessage4(err)}`, false);
|
|
8210
8954
|
}
|
|
8211
8955
|
});
|
|
8212
8956
|
continue;
|
|
@@ -8218,6 +8962,7 @@ var TaskRunner = class {
|
|
|
8218
8962
|
const outcome = await this.resolveResultDocument(active, finalOutput);
|
|
8219
8963
|
if (!outcome.deliver) return;
|
|
8220
8964
|
await this.observeGit(active, "completed");
|
|
8965
|
+
if (this.tasks.get(active.taskId) !== active || active.beingTornDown) return;
|
|
8221
8966
|
if (outcome.document !== void 0 && !this.hasResultDocumentCapability()) {
|
|
8222
8967
|
await this.fail(
|
|
8223
8968
|
active.taskId,
|
|
@@ -8226,6 +8971,7 @@ var TaskRunner = class {
|
|
|
8226
8971
|
);
|
|
8227
8972
|
return;
|
|
8228
8973
|
}
|
|
8974
|
+
if (!this.reserveSemanticTerminal(active)) return;
|
|
8229
8975
|
this.deps.send(
|
|
8230
8976
|
createEnvelope(
|
|
8231
8977
|
"task.complete",
|
|
@@ -8261,11 +9007,17 @@ var TaskRunner = class {
|
|
|
8261
9007
|
active.batcher.push(event);
|
|
8262
9008
|
}
|
|
8263
9009
|
if (this.tasks.get(active.taskId) !== active || active.beingTornDown) return;
|
|
8264
|
-
|
|
9010
|
+
const failure = projectRuntimeBoundaryFailure(void 0, "run");
|
|
9011
|
+
console.error("[byok/client] runtime adapter events iterable ended without terminal authority");
|
|
9012
|
+
await this.fail(active.taskId, failure.reason, failure.retryable);
|
|
8265
9013
|
} catch (err) {
|
|
8266
9014
|
if (this.tasks.get(active.taskId) !== active || active.beingTornDown) return;
|
|
8267
9015
|
active.batcher.flush();
|
|
8268
|
-
|
|
9016
|
+
const failure = projectRuntimeBoundaryFailure(err, "run");
|
|
9017
|
+
if (failure.contractViolation) {
|
|
9018
|
+
console.error("[byok/client] runtime adapter events iterable returned an untyped failure", err);
|
|
9019
|
+
}
|
|
9020
|
+
await this.fail(active.taskId, failure.reason, failure.retryable);
|
|
8269
9021
|
}
|
|
8270
9022
|
}
|
|
8271
9023
|
/**
|
|
@@ -8304,7 +9056,7 @@ var TaskRunner = class {
|
|
|
8304
9056
|
try {
|
|
8305
9057
|
bytes = await opened.handle.readFile();
|
|
8306
9058
|
} catch (err) {
|
|
8307
|
-
this.reportArtifactError(active, name, `failed to read artifact "${name}": ${
|
|
9059
|
+
this.reportArtifactError(active, name, `failed to read artifact "${name}": ${errorMessage4(err)}`);
|
|
8308
9060
|
return;
|
|
8309
9061
|
} finally {
|
|
8310
9062
|
await opened.handle.close().catch(() => {
|
|
@@ -8321,7 +9073,7 @@ var TaskRunner = class {
|
|
|
8321
9073
|
const blobRef = await this.deps.blobClient.uploadArtifact(bytes, contentType);
|
|
8322
9074
|
this.deps.send(createEnvelope("task.artifact", { name, contentType, blobRef }, { taskId: active.taskId }));
|
|
8323
9075
|
} catch (err) {
|
|
8324
|
-
this.reportArtifactError(active, name, `failed to upload artifact "${name}": ${
|
|
9076
|
+
this.reportArtifactError(active, name, `failed to upload artifact "${name}": ${errorMessage4(err)}`);
|
|
8325
9077
|
}
|
|
8326
9078
|
}
|
|
8327
9079
|
/** Loud, non-silent artifact failure (finding F7): logged, and folded into this task's own progress stream as an `error` AgentEvent rather than swallowed — the task itself can still complete normally, but the omission is now visible. */
|
|
@@ -8335,6 +9087,14 @@ var TaskRunner = class {
|
|
|
8335
9087
|
this.setPendingCancelled(taskId, reason);
|
|
8336
9088
|
return;
|
|
8337
9089
|
}
|
|
9090
|
+
if (active.finalizationStarted) {
|
|
9091
|
+
await this.finish(taskId);
|
|
9092
|
+
return;
|
|
9093
|
+
}
|
|
9094
|
+
if (!this.reserveSemanticTerminal(active)) {
|
|
9095
|
+
await active.semanticTerminalSettled;
|
|
9096
|
+
return;
|
|
9097
|
+
}
|
|
8338
9098
|
try {
|
|
8339
9099
|
await active.session.interrupt();
|
|
8340
9100
|
} catch {
|
|
@@ -8363,7 +9123,7 @@ var TaskRunner = class {
|
|
|
8363
9123
|
*
|
|
8364
9124
|
* `inFlightOffers` is naturally tiny (bounded by this device's real
|
|
8365
9125
|
* concurrent-offer-processing count — normally single digits, driven by
|
|
8366
|
-
* how many `task.offer`s are simultaneously mid
|
|
9126
|
+
* how many `task.offer`s are simultaneously mid-prepared-operation start() — nowhere
|
|
8367
9127
|
* near `MAX_TRACKED_TASK_IDS`), so this scan is cheap in practice: it
|
|
8368
9128
|
* finds a safe entry at or near the front almost always. The only case
|
|
8369
9129
|
* where NO entry is safe to evict is every single tracked cancel
|
|
@@ -8716,7 +9476,7 @@ var TaskRunner = class {
|
|
|
8716
9476
|
this.deps.onStaleApprovalDecision?.(taskId, "approve");
|
|
8717
9477
|
return;
|
|
8718
9478
|
}
|
|
8719
|
-
await this.fail(taskId, `failed to resume session after approval: ${
|
|
9479
|
+
await this.fail(taskId, `failed to resume session after approval: ${errorMessage4(err)}`, false);
|
|
8720
9480
|
return;
|
|
8721
9481
|
}
|
|
8722
9482
|
this.clearPendingApproval(resolvedId, "approve", void 0);
|
|
@@ -8747,6 +9507,10 @@ var TaskRunner = class {
|
|
|
8747
9507
|
async handleReject(taskId, reason, approvalId) {
|
|
8748
9508
|
const active = this.tasks.get(taskId);
|
|
8749
9509
|
if (!active) return;
|
|
9510
|
+
if (active.finalizationStarted) {
|
|
9511
|
+
await this.finish(taskId);
|
|
9512
|
+
return;
|
|
9513
|
+
}
|
|
8750
9514
|
if (approvalId !== void 0 && approvalId !== active.pendingApprovalId) {
|
|
8751
9515
|
this.deps.onStaleApprovalDecision?.(
|
|
8752
9516
|
taskId,
|
|
@@ -8765,6 +9529,10 @@ var TaskRunner = class {
|
|
|
8765
9529
|
}
|
|
8766
9530
|
}
|
|
8767
9531
|
this.clearPendingApproval(resolvedId, "reject", reason);
|
|
9532
|
+
if (!this.reserveSemanticTerminal(active)) {
|
|
9533
|
+
await active.semanticTerminalSettled;
|
|
9534
|
+
return;
|
|
9535
|
+
}
|
|
8768
9536
|
try {
|
|
8769
9537
|
await active.session.interrupt();
|
|
8770
9538
|
} catch {
|
|
@@ -8779,6 +9547,14 @@ var TaskRunner = class {
|
|
|
8779
9547
|
}
|
|
8780
9548
|
async fail(taskId, reason, retryable) {
|
|
8781
9549
|
const active = this.tasks.get(taskId);
|
|
9550
|
+
if (active?.finalizationStarted) {
|
|
9551
|
+
await this.finish(taskId);
|
|
9552
|
+
return;
|
|
9553
|
+
}
|
|
9554
|
+
if (active && !this.reserveSemanticTerminal(active)) {
|
|
9555
|
+
await active.semanticTerminalSettled;
|
|
9556
|
+
return;
|
|
9557
|
+
}
|
|
8782
9558
|
if (active) await this.observeGit(active, "salvage");
|
|
8783
9559
|
this.deps.send(createEnvelope("task.fail", { reason, retryable }, { taskId }));
|
|
8784
9560
|
await this.finish(taskId);
|
|
@@ -8838,7 +9614,7 @@ var TaskRunner = class {
|
|
|
8838
9614
|
} catch (err) {
|
|
8839
9615
|
await this.fail(
|
|
8840
9616
|
active.taskId,
|
|
8841
|
-
`${RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX}: the configured resultDocument.extract threw: ${
|
|
9617
|
+
`${RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX}: the configured resultDocument.extract threw: ${errorMessage4(err)}`,
|
|
8842
9618
|
false
|
|
8843
9619
|
);
|
|
8844
9620
|
return { deliver: false };
|
|
@@ -8913,32 +9689,64 @@ var TaskRunner = class {
|
|
|
8913
9689
|
}
|
|
8914
9690
|
async finish(taskId) {
|
|
8915
9691
|
const active = this.tasks.get(taskId);
|
|
8916
|
-
if (!active) return;
|
|
8917
|
-
if (active.
|
|
8918
|
-
|
|
8919
|
-
active.
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
9692
|
+
if (!active) return true;
|
|
9693
|
+
if (!active.finalizationStarted) {
|
|
9694
|
+
active.finalizationStarted = true;
|
|
9695
|
+
active.beingTornDown = true;
|
|
9696
|
+
if (active.maxDurationTimer) {
|
|
9697
|
+
clearTimeout(active.maxDurationTimer);
|
|
9698
|
+
active.maxDurationTimer = void 0;
|
|
9699
|
+
}
|
|
9700
|
+
active.batcher.stop();
|
|
9701
|
+
this.addFinishedTaskId(taskId);
|
|
9702
|
+
const queued = active.approvalQueue.splice(0);
|
|
9703
|
+
for (const request of queued) {
|
|
9704
|
+
request.resolve({
|
|
9705
|
+
approved: false,
|
|
9706
|
+
reason: `task ${taskId} finished before this queued approval request could be dispatched`
|
|
9707
|
+
});
|
|
9708
|
+
}
|
|
9709
|
+
if (active.pendingApprovalId !== void 0) {
|
|
9710
|
+
try {
|
|
9711
|
+
this.deps.approvalRegistry.resolve(active.pendingApprovalId, "reject", `task ${taskId} finished`);
|
|
9712
|
+
} catch {
|
|
9713
|
+
}
|
|
8935
9714
|
}
|
|
8936
9715
|
}
|
|
8937
|
-
|
|
9716
|
+
const attempt = active.disposalAttempt ?? active.session.close();
|
|
9717
|
+
active.disposalAttempt = attempt;
|
|
8938
9718
|
try {
|
|
8939
|
-
await
|
|
8940
|
-
} catch {
|
|
9719
|
+
await attempt;
|
|
9720
|
+
} catch (caught) {
|
|
9721
|
+
if (active.disposalAttempt === attempt) active.disposalAttempt = void 0;
|
|
9722
|
+
const failure = isRuntimeDisposalFailure(caught) ? caught : new RuntimeDisposalFailure({
|
|
9723
|
+
stage: "quiescence",
|
|
9724
|
+
reason: `${active.adapter.descriptor.id} session.close() returned an untyped disposal failure`
|
|
9725
|
+
}, { cause: caught });
|
|
9726
|
+
console.error(`[byok/client] runtime disposal failed for task ${taskId}: ${failure.message}`);
|
|
9727
|
+
this.deps.onRuntimeDisposalFailure?.({
|
|
9728
|
+
taskId,
|
|
9729
|
+
runtimeId: active.adapter.descriptor.id,
|
|
9730
|
+
stage: failure.stage,
|
|
9731
|
+
reason: failure.message
|
|
9732
|
+
});
|
|
9733
|
+
active.resolveSemanticTerminalSettled?.(false);
|
|
9734
|
+
return false;
|
|
8941
9735
|
}
|
|
9736
|
+
if (this.tasks.get(taskId) !== active) return true;
|
|
9737
|
+
active.gitLease?.release();
|
|
9738
|
+
this.tasks.delete(taskId);
|
|
9739
|
+
active.resolveSemanticTerminalSettled?.(true);
|
|
9740
|
+
return true;
|
|
9741
|
+
}
|
|
9742
|
+
reserveSemanticTerminal(active) {
|
|
9743
|
+
if (active.semanticTerminalReserved || active.finalizationStarted) return false;
|
|
9744
|
+
active.semanticTerminalReserved = true;
|
|
9745
|
+
active.beingTornDown = true;
|
|
9746
|
+
active.semanticTerminalSettled = new Promise((resolve) => {
|
|
9747
|
+
active.resolveSemanticTerminalSettled = resolve;
|
|
9748
|
+
});
|
|
9749
|
+
return true;
|
|
8942
9750
|
}
|
|
8943
9751
|
/** M3-B: bounded insert for `finishedTaskIds` — see its class-level doc comment and `MAX_TRACKED_TASK_IDS`. Evicts the oldest (first-inserted) entry once over cap, same idiom as `ConnectionHub.checkAndRecordDuplicate` (packages/server/src/hub.ts). */
|
|
8944
9752
|
addFinishedTaskId(taskId) {
|
|
@@ -8991,18 +9799,19 @@ var TaskRunner = class {
|
|
|
8991
9799
|
retryable: false
|
|
8992
9800
|
};
|
|
8993
9801
|
}
|
|
8994
|
-
const adapter = this.deps.adapters.find((a) => a.id === requestedRuntime);
|
|
9802
|
+
const adapter = this.deps.adapters.find((a) => a.descriptor.id === requestedRuntime);
|
|
8995
9803
|
if (!adapter) {
|
|
8996
9804
|
return { ok: false, reason: `unknown runtime "${requestedRuntime}"`, retryable: false };
|
|
8997
9805
|
}
|
|
8998
|
-
|
|
9806
|
+
const descriptor = freezeRuntimeAdapterDescriptor(adapter.descriptor);
|
|
9807
|
+
if (!adapterSupportsMode(descriptor, policyMode)) {
|
|
8999
9808
|
return {
|
|
9000
9809
|
ok: false,
|
|
9001
9810
|
reason: `runtime "${requestedRuntime}" cannot express permission mode "${policyMode}"`,
|
|
9002
9811
|
retryable: false
|
|
9003
9812
|
};
|
|
9004
9813
|
}
|
|
9005
|
-
if (requiresMcpToolsets && !adapterSupportsMcpToolsets(
|
|
9814
|
+
if (requiresMcpToolsets && !adapterSupportsMcpToolsets(descriptor)) {
|
|
9006
9815
|
return {
|
|
9007
9816
|
ok: false,
|
|
9008
9817
|
reason: `runtime "${requestedRuntime}" cannot project required MCP toolsets`,
|
|
@@ -9017,15 +9826,16 @@ var TaskRunner = class {
|
|
|
9017
9826
|
retryable: true
|
|
9018
9827
|
};
|
|
9019
9828
|
}
|
|
9020
|
-
return { ok: true, adapter };
|
|
9829
|
+
return { ok: true, adapter, descriptor };
|
|
9021
9830
|
}
|
|
9022
|
-
const eligible = allowlist ? this.deps.adapters.filter((a) => allowlist.includes(a.id)) : this.deps.adapters;
|
|
9831
|
+
const eligible = allowlist ? this.deps.adapters.filter((a) => allowlist.includes(a.descriptor.id)) : this.deps.adapters;
|
|
9023
9832
|
const candidates = orderByPreference(eligible, this.deps.runtimePreference ?? DEFAULT_RUNTIME_PREFERENCE);
|
|
9024
9833
|
for (const adapter of candidates) {
|
|
9025
|
-
|
|
9026
|
-
if (
|
|
9834
|
+
const descriptor = freezeRuntimeAdapterDescriptor(adapter.descriptor);
|
|
9835
|
+
if (!adapterSupportsMode(descriptor, policyMode)) continue;
|
|
9836
|
+
if (requiresMcpToolsets && !adapterSupportsMcpToolsets(descriptor)) continue;
|
|
9027
9837
|
const detected = await adapter.detect();
|
|
9028
|
-
if (detected.present) return { ok: true, adapter };
|
|
9838
|
+
if (detected.present) return { ok: true, adapter, descriptor };
|
|
9029
9839
|
}
|
|
9030
9840
|
return {
|
|
9031
9841
|
ok: false,
|
|
@@ -9070,27 +9880,27 @@ async function detectRuntimes(adapters) {
|
|
|
9070
9880
|
const detections = await Promise.all(adapters.map(async (adapter) => ({ adapter, detected: await adapter.detect() })));
|
|
9071
9881
|
const runtimes = [];
|
|
9072
9882
|
for (const { adapter, detected } of detections) {
|
|
9073
|
-
if (!detected.present || !isRuntimeId(adapter.id)) continue;
|
|
9074
|
-
const info = { id: adapter.id };
|
|
9883
|
+
if (!detected.present || !isRuntimeId(adapter.descriptor.id)) continue;
|
|
9884
|
+
const info = { id: adapter.descriptor.id };
|
|
9075
9885
|
if (detected.version !== void 0) info.version = detected.version;
|
|
9076
9886
|
if (detected.authPresent !== void 0) info.authPresent = detected.authPresent;
|
|
9077
|
-
info.capabilities = toRuntimeInfoCapabilities(adapter.capabilities
|
|
9887
|
+
info.capabilities = toRuntimeInfoCapabilities(adapter.descriptor.capabilities);
|
|
9078
9888
|
runtimes.push(info);
|
|
9079
9889
|
}
|
|
9080
9890
|
return runtimes;
|
|
9081
9891
|
}
|
|
9082
9892
|
function computeCapabilities(adapters) {
|
|
9083
9893
|
const flags = [];
|
|
9084
|
-
if (adapters.some((adapter) => adapter.capabilities
|
|
9894
|
+
if (adapters.some((adapter) => adapter.descriptor.capabilities.steer)) flags.push("steer");
|
|
9085
9895
|
flags.push("blob-upload");
|
|
9086
9896
|
flags.push("approval-targeting");
|
|
9087
9897
|
const selectionAdapters = adapters.filter(
|
|
9088
|
-
(adapter) => ALL_RUNTIME_IDS.includes(adapter.id)
|
|
9898
|
+
(adapter) => ALL_RUNTIME_IDS.includes(adapter.descriptor.id)
|
|
9089
9899
|
);
|
|
9090
|
-
if (selectionAdapters.length > 0 && selectionAdapters.every((adapter) => adapter.supportsDispatchSelection === true)) {
|
|
9900
|
+
if (selectionAdapters.length > 0 && selectionAdapters.every((adapter) => adapter.descriptor.supportsDispatchSelection === true)) {
|
|
9091
9901
|
flags.push("dispatch-selection");
|
|
9092
9902
|
}
|
|
9093
|
-
if (adapters.some((adapter) => adapter.capabilities
|
|
9903
|
+
if (adapters.some((adapter) => adapter.descriptor.capabilities.mcpToolsets === true)) {
|
|
9094
9904
|
flags.push("toolset-selection");
|
|
9095
9905
|
}
|
|
9096
9906
|
return flags;
|
|
@@ -9150,7 +9960,6 @@ function validatePiByokLauncherConfig(launcher) {
|
|
|
9150
9960
|
throw new Error("DaemonConfig.piByokLauncher.args must contain only non-empty single-line strings");
|
|
9151
9961
|
}
|
|
9152
9962
|
}
|
|
9153
|
-
var MAX_LOCAL_MCP_TOOLSETS = 64;
|
|
9154
9963
|
var MAX_LOCAL_MCP_SERVERS_PER_TOOLSET = 16;
|
|
9155
9964
|
var MAX_LOCAL_MCP_ARGS = 64;
|
|
9156
9965
|
var MAX_LOCAL_MCP_TOKEN_CHARS = 4096;
|
|
@@ -9163,8 +9972,10 @@ function resolveMcpToolsets(configured) {
|
|
|
9163
9972
|
throw new Error("DaemonConfig.mcpToolsets must be an object keyed by logical toolset id");
|
|
9164
9973
|
}
|
|
9165
9974
|
const toolsetEntries = Object.entries(configured);
|
|
9166
|
-
if (toolsetEntries.length >
|
|
9167
|
-
throw new Error(
|
|
9975
|
+
if (toolsetEntries.length > CONFIGURED_TOOLSETS_MAX_ITEMS) {
|
|
9976
|
+
throw new Error(
|
|
9977
|
+
`DaemonConfig.mcpToolsets may contain at most ${CONFIGURED_TOOLSETS_MAX_ITEMS} toolsets`
|
|
9978
|
+
);
|
|
9168
9979
|
}
|
|
9169
9980
|
const resolved = /* @__PURE__ */ new Map();
|
|
9170
9981
|
for (const [toolsetId, rawToolset] of toolsetEntries) {
|
|
@@ -9277,6 +10088,9 @@ function createDaemonWithAdapters(config, adapters, overrides = {}) {
|
|
|
9277
10088
|
}
|
|
9278
10089
|
function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProbe) {
|
|
9279
10090
|
const mcpToolsets = resolveMcpToolsets(config.mcpToolsets);
|
|
10091
|
+
const configuredToolsets = Object.freeze(
|
|
10092
|
+
[...mcpToolsets?.keys() ?? []].sort()
|
|
10093
|
+
);
|
|
9280
10094
|
if (config.piByokLauncher !== void 0) {
|
|
9281
10095
|
validatePiByokLauncherConfig(config.piByokLauncher);
|
|
9282
10096
|
}
|
|
@@ -9583,12 +10397,13 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
9583
10397
|
dirty: event.observation ? { staged: event.observation.staged, unstaged: event.observation.unstaged, untracked: event.observation.untracked, conflicted: event.observation.conflicted } : void 0,
|
|
9584
10398
|
errorCategory: event.errorCategory
|
|
9585
10399
|
}),
|
|
10400
|
+
onRuntimeDisposalFailure: (event) => observer.noteRuntimeDisposalFailure(event),
|
|
9586
10401
|
// M4 Phase 3: the SAME `ApprovalRegistry` instance the control
|
|
9587
10402
|
// socket's own `approvals.list`/`approvals.resolve` methods already
|
|
9588
10403
|
// share (see that field's own construction above) — `TaskRunner
|
|
9589
10404
|
// .requestApproval` registers into it directly, so a decision arriving
|
|
9590
10405
|
// via either the server wire or the local CLI resolves the identical
|
|
9591
|
-
// entry. `storeDir`/`productId` let
|
|
10406
|
+
// entry. `storeDir`/`productId` let the prepared operation approval channel
|
|
9592
10407
|
// (populated per-task by `TaskRunner`) tell an out-of-process helper
|
|
9593
10408
|
// (`bin/byok-approval-mcp.ts`) exactly which control socket to dial.
|
|
9594
10409
|
approvalRegistry,
|
|
@@ -9640,6 +10455,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
9640
10455
|
productId: config.productId,
|
|
9641
10456
|
capabilities,
|
|
9642
10457
|
runtimes,
|
|
10458
|
+
configuredToolsets,
|
|
9643
10459
|
auth,
|
|
9644
10460
|
cursorStore,
|
|
9645
10461
|
// Finding F3: return (not void-and-forget) so ConnectionManager can
|
|
@@ -9726,6 +10542,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
9726
10542
|
presencePublisher ??= new PresencePublisher({
|
|
9727
10543
|
serverUrl: config.serverUrl,
|
|
9728
10544
|
auth,
|
|
10545
|
+
configuredToolsets,
|
|
9729
10546
|
...presenceCadence,
|
|
9730
10547
|
onDegraded: (reason) => console.warn(`[byok/client] ${reason}`)
|
|
9731
10548
|
});
|
|
@@ -9819,8 +10636,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
9819
10636
|
await attempt(() => auth.stop(), true);
|
|
9820
10637
|
connectionState = "closed";
|
|
9821
10638
|
await attempt(async () => {
|
|
9822
|
-
await controlServerHandle?.
|
|
9823
|
-
controlServerHandle = void 0;
|
|
10639
|
+
await controlServerHandle?.stopServing();
|
|
9824
10640
|
}, true);
|
|
9825
10641
|
if (mutationBarrierComplete && daemonOwnerLease) {
|
|
9826
10642
|
await attempt(() => operationalHealth.markCleanStop(), false);
|
|
@@ -9829,6 +10645,12 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
9829
10645
|
daemonOwnerLease = void 0;
|
|
9830
10646
|
}, false);
|
|
9831
10647
|
}
|
|
10648
|
+
if (daemonOwnerLease === void 0) {
|
|
10649
|
+
await attempt(async () => {
|
|
10650
|
+
await controlServerHandle?.close();
|
|
10651
|
+
controlServerHandle = void 0;
|
|
10652
|
+
}, false);
|
|
10653
|
+
}
|
|
9832
10654
|
if (errors.length === 1) throw errors[0];
|
|
9833
10655
|
if (errors.length > 1) {
|
|
9834
10656
|
throw new AggregateError(errors, "daemon shutdown completed with errors");
|
|
@@ -9908,7 +10730,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
9908
10730
|
deviceId: auth.deviceId,
|
|
9909
10731
|
transport: connectionState,
|
|
9910
10732
|
activeTasks,
|
|
9911
|
-
runtimeIds: adapters.map((adapter) => adapter.id),
|
|
10733
|
+
runtimeIds: adapters.map((adapter) => adapter.descriptor.id),
|
|
9912
10734
|
// M4 Phase 4 (part B.3): queue watermarks come from TaskRunner's own
|
|
9913
10735
|
// active-task map (distinct from `observer.tasks()` above, which is
|
|
9914
10736
|
// derived from the envelope feed) — see `TaskRunner.getQueueWatermarks`'s
|
|
@@ -10116,7 +10938,7 @@ function createDaemon(config) {
|
|
|
10116
10938
|
return createDaemonWithAdapters(config, buildDefaultAdapters(config));
|
|
10117
10939
|
}
|
|
10118
10940
|
var MAX_CONTROL_TOKEN_BYTES = 256;
|
|
10119
|
-
function
|
|
10941
|
+
function errorMessage5(err) {
|
|
10120
10942
|
return err instanceof Error ? err.message : String(err);
|
|
10121
10943
|
}
|
|
10122
10944
|
function sameFileState3(left, right) {
|
|
@@ -10169,7 +10991,7 @@ async function connectControlClient(opts) {
|
|
|
10169
10991
|
}
|
|
10170
10992
|
token = read;
|
|
10171
10993
|
} catch (err) {
|
|
10172
|
-
return { ok: false, reason: `could not read the control token: ${
|
|
10994
|
+
return { ok: false, reason: `could not read the control token: ${errorMessage5(err)}` };
|
|
10173
10995
|
}
|
|
10174
10996
|
if (!token) {
|
|
10175
10997
|
return { ok: false, reason: "control token file is empty" };
|
|
@@ -10179,7 +11001,7 @@ async function connectControlClient(opts) {
|
|
|
10179
11001
|
const client = await connectAndHandshake(endpoint, token, opts);
|
|
10180
11002
|
return { ok: true, client };
|
|
10181
11003
|
} catch (err) {
|
|
10182
|
-
return { ok: false, reason: `daemon control socket not reachable: ${
|
|
11004
|
+
return { ok: false, reason: `daemon control socket not reachable: ${errorMessage5(err)}` };
|
|
10183
11005
|
}
|
|
10184
11006
|
}
|
|
10185
11007
|
function connectAndHandshake(endpoint, token, opts) {
|
|
@@ -10356,7 +11178,7 @@ function createControlClient(socket, reader, opts) {
|
|
|
10356
11178
|
}
|
|
10357
11179
|
|
|
10358
11180
|
// src/daemon/assertion-client.ts
|
|
10359
|
-
function
|
|
11181
|
+
function errorMessage6(err) {
|
|
10360
11182
|
return err instanceof Error ? err.message : String(err);
|
|
10361
11183
|
}
|
|
10362
11184
|
async function requestDeviceAssertion(options) {
|
|
@@ -10378,7 +11200,7 @@ async function requestDeviceAssertion(options) {
|
|
|
10378
11200
|
return { ok: true, assertion, expiresAt: result.expiresAt };
|
|
10379
11201
|
} catch (err) {
|
|
10380
11202
|
if (err instanceof ControlError) return { ok: false, code: err.code, reason: err.message };
|
|
10381
|
-
return { ok: false, code: "bad_response", reason:
|
|
11203
|
+
return { ok: false, code: "bad_response", reason: errorMessage6(err) };
|
|
10382
11204
|
} finally {
|
|
10383
11205
|
connected.client.close();
|
|
10384
11206
|
}
|
|
@@ -11614,6 +12436,6 @@ function createServiceLifecycle(def, opts = {}) {
|
|
|
11614
12436
|
}
|
|
11615
12437
|
}
|
|
11616
12438
|
|
|
11617
|
-
export { AuthManager, BlobClient, ClaudeAdapter, CodexAdapter, DEFAULT_ACK_CRITICAL_RESERVE_BYTES, DEFAULT_CLEANUP_BATCH_LIMIT, DEFAULT_HARD_BUDGET_RATIO, DEFAULT_INCREMENTAL_VACUUM_PAGES, DEFAULT_LOG_ROTATION, DEFAULT_NORMAL_COMPACTION_INTERVAL_MS, DEFAULT_PRESSURE_COMPACTION_INTERVAL_MS, DEFAULT_RETENTION_MS, DEFAULT_SOFT_BUDGET_RATIO, DaemonObserver, DeviceRevokedError, GitWorkspaceError, GitWorkspaceManager, GitWorkspaceStore, JOURNAL_DB_FILENAME, JOURNAL_QUARANTINE_DIRNAME, JOURNAL_TASK_REF_PREFIX, JournalClosedError, JournalCorruptError, JournalRecordTooLargeError, JournalUnavailableError, JournalUnknownTaskError, LocalStorageEmergencyError, LocalStoragePolicyError, LocalStoragePressureEngine, PI_PACKAGE_NAME, PiAdapter, PolicyUnsupportedError, SKILL_PACKS_CAPABILITY, SKILL_PACKS_DIRNAME, SKILL_PACK_AUDIT_FILENAME, SKILL_PACK_INSTALL_ERROR_CODES, SKILL_PACK_LOCK_FILENAME, SKILL_PACK_LOCK_SCHEMA, SKILL_PACK_RESPONSE_MAX_BYTES, SecureDirHardeningError, SkillPackInstallError, SqliteLocalTaskJournal, SteerUnsupportedError, StoredDeviceProofSigner, TruthMemoryClient, TruthMemoryClientError, UnsupportedServicePlatformError, buildIcaclsArgs, cleanupEligibleAt, cleanupOrderFor, computePressureState, createDaemon, createDaemonWithAdapters, createFilesystemCleanupExecutor, createServiceLifecycle, createStatfsFreeBytesProvider, ensureSecureDir, generateLaunchdPlist, generateSystemdUnit, generateWinswXml, installSkillPacks, isGitWorkspaceConfig, journalHash, listInstalledSkillPacks, nodeAgentProgram, prependGitWorkspaceGuidance, projectSkillPack, requestDeviceAssertion, resolveLocalStoragePolicy, sanitizeServiceName, skillPacksRoot };
|
|
12439
|
+
export { AuthManager, BlobClient, ClaudeAdapter, CodexAdapter, DEFAULT_ACK_CRITICAL_RESERVE_BYTES, DEFAULT_CLEANUP_BATCH_LIMIT, DEFAULT_HARD_BUDGET_RATIO, DEFAULT_INCREMENTAL_VACUUM_PAGES, DEFAULT_LOG_ROTATION, DEFAULT_NORMAL_COMPACTION_INTERVAL_MS, DEFAULT_PRESSURE_COMPACTION_INTERVAL_MS, DEFAULT_RETENTION_MS, DEFAULT_SOFT_BUDGET_RATIO, DaemonObserver, DeviceRevokedError, GitWorkspaceError, GitWorkspaceManager, GitWorkspaceStore, JOURNAL_DB_FILENAME, JOURNAL_QUARANTINE_DIRNAME, JOURNAL_TASK_REF_PREFIX, JournalClosedError, JournalCorruptError, JournalRecordTooLargeError, JournalUnavailableError, JournalUnknownTaskError, LocalStorageEmergencyError, LocalStoragePolicyError, LocalStoragePressureEngine, PI_PACKAGE_NAME, PiAdapter, PolicyUnsupportedError, RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON, RuntimeDisposalFailure, RuntimeExecutionFailure, SKILL_PACKS_CAPABILITY, SKILL_PACKS_DIRNAME, SKILL_PACK_AUDIT_FILENAME, SKILL_PACK_INSTALL_ERROR_CODES, SKILL_PACK_LOCK_FILENAME, SKILL_PACK_LOCK_SCHEMA, SKILL_PACK_RESPONSE_MAX_BYTES, SecureDirHardeningError, SkillPackInstallError, SqliteLocalTaskJournal, SteerUnsupportedError, StoredDeviceProofSigner, TruthMemoryClient, TruthMemoryClientError, UnsupportedServicePlatformError, buildIcaclsArgs, cleanupEligibleAt, cleanupOrderFor, computePressureState, createDaemon, createDaemonWithAdapters, createFilesystemCleanupExecutor, createServiceLifecycle, createStatfsFreeBytesProvider, ensureSecureDir, freezeRuntimeAdapterDescriptor, generateLaunchdPlist, generateSystemdUnit, generateWinswXml, installSkillPacks, isGitWorkspaceConfig, isRuntimeDisposalFailure, isRuntimeExecutionFailure, journalHash, listInstalledSkillPacks, nodeAgentProgram, prependGitWorkspaceGuidance, projectRuntimeBoundaryFailure, projectRuntimeExecutionFailure, projectSkillPack, requestDeviceAssertion, resolveLocalStoragePolicy, sanitizeServiceName, sealRuntimeOperationManifest, skillPacksRoot };
|
|
11618
12440
|
//# sourceMappingURL=index.js.map
|
|
11619
12441
|
//# sourceMappingURL=index.js.map
|