@mono-agent/agent-runtime 0.20.11 → 0.21.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/ARCHITECTURE.md +50 -11
- package/MIGRATION.md +288 -26
- package/README.md +352 -477
- package/package.json +13 -44
- package/src/agent/tool-bloat.js +145 -9
- package/src/agent/tools/agent-tool.js +108 -9
- package/src/agent/tools/bash.js +11 -26
- package/src/agent/tools/codex-subscription-search.js +123 -29
- package/src/agent/tools/exec.js +10 -2
- package/src/agent/tools/index.js +7 -0
- package/src/agent/tools/monitor.js +149 -0
- package/src/agent/tools/pi-bridge.js +123 -19
- package/src/agent/tools/shared/bash-environment.js +31 -0
- package/src/agent/tools/shared/monitors.js +293 -0
- package/src/agent/tools/shared/path-resolver.js +25 -6
- package/src/agent/tools/shared/process-jobs.js +6 -1
- package/src/agent/tools/shared/process-runner.js +26 -6
- package/src/agent/tools/shared/tool-context.js +8 -0
- package/src/agent/tools/web-access-interstitial.js +70 -0
- package/src/agent/tools/web-browser-render.js +83 -58
- package/src/agent/tools/web-controller.js +112 -21
- package/src/agent/tools/web-document-extractor.js +379 -0
- package/src/agent/tools/web-fetch.js +271 -243
- package/src/agent/tools/web-request.js +65 -0
- package/src/agent/tools/web-search-output.js +165 -0
- package/src/agent/tools/web-search-state.js +75 -0
- package/src/agent/tools/web-search.js +532 -71
- package/src/ai/cost.js +13 -68
- package/src/ai/failure.js +3 -3
- package/src/ai/index.js +5 -17
- package/src/ai/observer.js +8 -0
- package/src/ai/pi-interop.js +221 -1
- package/src/ai/pi-oauth-compat.js +1 -1
- package/src/ai/provider-check.js +131 -0
- 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 +94 -42
- package/src/ai/providers/pi-native/compaction-summary.js +140 -0
- package/src/ai/providers/pi-native/harness-adapter.js +376 -0
- package/src/ai/providers/pi-native/prompt-cache-diagnostics.js +103 -0
- package/src/ai/providers/pi-native/provider-attribution.js +102 -0
- package/src/ai/providers/pi-native/result-builder.js +38 -14
- package/src/ai/providers/pi-native/session-lifecycle.js +253 -55
- package/src/ai/providers/pi-native/stream-subscriber.js +52 -6
- package/src/ai/providers/pi-native/terminal-recovery.js +40 -0
- package/src/ai/providers/pi-native/turn-runner.js +279 -28
- package/src/ai/providers/pi-native.js +206 -61
- package/src/ai/runtime/capabilities.js +11 -56
- package/src/ai/runtime/live-input-events.js +250 -54
- package/src/ai/runtime/model-refs.js +118 -153
- package/src/ai/runtime/registry.js +22 -56
- package/src/ai/runtime/router.js +76 -417
- 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/tool-lifecycle.js +32 -18
- package/src/ai/types.js +37 -112
- package/src/index.js +0 -6
- package/src/runtime.js +29 -16
- package/types/agent/tool-bloat.d.ts +1 -1
- package/types/agent/tools/agent-tool.d.ts +4 -2
- package/types/agent/tools/bash.d.ts +5 -3
- package/types/agent/tools/codex-subscription-search.d.ts +7 -3
- package/types/agent/tools/exec.d.ts +5 -3
- package/types/agent/tools/index.d.ts +1 -0
- package/types/agent/tools/monitor.d.ts +47 -0
- package/types/agent/tools/pi-bridge.d.ts +7 -4
- package/types/agent/tools/shared/bash-environment.d.ts +4 -0
- package/types/agent/tools/shared/monitors.d.ts +98 -0
- package/types/agent/tools/shared/process-jobs.d.ts +5 -1
- package/types/agent/tools/shared/process-runner.d.ts +14 -4
- package/types/agent/tools/shared/tool-context.d.ts +2 -0
- package/types/agent/tools/web-access-interstitial.d.ts +23 -0
- package/types/agent/tools/web-browser-render.d.ts +4 -1
- package/types/agent/tools/web-controller.d.ts +4 -2
- package/types/agent/tools/web-document-extractor.d.ts +27 -0
- package/types/agent/tools/web-fetch.d.ts +19 -24
- package/types/agent/tools/web-request.d.ts +20 -0
- package/types/agent/tools/web-search-output.d.ts +31 -0
- package/types/agent/tools/web-search-state.d.ts +21 -0
- package/types/agent/tools/web-search.d.ts +10 -45
- package/types/ai/cost.d.ts +1 -2
- package/types/ai/index.d.ts +2 -4
- package/types/ai/observer.d.ts +6 -0
- package/types/ai/pi-interop.d.ts +81 -0
- package/types/ai/provider-check.d.ts +53 -0
- package/types/ai/providers/codex/app-server-client.d.ts +37 -0
- package/types/ai/providers/pi-native/compaction-driver.d.ts +2 -1
- package/types/ai/providers/pi-native/compaction-summary.d.ts +19 -0
- package/types/ai/providers/pi-native/harness-adapter.d.ts +58 -0
- package/types/ai/providers/pi-native/prompt-cache-diagnostics.d.ts +3 -0
- package/types/ai/providers/pi-native/provider-attribution.d.ts +26 -0
- package/types/ai/providers/pi-native/result-builder.d.ts +14 -4
- package/types/ai/providers/pi-native/session-lifecycle.d.ts +25 -6
- package/types/ai/providers/pi-native/stream-subscriber.d.ts +2 -2
- package/types/ai/providers/pi-native/terminal-recovery.d.ts +2 -0
- package/types/ai/providers/pi-native/turn-runner.d.ts +68 -10
- package/types/ai/providers/pi-native.d.ts +21 -4
- package/types/ai/runtime/capabilities.d.ts +21 -70
- package/types/ai/runtime/live-input-events.d.ts +32 -8
- 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/tool-lifecycle.d.ts +4 -3
- package/types/ai/types.d.ts +162 -256
- 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
|
@@ -55,12 +55,15 @@ import {
|
|
|
55
55
|
buildErrorDetails,
|
|
56
56
|
buildErrorResult,
|
|
57
57
|
buildSuccessResult,
|
|
58
|
+
failureKindForPiError,
|
|
58
59
|
emitCapabilitiesResolved,
|
|
59
60
|
emitUsageCostEvents,
|
|
60
61
|
usageFromMessages,
|
|
62
|
+
hasMeasuredUsage,
|
|
61
63
|
withSubagentUsage,
|
|
62
64
|
} from "./pi-native/result-builder.js";
|
|
63
65
|
import {
|
|
66
|
+
captureSessionRecovery,
|
|
64
67
|
cleanupSessionOnThrow,
|
|
65
68
|
commitSession,
|
|
66
69
|
discardUncommittedSession,
|
|
@@ -74,18 +77,22 @@ import {
|
|
|
74
77
|
runReactiveCompaction,
|
|
75
78
|
} from "./pi-native/compaction-driver.js";
|
|
76
79
|
import {
|
|
80
|
+
activateTurnHarness,
|
|
77
81
|
buildTurnHarness,
|
|
78
82
|
buildTurnTools,
|
|
83
|
+
createLiveInputPromptEpoch,
|
|
79
84
|
runHarnessPrompt,
|
|
80
85
|
startLiveInput,
|
|
81
86
|
thinkingLevelForEffort,
|
|
82
87
|
} from "./pi-native/turn-runner.js";
|
|
83
88
|
import { resolvePiTransport } from "./pi-native/transport.js";
|
|
89
|
+
import { withOpenCodeSessionHeaders } from "./pi-native/provider-attribution.js";
|
|
84
90
|
|
|
85
91
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
92
|
+
* Resolve mono-agent's programmatic mode once per run. Tool builders mark
|
|
93
|
+
* stateful/mutating tools sequential; the Pi 0.85 harness adapter promotes any
|
|
94
|
+
* such marker to its global toolExecution setting because mixed scheduling is
|
|
95
|
+
* no longer available upstream.
|
|
89
96
|
*/
|
|
90
97
|
export function resolvePiToolExecutionMode(options = {}) {
|
|
91
98
|
const warnings = [];
|
|
@@ -188,23 +195,82 @@ export function createDynamicCredentialStore(apiKeys, resolvePiApiKey, runtimeWa
|
|
|
188
195
|
// resolved model. `piResolvedModels` is an advanced/test seam mirroring
|
|
189
196
|
// `piResolvedModel`: when supplied it is used verbatim (the model dispatched via
|
|
190
197
|
// `piResolvedModel` may live outside pi's builtin catalog, e.g. a faux model).
|
|
191
|
-
function buildRunModels(runtime, options, runtimeWarnings) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
198
|
+
function buildRunModels(runtime, options, runtimeWarnings, providerAttributionSessionId) {
|
|
199
|
+
let models;
|
|
200
|
+
if (options.piResolvedModels) {
|
|
201
|
+
models = options.piResolvedModels;
|
|
202
|
+
} else {
|
|
203
|
+
const credentials = createDynamicCredentialStore(runtime.apiKeys, options.resolvePiApiKey, runtimeWarnings);
|
|
204
|
+
if (options.customProvider) {
|
|
205
|
+
const model = runtime.model;
|
|
206
|
+
models = createModels({ credentials });
|
|
207
|
+
models.setProvider(createProvider({
|
|
208
|
+
id: model.provider,
|
|
209
|
+
name: model.name || model.provider,
|
|
210
|
+
baseUrl: model.baseUrl,
|
|
211
|
+
auth: { apiKey: envApiKeyAuth(model.name || model.provider, []) },
|
|
212
|
+
models: [model],
|
|
213
|
+
api: openAICompletionsApi(),
|
|
214
|
+
}));
|
|
215
|
+
} else {
|
|
216
|
+
models = builtinModels({
|
|
217
|
+
credentials,
|
|
218
|
+
...(options.providerCheckAuthContext === undefined
|
|
219
|
+
? {}
|
|
220
|
+
: { authContext: options.providerCheckAuthContext }),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
206
223
|
}
|
|
207
|
-
return
|
|
224
|
+
return withProviderCheckOutputCap(
|
|
225
|
+
withOpenCodeSessionHeaders(models, providerAttributionSessionId),
|
|
226
|
+
options.providerCheckMaxTokens,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const PROVIDER_CHECK_REQUEST_METHODS = new Set([
|
|
231
|
+
"stream",
|
|
232
|
+
"complete",
|
|
233
|
+
"streamSimple",
|
|
234
|
+
"completeSimple",
|
|
235
|
+
"streamDeferred",
|
|
236
|
+
"fetchDeferred",
|
|
237
|
+
"cancelDeferred",
|
|
238
|
+
]);
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Pi's Agent resolves the transport model through `Models` again, so capping
|
|
242
|
+
* only the model handed to the harness does not constrain the actual provider
|
|
243
|
+
* request. Bind the same cap at the dispatcher boundary used by every request
|
|
244
|
+
* path. This wrapper is activated only for explicit provider checks.
|
|
245
|
+
*
|
|
246
|
+
* @param {import("@earendil-works/pi-ai").Models} models
|
|
247
|
+
* @param {unknown} requestedCap
|
|
248
|
+
* @returns {import("@earendil-works/pi-ai").Models}
|
|
249
|
+
*/
|
|
250
|
+
function withProviderCheckOutputCap(models, requestedCap) {
|
|
251
|
+
const cap = Number(requestedCap);
|
|
252
|
+
if (!Number.isSafeInteger(cap) || cap <= 0) return models;
|
|
253
|
+
const wrappers = new Map();
|
|
254
|
+
return /** @type {import("@earendil-works/pi-ai").Models} */ (new Proxy(models, {
|
|
255
|
+
get(target, property) {
|
|
256
|
+
const value = Reflect.get(target, property, target);
|
|
257
|
+
if (typeof property !== "string" || typeof value !== "function") return value;
|
|
258
|
+
if (!PROVIDER_CHECK_REQUEST_METHODS.has(property)) return value.bind(target);
|
|
259
|
+
let wrapper = wrappers.get(property);
|
|
260
|
+
if (wrapper === undefined) {
|
|
261
|
+
wrapper = (model, ...args) => {
|
|
262
|
+
const current = Number(model?.maxTokens);
|
|
263
|
+
const cappedModel = {
|
|
264
|
+
...model,
|
|
265
|
+
maxTokens: Number.isFinite(current) && current > 0 ? Math.min(current, cap) : cap,
|
|
266
|
+
};
|
|
267
|
+
return value.call(target, cappedModel, ...args);
|
|
268
|
+
};
|
|
269
|
+
wrappers.set(property, wrapper);
|
|
270
|
+
}
|
|
271
|
+
return wrapper;
|
|
272
|
+
},
|
|
273
|
+
}));
|
|
208
274
|
}
|
|
209
275
|
|
|
210
276
|
// Normalize the incoming runtime messages into AgentMessages the harness can
|
|
@@ -258,6 +324,14 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
258
324
|
// an intermediate layer flattens the Error to its message.
|
|
259
325
|
installTransportErrorProbe();
|
|
260
326
|
const resolved = options.model;
|
|
327
|
+
if (
|
|
328
|
+
typeof resolved?.provider !== "string"
|
|
329
|
+
|| resolved.provider.length === 0
|
|
330
|
+
|| typeof resolved.model !== "string"
|
|
331
|
+
|| resolved.model.length === 0
|
|
332
|
+
) {
|
|
333
|
+
throw new Error("invalid pi model reference: provider and model are required");
|
|
334
|
+
}
|
|
261
335
|
const start = Date.now();
|
|
262
336
|
const events = [];
|
|
263
337
|
const runtimeWarnings = [];
|
|
@@ -303,7 +377,14 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
303
377
|
diagnostics: {},
|
|
304
378
|
},
|
|
305
379
|
session: null,
|
|
380
|
+
// Fresh stateless calls own a private repo, even when attribution matches a
|
|
381
|
+
// live primary session. Cleanup must never delete that primary's record.
|
|
382
|
+
ephemeralSessionRepo: null,
|
|
306
383
|
sessionEntry: null,
|
|
384
|
+
// Fresh keep-alive sessions are registered busy until the harness closes;
|
|
385
|
+
// this prevents another turn from reopening the same repo record while its
|
|
386
|
+
// first handle is still live.
|
|
387
|
+
registeredSessionEntry: null,
|
|
307
388
|
// True when a requestedSessionId had no live entry AND no durable transcript,
|
|
308
389
|
// so a fresh durable session was created under that id (cross-restart resume,
|
|
309
390
|
// first turn for the conversation). Distinct from a true resume (sessionEntry
|
|
@@ -322,12 +403,17 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
322
403
|
// (host/runtime-side throws after the session mutated) can roll back too, not
|
|
323
404
|
// just the success path.
|
|
324
405
|
baselineLeafId: null,
|
|
406
|
+
recoveryOperationId: undefined,
|
|
407
|
+
recoveryInputIds: null,
|
|
408
|
+
retainRecoveryTail: false,
|
|
325
409
|
};
|
|
326
410
|
|
|
327
411
|
const providerSessionId = options.sessionId
|
|
328
412
|
|| options.providerSessionId
|
|
413
|
+
|| options.providerAttributionSessionId
|
|
329
414
|
|| options.runId
|
|
330
415
|
|| randomUUID();
|
|
416
|
+
const providerAttributionSessionId = options.providerAttributionSessionId || providerSessionId;
|
|
331
417
|
// Prefer the explicit sessionId, but fall back to providerSessionId so a caller
|
|
332
418
|
// that only supplies providerSessionId still resumes the prior session instead
|
|
333
419
|
// of being treated as a fresh run (which would drop prior context).
|
|
@@ -352,13 +438,17 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
352
438
|
}
|
|
353
439
|
emitCaptured(events, options.onEvent, event);
|
|
354
440
|
};
|
|
441
|
+
// Exec and Monitor both run a command a host configured Bash's risk tier for.
|
|
442
|
+
// Leaving either unmapped would let the same shell command take a lower
|
|
443
|
+
// approval tier simply by being started through a different tool.
|
|
444
|
+
const bashRiskTier = options.toolRiskTiers?.Bash;
|
|
355
445
|
const approvalRiskTiers = {
|
|
356
446
|
...(options.toolRiskTiers || {}),
|
|
357
|
-
...(
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
447
|
+
...(bashRiskTier !== undefined
|
|
448
|
+
? Object.fromEntries(["Exec", "Monitor"]
|
|
449
|
+
.filter((name) => options.toolRiskTiers?.[name] === undefined)
|
|
450
|
+
.map((name) => [name, bashRiskTier]))
|
|
451
|
+
: {}),
|
|
362
452
|
};
|
|
363
453
|
const approvalManager = options.onToolApprovalRequest
|
|
364
454
|
? createApprovalManager({
|
|
@@ -405,7 +495,7 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
405
495
|
// ready pi-ai Model (e.g. a registered faux provider model) plus optional
|
|
406
496
|
// capabilities, bypassing the static model-registry lookup. Production
|
|
407
497
|
// callers leave it undefined and resolve through pi-ai's registry.
|
|
408
|
-
|
|
498
|
+
let runtime = options.piResolvedModel
|
|
409
499
|
? {
|
|
410
500
|
model: options.piResolvedModel,
|
|
411
501
|
capabilities: options.piResolvedCapabilities || {
|
|
@@ -420,10 +510,18 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
420
510
|
apiKeys: new Map(),
|
|
421
511
|
}
|
|
422
512
|
: resolvePiRuntimeModel(resolved, options);
|
|
513
|
+
if (Number.isSafeInteger(options.providerCheckMaxTokens) && options.providerCheckMaxTokens > 0) {
|
|
514
|
+
runtime = {
|
|
515
|
+
...runtime,
|
|
516
|
+
model: {
|
|
517
|
+
...runtime.model,
|
|
518
|
+
maxTokens: Math.min(runtime.model.maxTokens, options.providerCheckMaxTokens),
|
|
519
|
+
},
|
|
520
|
+
};
|
|
521
|
+
}
|
|
423
522
|
const capabilities = runtime.capabilities || {};
|
|
424
523
|
const effectiveThinkingLevel = thinkingLevelForEffort(options.effort || "medium", capabilities);
|
|
425
|
-
const reference = resolved.reference
|
|
426
|
-
|| (resolved.sdk === "pi" ? `pi:${resolved.provider}:${resolved.model}` : `${resolved.sdk}:${resolved.model}`);
|
|
524
|
+
const reference = resolved.reference || `${resolved.provider}:${resolved.model}`;
|
|
427
525
|
|
|
428
526
|
// Resolve the typed `toolLimits` / `compaction` policy objects against the
|
|
429
527
|
// deprecated `settings` fallback (per-group precedence: a present typed
|
|
@@ -486,15 +584,17 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
486
584
|
? Number(options.maxRetryDelayMs)
|
|
487
585
|
: 60_000;
|
|
488
586
|
// Steering controls user follow-up delivery, not tool scheduling. Keep it
|
|
489
|
-
// independent from piToolExecutionMode;
|
|
587
|
+
// independent from piToolExecutionMode; the adapter derives Pi 0.85's
|
|
588
|
+
// global scheduling mode from the tools' executionMode markers.
|
|
490
589
|
const toolSteeringMode = "one-at-a-time";
|
|
491
590
|
|
|
492
|
-
const piModels = buildRunModels(runtime, options, runtimeWarnings);
|
|
591
|
+
const piModels = buildRunModels(runtime, options, runtimeWarnings, providerAttributionSessionId);
|
|
493
592
|
|
|
494
|
-
// Construct the harness
|
|
495
|
-
// external abort handler
|
|
496
|
-
//
|
|
497
|
-
|
|
593
|
+
// Construct the harness and recover any interrupted durable operation.
|
|
594
|
+
// Stream subscription and the external abort handler are activated only
|
|
595
|
+
// after prior transcript seeding, so manual append lifecycle events cannot
|
|
596
|
+
// surface as output from this run.
|
|
597
|
+
harness = await buildTurnHarness(runState, {
|
|
498
598
|
session: runState.session,
|
|
499
599
|
piModels,
|
|
500
600
|
model: runtime.model,
|
|
@@ -506,11 +606,7 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
506
606
|
maxRetries,
|
|
507
607
|
maxRetryDelayMs,
|
|
508
608
|
steeringMode: toolSteeringMode,
|
|
509
|
-
onEvent,
|
|
510
609
|
options,
|
|
511
|
-
toolLimits,
|
|
512
|
-
sdk: resolved.sdk,
|
|
513
|
-
reference,
|
|
514
610
|
});
|
|
515
611
|
|
|
516
612
|
// Seed prior transcript (everything before the trailing user turn) into the
|
|
@@ -536,10 +632,14 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
536
632
|
try { runState.baselineLeafId = await runState.session.getLeafId(); } catch { /* best-effort */ }
|
|
537
633
|
}
|
|
538
634
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
635
|
+
activateTurnHarness(runState, {
|
|
636
|
+
harness,
|
|
637
|
+
onEvent,
|
|
638
|
+
options,
|
|
639
|
+
toolLimits,
|
|
640
|
+
sdk: "pi",
|
|
641
|
+
reference,
|
|
642
|
+
});
|
|
543
643
|
|
|
544
644
|
// Re-check abort right before issuing the provider request. The abort
|
|
545
645
|
// handler is only installed at ~:639, AFTER a long stretch of awaited setup
|
|
@@ -582,19 +682,53 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
582
682
|
runtimeWarnings,
|
|
583
683
|
});
|
|
584
684
|
|
|
685
|
+
// Arm the main-prompt epoch after proactive compaction so compaction and
|
|
686
|
+
// transcript seeding cannot be mistaken for live-input consumption.
|
|
687
|
+
runState.recoveryBaselineTipId = await runState.session.getLeafId();
|
|
688
|
+
const liveInputEpoch = createLiveInputPromptEpoch({ harness, onEvent });
|
|
689
|
+
const liveInput = startLiveInput({ harness, options, onEvent, promptEpoch: liveInputEpoch });
|
|
690
|
+
|
|
691
|
+
// Report the live harness value, not a downstream recreation of Pi's
|
|
692
|
+
// thinking-level normalization. This also captures any future adapter-side
|
|
693
|
+
// adjustment between construction and the provider request.
|
|
694
|
+
const providerEffectiveEffort = harness.getThinkingLevel();
|
|
695
|
+
onEvent({
|
|
696
|
+
type: "provider_execution_config",
|
|
697
|
+
sdk: "pi",
|
|
698
|
+
model: reference,
|
|
699
|
+
...(options.effort ? { effort: options.effort } : {}),
|
|
700
|
+
effectiveEffort: providerEffectiveEffort,
|
|
701
|
+
timestamp: Date.now(),
|
|
702
|
+
});
|
|
703
|
+
|
|
585
704
|
onEvent({
|
|
586
705
|
type: "provider_request_started",
|
|
587
|
-
sdk:
|
|
706
|
+
sdk: "pi",
|
|
588
707
|
model: reference,
|
|
589
708
|
runtime: "pi",
|
|
590
709
|
timestamp: Date.now(),
|
|
591
710
|
});
|
|
592
711
|
|
|
593
|
-
let
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
712
|
+
let runError;
|
|
713
|
+
try {
|
|
714
|
+
// Own the operation at admission so steers consumed mid-run settle as
|
|
715
|
+
// applied immediately; finish() re-checks the settled id at the end.
|
|
716
|
+
const promptResult = await runHarnessPrompt(harness, promptText, promptImages, {
|
|
717
|
+
onOperationAdmitted: (operationId) => {
|
|
718
|
+
runState.recoveryOperationId = operationId;
|
|
719
|
+
liveInputEpoch.confirm(operationId);
|
|
720
|
+
},
|
|
721
|
+
});
|
|
722
|
+
runError = promptResult.runError;
|
|
723
|
+
liveInputEpoch.finish(promptResult.operationId);
|
|
724
|
+
} finally {
|
|
725
|
+
// Stop joins unresolved native enqueue and reconciles every returned
|
|
726
|
+
// entry before the exact-operation subscription is removed.
|
|
727
|
+
try { await liveInput.stop(); } finally {
|
|
728
|
+
runState.recoveryInputIds = liveInputEpoch.consumedInputIds();
|
|
729
|
+
liveInputEpoch.close();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
598
732
|
|
|
599
733
|
runState.externalAbort ||= !!options.abortSignal?.aborted;
|
|
600
734
|
|
|
@@ -765,7 +899,7 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
765
899
|
// runtime.compaction.enabled. See docs/reference/feature-registry.md runtime.context-compaction.
|
|
766
900
|
contextCompactionApplied: runState.compaction.policy?.enabled ? runState.compaction.applied : null,
|
|
767
901
|
});
|
|
768
|
-
emitCapabilitiesResolved(onEvent, { sdk:
|
|
902
|
+
emitCapabilitiesResolved(onEvent, { sdk: "pi", model: reference, capabilitiesUsed });
|
|
769
903
|
|
|
770
904
|
// Re-check the abort signal: a cancel can land during the post-run work above
|
|
771
905
|
// (live-input teardown, structured-output finalization retry) after the line
|
|
@@ -780,6 +914,12 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
780
914
|
// tracks LIVENESS so disposeProviderSession / idle-TTL eviction can reach
|
|
781
915
|
// native sessions, keep-alive registers the session, and a failed/aborted
|
|
782
916
|
// resumed turn rolls back to its pre-turn leaf.
|
|
917
|
+
runState.retainRecoveryTail = !!durableRepo && options.sessionKeepAlive === true
|
|
918
|
+
&& !runState.maxTurnsHit
|
|
919
|
+
&& (!errorMessage || failureKindForPiError(errorMessage, diagnostics) === "provider_unavailable")
|
|
920
|
+
&& typeof options.sessionRecovery?.runId === "string" && options.sessionRecovery.runId.length > 0
|
|
921
|
+
&& Number.isSafeInteger(options.sessionRecovery?.revision) && options.sessionRecovery.revision >= 0
|
|
922
|
+
&& typeof runState.recoveryOperationId === "string";
|
|
783
923
|
await commitSession(runState, {
|
|
784
924
|
options,
|
|
785
925
|
requestedSessionId,
|
|
@@ -791,21 +931,21 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
791
931
|
onEvent,
|
|
792
932
|
});
|
|
793
933
|
|
|
794
|
-
//
|
|
795
|
-
//
|
|
796
|
-
//
|
|
797
|
-
// and (for keep-alive) the live registry. Roll it back so the next resume sees
|
|
798
|
-
// the pre-turn state (rollbackAbortedTurn: a resumed session moves to its
|
|
799
|
-
// baseline leaf and drops its live entry; a fresh durable session deletes its
|
|
800
|
-
// jsonl). The abort re-check + return stay inline with NO await between the
|
|
801
|
-
// false-branch check and the return, so an external cancel cannot newly fire
|
|
802
|
-
// past it (I10).
|
|
934
|
+
// Uncoordinated calls keep their final rollback guard. Coordinated calls
|
|
935
|
+
// retain the tail and close it before granting a recovery receipt; recheck
|
|
936
|
+
// cancellation after that await as well.
|
|
803
937
|
if (!runState.externalAbort && options.abortSignal?.aborted) {
|
|
804
938
|
runState.externalAbort = true;
|
|
805
|
-
await rollbackAbortedTurn(runState, { requestedSessionId, providerSessionId, durableRepo });
|
|
939
|
+
if (!runState.retainRecoveryTail) await rollbackAbortedTurn(runState, { requestedSessionId, providerSessionId, durableRepo });
|
|
806
940
|
}
|
|
807
941
|
|
|
808
|
-
|
|
942
|
+
// A receipt is proof of a successfully closed native operation, never a
|
|
943
|
+
// promise that the finally block will eventually close it.
|
|
944
|
+
const providerSessionRecovery = runState.retainRecoveryTail
|
|
945
|
+
? await captureSessionRecovery(runState, { options, providerSessionId, modelKey: `${resolved.provider}:${resolved.model}`, model: runtime.model, pending: !!errorMessage || runState.externalAbort })
|
|
946
|
+
: undefined;
|
|
947
|
+
if (runState.retainRecoveryTail) runState.externalAbort ||= !!options.abortSignal?.aborted;
|
|
948
|
+
return { ...buildSuccessResult({
|
|
809
949
|
finalText,
|
|
810
950
|
finalThinking,
|
|
811
951
|
events,
|
|
@@ -824,8 +964,10 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
824
964
|
providerSessionId,
|
|
825
965
|
runtimeWarnings,
|
|
826
966
|
capabilitiesUsed,
|
|
967
|
+
usageMeasured: hasMeasuredUsage(runTranscript),
|
|
827
968
|
structuredResult: runState.structuredResult,
|
|
828
|
-
|
|
969
|
+
effectiveEffort: providerEffectiveEffort,
|
|
970
|
+
}), ...(providerSessionRecovery ? { providerSessionRecovery } : {}) };
|
|
829
971
|
} catch (err) {
|
|
830
972
|
runState.externalAbort ||= !!options.abortSignal?.aborted;
|
|
831
973
|
// Drop a just-created fresh durable session, release a create-on-miss
|
|
@@ -859,9 +1001,12 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
859
1001
|
runtimeWarnings,
|
|
860
1002
|
isRetryable,
|
|
861
1003
|
piTransport,
|
|
1004
|
+
effectiveEffort: harness?.getThinkingLevel?.(),
|
|
862
1005
|
});
|
|
863
1006
|
} finally {
|
|
1007
|
+
try { await harness?.close?.(); } catch { /* best-effort */ }
|
|
864
1008
|
if (runState.sessionEntry) runState.sessionEntry.busy = false;
|
|
1009
|
+
if (runState.registeredSessionEntry) runState.registeredSessionEntry.busy = false;
|
|
865
1010
|
runState.removeAbortHandler?.();
|
|
866
1011
|
try {
|
|
867
1012
|
await closeRunTools();
|
|
@@ -875,6 +1020,6 @@ export const piNativeRuntimeBridge = {
|
|
|
875
1020
|
id: "pi",
|
|
876
1021
|
kind: "pi",
|
|
877
1022
|
capabilities: runtimeCapabilities("pi"),
|
|
878
|
-
supports: (ref) => ref?.
|
|
1023
|
+
supports: (ref) => Boolean(ref?.provider && ref?.model),
|
|
879
1024
|
execute: generatePiNativeResponse,
|
|
880
1025
|
};
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
TOOL_POLICY_ALLOW_ALL_ONLY,
|
|
3
|
-
TOOL_POLICY_PROJECTED,
|
|
4
|
-
} from "./tool-policy.js";
|
|
1
|
+
import { TOOL_POLICY_PROJECTED } from "./tool-policy.js";
|
|
5
2
|
|
|
6
3
|
export const COMMON_CAPABILITIES = {
|
|
7
4
|
streaming: true,
|
|
@@ -15,18 +12,9 @@ export const COMMON_CAPABILITIES = {
|
|
|
15
12
|
supports_live_input: true,
|
|
16
13
|
supports_native_subagents: true,
|
|
17
14
|
supports_request_tool_environment: false,
|
|
18
|
-
supports_fast_mode: false,
|
|
19
|
-
tool_policy: TOOL_POLICY_PROJECTED,
|
|
20
15
|
};
|
|
21
16
|
|
|
22
17
|
export const RUNTIME_CAPABILITIES = {
|
|
23
|
-
claude: {
|
|
24
|
-
runtime: "sdk",
|
|
25
|
-
...COMMON_CAPABILITIES,
|
|
26
|
-
// The Claude Agent SDK persists sessions on disk; the bridge resumes
|
|
27
|
-
// them via queryOptions.resume when options.sessionId is supplied.
|
|
28
|
-
supports_session_resume: true,
|
|
29
|
-
},
|
|
30
18
|
pi: {
|
|
31
19
|
runtime: "pi-agent",
|
|
32
20
|
...COMMON_CAPABILITIES,
|
|
@@ -38,50 +26,17 @@ export const RUNTIME_CAPABILITIES = {
|
|
|
38
26
|
supports_native_subagents: false,
|
|
39
27
|
supports_request_tool_environment: true,
|
|
40
28
|
supports_mcp_apps: true,
|
|
41
|
-
|
|
42
|
-
codex: {
|
|
43
|
-
runtime: "cli",
|
|
44
|
-
...COMMON_CAPABILITIES,
|
|
45
|
-
// The codex-app bridge keeps the app-server subprocess + thread alive
|
|
46
|
-
// when options.sessionKeepAlive is set; resumed turns reuse the thread.
|
|
47
|
-
supports_session_resume: true,
|
|
48
|
-
supports_fast_mode: true,
|
|
49
|
-
tool_policy: TOOL_POLICY_ALLOW_ALL_ONLY,
|
|
50
|
-
},
|
|
51
|
-
opencode: {
|
|
52
|
-
runtime: "cli",
|
|
53
|
-
...COMMON_CAPABILITIES,
|
|
54
|
-
structured_output: false,
|
|
55
|
-
supports_session_resume: false,
|
|
56
|
-
supports_mcp: false,
|
|
57
|
-
supports_skills: false,
|
|
58
|
-
supports_live_input: false,
|
|
59
|
-
supports_native_subagents: false,
|
|
60
|
-
tool_policy: TOOL_POLICY_ALLOW_ALL_ONLY,
|
|
61
|
-
},
|
|
62
|
-
acp: {
|
|
63
|
-
runtime: "acp-stdio",
|
|
64
|
-
...COMMON_CAPABILITIES,
|
|
65
|
-
structured_output: false,
|
|
66
|
-
supports_session_resume: true,
|
|
67
|
-
// Runtime request-scoped MCP servers are not projected into ACP sessions.
|
|
68
|
-
// Profiles may own static ACP MCP configuration, but that is not the
|
|
69
|
-
// supports_mcp contract advertised to the route capability gate.
|
|
70
|
-
supports_mcp: false,
|
|
71
|
-
supports_skills: false,
|
|
72
|
-
supports_builtin_tools: false,
|
|
73
|
-
supports_live_input: false,
|
|
74
|
-
supports_native_subagents: false,
|
|
75
|
-
supports_request_tool_environment: false,
|
|
76
|
-
tool_policy: TOOL_POLICY_ALLOW_ALL_ONLY,
|
|
29
|
+
tool_policy: TOOL_POLICY_PROJECTED,
|
|
77
30
|
},
|
|
78
31
|
};
|
|
79
32
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
33
|
+
/**
|
|
34
|
+
* @param {*} [_legacySelection]
|
|
35
|
+
* @returns {{kind: "pi"} & typeof RUNTIME_CAPABILITIES.pi}
|
|
36
|
+
*/
|
|
37
|
+
export function runtimeCapabilities(_legacySelection) {
|
|
38
|
+
// Capability lookup no longer selects a backend. The ignored optional value
|
|
39
|
+
// keeps the still-separate router migration type-safe without allowing its
|
|
40
|
+
// legacy route ids to influence the sole surviving runtime contract.
|
|
41
|
+
return { kind: "pi", ...RUNTIME_CAPABILITIES.pi };
|
|
87
42
|
}
|