@mono-agent/agent-runtime 0.13.0 → 0.15.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 +62 -20
- package/MIGRATION.md +44 -36
- package/README.md +181 -114
- package/package.json +3 -6
- package/src/agent/approval.js +4 -2
- package/src/ai/index.js +0 -1
- package/src/ai/observer.js +48 -13
- package/src/ai/providers/claude-cli.js +2 -13
- package/src/ai/providers/claude-sdk.js +12 -7
- package/src/ai/providers/codex-app.js +205 -55
- package/src/ai/providers/opencode-app.js +168 -3
- package/src/ai/providers/pi-messages.js +0 -8
- package/src/ai/providers/pi-native/compaction-driver.js +106 -10
- package/src/ai/providers/pi-native/result-builder.js +2 -14
- package/src/ai/providers/pi-native/stream-subscriber.js +20 -2
- package/src/ai/providers/pi-native/turn-runner.js +31 -8
- package/src/ai/providers/pi-native.js +2 -5
- package/src/ai/runtime/model-refs.js +1 -1
- package/src/ai/runtime/registry.js +8 -1
- package/src/ai/types.js +1 -1
- package/src/runtime.js +4 -4
- package/types/ai/index.d.ts +0 -1
- package/types/ai/observer.d.ts +4 -2
- package/types/ai/providers/claude-cli.d.ts +6 -30
- package/types/ai/providers/claude-sdk.d.ts +2 -9
- package/types/ai/providers/codex-app.d.ts +4 -10
- package/types/ai/providers/pi-messages.d.ts +0 -1
- package/types/ai/providers/pi-native/result-builder.d.ts +3 -11
- package/types/ai/providers/pi-native/stream-subscriber.d.ts +4 -2
- package/types/ai/providers/pi-native/turn-runner.d.ts +1 -1
- package/types/ai/types.d.ts +9 -3
- package/src/ai/backend.js +0 -17
- package/src/ai/registry.js +0 -5
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
prepareCompaction,
|
|
34
34
|
shouldCompact,
|
|
35
35
|
} from "@earendil-works/pi-agent-core";
|
|
36
|
+
import { randomUUID } from "node:crypto";
|
|
36
37
|
import {
|
|
37
38
|
estimateFixedOverheadTokens,
|
|
38
39
|
isLikelyContextTermination,
|
|
@@ -204,6 +205,52 @@ function previewCompactedContext(branchEntries, result) {
|
|
|
204
205
|
return estimateBuiltContextTokens([...branchEntries, previewEntry]);
|
|
205
206
|
}
|
|
206
207
|
|
|
208
|
+
function canonicalCompactionTrigger(trigger) {
|
|
209
|
+
return trigger === "reactive_overflow" ? "overflow" : trigger;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function finiteTokenCount(value) {
|
|
213
|
+
if (value === null || value === undefined) return undefined;
|
|
214
|
+
const count = Number(value);
|
|
215
|
+
return Number.isFinite(count) && count >= 0 ? count : undefined;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @param {((event: any) => void)|undefined} onEvent
|
|
220
|
+
* @param {{operationId: string, status: string, trigger: string, model?: string, tokensBefore?: number|null, tokensAfter?: number|null, reason?: string, message?: string}} event
|
|
221
|
+
*/
|
|
222
|
+
function emitCompactionEvent(onEvent, {
|
|
223
|
+
operationId,
|
|
224
|
+
status,
|
|
225
|
+
trigger,
|
|
226
|
+
model,
|
|
227
|
+
tokensBefore,
|
|
228
|
+
tokensAfter,
|
|
229
|
+
reason,
|
|
230
|
+
message,
|
|
231
|
+
}) {
|
|
232
|
+
const before = finiteTokenCount(tokensBefore);
|
|
233
|
+
const after = finiteTokenCount(tokensAfter);
|
|
234
|
+
try {
|
|
235
|
+
onEvent?.({
|
|
236
|
+
type: "context_compaction",
|
|
237
|
+
operationId,
|
|
238
|
+
status,
|
|
239
|
+
sdk: "pi",
|
|
240
|
+
trigger: canonicalCompactionTrigger(trigger),
|
|
241
|
+
timestamp: Date.now(),
|
|
242
|
+
...(model ? { model } : {}),
|
|
243
|
+
...(before === undefined ? {} : { tokensBefore: before }),
|
|
244
|
+
...(after === undefined ? {} : { tokensAfter: after }),
|
|
245
|
+
...(before === undefined && after === undefined ? {} : { tokenCountsExact: false }),
|
|
246
|
+
...(reason ? { reason } : {}),
|
|
247
|
+
...(message ? { message } : {}),
|
|
248
|
+
});
|
|
249
|
+
} catch {
|
|
250
|
+
// Observability must never change whether compaction itself succeeds.
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
207
254
|
// Run a single guarded compaction. Requires the harness idle (callers
|
|
208
255
|
// waitForIdle first). Never throws — classifies AgentHarnessError into a warning
|
|
209
256
|
// and reports back whether anything was compacted. Fires onCompactionRecorded on
|
|
@@ -218,14 +265,22 @@ export async function tryCompact(harness, {
|
|
|
218
265
|
session,
|
|
219
266
|
policy,
|
|
220
267
|
}) {
|
|
221
|
-
const
|
|
222
|
-
|
|
268
|
+
const operationId = randomUUID();
|
|
269
|
+
emitCompactionEvent(onEvent, {
|
|
270
|
+
operationId,
|
|
271
|
+
status: "running",
|
|
272
|
+
trigger,
|
|
273
|
+
model,
|
|
223
274
|
});
|
|
224
|
-
|
|
275
|
+
let effectivePolicy = policy || {};
|
|
225
276
|
/** @type {null | {kind: string, tokensBefore?: number|null, tokensAfter?: number|null, savings?: number|null, error?: any}} */
|
|
226
277
|
let hookDecision = null;
|
|
227
278
|
let removeHook = null;
|
|
228
279
|
try {
|
|
280
|
+
const adaptivePolicy = resolveAgentCompactionPolicy({}, {
|
|
281
|
+
contextWindow: typeof harness?.getModel === "function" ? harness.getModel()?.contextWindow : undefined,
|
|
282
|
+
});
|
|
283
|
+
effectivePolicy = { ...adaptivePolicy, ...(policy || {}) };
|
|
229
284
|
if (typeof harness?.on !== "function") {
|
|
230
285
|
throw new Error("Pi AgentHarness does not expose session_before_compact hooks");
|
|
231
286
|
}
|
|
@@ -298,14 +353,13 @@ export async function tryCompact(harness, {
|
|
|
298
353
|
const reduced = measuredTokensBefore === null || tokensAfter === null
|
|
299
354
|
? null
|
|
300
355
|
: tokensAfter < measuredTokensBefore;
|
|
301
|
-
onEvent
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
source: "pi",
|
|
356
|
+
emitCompactionEvent(onEvent, {
|
|
357
|
+
operationId,
|
|
358
|
+
status: "succeeded",
|
|
305
359
|
trigger,
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
360
|
+
model,
|
|
361
|
+
tokensBefore,
|
|
362
|
+
tokensAfter,
|
|
309
363
|
});
|
|
310
364
|
if (reduced === false) {
|
|
311
365
|
runtimeWarnings?.push({
|
|
@@ -354,6 +408,15 @@ export async function tryCompact(harness, {
|
|
|
354
408
|
? { minimum_savings_tokens: effectivePolicy.compactionMinSavingsTokens }
|
|
355
409
|
: {}),
|
|
356
410
|
});
|
|
411
|
+
emitCompactionEvent(onEvent, {
|
|
412
|
+
operationId,
|
|
413
|
+
status: "skipped",
|
|
414
|
+
trigger,
|
|
415
|
+
model,
|
|
416
|
+
tokensBefore: hookDecision.tokensBefore,
|
|
417
|
+
tokensAfter: hookDecision.tokensAfter,
|
|
418
|
+
reason: hookDecision.kind,
|
|
419
|
+
});
|
|
357
420
|
return {
|
|
358
421
|
applied: false,
|
|
359
422
|
tokensBefore: hookDecision.tokensBefore ?? null,
|
|
@@ -369,6 +432,13 @@ export async function tryCompact(harness, {
|
|
|
369
432
|
trigger,
|
|
370
433
|
message: "Nothing to compact",
|
|
371
434
|
});
|
|
435
|
+
emitCompactionEvent(onEvent, {
|
|
436
|
+
operationId,
|
|
437
|
+
status: "skipped",
|
|
438
|
+
trigger,
|
|
439
|
+
model,
|
|
440
|
+
reason: "nothing_to_compact",
|
|
441
|
+
});
|
|
372
442
|
return { applied: false, tokensBefore: null, tokensAfter: null, reduced: null, nothingToCompact: true };
|
|
373
443
|
}
|
|
374
444
|
const effectiveError = hookDecision?.kind === "failed" && hookDecision.error
|
|
@@ -385,6 +455,32 @@ export async function tryCompact(harness, {
|
|
|
385
455
|
? "context_compaction_busy"
|
|
386
456
|
: "context_compaction_failed";
|
|
387
457
|
runtimeWarnings?.push({ warning_kind: warningKind, source: "pi", trigger, message });
|
|
458
|
+
emitCompactionEvent(onEvent, {
|
|
459
|
+
operationId,
|
|
460
|
+
status: nothingToCompact ? "skipped" : "failed",
|
|
461
|
+
trigger,
|
|
462
|
+
model,
|
|
463
|
+
reason: nothingToCompact
|
|
464
|
+
? "nothing_to_compact"
|
|
465
|
+
: code === "auth"
|
|
466
|
+
? "authentication"
|
|
467
|
+
: code === "busy"
|
|
468
|
+
? "busy"
|
|
469
|
+
: code === "aborted"
|
|
470
|
+
? "cancelled"
|
|
471
|
+
: "provider_error",
|
|
472
|
+
...(nothingToCompact
|
|
473
|
+
? {}
|
|
474
|
+
: {
|
|
475
|
+
message: code === "auth"
|
|
476
|
+
? "Compaction authentication failed."
|
|
477
|
+
: code === "busy"
|
|
478
|
+
? "Context was busy and could not be compacted."
|
|
479
|
+
: code === "aborted"
|
|
480
|
+
? "Compaction was cancelled."
|
|
481
|
+
: "Compaction failed.",
|
|
482
|
+
}),
|
|
483
|
+
});
|
|
388
484
|
return { applied: false, tokensBefore: null, tokensAfter: null, reduced: null, nothingToCompact };
|
|
389
485
|
} finally {
|
|
390
486
|
removeHook?.();
|
|
@@ -33,7 +33,7 @@ export function usageFromMessages(messages = []) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* Normalize
|
|
36
|
+
* Normalize one provider request's usage into an exact context snapshot.
|
|
37
37
|
* Unlike usageFromMessages(), this deliberately does not aggregate earlier
|
|
38
38
|
* requests in the run: the last assistant usage is the same provider-counted
|
|
39
39
|
* value Pi's compaction logic trusts, so it can decrease after compaction.
|
|
@@ -74,15 +74,13 @@ export function failureKindForPiError(message, diagnostics, { maxTurnsHit = fals
|
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Emit the per-run cache / cost / provider-completed events.
|
|
77
|
-
* @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number},
|
|
77
|
+
* @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}, estimatedCost: number, start: number, externalAbort: boolean}} params
|
|
78
78
|
*/
|
|
79
79
|
export function emitUsageCostEvents({
|
|
80
80
|
onEvent,
|
|
81
81
|
resolved,
|
|
82
82
|
reference,
|
|
83
83
|
usage,
|
|
84
|
-
contextUsage,
|
|
85
|
-
contextWindow,
|
|
86
84
|
estimatedCost,
|
|
87
85
|
start,
|
|
88
86
|
externalAbort,
|
|
@@ -105,16 +103,6 @@ export function emitUsageCostEvents({
|
|
|
105
103
|
cacheCreationTokens: Number(usage.cacheWrite) || 0,
|
|
106
104
|
},
|
|
107
105
|
});
|
|
108
|
-
if (contextUsage) {
|
|
109
|
-
const effectiveContextWindow = Number(contextWindow) || 0;
|
|
110
|
-
onEvent({
|
|
111
|
-
type: "context_usage",
|
|
112
|
-
sdk: resolved.sdk,
|
|
113
|
-
model: reference,
|
|
114
|
-
...(effectiveContextWindow > 0 ? { contextWindow: effectiveContextWindow } : {}),
|
|
115
|
-
tokens: contextUsage,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
106
|
onEvent({
|
|
119
107
|
type: "provider_request_completed",
|
|
120
108
|
sdk: resolved.sdk,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
jsonSerializable,
|
|
14
14
|
streamContentKey,
|
|
15
15
|
} from "../pi-events.js";
|
|
16
|
+
import { contextUsageFromAssistantMessage } from "./result-builder.js";
|
|
16
17
|
|
|
17
18
|
function toolResultFileChange(result) {
|
|
18
19
|
const fileChange = result?.details?.file_change;
|
|
@@ -41,10 +42,10 @@ function toolResultFileChange(result) {
|
|
|
41
42
|
* abort; it is already constructed when this is wired (subscribe follows the
|
|
42
43
|
* AgentHarness constructor).
|
|
43
44
|
* @param {StreamSubscriberState} runState
|
|
44
|
-
* @param {{onEvent: (event: any) => void, options: any, toolLimits: any, harness: any}} deps
|
|
45
|
+
* @param {{onEvent: (event: any) => void, options: any, toolLimits: any, harness: any, sdk: string, model: string}} deps
|
|
45
46
|
* @returns {(event: any) => void}
|
|
46
47
|
*/
|
|
47
|
-
export function createStreamSubscriber(runState, { onEvent, options, toolLimits, harness }) {
|
|
48
|
+
export function createStreamSubscriber(runState, { onEvent, options, toolLimits, harness, sdk, model }) {
|
|
48
49
|
return (event) => {
|
|
49
50
|
if (event.type === "message_update") {
|
|
50
51
|
const streamEvent = event.assistantMessageEvent;
|
|
@@ -69,6 +70,23 @@ export function createStreamSubscriber(runState, { onEvent, options, toolLimits,
|
|
|
69
70
|
onEvent({ type: "assistant", message: { content: [{ type: "thinking", text: streamEvent.content }] } });
|
|
70
71
|
}
|
|
71
72
|
}
|
|
73
|
+
} else if (event.type === "message_end") {
|
|
74
|
+
const contextUsage = contextUsageFromAssistantMessage(event.message);
|
|
75
|
+
if (contextUsage) {
|
|
76
|
+
const contextWindow = Number(harness?.getModel?.()?.contextWindow) || 0;
|
|
77
|
+
const measurementId = typeof event.message?.id === "string" && event.message.id.trim().length > 0
|
|
78
|
+
? event.message.id
|
|
79
|
+
: undefined;
|
|
80
|
+
onEvent({
|
|
81
|
+
type: "context_usage",
|
|
82
|
+
sdk,
|
|
83
|
+
model,
|
|
84
|
+
timestamp: Date.now(),
|
|
85
|
+
...(measurementId === undefined ? {} : { measurementId }),
|
|
86
|
+
...(contextWindow > 0 ? { contextWindow } : {}),
|
|
87
|
+
tokens: contextUsage,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
72
90
|
} else if (event.type === "tool_execution_start") {
|
|
73
91
|
if (event.toolName) runState.lastToolName = event.toolName;
|
|
74
92
|
if (event.toolCallId) runState.toolStartTimes.set(event.toolCallId, Date.now());
|
|
@@ -205,6 +205,8 @@ export function buildTurnHarness(runState, {
|
|
|
205
205
|
onEvent,
|
|
206
206
|
options,
|
|
207
207
|
toolLimits,
|
|
208
|
+
sdk,
|
|
209
|
+
reference,
|
|
208
210
|
}) {
|
|
209
211
|
const harness = new AgentHarness({
|
|
210
212
|
env: new NodeExecutionEnv({ cwd: cwd || process.cwd() }),
|
|
@@ -228,7 +230,14 @@ export function buildTurnHarness(runState, {
|
|
|
228
230
|
: undefined);
|
|
229
231
|
runState.harness = harness;
|
|
230
232
|
|
|
231
|
-
harness.subscribe(createStreamSubscriber(runState, {
|
|
233
|
+
harness.subscribe(createStreamSubscriber(runState, {
|
|
234
|
+
onEvent,
|
|
235
|
+
options,
|
|
236
|
+
toolLimits,
|
|
237
|
+
harness,
|
|
238
|
+
sdk,
|
|
239
|
+
model: reference,
|
|
240
|
+
}));
|
|
232
241
|
|
|
233
242
|
const abortHandler = () => {
|
|
234
243
|
runState.externalAbort = true;
|
|
@@ -255,12 +264,24 @@ export function startLiveInput({ harness, options, onEvent }) {
|
|
|
255
264
|
? options.liveInput[Symbol.asyncIterator]()
|
|
256
265
|
: options.liveInput;
|
|
257
266
|
let runComplete = false;
|
|
267
|
+
/** @type {() => void} */
|
|
268
|
+
let signalStop = () => {};
|
|
269
|
+
const stopped = new Promise((resolve) => { signalStop = () => resolve(); });
|
|
258
270
|
const task = (async () => {
|
|
259
271
|
try {
|
|
260
272
|
while (!runComplete && !options.abortSignal?.aborted) {
|
|
261
|
-
const next = await
|
|
273
|
+
const next = await Promise.race([
|
|
274
|
+
iterator.next(),
|
|
275
|
+
stopped.then(() => ({ done: true, value: undefined })),
|
|
276
|
+
]);
|
|
262
277
|
if (next.done || runComplete || options.abortSignal?.aborted) break;
|
|
263
|
-
|
|
278
|
+
try {
|
|
279
|
+
await harness.steer(formatLiveInputGuidance(next.value.body, options.prompts));
|
|
280
|
+
next.value.acknowledge?.();
|
|
281
|
+
} catch (err) {
|
|
282
|
+
next.value.reject?.(err);
|
|
283
|
+
throw err;
|
|
284
|
+
}
|
|
264
285
|
}
|
|
265
286
|
} catch (err) {
|
|
266
287
|
onEvent({
|
|
@@ -273,15 +294,17 @@ export function startLiveInput({ harness, options, onEvent }) {
|
|
|
273
294
|
return {
|
|
274
295
|
// The run is done: stop the live-steering consumer so it cannot steer a
|
|
275
296
|
// finished harness or swallow a follow-up meant for the next turn. We signal
|
|
276
|
-
// completion, then best-effort return() the iterator
|
|
277
|
-
//
|
|
278
|
-
//
|
|
297
|
+
// completion, then best-effort return() the iterator. The explicit stop
|
|
298
|
+
// race releases the task even when a third-party iterator's return() does
|
|
299
|
+
// not unblock its pending next(); awaiting the task still closes any steer
|
|
300
|
+
// acknowledgement already in progress.
|
|
279
301
|
stop: async () => {
|
|
280
302
|
runComplete = true;
|
|
303
|
+
signalStop();
|
|
281
304
|
if (iterator && typeof iterator.return === "function") {
|
|
282
|
-
try {
|
|
305
|
+
try { void Promise.resolve(iterator.return()).catch(() => {}); } catch { /* best-effort */ }
|
|
283
306
|
}
|
|
284
|
-
|
|
307
|
+
await task;
|
|
285
308
|
},
|
|
286
309
|
};
|
|
287
310
|
}
|
|
@@ -52,7 +52,6 @@ import {
|
|
|
52
52
|
buildErrorDetails,
|
|
53
53
|
buildErrorResult,
|
|
54
54
|
buildSuccessResult,
|
|
55
|
-
contextUsageFromAssistantMessage,
|
|
56
55
|
emitCapabilitiesResolved,
|
|
57
56
|
emitUsageCostEvents,
|
|
58
57
|
usageFromMessages,
|
|
@@ -453,6 +452,8 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
453
452
|
onEvent,
|
|
454
453
|
options,
|
|
455
454
|
toolLimits,
|
|
455
|
+
sdk: resolved.sdk,
|
|
456
|
+
reference,
|
|
456
457
|
});
|
|
457
458
|
|
|
458
459
|
// Seed prior transcript (everything before the trailing user turn) into the
|
|
@@ -614,10 +615,6 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
614
615
|
resolved,
|
|
615
616
|
reference,
|
|
616
617
|
usage,
|
|
617
|
-
contextUsage: runState.externalAbort || runState.maxTurnsHit || runError
|
|
618
|
-
? null
|
|
619
|
-
: contextUsageFromAssistantMessage(lastAssistant),
|
|
620
|
-
contextWindow: runState.compaction.policy?.contextWindow,
|
|
621
618
|
estimatedCost,
|
|
622
619
|
start,
|
|
623
620
|
externalAbort: runState.externalAbort,
|
|
@@ -123,7 +123,7 @@ export const RESERVED_RUNTIME_KINDS = [...RESERVED_RUNTIME_IDS];
|
|
|
123
123
|
// sdk='claude' → CLI (claude binary) or SDK (Anthropic)
|
|
124
124
|
// sdk='codex' → CLI only (codex app-server)
|
|
125
125
|
// sdk='opencode' → CLI only (opencode server via @opencode-ai/sdk)
|
|
126
|
-
// sdk='pi' → SDK only (pi-
|
|
126
|
+
// sdk='pi' → SDK only (the pi-native bridge handles openai-codex and other providers)
|
|
127
127
|
|
|
128
128
|
// Returns null when the combo is fine; otherwise a short reason string the
|
|
129
129
|
// UI / API can show.
|
|
@@ -29,7 +29,14 @@ const builtinBridgeSpecs = {
|
|
|
29
29
|
id: "claude-code",
|
|
30
30
|
supports: (ref, options) => ref?.sdk === "claude" && options?.executionMode === "cli",
|
|
31
31
|
// The claude CLI resumes prior sessions via `--resume <sessionId>`.
|
|
32
|
-
capabilities: () => ({
|
|
32
|
+
capabilities: () => ({
|
|
33
|
+
kind: "claude-code",
|
|
34
|
+
runtime: "cli",
|
|
35
|
+
...COMMON_CAPABILITIES,
|
|
36
|
+
supports_session_resume: true,
|
|
37
|
+
// The one-shot CLI bridge has no bidirectional stdin steering channel.
|
|
38
|
+
supports_live_input: false,
|
|
39
|
+
}),
|
|
33
40
|
load: async () => (await import("../providers/claude-cli.js")).claudeCodeRuntimeBridge,
|
|
34
41
|
},
|
|
35
42
|
"codex-app": {
|
package/src/ai/types.js
CHANGED
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
* @property {string} [providerSessionId] Provider-owned resume id for resumable bridges.
|
|
134
134
|
* @property {boolean} [sessionKeepAlive] Keep resumable provider state alive after the turn.
|
|
135
135
|
* @property {number} [sessionIdleTimeoutMs] Idle TTL for resumable provider state.
|
|
136
|
-
* @property {
|
|
136
|
+
* @property {AsyncIterable<{body: string, id?: string, receivedAt?: string, acknowledge?: () => void, reject?: (error?: unknown) => void}>} [liveInput] Stream of in-flight user messages for steering an active run. Providers acknowledge only after accepting a message into the active turn.
|
|
137
137
|
* @property {ReadonlyArray<*>} [observers] Per-call observers (see RuntimeObserver) merged with host-level (createRuntime) observers.
|
|
138
138
|
* @property {(event: RuntimeEvent) => void} [onEvent]
|
|
139
139
|
* @property {ReadonlyArray<Object>} [messages]
|
package/src/runtime.js
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
// method that resolves the right provider bridge based on `options.model` +
|
|
9
9
|
// `options.executionMode`.
|
|
10
10
|
//
|
|
11
|
-
// The
|
|
12
|
-
//
|
|
13
|
-
// that need
|
|
14
|
-
//
|
|
11
|
+
// The runtime registry contains a static table for the five built-in bridges
|
|
12
|
+
// (claude-sdk, claude-cli, pi-native, codex-app, opencode-app) and lazily imports
|
|
13
|
+
// the matching implementation only when a run selects it. Hosts that need finer
|
|
14
|
+
// control can keep using the named exports (resolveRuntimeBridge,
|
|
15
15
|
// generateClaudeResponse, etc.) directly.
|
|
16
16
|
//
|
|
17
17
|
// Return shape from `.run()`:
|
package/types/ai/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export * from "./registry.js";
|
|
2
1
|
export * from "./runtime/model-refs.js";
|
|
3
2
|
export * from "./runtime/registry.js";
|
|
4
3
|
export { createSessionRegistry, disposeAllProviderSessions, disposeProviderSession, invalidateProviderSession, refreshProviderSession, syncProviderSession } from "./runtime/sessions.js";
|
package/types/ai/observer.d.ts
CHANGED
|
@@ -14,8 +14,9 @@ export function createObserverHub({ observers, onEvent }?: {
|
|
|
14
14
|
flush: () => Promise<void>;
|
|
15
15
|
observers: () => any[];
|
|
16
16
|
};
|
|
17
|
-
export function createMetricsObserver({ name }?: {
|
|
17
|
+
export function createMetricsObserver({ name, maxLatencySamples }?: {
|
|
18
18
|
name?: string;
|
|
19
|
+
maxLatencySamples?: number;
|
|
19
20
|
}): {
|
|
20
21
|
name: string;
|
|
21
22
|
recordEvent: (event: any) => void;
|
|
@@ -49,7 +50,8 @@ export function createMetricsObserver({ name }?: {
|
|
|
49
50
|
byKind: any;
|
|
50
51
|
};
|
|
51
52
|
turns: {
|
|
52
|
-
count:
|
|
53
|
+
count: any;
|
|
54
|
+
sampleCount: number;
|
|
53
55
|
latencyMsP50: any;
|
|
54
56
|
latencyMsP95: any;
|
|
55
57
|
};
|
|
@@ -155,9 +155,10 @@ export function generateCliResponse(systemPrompt: any, options?: {}): Promise<{
|
|
|
155
155
|
context_compaction_applied: any;
|
|
156
156
|
};
|
|
157
157
|
}>;
|
|
158
|
-
export namespace
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
export namespace claudeCodeRuntimeBridge {
|
|
159
|
+
let id: string;
|
|
160
|
+
let kind: string;
|
|
161
|
+
namespace capabilities {
|
|
161
162
|
export let streaming: boolean;
|
|
162
163
|
export let structured_output: boolean;
|
|
163
164
|
export let supports_session_resume: boolean;
|
|
@@ -171,33 +172,8 @@ export namespace claudeCodeBackend {
|
|
|
171
172
|
export { kind_1 as kind };
|
|
172
173
|
export let runtime: string;
|
|
173
174
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
export namespace codexCliBackend {
|
|
177
|
-
let kind_2: string;
|
|
178
|
-
export { kind_2 as kind };
|
|
179
|
-
export namespace capabilities_1 {
|
|
180
|
-
let kind_3: string;
|
|
181
|
-
export { kind_3 as kind };
|
|
182
|
-
let runtime_1: string;
|
|
183
|
-
export { runtime_1 as runtime };
|
|
184
|
-
}
|
|
185
|
-
export { capabilities_1 as capabilities };
|
|
186
|
-
export { generateCliResponse as execute };
|
|
187
|
-
}
|
|
188
|
-
export namespace claudeCodeRuntimeBridge {
|
|
189
|
-
export let id: string;
|
|
190
|
-
let kind_4: string;
|
|
191
|
-
export { kind_4 as kind };
|
|
192
|
-
export namespace capabilities_2 {
|
|
193
|
-
let kind_5: string;
|
|
194
|
-
export { kind_5 as kind };
|
|
195
|
-
let runtime_2: string;
|
|
196
|
-
export { runtime_2 as runtime };
|
|
197
|
-
}
|
|
198
|
-
export { capabilities_2 as capabilities };
|
|
199
|
-
export function supports(ref: any, options: any): boolean;
|
|
200
|
-
export function execute(systemPrompt: any, options: any): Promise<{
|
|
175
|
+
function supports(ref: any, options: any): boolean;
|
|
176
|
+
function execute(systemPrompt: any, options: any): Promise<{
|
|
201
177
|
text: any;
|
|
202
178
|
structuredResult: any;
|
|
203
179
|
structuredResultSource: any;
|
|
@@ -129,17 +129,10 @@ export function generateClaudeResponse(systemPrompt: any, options: any): Promise
|
|
|
129
129
|
context_compaction_applied: any;
|
|
130
130
|
};
|
|
131
131
|
}>;
|
|
132
|
-
export namespace claudeSdkBackend {
|
|
133
|
-
export let kind: string;
|
|
134
|
-
export let capabilities: any;
|
|
135
|
-
export { generateClaudeResponse as execute };
|
|
136
|
-
}
|
|
137
132
|
export namespace claudeRuntimeBridge {
|
|
138
133
|
export let id: string;
|
|
139
|
-
let
|
|
140
|
-
export
|
|
141
|
-
let capabilities_1: any;
|
|
142
|
-
export { capabilities_1 as capabilities };
|
|
134
|
+
export let kind: string;
|
|
135
|
+
export let capabilities: any;
|
|
143
136
|
export function supports(ref: any): boolean;
|
|
144
137
|
export { generateClaudeResponse as execute };
|
|
145
138
|
}
|
|
@@ -62,7 +62,7 @@ export function generateCodexAppResponse(systemPrompt: any, options?: {}): Promi
|
|
|
62
62
|
};
|
|
63
63
|
durationMs: number;
|
|
64
64
|
numTurns: number;
|
|
65
|
-
model:
|
|
65
|
+
model: any;
|
|
66
66
|
effort: any;
|
|
67
67
|
sdk: string;
|
|
68
68
|
providerSessionId: any;
|
|
@@ -124,22 +124,16 @@ export function generateCodexAppResponse(systemPrompt: any, options?: {}): Promi
|
|
|
124
124
|
context_compaction_applied: any;
|
|
125
125
|
};
|
|
126
126
|
}>;
|
|
127
|
-
export namespace codexAppBackend {
|
|
128
|
-
export let kind: string;
|
|
129
|
-
export { CODEX_APP_CAPABILITIES as capabilities };
|
|
130
|
-
export { generateCodexAppResponse as execute };
|
|
131
|
-
}
|
|
132
127
|
export namespace codexAppRuntimeBridge {
|
|
133
128
|
export let id: string;
|
|
134
|
-
let
|
|
135
|
-
export { kind_1 as kind };
|
|
129
|
+
export let kind: string;
|
|
136
130
|
export { CODEX_APP_CAPABILITIES as capabilities };
|
|
137
131
|
export function supports(ref: any, options: any): boolean;
|
|
138
132
|
export { generateCodexAppResponse as execute };
|
|
139
133
|
}
|
|
140
134
|
declare namespace CODEX_APP_CAPABILITIES {
|
|
141
|
-
let
|
|
142
|
-
export {
|
|
135
|
+
let kind_1: string;
|
|
136
|
+
export { kind_1 as kind };
|
|
143
137
|
export let runtime: string;
|
|
144
138
|
export let streaming: boolean;
|
|
145
139
|
export let structured_output: boolean;
|
|
@@ -11,7 +11,7 @@ export function usageFromMessages(messages?: Array<any>): {
|
|
|
11
11
|
cost: number;
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
|
-
* Normalize
|
|
14
|
+
* Normalize one provider request's usage into an exact context snapshot.
|
|
15
15
|
* Unlike usageFromMessages(), this deliberately does not aggregate earlier
|
|
16
16
|
* requests in the run: the last assistant usage is the same provider-counted
|
|
17
17
|
* value Pi's compaction logic trusts, so it can decrease after compaction.
|
|
@@ -40,9 +40,9 @@ export function failureKindForPiError(message: string | null, diagnostics: Recor
|
|
|
40
40
|
}): string | null;
|
|
41
41
|
/**
|
|
42
42
|
* Emit the per-run cache / cost / provider-completed events.
|
|
43
|
-
* @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number},
|
|
43
|
+
* @param {{onEvent: (event: any) => void, resolved: any, reference: string, usage: {input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}, estimatedCost: number, start: number, externalAbort: boolean}} params
|
|
44
44
|
*/
|
|
45
|
-
export function emitUsageCostEvents({ onEvent, resolved, reference, usage,
|
|
45
|
+
export function emitUsageCostEvents({ onEvent, resolved, reference, usage, estimatedCost, start, externalAbort, }: {
|
|
46
46
|
onEvent: (event: any) => void;
|
|
47
47
|
resolved: any;
|
|
48
48
|
reference: string;
|
|
@@ -53,14 +53,6 @@ export function emitUsageCostEvents({ onEvent, resolved, reference, usage, conte
|
|
|
53
53
|
cacheWrite: number;
|
|
54
54
|
cost: number;
|
|
55
55
|
};
|
|
56
|
-
contextUsage?: {
|
|
57
|
-
input: number;
|
|
58
|
-
output: number;
|
|
59
|
-
cacheRead: number;
|
|
60
|
-
cacheCreation: number;
|
|
61
|
-
total: number;
|
|
62
|
-
} | null;
|
|
63
|
-
contextWindow?: number;
|
|
64
56
|
estimatedCost: number;
|
|
65
57
|
start: number;
|
|
66
58
|
externalAbort: boolean;
|
|
@@ -17,14 +17,16 @@
|
|
|
17
17
|
* abort; it is already constructed when this is wired (subscribe follows the
|
|
18
18
|
* AgentHarness constructor).
|
|
19
19
|
* @param {StreamSubscriberState} runState
|
|
20
|
-
* @param {{onEvent: (event: any) => void, options: any, toolLimits: any, harness: any}} deps
|
|
20
|
+
* @param {{onEvent: (event: any) => void, options: any, toolLimits: any, harness: any, sdk: string, model: string}} deps
|
|
21
21
|
* @returns {(event: any) => void}
|
|
22
22
|
*/
|
|
23
|
-
export function createStreamSubscriber(runState: StreamSubscriberState, { onEvent, options, toolLimits, harness }: {
|
|
23
|
+
export function createStreamSubscriber(runState: StreamSubscriberState, { onEvent, options, toolLimits, harness, sdk, model }: {
|
|
24
24
|
onEvent: (event: any) => void;
|
|
25
25
|
options: any;
|
|
26
26
|
toolLimits: any;
|
|
27
27
|
harness: any;
|
|
28
|
+
sdk: string;
|
|
29
|
+
model: string;
|
|
28
30
|
}): (event: any) => void;
|
|
29
31
|
/**
|
|
30
32
|
* The slice of run state the stream subscriber reads and mutates. A structural
|
|
@@ -32,7 +32,7 @@ export function thinkingLevelForEffort(effort: string, capabilities: any): strin
|
|
|
32
32
|
* @param {any} params
|
|
33
33
|
* @returns {any}
|
|
34
34
|
*/
|
|
35
|
-
export function buildTurnHarness(runState: any, { cwd, session, piModels, model, thinkingLevel, systemPrompt, outputSchema, tools, transport, maxRetries, maxRetryDelayMs, steeringMode, onEvent, options, toolLimits, }: any): any;
|
|
35
|
+
export function buildTurnHarness(runState: any, { cwd, session, piModels, model, thinkingLevel, systemPrompt, outputSchema, tools, transport, maxRetries, maxRetryDelayMs, steeringMode, onEvent, options, toolLimits, sdk, reference, }: any): any;
|
|
36
36
|
/**
|
|
37
37
|
* Start the live-input steering consumer. Consumes follow-up messages and steers
|
|
38
38
|
* the harness mid-run; the consumer is tied to run completion (an internal
|
package/types/ai/types.d.ts
CHANGED
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
* @property {string} [providerSessionId] Provider-owned resume id for resumable bridges.
|
|
105
105
|
* @property {boolean} [sessionKeepAlive] Keep resumable provider state alive after the turn.
|
|
106
106
|
* @property {number} [sessionIdleTimeoutMs] Idle TTL for resumable provider state.
|
|
107
|
-
* @property {
|
|
107
|
+
* @property {AsyncIterable<{body: string, id?: string, receivedAt?: string, acknowledge?: () => void, reject?: (error?: unknown) => void}>} [liveInput] Stream of in-flight user messages for steering an active run. Providers acknowledge only after accepting a message into the active turn.
|
|
108
108
|
* @property {ReadonlyArray<*>} [observers] Per-call observers (see RuntimeObserver) merged with host-level (createRuntime) observers.
|
|
109
109
|
* @property {(event: RuntimeEvent) => void} [onEvent]
|
|
110
110
|
* @property {ReadonlyArray<Object>} [messages]
|
|
@@ -490,9 +490,15 @@ export type RuntimeRunOptions = {
|
|
|
490
490
|
*/
|
|
491
491
|
sessionIdleTimeoutMs?: number;
|
|
492
492
|
/**
|
|
493
|
-
*
|
|
493
|
+
* Stream of in-flight user messages for steering an active run. Providers acknowledge only after accepting a message into the active turn.
|
|
494
494
|
*/
|
|
495
|
-
liveInput?:
|
|
495
|
+
liveInput?: AsyncIterable<{
|
|
496
|
+
body: string;
|
|
497
|
+
id?: string;
|
|
498
|
+
receivedAt?: string;
|
|
499
|
+
acknowledge?: () => void;
|
|
500
|
+
reject?: (error?: unknown) => void;
|
|
501
|
+
}>;
|
|
496
502
|
/**
|
|
497
503
|
* Per-call observers (see RuntimeObserver) merged with host-level (createRuntime) observers.
|
|
498
504
|
*/
|