@mono-agent/agent-runtime 0.20.10 → 0.20.14
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/MIGRATION.md +264 -25
- package/README.md +139 -448
- package/package.json +7 -43
- package/src/agent/tools/agent-tool.js +5 -5
- package/src/agent/tools/bash.js +1 -24
- package/src/agent/tools/codex-subscription-search.js +1 -1
- package/src/agent/tools/index.js +7 -0
- package/src/agent/tools/monitor.js +140 -0
- package/src/agent/tools/pi-bridge.js +92 -7
- package/src/agent/tools/shared/bash-environment.js +31 -0
- package/src/agent/tools/shared/monitors.js +274 -0
- package/src/agent/tools/shared/process-runner.js +23 -5
- package/src/ai/cost.js +13 -68
- package/src/ai/index.js +4 -17
- package/src/ai/pi-interop.js +65 -1
- package/src/ai/pi-oauth-compat.js +1 -1
- package/src/ai/providers/codex/app-server-client.js +592 -0
- package/src/ai/providers/pi-models.js +18 -10
- package/src/ai/providers/pi-native/compaction-driver.js +49 -21
- package/src/ai/providers/pi-native/harness-adapter.js +338 -0
- package/src/ai/providers/pi-native/result-builder.js +10 -10
- package/src/ai/providers/pi-native/session-lifecycle.js +95 -40
- package/src/ai/providers/pi-native/stream-subscriber.js +23 -5
- package/src/ai/providers/pi-native/turn-runner.js +34 -15
- package/src/ai/providers/pi-native.js +48 -22
- package/src/ai/runtime/capabilities.js +11 -56
- package/src/ai/runtime/model-refs.js +118 -153
- package/src/ai/runtime/registry.js +22 -56
- package/src/ai/runtime/router.js +48 -408
- package/src/ai/runtime/session-liveness.js +3 -4
- package/src/ai/runtime/sessions.js +4 -5
- package/src/ai/runtime/tool-policy.js +0 -2
- package/src/ai/types.js +12 -108
- package/src/index.js +0 -6
- package/src/runtime.js +8 -14
- package/types/agent/tools/agent-tool.d.ts +1 -2
- package/types/agent/tools/codex-subscription-search.d.ts +1 -1
- package/types/agent/tools/index.d.ts +1 -0
- package/types/agent/tools/monitor.d.ts +44 -0
- package/types/agent/tools/pi-bridge.d.ts +3 -2
- package/types/agent/tools/shared/bash-environment.d.ts +4 -0
- package/types/agent/tools/shared/monitors.d.ts +83 -0
- package/types/agent/tools/shared/process-runner.d.ts +11 -2
- package/types/ai/cost.d.ts +1 -2
- package/types/ai/index.d.ts +1 -4
- package/types/ai/pi-interop.d.ts +20 -0
- package/types/ai/providers/codex/app-server-client.d.ts +37 -0
- package/types/ai/providers/pi-native/harness-adapter.d.ts +56 -0
- package/types/ai/providers/pi-native/result-builder.d.ts +3 -3
- package/types/ai/providers/pi-native/session-lifecycle.d.ts +2 -1
- package/types/ai/providers/pi-native/stream-subscriber.d.ts +2 -2
- package/types/ai/providers/pi-native/turn-runner.d.ts +33 -6
- package/types/ai/providers/pi-native.d.ts +21 -4
- package/types/ai/runtime/capabilities.d.ts +21 -70
- package/types/ai/runtime/model-refs.d.ts +0 -24
- package/types/ai/runtime/router.d.ts +3 -10
- package/types/ai/runtime/tool-policy.d.ts +0 -2
- package/types/ai/types.d.ts +28 -250
- package/types/index.d.ts +0 -1
- package/src/ai/providers/acp-client.js +0 -1149
- package/src/ai/providers/acp-privacy.js +0 -124
- package/src/ai/providers/acp-public.js +0 -21
- package/src/ai/providers/acp-session-tokens.js +0 -282
- package/src/ai/providers/acp-transport.js +0 -356
- package/src/ai/providers/acp.js +0 -543
- package/src/ai/providers/claude-cli.js +0 -883
- package/src/ai/providers/claude-sandbox.js +0 -71
- package/src/ai/providers/claude-sdk-discovery-worker.js +0 -53
- package/src/ai/providers/claude-sdk-discovery.js +0 -352
- package/src/ai/providers/claude-sdk.js +0 -1127
- package/src/ai/providers/claude-subagent-activity.js +0 -719
- package/src/ai/providers/claude-subagents.js +0 -88
- package/src/ai/providers/codex-app.js +0 -2946
- package/src/ai/providers/opencode-app.js +0 -1109
- package/src/ai/providers/opencode-discovery.js +0 -39
- package/src/ai/providers/opencode-server.js +0 -508
- package/src/ai/runtime/context-windows.js +0 -46
- package/src/ai/runtime/fast-mode.js +0 -8
- package/src/ai/streaming/codex-events.js +0 -146
- package/src/ai/streaming/opencode-events.js +0 -59
- package/types/ai/providers/acp-client.d.ts +0 -227
- package/types/ai/providers/acp-privacy.d.ts +0 -25
- package/types/ai/providers/acp-public.d.ts +0 -7
- package/types/ai/providers/acp-session-tokens.d.ts +0 -41
- package/types/ai/providers/acp-transport.d.ts +0 -45
- package/types/ai/providers/acp.d.ts +0 -93
- package/types/ai/providers/claude-cli.d.ts +0 -305
- package/types/ai/providers/claude-sandbox.d.ts +0 -79
- package/types/ai/providers/claude-sdk-discovery-worker.d.ts +0 -1
- package/types/ai/providers/claude-sdk-discovery.d.ts +0 -97
- package/types/ai/providers/claude-sdk.d.ts +0 -138
- package/types/ai/providers/claude-subagent-activity.d.ts +0 -53
- package/types/ai/providers/claude-subagents.d.ts +0 -18
- package/types/ai/providers/codex-app.d.ts +0 -151
- package/types/ai/providers/opencode-app.d.ts +0 -96
- package/types/ai/providers/opencode-discovery.d.ts +0 -4
- package/types/ai/providers/opencode-server.d.ts +0 -20
- package/types/ai/runtime/context-windows.d.ts +0 -9
- package/types/ai/runtime/fast-mode.d.ts +0 -2
- package/types/ai/streaming/codex-events.d.ts +0 -40
- package/types/ai/streaming/opencode-events.d.ts +0 -42
package/src/ai/runtime/router.js
CHANGED
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
// so the router is a drop-in replacement for createRuntime(host).
|
|
21
21
|
//
|
|
22
22
|
// chain entries:
|
|
23
|
-
// { model: ModelRef,
|
|
24
|
-
// requires?: Capabilities }
|
|
23
|
+
// { model: ModelRef, effort?: string|null, requires?: Capabilities }
|
|
25
24
|
// shorthand: a bare ModelRef is also accepted (no requirements).
|
|
26
25
|
// effort string = fixed for that route, undefined = inherit the legacy run
|
|
27
26
|
// effort, null = omit effort so the provider chooses its default.
|
|
@@ -55,10 +54,8 @@ import { instrumentLiveInputAppliedEvents } from "./live-input-events.js";
|
|
|
55
54
|
/**
|
|
56
55
|
* @typedef {Object} RouterChainEntryInput
|
|
57
56
|
* A chain entry as accepted by createRouterRuntime: either the shorthand bare
|
|
58
|
-
* RuntimeModelRef, or the full `{model,
|
|
59
|
-
* attempts?}` form.
|
|
57
|
+
* RuntimeModelRef, or the full `{model, effort?, requires?, attempts?}` form.
|
|
60
58
|
* @property {RuntimeModelRef} model
|
|
61
|
-
* @property {string} [executionMode]
|
|
62
59
|
* @property {string|null} [effort]
|
|
63
60
|
* @property {Object<string, *>} [requires]
|
|
64
61
|
* @property {number} [attempts]
|
|
@@ -67,7 +64,6 @@ import { instrumentLiveInputAppliedEvents } from "./live-input-events.js";
|
|
|
67
64
|
/**
|
|
68
65
|
* @typedef {Object} RouterChainEntry
|
|
69
66
|
* @property {RuntimeModelRef} model
|
|
70
|
-
* @property {string|null} executionMode
|
|
71
67
|
* @property {string|null|undefined} effort
|
|
72
68
|
* @property {Object<string, *>|null} requires
|
|
73
69
|
* @property {number} attempts Total attempts on this route including the first.
|
|
@@ -91,18 +87,16 @@ import { instrumentLiveInputAppliedEvents } from "./live-input-events.js";
|
|
|
91
87
|
* @property {() => (void|Promise<void>)} [cleanup]
|
|
92
88
|
*/
|
|
93
89
|
|
|
94
|
-
const ROUTE_SAFETY_MODES = new Set(["uniform", "per-route-native"]);
|
|
95
90
|
const ATTEMPT_SCOPED_OPTION_KEYS = ["customProvider", "customModel", "modelCapabilities", "isPrivateProvider"];
|
|
96
91
|
const ROUTER_TOOL_CONTEXT_KEYS = [
|
|
97
92
|
"workspace", "repoRoot", "ripgrepPath", "qaOutputDir", "sandboxPolicy", "sandboxEngine",
|
|
98
93
|
];
|
|
99
94
|
const RESOLVER_PROTECTED_OPTION_KEYS = new Set([
|
|
100
|
-
"model", "
|
|
95
|
+
"model", "effort", "messages", "abortSignal", "onEvent",
|
|
101
96
|
"sessionId", "providerSessionId", "sessionKeepAlive", "sessionIdleTimeoutMs",
|
|
102
97
|
"diagnosticsSeed", "systemPromptPrefix", "sandboxPolicy", "sandboxEngine", "sandbox",
|
|
103
98
|
"allowedTools", "disallowedTools", "permissionMode", "mcpServers", "mcpApps", "skills",
|
|
104
|
-
"outputSchema", "
|
|
105
|
-
"codexSandboxNetworkAccess",
|
|
99
|
+
"outputSchema", "liveInput", "toolEnvironment",
|
|
106
100
|
]);
|
|
107
101
|
|
|
108
102
|
class ResolverProtectedOptionError extends Error {
|
|
@@ -117,16 +111,12 @@ class ResolverProtectedOptionError extends Error {
|
|
|
117
111
|
* @param {Object} [options]
|
|
118
112
|
* @param {AgentRuntimeHostOptions} [options.host]
|
|
119
113
|
* @param {ReadonlyArray<RuntimeModelRef|RouterChainEntryInput>} [options.chain]
|
|
120
|
-
* @param {
|
|
121
|
-
* @param {(input: {model: RuntimeModelRef, executionMode: string|null, attemptIndex: number, retryIndex: number, routeSafety: "uniform"|"per-route-native"}) => (RouterAttemptResolution|Promise<RouterAttemptResolution>)} [options.resolveAttempt]
|
|
114
|
+
* @param {(input: {model: RuntimeModelRef, attemptIndex: number, retryIndex: number}) => (RouterAttemptResolution|Promise<RouterAttemptResolution>)} [options.resolveAttempt]
|
|
122
115
|
* @param {Partial<RouterRetryPolicy>} [options.retry] Backoff shape for same-model
|
|
123
116
|
* retries. Per-route retry counts live on each chain entry's `attempts`.
|
|
124
117
|
* @returns {AgentRuntimeInstance & {chain: () => Array<RouterChainEntry>}}
|
|
125
118
|
*/
|
|
126
|
-
export function createRouterRuntime({ host = {}, chain = [],
|
|
127
|
-
if (!ROUTE_SAFETY_MODES.has(routeSafety)) {
|
|
128
|
-
throw new Error("createRouterRuntime routeSafety must be uniform or per-route-native");
|
|
129
|
-
}
|
|
119
|
+
export function createRouterRuntime({ host = {}, chain = [], resolveAttempt, retry } = {}) {
|
|
130
120
|
const retryPolicy = normalizeRetryPolicy(retry);
|
|
131
121
|
const entries = normaliseChain(chain);
|
|
132
122
|
if (entries.length === 0) {
|
|
@@ -134,8 +124,6 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
134
124
|
}
|
|
135
125
|
assertUniqueEntries(entries);
|
|
136
126
|
const inner = createRuntime(host);
|
|
137
|
-
/** @type {Map<string, AgentRuntimeInstance>} */
|
|
138
|
-
const routeRuntimes = new Map();
|
|
139
127
|
/** @type {import('../types.js').AgentRuntimeToolOptions|undefined} */
|
|
140
128
|
let configuredTools;
|
|
141
129
|
// The router builds transcript-tail snapshots outside the inner runtime's
|
|
@@ -148,8 +136,8 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
148
136
|
/**
|
|
149
137
|
* @param {string} systemPrompt
|
|
150
138
|
* @param {Partial<RuntimeRunOptions>} [options] Optional so a bare `{}` call
|
|
151
|
-
* is legal; the router always overrides `model
|
|
152
|
-
*
|
|
139
|
+
* is legal; the router always overrides `model` per chain entry (see
|
|
140
|
+
* AgentRuntimeInstance.run for the public, model-required contract).
|
|
153
141
|
* @returns {Promise<RuntimeResult>}
|
|
154
142
|
*/
|
|
155
143
|
async run(systemPrompt, options = {}) {
|
|
@@ -169,7 +157,7 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
169
157
|
};
|
|
170
158
|
}
|
|
171
159
|
try {
|
|
172
|
-
/** @type {Array<{model: RuntimeModelRef, failureKind: (string|null), requestId?: (string|null|undefined), retryableSubkind?: (string|null|undefined), requirements?: (Object<string,*>|null),
|
|
160
|
+
/** @type {Array<{model: RuntimeModelRef, failureKind: (string|null), requestId?: (string|null|undefined), retryableSubkind?: (string|null|undefined), requirements?: (Object<string,*>|null), retryIndex?: number}>} */
|
|
173
161
|
const failoverHistory = [];
|
|
174
162
|
/** @type {RuntimeResult|null} */
|
|
175
163
|
let lastResult = null;
|
|
@@ -178,46 +166,9 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
178
166
|
const promptBase = systemPrompt;
|
|
179
167
|
/** @type {*} */
|
|
180
168
|
let pendingSnapshot = null;
|
|
181
|
-
/** @type {Array<{attemptIndex: number, model: RuntimeModelRef, routeSafety: import('../types.js').RuntimeRouteSafetyMode, safetyContract: import('../types.js').RuntimeRouteSafetyContract, status: string}>} */
|
|
182
|
-
const routeSafetyHistory = [];
|
|
183
|
-
|
|
184
169
|
for (let i = 0; i < entries.length; i += 1) {
|
|
185
170
|
const entry = entries[i];
|
|
186
171
|
const effectiveToolOptions = effectiveRouterToolOptions(host, configuredTools);
|
|
187
|
-
const entrySafetyContract = routeSafetyContract(
|
|
188
|
-
routeSafety,
|
|
189
|
-
entry,
|
|
190
|
-
entry.model.sdk === "pi"
|
|
191
|
-
? effectivePiSandboxPolicy(effectiveToolOptions, options)
|
|
192
|
-
: undefined,
|
|
193
|
-
);
|
|
194
|
-
// Internal protected roots are enforced by mono-agent's Pi tool layer
|
|
195
|
-
// and SRT projection. Provider-native non-Pi routes deliberately drop
|
|
196
|
-
// that layer, so attempting one would turn the router into a confused
|
|
197
|
-
// deputy for private host state. Reject the route before resolution or
|
|
198
|
-
// provider construction; a later Pi entry may still satisfy the run.
|
|
199
|
-
if (
|
|
200
|
-
entry.model.sdk !== "pi"
|
|
201
|
-
&& attemptCarriesProtectedRoots(effectiveToolOptions, options)
|
|
202
|
-
) {
|
|
203
|
-
const failure = safetyUnavailableResult();
|
|
204
|
-
lastRouteSkip = failure;
|
|
205
|
-
failoverHistory.push({
|
|
206
|
-
model: entry.model,
|
|
207
|
-
failureKind: "safety_unavailable",
|
|
208
|
-
routeSafety,
|
|
209
|
-
safetyContract: entrySafetyContract,
|
|
210
|
-
});
|
|
211
|
-
const unavailableRecord = routeSafetyRecord(
|
|
212
|
-
i,
|
|
213
|
-
entry,
|
|
214
|
-
entrySafetyContract,
|
|
215
|
-
"safety_unavailable",
|
|
216
|
-
);
|
|
217
|
-
routeSafetyHistory.push(unavailableRecord);
|
|
218
|
-
emit(options, { type: "provider_route_safety", ...unavailableRecord });
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
172
|
if (!entrySatisfiesRequirements(entry, options)) {
|
|
222
173
|
lastRouteSkip = {
|
|
223
174
|
text: null,
|
|
@@ -231,12 +182,7 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
231
182
|
model: entry.model,
|
|
232
183
|
failureKind: "skipped_capability_mismatch",
|
|
233
184
|
requirements: entry.requires,
|
|
234
|
-
routeSafety,
|
|
235
|
-
safetyContract: entrySafetyContract,
|
|
236
185
|
});
|
|
237
|
-
const skippedRecord = routeSafetyRecord(i, entry, entrySafetyContract, "skipped_capability_mismatch");
|
|
238
|
-
routeSafetyHistory.push(skippedRecord);
|
|
239
|
-
emit(options, { type: "provider_route_safety", ...skippedRecord });
|
|
240
186
|
continue;
|
|
241
187
|
}
|
|
242
188
|
|
|
@@ -249,7 +195,6 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
249
195
|
const entryOptionsBase = {
|
|
250
196
|
...options,
|
|
251
197
|
model: entry.model,
|
|
252
|
-
executionMode: entry.executionMode || options.executionMode,
|
|
253
198
|
};
|
|
254
199
|
// The legacy run-level custom-provider bag describes the primary
|
|
255
200
|
// route. Without a route resolver there is no authoritative metadata
|
|
@@ -263,7 +208,6 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
263
208
|
let terminalResult = null;
|
|
264
209
|
|
|
265
210
|
for (let retryIndex = 0; retryIndex < entry.attempts; retryIndex += 1) {
|
|
266
|
-
let safetyContract = entrySafetyContract;
|
|
267
211
|
/** @type {*} */
|
|
268
212
|
let callOptions = { ...entryCallBase };
|
|
269
213
|
/** @type {AgentRuntimeInstance} */
|
|
@@ -275,10 +219,8 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
275
219
|
? undefined
|
|
276
220
|
: await resolveAttempt({
|
|
277
221
|
model: entry.model,
|
|
278
|
-
executionMode: entry.executionMode,
|
|
279
222
|
attemptIndex: i,
|
|
280
223
|
retryIndex,
|
|
281
|
-
routeSafety,
|
|
282
224
|
});
|
|
283
225
|
const resolution = normalizeAttemptResolution(resolved);
|
|
284
226
|
attemptCleanup = resolution?.cleanup;
|
|
@@ -286,50 +228,19 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
286
228
|
callOptions = mergeAttemptOptions(callOptions, resolution?.options);
|
|
287
229
|
callOptions = mergeAttemptPolicyOptions(callOptions, resolution?.policyOptions);
|
|
288
230
|
}
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (resolvedRuntime !== undefined) {
|
|
294
|
-
assertRuntimeLike(resolvedRuntime);
|
|
295
|
-
const previousRuntime = routeRuntimes.get(key);
|
|
296
|
-
if (previousRuntime !== undefined && previousRuntime !== resolvedRuntime) {
|
|
297
|
-
try { await previousRuntime.disposeAllSessions?.(); } catch { /* best-effort replacement */ }
|
|
298
|
-
}
|
|
299
|
-
routeRuntimes.set(key, resolvedRuntime);
|
|
300
|
-
if (entry.model.sdk !== "pi") {
|
|
301
|
-
resolvedRuntime.configureTools?.(projectPerRouteNativeToolOptions(entry, configuredTools));
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
attemptRuntime = routeRuntimes.get(key) ?? createRouteRuntime(key, entry, host, routeRuntimes, configuredTools);
|
|
305
|
-
if (entry.model.sdk === "pi") {
|
|
306
|
-
projectPiRuntimeToolContext(attemptRuntime, effectiveToolOptions);
|
|
307
|
-
// Derive the attestation from the same complete base context and
|
|
308
|
-
// request-scoped inputs that the supplied/runtime-owned Pi
|
|
309
|
-
// bridge will actually receive. Resolver options cannot alter
|
|
310
|
-
// these protected fields.
|
|
311
|
-
safetyContract = routeSafetyContract(
|
|
312
|
-
routeSafety,
|
|
313
|
-
entry,
|
|
314
|
-
effectivePiSandboxPolicy(effectiveToolOptions, callOptions),
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
} else if (resolution?.runtime !== undefined && resolution.runtime !== inner) {
|
|
318
|
-
throw new Error("uniform route safety cannot replace the shared monotonic runtime");
|
|
231
|
+
if (resolution?.runtime !== undefined) {
|
|
232
|
+
assertRuntimeLike(resolution.runtime);
|
|
233
|
+
attemptRuntime = resolution.runtime;
|
|
234
|
+
projectPiRuntimeToolContext(attemptRuntime, effectiveToolOptions);
|
|
319
235
|
}
|
|
320
236
|
} catch (error) {
|
|
321
237
|
try { await attemptCleanup?.(); } catch { /* cleanup is additive */ }
|
|
322
|
-
const failure =
|
|
323
|
-
|
|
238
|
+
const failure = attemptResolutionFailureResult(error);
|
|
239
|
+
lastResult = failure;
|
|
324
240
|
failoverHistory.push({
|
|
325
241
|
model: entry.model,
|
|
326
|
-
failureKind:
|
|
327
|
-
routeSafety,
|
|
328
|
-
safetyContract,
|
|
242
|
+
failureKind: failure.failureKind || null,
|
|
329
243
|
});
|
|
330
|
-
const unavailableRecord = routeSafetyRecord(i, entry, safetyContract, "safety_unavailable");
|
|
331
|
-
routeSafetyHistory.push(unavailableRecord);
|
|
332
|
-
emit(callOptions, { type: "provider_route_safety", ...unavailableRecord });
|
|
333
244
|
// A resolver fault is a config/credential problem, not a transient
|
|
334
245
|
// provider blip: retrying the same route cannot fix it. Advance.
|
|
335
246
|
break;
|
|
@@ -363,17 +274,6 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
363
274
|
}
|
|
364
275
|
}
|
|
365
276
|
|
|
366
|
-
// The safety contract is a property of the route: resolver options can
|
|
367
|
-
// never reach the sandbox/tool policy (RESOLVER_PROTECTED_OPTION_KEYS)
|
|
368
|
-
// and effectivePiSandboxPolicy reads only protected fields, so every
|
|
369
|
-
// retry of one entry derives an identical contract. Record it once so
|
|
370
|
-
// the bounded safety telemetry stays one record per chain entry.
|
|
371
|
-
if (retryIndex === 0) {
|
|
372
|
-
const safetyRecord = routeSafetyRecord(i, entry, safetyContract, "attempted");
|
|
373
|
-
routeSafetyHistory.push(safetyRecord);
|
|
374
|
-
emit(callOptions, { type: "provider_route_safety", ...safetyRecord });
|
|
375
|
-
}
|
|
376
|
-
|
|
377
277
|
// A same-model retry is not a failover: only the first attempt of a new
|
|
378
278
|
// route announces a transition.
|
|
379
279
|
if (retryIndex === 0 && failoverHistory.length > 0) {
|
|
@@ -433,7 +333,7 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
433
333
|
model: modelKey(entry.model),
|
|
434
334
|
});
|
|
435
335
|
}
|
|
436
|
-
return { ...result, failoverHistory
|
|
336
|
+
return { ...result, failoverHistory };
|
|
437
337
|
}
|
|
438
338
|
|
|
439
339
|
failoverHistory.push({
|
|
@@ -442,8 +342,6 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
442
342
|
requestId: retryability.requestId,
|
|
443
343
|
retryableSubkind: retryability.subkind,
|
|
444
344
|
...(retryIndex > 0 ? { retryIndex } : {}),
|
|
445
|
-
routeSafety,
|
|
446
|
-
safetyContract,
|
|
447
345
|
});
|
|
448
346
|
if (result.failureKind === "skipped_capability_mismatch") {
|
|
449
347
|
lastRouteSkip = result;
|
|
@@ -494,16 +392,16 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
494
392
|
reason: retryability.subkind || result.failureKind || null,
|
|
495
393
|
});
|
|
496
394
|
if (callOptions.abortSignal?.aborted) {
|
|
497
|
-
return { ...result, cancelled: true, failoverHistory
|
|
395
|
+
return { ...result, cancelled: true, failoverHistory };
|
|
498
396
|
}
|
|
499
397
|
await delay(backoffMs, callOptions.abortSignal);
|
|
500
398
|
if (callOptions.abortSignal?.aborted) {
|
|
501
|
-
return { ...result, cancelled: true, failoverHistory
|
|
399
|
+
return { ...result, cancelled: true, failoverHistory };
|
|
502
400
|
}
|
|
503
401
|
}
|
|
504
402
|
|
|
505
403
|
if (terminalResult !== null) {
|
|
506
|
-
return { ...terminalResult, failoverHistory
|
|
404
|
+
return { ...terminalResult, failoverHistory };
|
|
507
405
|
}
|
|
508
406
|
// Every other inner break falls through to the next chain entry.
|
|
509
407
|
}
|
|
@@ -520,7 +418,6 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
520
418
|
...exhaustedResult,
|
|
521
419
|
failureKind: lastResult ? "provider_unavailable_exhausted" : exhaustedResult.failureKind,
|
|
522
420
|
failoverHistory,
|
|
523
|
-
routeSafetyHistory,
|
|
524
421
|
};
|
|
525
422
|
} finally {
|
|
526
423
|
await liveInputHub?.flush();
|
|
@@ -529,65 +426,39 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
529
426
|
chain: () => entries.slice(),
|
|
530
427
|
configureTools(next = {}) {
|
|
531
428
|
configuredTools = { ...(configuredTools || {}), ...next };
|
|
532
|
-
|
|
533
|
-
inner.configureTools?.(next);
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
entries.forEach((entry, index) => {
|
|
537
|
-
const runtime = routeRuntimes.get(routeRuntimeKey(entry, index));
|
|
538
|
-
runtime?.configureTools?.(projectPerRouteNativeToolOptions(entry, next));
|
|
539
|
-
});
|
|
540
|
-
// `inner` is deliberately not configured in per-route-native mode: all
|
|
541
|
-
// attempts use their isolated route runtime, and applying one route's
|
|
542
|
-
// policy to this shared standby would defeat that isolation.
|
|
429
|
+
inner.configureTools?.(next);
|
|
543
430
|
},
|
|
544
431
|
async syncSession(providerSessionId) {
|
|
545
|
-
|
|
546
|
-
for (const runtime of allRuntimes(inner, routeRuntimes)) {
|
|
547
|
-
synced = Boolean(await runtime.syncSession?.(providerSessionId)) || synced;
|
|
548
|
-
}
|
|
549
|
-
return synced;
|
|
432
|
+
return Boolean(await inner.syncSession?.(providerSessionId));
|
|
550
433
|
},
|
|
551
434
|
async refreshSession(providerSessionId) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
throw new Error("A routed runtime cannot guarantee a cold provider-session reopen");
|
|
555
|
-
}
|
|
556
|
-
await runtime.refreshSession(providerSessionId);
|
|
435
|
+
if (typeof inner.refreshSession !== "function") {
|
|
436
|
+
throw new Error("A routed runtime cannot guarantee a cold provider-session reopen");
|
|
557
437
|
}
|
|
438
|
+
await inner.refreshSession(providerSessionId);
|
|
558
439
|
},
|
|
559
440
|
async retireDurableSession(providerSessionId, sessionsRoot) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
throw new Error("A routed runtime cannot retire durable provider-session state");
|
|
563
|
-
}
|
|
564
|
-
await runtime.retireDurableSession(providerSessionId, sessionsRoot);
|
|
441
|
+
if (typeof inner.retireDurableSession !== "function") {
|
|
442
|
+
throw new Error("A routed runtime cannot retire durable provider-session state");
|
|
565
443
|
}
|
|
444
|
+
await inner.retireDurableSession(providerSessionId, sessionsRoot);
|
|
566
445
|
},
|
|
567
446
|
async disposeSession(providerSessionId) {
|
|
568
|
-
|
|
569
|
-
for (const runtime of allRuntimes(inner, routeRuntimes)) {
|
|
570
|
-
disposed = Boolean(await runtime.disposeSession?.(providerSessionId)) || disposed;
|
|
571
|
-
}
|
|
572
|
-
return disposed;
|
|
447
|
+
return Boolean(await inner.disposeSession?.(providerSessionId));
|
|
573
448
|
},
|
|
574
449
|
async invalidateSession(providerSessionId) {
|
|
575
|
-
|
|
576
|
-
for (const runtime of allRuntimes(inner, routeRuntimes)) {
|
|
577
|
-
invalidated = Boolean(await runtime.invalidateSession?.(providerSessionId)) || invalidated;
|
|
578
|
-
}
|
|
579
|
-
return invalidated;
|
|
450
|
+
return Boolean(await inner.invalidateSession?.(providerSessionId));
|
|
580
451
|
},
|
|
581
452
|
async disposeAllSessions() {
|
|
582
|
-
await
|
|
453
|
+
await inner.disposeAllSessions?.();
|
|
583
454
|
},
|
|
584
455
|
};
|
|
585
456
|
}
|
|
586
457
|
|
|
587
458
|
/**
|
|
588
459
|
* @param {ReadonlyArray<*>} chain ReadonlyArray<RuntimeModelRef|RouterChainEntryInput>, loosened
|
|
589
|
-
* here because distinguishing the two shapes is a runtime duck-type check
|
|
590
|
-
*
|
|
460
|
+
* here because distinguishing the two shapes is a runtime duck-type check,
|
|
461
|
+
* not something a union type narrows cleanly.
|
|
591
462
|
* @returns {Array<RouterChainEntry>}
|
|
592
463
|
*/
|
|
593
464
|
function normaliseChain(chain) {
|
|
@@ -595,14 +466,13 @@ function normaliseChain(chain) {
|
|
|
595
466
|
return /** @type {Array<RouterChainEntry>} */ (chain
|
|
596
467
|
.map((entry) => {
|
|
597
468
|
if (!entry) return null;
|
|
598
|
-
if (entry.
|
|
599
|
-
// ModelRef shorthand: {
|
|
600
|
-
return { model: entry,
|
|
469
|
+
if (typeof entry.provider === "string" && typeof entry.model === "string") {
|
|
470
|
+
// ModelRef shorthand: { provider, model, reference }
|
|
471
|
+
return { model: entry, effort: undefined, requires: null, attempts: 1 };
|
|
601
472
|
}
|
|
602
473
|
if (entry.model) {
|
|
603
474
|
return {
|
|
604
475
|
model: entry.model,
|
|
605
|
-
executionMode: typeof entry.executionMode === "string" ? entry.executionMode : null,
|
|
606
476
|
effort: normalizeChainEffort(entry.effort),
|
|
607
477
|
requires: entry.requires && typeof entry.requires === "object" ? entry.requires : null,
|
|
608
478
|
attempts: normalizeChainAttempts(entry.attempts),
|
|
@@ -692,87 +562,7 @@ function assertUniqueEntries(entries) {
|
|
|
692
562
|
|
|
693
563
|
/** @param {RuntimeModelRef} model */
|
|
694
564
|
function modelKey(model) {
|
|
695
|
-
|
|
696
|
-
return `${model.sdk}:${provider}${model.model}`;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
/**
|
|
700
|
-
* A small fixed vocabulary keeps safety telemetry bounded and prevents route
|
|
701
|
-
* credentials/options from accidentally entering events or persisted results.
|
|
702
|
-
* @param {"uniform"|"per-route-native"} mode
|
|
703
|
-
* @param {RouterChainEntry} entry
|
|
704
|
-
* @param {Object<string, *>|undefined} piSandboxPolicy
|
|
705
|
-
* @returns {import('../types.js').RuntimeRouteSafetyContract}
|
|
706
|
-
*/
|
|
707
|
-
function routeSafetyContract(mode, entry, piSandboxPolicy) {
|
|
708
|
-
if (mode === "uniform") {
|
|
709
|
-
return {
|
|
710
|
-
mode,
|
|
711
|
-
sandbox: "mono-agent-monotonic",
|
|
712
|
-
tools: "mono-agent-monotonic",
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
switch (entry.model.sdk) {
|
|
716
|
-
case "pi":
|
|
717
|
-
return {
|
|
718
|
-
mode,
|
|
719
|
-
sandbox: piSandboxContract(piSandboxPolicy),
|
|
720
|
-
tools: "mono-agent-policy",
|
|
721
|
-
};
|
|
722
|
-
case "claude":
|
|
723
|
-
return { mode, sandbox: "provider-native", tools: "provider-representable" };
|
|
724
|
-
case "codex":
|
|
725
|
-
return { mode, sandbox: "codex-native", tools: "exact-allow-all" };
|
|
726
|
-
case "opencode":
|
|
727
|
-
return { mode, sandbox: "provider-native", tools: "exact-allow-all" };
|
|
728
|
-
case "acp":
|
|
729
|
-
return { mode, sandbox: "provider-native", tools: "exact-allow-all" };
|
|
730
|
-
default:
|
|
731
|
-
return { mode, sandbox: "unsupported", tools: "unsupported" };
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* Describe the Pi sandbox posture without claiming that SRT is enforced when
|
|
737
|
-
* the effective policy explicitly permits an unavailable engine to fall back
|
|
738
|
-
* to an unsandboxed host process. Both fields are required because the runtime
|
|
739
|
-
* adapter treats an unsafe fallback without its explicit opt-in as fail-closed.
|
|
740
|
-
*
|
|
741
|
-
* @param {Object<string, *>|undefined} policy
|
|
742
|
-
* @returns {import('../types.js').RuntimeRouteSandboxContract}
|
|
743
|
-
*/
|
|
744
|
-
function piSandboxContract(policy) {
|
|
745
|
-
if (policy === undefined) return "disabled";
|
|
746
|
-
if (policy.fallback === "unsafe-host-process" && policy.unsafeAllowHostProcess === true) {
|
|
747
|
-
return "mono-agent-srt-unsafe-host-fallback";
|
|
748
|
-
}
|
|
749
|
-
return "mono-agent-srt";
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
/**
|
|
753
|
-
* Describe the policy Pi tools actually receive after host/configure/run
|
|
754
|
-
* precedence. A request-scoped `off` policy cannot weaken an active host
|
|
755
|
-
* policy, while an active request policy can tighten an absent/off host.
|
|
756
|
-
* `configureTools({ sandboxPolicy: undefined })` explicitly clears the host
|
|
757
|
-
* tool-context policy and must therefore be distinguished from an omitted key.
|
|
758
|
-
*
|
|
759
|
-
* @param {import('../types.js').AgentRuntimeToolOptions} toolOptions
|
|
760
|
-
* @param {Object<string, *>} runOptions
|
|
761
|
-
* @returns {Object<string, *>|undefined}
|
|
762
|
-
*/
|
|
763
|
-
function effectivePiSandboxPolicy(toolOptions, runOptions) {
|
|
764
|
-
// Match the Pi turn runner's implementation precedence: a run-scoped
|
|
765
|
-
// RuntimeSandbox wins, followed by configureTools/host, then the kernel's
|
|
766
|
-
// fail-closed passthrough. Delegating the merge is essential here: the real
|
|
767
|
-
// adapter makes fail-closed dominate unsafe-host-process, so inspecting only
|
|
768
|
-
// the first non-off policy would produce false safety telemetry.
|
|
769
|
-
const sandbox = runOptions.sandbox
|
|
770
|
-
?? toolOptions.sandbox
|
|
771
|
-
?? passthroughSandbox;
|
|
772
|
-
const policy = sandbox.mergePolicies(toolOptions.sandboxPolicy, runOptions.sandboxPolicy);
|
|
773
|
-
return policy && typeof policy === "object" && policy.mode !== "off"
|
|
774
|
-
? policy
|
|
775
|
-
: undefined;
|
|
565
|
+
return model.reference;
|
|
776
566
|
}
|
|
777
567
|
|
|
778
568
|
/**
|
|
@@ -801,70 +591,22 @@ function effectiveRouterToolOptions(host, configuredTools) {
|
|
|
801
591
|
return effective;
|
|
802
592
|
}
|
|
803
593
|
|
|
804
|
-
/**
|
|
805
|
-
* A protected-root policy is host-internal and intentionally has no public
|
|
806
|
-
* provider-native projection. Treat a present malformed value or an accessor
|
|
807
|
-
* failure as protected so untrusted option shapes cannot turn this gate into a
|
|
808
|
-
* fail-open boundary. Empty arrays preserve ordinary provider-native routing.
|
|
809
|
-
*
|
|
810
|
-
* @param {unknown} policy
|
|
811
|
-
* @returns {boolean}
|
|
812
|
-
*/
|
|
813
|
-
function sandboxPolicyHasProtectedRoots(policy) {
|
|
814
|
-
if (policy === null || typeof policy !== "object") return false;
|
|
815
|
-
try {
|
|
816
|
-
if (!("protectedRoots" in policy)) return false;
|
|
817
|
-
const protectedRoots = /** @type {{protectedRoots?: unknown}} */ (policy).protectedRoots;
|
|
818
|
-
if (protectedRoots === undefined) return false;
|
|
819
|
-
return !Array.isArray(protectedRoots) || protectedRoots.length > 0;
|
|
820
|
-
} catch {
|
|
821
|
-
return true;
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
/**
|
|
826
|
-
* @param {import('../types.js').AgentRuntimeToolOptions} toolOptions
|
|
827
|
-
* @param {Object<string, *>} runOptions
|
|
828
|
-
* @returns {boolean}
|
|
829
|
-
*/
|
|
830
|
-
function attemptCarriesProtectedRoots(toolOptions, runOptions) {
|
|
831
|
-
return sandboxPolicyHasProtectedRoots(toolOptions.sandboxPolicy)
|
|
832
|
-
|| sandboxPolicyHasProtectedRoots(runOptions.sandboxPolicy);
|
|
833
|
-
}
|
|
834
|
-
|
|
835
594
|
/**
|
|
836
595
|
* A resolver-supplied Pi runtime is allowed to own credentials/provider
|
|
837
|
-
* lifecycle, never the
|
|
838
|
-
* ToolContext before every execution so a blank
|
|
839
|
-
*
|
|
596
|
+
* lifecycle, never the router-owned tool context. Replace its complete mutable
|
|
597
|
+
* ToolContext before every execution so a blank or stale runtime cannot diverge
|
|
598
|
+
* from the router's host/configured/run policy or telemetry.
|
|
840
599
|
*
|
|
841
600
|
* @param {AgentRuntimeInstance} runtime
|
|
842
601
|
* @param {import('../types.js').AgentRuntimeToolOptions} toolOptions
|
|
843
602
|
*/
|
|
844
603
|
function projectPiRuntimeToolContext(runtime, toolOptions) {
|
|
845
604
|
if (typeof runtime.configureTools !== "function") {
|
|
846
|
-
throw new Error("
|
|
605
|
+
throw new Error("route attempt runtime must expose configureTools() for tool-context projection");
|
|
847
606
|
}
|
|
848
607
|
runtime.configureTools(toolOptions);
|
|
849
608
|
}
|
|
850
609
|
|
|
851
|
-
/**
|
|
852
|
-
* @param {number} attemptIndex
|
|
853
|
-
* @param {RouterChainEntry} entry
|
|
854
|
-
* @param {import('../types.js').RuntimeRouteSafetyContract} contract
|
|
855
|
-
* @param {string} status
|
|
856
|
-
* @returns {{attemptIndex: number, model: RuntimeModelRef, routeSafety: import('../types.js').RuntimeRouteSafetyMode, safetyContract: import('../types.js').RuntimeRouteSafetyContract, status: string}}
|
|
857
|
-
*/
|
|
858
|
-
function routeSafetyRecord(attemptIndex, entry, contract, status) {
|
|
859
|
-
return {
|
|
860
|
-
attemptIndex,
|
|
861
|
-
model: entry.model,
|
|
862
|
-
routeSafety: contract.mode,
|
|
863
|
-
safetyContract: contract,
|
|
864
|
-
status,
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
|
|
868
610
|
/** @param {RouterAttemptResolution|undefined} value */
|
|
869
611
|
function normalizeAttemptResolution(value) {
|
|
870
612
|
if (value === undefined) return undefined;
|
|
@@ -947,89 +689,6 @@ function withoutAttemptScopedOptions(options) {
|
|
|
947
689
|
return projected;
|
|
948
690
|
}
|
|
949
691
|
|
|
950
|
-
/**
|
|
951
|
-
* Explicit mixed-route projection. Capability-bearing inputs (MCP, skills,
|
|
952
|
-
* schema, live input, native subagents) are never removed here; the capability
|
|
953
|
-
* gate either proves support or skips the route before execution.
|
|
954
|
-
* @param {RouterChainEntry} entry
|
|
955
|
-
* @param {Object<string, *>} options
|
|
956
|
-
*/
|
|
957
|
-
function projectPerRouteNativeOptions(entry, options) {
|
|
958
|
-
const projected = { ...options };
|
|
959
|
-
switch (entry.model.sdk) {
|
|
960
|
-
case "pi":
|
|
961
|
-
return projected;
|
|
962
|
-
case "claude":
|
|
963
|
-
delete projected.sandboxPolicy;
|
|
964
|
-
delete projected.sandboxEngine;
|
|
965
|
-
return projected;
|
|
966
|
-
case "codex":
|
|
967
|
-
case "opencode":
|
|
968
|
-
case "acp":
|
|
969
|
-
delete projected.sandboxPolicy;
|
|
970
|
-
delete projected.sandboxEngine;
|
|
971
|
-
projected.allowedTools = ["*"];
|
|
972
|
-
projected.disallowedTools = [];
|
|
973
|
-
return projected;
|
|
974
|
-
default:
|
|
975
|
-
throw new Error(`per-route-native safety has no contract for sdk ${entry.model.sdk}`);
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
/**
|
|
980
|
-
* @param {string} key
|
|
981
|
-
* @param {RouterChainEntry} entry
|
|
982
|
-
* @param {AgentRuntimeHostOptions} host
|
|
983
|
-
* @param {Map<string, AgentRuntimeInstance>} runtimes
|
|
984
|
-
* @param {import('../types.js').AgentRuntimeToolOptions|undefined} configuredTools
|
|
985
|
-
*/
|
|
986
|
-
function createRouteRuntime(key, entry, host, runtimes, configuredTools) {
|
|
987
|
-
const runtime = createRuntime(projectPerRouteNativeHost(entry, host));
|
|
988
|
-
if (configuredTools !== undefined) {
|
|
989
|
-
runtime.configureTools?.(projectPerRouteNativeToolOptions(entry, configuredTools));
|
|
990
|
-
}
|
|
991
|
-
runtimes.set(key, runtime);
|
|
992
|
-
return runtime;
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
/**
|
|
996
|
-
* Provider-native routes retain the injected sandbox implementation seam, but
|
|
997
|
-
* must never inherit mono-agent policy data or a concrete srt engine. Assigning
|
|
998
|
-
* explicit `undefined` values (rather than deleting the keys) also clears a
|
|
999
|
-
* previously configured runtime when configureTools is called again.
|
|
1000
|
-
* @param {RouterChainEntry} entry
|
|
1001
|
-
* @param {import('../types.js').AgentRuntimeToolOptions|undefined} configuredTools
|
|
1002
|
-
* @returns {import('../types.js').AgentRuntimeToolOptions}
|
|
1003
|
-
*/
|
|
1004
|
-
function projectPerRouteNativeToolOptions(entry, configuredTools = {}) {
|
|
1005
|
-
const projected = { ...configuredTools };
|
|
1006
|
-
if (entry.model.sdk !== "pi") {
|
|
1007
|
-
projected.sandboxPolicy = undefined;
|
|
1008
|
-
projected.sandboxEngine = undefined;
|
|
1009
|
-
}
|
|
1010
|
-
return projected;
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
/**
|
|
1014
|
-
* Host-level policy must be isolated alongside request-level policy. The
|
|
1015
|
-
* sandbox implementation itself remains available; only enforcing policy data
|
|
1016
|
-
* and its route-specific engine are removed for provider-native bridges.
|
|
1017
|
-
* @param {RouterChainEntry} entry
|
|
1018
|
-
* @param {AgentRuntimeHostOptions} host
|
|
1019
|
-
*/
|
|
1020
|
-
function projectPerRouteNativeHost(entry, host) {
|
|
1021
|
-
if (entry.model.sdk === "pi") return host;
|
|
1022
|
-
const projected = { ...host };
|
|
1023
|
-
delete projected.sandboxPolicy;
|
|
1024
|
-
delete projected.sandboxEngine;
|
|
1025
|
-
return projected;
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
/** @param {RouterChainEntry} entry @param {number} index */
|
|
1029
|
-
function routeRuntimeKey(entry, index) {
|
|
1030
|
-
return `${index}:${modelKey(entry.model)}:${entry.executionMode ?? "default"}`;
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
692
|
/** @param {AgentRuntimeInstance} runtime */
|
|
1034
693
|
function assertRuntimeLike(runtime) {
|
|
1035
694
|
if (runtime === null || typeof runtime !== "object" || typeof runtime.run !== "function") {
|
|
@@ -1047,7 +706,7 @@ function applyEntryEffort(options, effort) {
|
|
|
1047
706
|
}
|
|
1048
707
|
|
|
1049
708
|
/** @param {unknown} error @returns {RuntimeResult} */
|
|
1050
|
-
function
|
|
709
|
+
function attemptResolutionFailureResult(error) {
|
|
1051
710
|
// Host resolvers may handle credentials. Never echo their exception text
|
|
1052
711
|
// into persisted results or route telemetry. ResolverProtectedOptionError is
|
|
1053
712
|
// constructed only from a repository-owned allowlist key, so it is safe and
|
|
@@ -1056,8 +715,8 @@ function safetyUnavailableResult(error) {
|
|
|
1056
715
|
text: null,
|
|
1057
716
|
error: error instanceof ResolverProtectedOptionError
|
|
1058
717
|
? error.message
|
|
1059
|
-
: "The route
|
|
1060
|
-
failureKind: "
|
|
718
|
+
: "The route attempt could not be resolved before execution.",
|
|
719
|
+
failureKind: "provider_unavailable",
|
|
1061
720
|
events: [],
|
|
1062
721
|
cancelled: false,
|
|
1063
722
|
usage: {},
|
|
@@ -1070,14 +729,6 @@ function isMidTurnSafetyFailure(failureKind) {
|
|
|
1070
729
|
&& (failureKind.startsWith("sandbox_") || failureKind.startsWith("safety_"));
|
|
1071
730
|
}
|
|
1072
731
|
|
|
1073
|
-
/**
|
|
1074
|
-
* @param {AgentRuntimeInstance} inner
|
|
1075
|
-
* @param {Map<string, AgentRuntimeInstance>} routeRuntimes
|
|
1076
|
-
*/
|
|
1077
|
-
function allRuntimes(inner, routeRuntimes) {
|
|
1078
|
-
return [...new Set([inner, ...routeRuntimes.values()])];
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
732
|
/**
|
|
1082
733
|
* Merge progress into one bounded snapshot so prompts never accumulate nested
|
|
1083
734
|
* resume blocks across a long provider chain.
|
|
@@ -1166,17 +817,6 @@ function entrySatisfiesRequirements(entry, options) {
|
|
|
1166
817
|
if (options.toolEnvironment !== undefined) {
|
|
1167
818
|
effectiveRequires.supports_request_tool_environment = true;
|
|
1168
819
|
}
|
|
1169
|
-
if (options.fastMode === true) {
|
|
1170
|
-
effectiveRequires.supports_fast_mode = true;
|
|
1171
|
-
}
|
|
1172
|
-
// Native teammates likewise require a capable route; Pi/OpenCode must skip
|
|
1173
|
-
// rather than silently dropping them.
|
|
1174
|
-
if (
|
|
1175
|
-
Array.isArray(options.nativeSubagents?.teammates)
|
|
1176
|
-
&& options.nativeSubagents.teammates.length > 0
|
|
1177
|
-
) {
|
|
1178
|
-
effectiveRequires.supports_native_subagents = true;
|
|
1179
|
-
}
|
|
1180
820
|
if (Object.keys(effectiveRequires).length === 0) return true;
|
|
1181
821
|
let caps;
|
|
1182
822
|
try {
|
|
@@ -1191,10 +831,10 @@ function entrySatisfiesRequirements(entry, options) {
|
|
|
1191
831
|
}
|
|
1192
832
|
|
|
1193
833
|
/**
|
|
1194
|
-
* Session identifiers belong to the
|
|
1195
|
-
*
|
|
1196
|
-
*
|
|
1197
|
-
*
|
|
834
|
+
* Session identifiers belong to the route that created them. Never forward one
|
|
835
|
+
* into a route whose capabilities declare no resume support, including when
|
|
836
|
+
* that route is reached through fallback. Unknown model references retain the
|
|
837
|
+
* existing fail-later behavior.
|
|
1198
838
|
* @param {RouterChainEntry} entry
|
|
1199
839
|
* @returns {boolean}
|
|
1200
840
|
*/
|