@arnilo/prism 0.8.0 → 0.9.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/CHANGELOG.md +38 -0
- package/README.md +11 -11
- package/dist/agent-approval.d.ts +11 -2
- package/dist/agent-event-source.d.ts +9 -1
- package/dist/agent-event-source.js +10 -3
- package/dist/agent-loops.js +7 -4
- package/dist/agent-run-lifecycle.d.ts +15 -1
- package/dist/agent-run-lifecycle.js +63 -6
- package/dist/agent-run-state.d.ts +22 -2
- package/dist/agent-run-state.js +57 -5
- package/dist/agent-session/helpers.js +14 -0
- package/dist/agent-session/session/assemble.js +126 -24
- package/dist/agent-session/session/persist.d.ts +11 -0
- package/dist/agent-session/session/persist.js +37 -11
- package/dist/agent-session/session/provider-round.d.ts +14 -4
- package/dist/agent-session/session/provider-round.js +185 -19
- package/dist/agent-session/session/tool-round.js +20 -1
- package/dist/agent-session/session/types.d.ts +25 -2
- package/dist/agent-session/session.d.ts +38 -4
- package/dist/agent-session/session.js +76 -5
- package/dist/attention-compiler.d.ts +51 -2
- package/dist/attention-compiler.js +282 -21
- package/dist/cache-helpers.d.ts +4 -2
- package/dist/cache-helpers.js +8 -6
- package/dist/checkpoint-restore.d.ts +45 -0
- package/dist/checkpoint-restore.js +54 -0
- package/dist/context-budget.d.ts +2 -1
- package/dist/context-budget.js +24 -2
- package/dist/contracts-core/agent.d.ts +30 -0
- package/dist/contracts-core/attention.d.ts +95 -0
- package/dist/contracts-core/content.d.ts +10 -0
- package/dist/contracts-core/guardrail-packs.d.ts +41 -0
- package/dist/contracts-core/guardrail-packs.js +2 -0
- package/dist/contracts-core/provider.d.ts +25 -0
- package/dist/contracts-core/run-limits.d.ts +19 -0
- package/dist/contracts-core/session.d.ts +23 -5
- package/dist/contracts-core/session.js +21 -2
- package/dist/contracts-core/usage.d.ts +40 -0
- package/dist/contracts-core/usage.js +8 -0
- package/dist/contracts-core.d.ts +2 -0
- package/dist/contracts-core.js +2 -0
- package/dist/contracts-protocol.d.ts +76 -2
- package/dist/contracts-run-state.d.ts +56 -1
- package/dist/guardrail-packs/coding-standard.d.ts +3 -0
- package/dist/guardrail-packs/coding-standard.js +63 -0
- package/dist/guardrail-packs/destructive-commands.d.ts +3 -0
- package/dist/guardrail-packs/destructive-commands.js +46 -0
- package/dist/guardrail-packs/errors.d.ts +7 -0
- package/dist/guardrail-packs/errors.js +9 -0
- package/dist/guardrail-packs/index.d.ts +4 -0
- package/dist/guardrail-packs/index.js +15 -0
- package/dist/guardrail-packs/secrets-hygiene.d.ts +3 -0
- package/dist/guardrail-packs/secrets-hygiene.js +23 -0
- package/dist/guardrail-packs/types.d.ts +16 -0
- package/dist/guardrail-packs/types.js +2 -0
- package/dist/guardrail-packs/validation-respect.d.ts +3 -0
- package/dist/guardrail-packs/validation-respect.js +53 -0
- package/dist/guardrails.d.ts +20 -1
- package/dist/guardrails.js +268 -0
- package/dist/index.d.ts +14 -9
- package/dist/index.js +9 -6
- package/dist/input.d.ts +8 -1
- package/dist/input.js +68 -6
- package/dist/middleware.d.ts +37 -2
- package/dist/middleware.js +41 -0
- package/dist/node/session-store-jsonl.js +18 -3
- package/dist/observability.js +6 -0
- package/dist/provider-events.d.ts +8 -2
- package/dist/provider-events.js +60 -2
- package/dist/providers/openai-compatible.js +6 -3
- package/dist/run-bundle.js +2 -1
- package/dist/run-limits.d.ts +11 -1
- package/dist/run-limits.js +46 -0
- package/dist/session-stores.d.ts +12 -1
- package/dist/session-stores.js +21 -4
- package/dist/testing/agent-event-source-conformance.js +41 -2
- package/dist/testing/prefix-stability-conformance.d.ts +30 -0
- package/dist/testing/prefix-stability-conformance.js +104 -0
- package/dist/testing/session-store-conformance.d.ts +3 -2
- package/dist/testing/session-store-conformance.js +48 -0
- package/dist/tools.d.ts +5 -0
- package/dist/tools.js +11 -3
- package/dist/usage-estimation.d.ts +29 -0
- package/dist/usage-estimation.js +79 -0
- package/docs/agent-events.md +68 -1
- package/docs/agent-session-runtime.md +1 -0
- package/docs/attention-compiler.md +89 -8
- package/docs/coding-agent-tools.md +1 -1
- package/docs/compaction-and-retry.md +1 -1
- package/docs/compaction-observational-memory.md +33 -6
- package/docs/durable-runs.md +42 -0
- package/docs/embeddings.md +5 -0
- package/docs/evaluations.md +5 -0
- package/docs/execution-timeline.md +78 -1
- package/docs/guardrails.md +38 -2
- package/docs/index.md +32 -13
- package/docs/input-and-prompt-assembly.md +3 -3
- package/docs/knowledge-sync.md +4 -0
- package/docs/middleware-hooks.md +38 -2
- package/docs/migrate-to-0.9.md +210 -0
- package/docs/migration.md +13 -0
- package/docs/multi-agent-patterns.md +25 -2
- package/docs/node-jsonl-session-store.md +7 -1
- package/docs/observability.md +7 -3
- package/docs/options-index.md +2 -1
- package/docs/policy-and-audit.md +13 -1
- package/docs/prefix-stability-conformance.md +93 -0
- package/docs/provider-caching.md +4 -4
- package/docs/provider-conformance.md +16 -0
- package/docs/provider-packages.md +20 -20
- package/docs/public-contracts.md +2 -2
- package/docs/rag.md +101 -3
- package/docs/release-and-install.md +39 -37
- package/docs/runs-and-usage.md +43 -6
- package/docs/scoped-agent-memory.md +262 -0
- package/docs/session-store-conformance.md +1 -2
- package/docs/session-stores.md +17 -17
- package/docs/supervisors.md +32 -12
- package/docs/tools.md +17 -0
- package/docs/workflows.md +5 -0
- package/package.json +5 -1
|
@@ -9,7 +9,7 @@ import { assertGuardrailsAllowed, runGuardrails } from "../../guardrails.js";
|
|
|
9
9
|
import { identityTelemetryAttributes, ownershipFromIdentity, resolveRunIdentity } from "../../identity.js";
|
|
10
10
|
import { assembleProviderInput } from "../../input.js";
|
|
11
11
|
import { errorToErrorInfo, redactRunLedgerRecord } from "../../redaction.js";
|
|
12
|
-
import { RunLimitError, RunLimitTracker, resolveRunLimits } from "../../run-limits.js";
|
|
12
|
+
import { describeBudgetExhaustion, RunLimitError, RunLimitTracker, resolveRunLimits } from "../../run-limits.js";
|
|
13
13
|
import { createSessionEntry } from "../../session-stores.js";
|
|
14
14
|
import { resolveSkillsDisclosure } from "../../skill-disclosure.js";
|
|
15
15
|
import { applyRestoredSkillBodies } from "../../skill-load.js";
|
|
@@ -17,15 +17,26 @@ import { assertStructuredOutputRequestSupported, resolveRunProviderOptions } fro
|
|
|
17
17
|
import { composeSystemPrompt, mergeSystemPromptConfig } from "../../system-prompts.js";
|
|
18
18
|
import { resolveToolResultFold } from "../../tool-result-fold.js";
|
|
19
19
|
import { createSearchToolsTool, createToolSearchState, resolveToolsDisclosure } from "../../tool-search.js";
|
|
20
|
-
import { createToolRegistry, selectRunTools } from "../../tools.js";
|
|
20
|
+
import { clampTurnToolNames, createToolRegistry, selectRunTools } from "../../tools.js";
|
|
21
21
|
import { bridgeAbort, createUsageAccumulator, inputToMessages, isDurableLoop, isSteerSoftInterrupt, mergeGuardrails, throwIfAborted, } from "../helpers.js";
|
|
22
|
-
import { checkpointDurableTurn, cleanupRun, persistDurable, persistSucceeded, suspendDurable } from "./persist.js";
|
|
23
|
-
import { generateWithRetry, recordProviderUsage } from "./provider-round.js";
|
|
22
|
+
import { checkpointDurableFold, checkpointDurableTurn, cleanupRun, persistDurable, persistSucceeded, suspendDurable } from "./persist.js";
|
|
23
|
+
import { generateWithRetry, recordProviderUsage, resolveDeterministicTurn } from "./provider-round.js";
|
|
24
24
|
import { bindChargeToolRound, bindDispatchToolCall, replayDurableNestedAndPending, runLoopUntilSettled, suspendGatedRound, } from "./tool-round.js";
|
|
25
25
|
const PROMPT_VERSION_MAX_NAME_BYTES = 256;
|
|
26
26
|
const PROMPT_VERSION_HASH_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
27
27
|
/** Cap on the host stop detail that reaches the result, ledger, and timeline (plan 084 Task 2). */
|
|
28
28
|
const TURN_STOP_DETAIL_MAX_BYTES = 256;
|
|
29
|
+
function lastAssistantText(history) {
|
|
30
|
+
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
31
|
+
const message = history[i];
|
|
32
|
+
if (message?.role !== "assistant")
|
|
33
|
+
continue;
|
|
34
|
+
const text = message.content.map((block) => (block.type === "text" ? block.text : "")).join("");
|
|
35
|
+
if (text)
|
|
36
|
+
return text;
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
29
40
|
/**
|
|
30
41
|
* `RunOptions.turnPolicy` stopped the run at a turn boundary (plan 084 Task 2). Internal control
|
|
31
42
|
* signal: it unwinds any loop shape and `executeRun` turns it into a clean terminal success with
|
|
@@ -140,7 +151,13 @@ async function assembleRoundContext(params) {
|
|
|
140
151
|
throwIfAborted(controller.signal);
|
|
141
152
|
session.emit({ type: "agent_started", sessionId: session.id, runId });
|
|
142
153
|
if (resumed)
|
|
143
|
-
session.emit({
|
|
154
|
+
session.emit({
|
|
155
|
+
type: "agent_resumed",
|
|
156
|
+
sessionId: session.id,
|
|
157
|
+
runId,
|
|
158
|
+
version: resumed.version,
|
|
159
|
+
...(resumed.restore ? { restore: resumed.restore } : {}),
|
|
160
|
+
});
|
|
144
161
|
const startRecord = {
|
|
145
162
|
id: runId,
|
|
146
163
|
sessionId: session.id,
|
|
@@ -176,6 +193,7 @@ async function assembleRoundContext(params) {
|
|
|
176
193
|
const tools = searchTool ? [...activeToolList, searchTool] : activeToolList;
|
|
177
194
|
const activeSkills = session.resolveRunSkills(options, tools);
|
|
178
195
|
session.activeRunSkills = activeSkills;
|
|
196
|
+
session.tailSegments.clear();
|
|
179
197
|
if (options.model && JSON.stringify(options.model) !== JSON.stringify(session.agent.config.model)) {
|
|
180
198
|
await session.appendEntry(createSessionEntry({
|
|
181
199
|
sessionId: session.id,
|
|
@@ -215,24 +233,41 @@ async function assembleRoundContext(params) {
|
|
|
215
233
|
assertStructuredOutputRequestSupported(options.model ?? session.agent.config.model, providerOptions);
|
|
216
234
|
const validate = options.validate ?? session.agent.config.validator;
|
|
217
235
|
// Resolved once per run, before any provider turn: a bad setting or a widening run overlay
|
|
218
|
-
// fails here rather than on the turn that happens to cross the ratio (plan 074 C12).
|
|
219
|
-
|
|
236
|
+
// fails here rather than on the turn that happens to cross the ratio (plan 074 C12). The
|
|
237
|
+
// resolved run input budget rides the handle so `run_input_ratio` folds against the same cap
|
|
238
|
+
// the run limit enforces (plan 086 T2); `null` (disabled) leaves that axis on the input cap.
|
|
239
|
+
const attentionCompiler = resolveRunAttentionCompiler(session.agent.config.attentionCompiler, options.attentionCompiler, options.model ?? session.agent.config.model, limits.limits.maxInputTokens);
|
|
240
|
+
// Plan 086 T3: durable folding writes the fold ledger to the run checkpoint, so it needs a
|
|
241
|
+
// durable run (the session's durable state is set before this call). Fail at run start, before
|
|
242
|
+
// any provider turn, rather than folding into memory only. The fold state rides that
|
|
243
|
+
// checkpoint independently of `persistSessionState`.
|
|
244
|
+
if (attentionCompiler?.durable && !session.activeDurable) {
|
|
245
|
+
throw new AgentRunStateError("attentionCompiler.durable requires a durable run: set AgentConfig or RunOptions runState with a checkpoint store");
|
|
246
|
+
}
|
|
247
|
+
session.attentionDurable = attentionCompiler?.durable === true;
|
|
220
248
|
// Telemetry seam (plan 074 T6): one `attention_compiled` per mutated turn, counts and the
|
|
221
249
|
// measured ratio inputs only. Under-ratio turns and compiler-off runs emit nothing.
|
|
250
|
+
// Plan 086 T3: a turn that folded new bodies is the fold-boundary durability signal, so it is
|
|
251
|
+
// remembered here (the callback is synchronous) and checkpointed by the assembler below.
|
|
252
|
+
let foldCheckpointPending = false;
|
|
222
253
|
const onAttentionReport = attentionCompiler
|
|
223
|
-
? (report) =>
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
254
|
+
? (report) => {
|
|
255
|
+
if (report.newFoldedBodies > 0)
|
|
256
|
+
foldCheckpointPending = true;
|
|
257
|
+
session.emit({
|
|
258
|
+
type: "attention_compiled",
|
|
259
|
+
sessionId: session.id,
|
|
260
|
+
runId,
|
|
261
|
+
used: report.used,
|
|
262
|
+
usedAfter: report.usedAfter,
|
|
263
|
+
inputCap: report.inputCap,
|
|
264
|
+
triggerRatio: report.triggerRatio,
|
|
265
|
+
droppedThinkingTurns: report.droppedThinkingTurns,
|
|
266
|
+
stubbedToolResults: report.stubbedToolResults,
|
|
267
|
+
stubbedBytes: report.stubbedBytes,
|
|
268
|
+
truncated: report.truncated,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
236
271
|
: undefined;
|
|
237
272
|
const instructionInjectors = options.instructionInjectors ?? session.agent.config.instructionInjectors ?? [];
|
|
238
273
|
const inputLayout = options.inputLayout ?? session.agent.config.inputLayout;
|
|
@@ -272,6 +307,8 @@ async function assembleRoundContext(params) {
|
|
|
272
307
|
runUsage,
|
|
273
308
|
loopCtx: undefined,
|
|
274
309
|
};
|
|
310
|
+
const toolNarrowing = options.toolNarrowing ?? session.agent.config.toolNarrowing;
|
|
311
|
+
let narrowedForTurn;
|
|
275
312
|
const loopCtx = {
|
|
276
313
|
sessionId: session.id,
|
|
277
314
|
runId,
|
|
@@ -285,6 +322,38 @@ async function assembleRoundContext(params) {
|
|
|
285
322
|
restoredLoopState: resumed?.state?.loopState?.snapshot,
|
|
286
323
|
assemble: async (nextInput, toolResults, turn) => {
|
|
287
324
|
limits.charge("maxTurns");
|
|
325
|
+
const turnIndex = turn ?? 1;
|
|
326
|
+
let turnTools = tools;
|
|
327
|
+
if (toolNarrowing) {
|
|
328
|
+
if (typeof toolNarrowing !== "function")
|
|
329
|
+
throw new TypeError("toolNarrowing must be a function");
|
|
330
|
+
if (narrowedForTurn?.turn === turnIndex) {
|
|
331
|
+
turnTools = narrowedForTurn.tools;
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
const assistant = lastAssistantText(session.history);
|
|
335
|
+
const requested = await toolNarrowing({
|
|
336
|
+
turn: turnIndex,
|
|
337
|
+
toolIds: tools.map((tool) => tool.name),
|
|
338
|
+
...(assistant !== undefined ? { lastAssistantText: assistant } : {}),
|
|
339
|
+
});
|
|
340
|
+
if (!Array.isArray(requested))
|
|
341
|
+
throw new TypeError("toolNarrowing must return a string array");
|
|
342
|
+
const clamped = clampTurnToolNames(tools, requested);
|
|
343
|
+
if (clamped.dropped.length > 0) {
|
|
344
|
+
session.emit({
|
|
345
|
+
type: "tool_narrowing_clamped",
|
|
346
|
+
sessionId: session.id,
|
|
347
|
+
runId,
|
|
348
|
+
turn: turnIndex,
|
|
349
|
+
dropped: clamped.dropped,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
turnTools = clamped.tools;
|
|
353
|
+
narrowedForTurn = { turn: turnIndex, tools: turnTools };
|
|
354
|
+
}
|
|
355
|
+
ctx.turnAllow = turnTools.map((tool) => tool.name);
|
|
356
|
+
}
|
|
288
357
|
const request = await assembleProviderInput({
|
|
289
358
|
model: options.model ?? session.agent.config.model,
|
|
290
359
|
input: nextInput,
|
|
@@ -308,9 +377,16 @@ async function assembleRoundContext(params) {
|
|
|
308
377
|
// Session-owned: a stub made earlier stays applied even on a later under-ratio turn, so
|
|
309
378
|
// the prompt-cache prefix is not rewritten (C10). Undefined when the compiler is off.
|
|
310
379
|
attentionSticky: attentionCompiler ? session.attentionStickyFor() : undefined,
|
|
380
|
+
// Folded bodies (plan 086 T3): a row summarized once is re-applied, never re-summarized,
|
|
381
|
+
// so sticky rows stay byte-identical and a resumed run reuses the persisted bodies.
|
|
382
|
+
attentionFold: attentionCompiler ? session.attentionFoldFor() : undefined,
|
|
383
|
+
// Charge-so-far for the `run_input_ratio` axis: the counter only holds completed turns,
|
|
384
|
+
// so the axis projects this turn's estimate onto it.
|
|
385
|
+
runInputTokens: limits.snapshot().inputTokens,
|
|
311
386
|
onAttentionReport,
|
|
312
387
|
loadedSkills: session.loadedSkills,
|
|
313
|
-
|
|
388
|
+
tailSegments: session.tailSegments,
|
|
389
|
+
tools: turnTools,
|
|
314
390
|
resourceLoader: session.agent.config.resourceLoader,
|
|
315
391
|
permission: session.agent.config.permission,
|
|
316
392
|
trust: session.agent.config.trust,
|
|
@@ -323,6 +399,13 @@ async function assembleRoundContext(params) {
|
|
|
323
399
|
signal: controller.signal,
|
|
324
400
|
});
|
|
325
401
|
ctx.assembledTurn = true;
|
|
402
|
+
if (foldCheckpointPending) {
|
|
403
|
+
foldCheckpointPending = false;
|
|
404
|
+
// Fold-boundary durability (plan 086 T3): one write per turn that added folded bodies,
|
|
405
|
+
// after the request is assembled and before the provider sees it, so a crash during this
|
|
406
|
+
// turn resumes with the same ledger. No-op unless the compiler is durable.
|
|
407
|
+
await checkpointDurableFold(session, { runId, model, limits });
|
|
408
|
+
}
|
|
326
409
|
return request;
|
|
327
410
|
},
|
|
328
411
|
chargeToolRound: bindChargeToolRound(ctx),
|
|
@@ -345,10 +428,15 @@ async function assembleRoundContext(params) {
|
|
|
345
428
|
if (session.activeDurable?.options.checkpointPolicy === "every-turn") {
|
|
346
429
|
await checkpointDurableTurn(session, { runId, model, limits });
|
|
347
430
|
}
|
|
431
|
+
// Deterministic no-model turn (plan 096): host middleware answers at the provider boundary,
|
|
432
|
+
// before any provider-round work. No answer → provider path unchanged.
|
|
433
|
+
const deterministic = await resolveDeterministicTurn(session, request, runId, session.activeLoopTurn, controller.signal, ctx.toolResults);
|
|
434
|
+
if (deterministic)
|
|
435
|
+
return deterministic;
|
|
348
436
|
const policyResult = await session.applyProviderRequestPolicies(request, runId, options, metadata, controller.signal);
|
|
349
437
|
const middlewareRequest = (await session.agent.config.middleware?.run("provider_request", policyResult.request)) ?? policyResult.request;
|
|
350
438
|
try {
|
|
351
|
-
return await generateWithRetry(session, session.redactProviderRequest(middlewareRequest), runId, options, controller.signal, policyResult.secrets, session.activeLoopTurn, (turnUsage, turn, attempt) => recordProviderUsage(ctx, turnUsage, turn, attempt), ctx.toolResults);
|
|
439
|
+
return await generateWithRetry(session, session.redactProviderRequest(middlewareRequest), runId, options, controller.signal, policyResult.secrets, session.activeLoopTurn, (turnUsage, turn, attempt) => recordProviderUsage(ctx, turnUsage, turn, attempt, middlewareRequest), ctx.toolResults);
|
|
352
440
|
}
|
|
353
441
|
catch (error) {
|
|
354
442
|
if (isSteerSoftInterrupt(error)) {
|
|
@@ -431,8 +519,11 @@ export async function executeRun(session, input, options, runId, resumed) {
|
|
|
431
519
|
if (session.activeIdentity && !session.activeOwnership)
|
|
432
520
|
session.activeOwnership = ownershipFromIdentity(session.activeIdentity);
|
|
433
521
|
session.activeIdempotencyKey = options.idempotencyKey ?? session.agent.config.idempotencyKey;
|
|
434
|
-
session.activeGuardrails = mergeGuardrails(session.agent.config.guardrails, options.guardrails);
|
|
522
|
+
session.activeGuardrails = mergeGuardrails(mergeGuardrails(session.agent.config.guardrails, session.packGuardrails), options.guardrails);
|
|
435
523
|
session.activeDurable = resumed ?? (durableOptions ? { options: durableOptions, version: 0 } : undefined);
|
|
524
|
+
// Plan 086 T3: reset here, so a suspension before the compiler is resolved (input guardrail)
|
|
525
|
+
// cannot inherit the previous run's durable-folding flag. `assembleRoundContext` sets it true.
|
|
526
|
+
session.attentionDurable = false;
|
|
436
527
|
session.activeGatedRound = undefined;
|
|
437
528
|
if (resumed)
|
|
438
529
|
session.invalidateSnapshot();
|
|
@@ -462,6 +553,7 @@ export async function executeRun(session, input, options, runId, resumed) {
|
|
|
462
553
|
deadlineAt: resumed?.state?.deadlineAt,
|
|
463
554
|
});
|
|
464
555
|
session.activeLimits = limits;
|
|
556
|
+
session.activeRecentToolCalls = [];
|
|
465
557
|
const hasFiniteTokenCap = (value) => typeof value === "number" && Number.isFinite(value);
|
|
466
558
|
session.activeLimitOutputBuffer = [session.agent.config.limits, requestedLimits].some((value) => hasFiniteTokenCap(value?.maxOutputTokens) || hasFiniteTokenCap(value?.maxTotalTokens) || value?.maxCost !== undefined);
|
|
467
559
|
try {
|
|
@@ -513,8 +605,18 @@ export async function executeRun(session, input, options, runId, resumed) {
|
|
|
513
605
|
return session.buildRunResult({ runId, status: "suspended", runState: error.state, interruption: error.interruption });
|
|
514
606
|
}
|
|
515
607
|
runError = errorToErrorInfo(error);
|
|
516
|
-
session.emit({ type: "error", sessionId: session.id, runId, error: runError });
|
|
517
608
|
const breach = error instanceof RunLimitError ? error.breach : limits.breach;
|
|
609
|
+
// Terminal attribution before the terminal `error`/finish records, so a subscriber that stops
|
|
610
|
+
// at the first terminal event still sees why the run died (plan 087 T2).
|
|
611
|
+
if (breach) {
|
|
612
|
+
session.emit({
|
|
613
|
+
type: "budget_exhausted",
|
|
614
|
+
sessionId: session.id,
|
|
615
|
+
runId,
|
|
616
|
+
...describeBudgetExhaustion(limits, breach, session.activeRecentToolCalls ?? []),
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
session.emit({ type: "error", sessionId: session.id, runId, error: runError });
|
|
518
620
|
runStatus = breach ? "failed" : controller.signal.aborted ? "aborted" : "failed";
|
|
519
621
|
const runState = session.activeDurable?.state
|
|
520
622
|
? await persistDurable(session, {
|
|
@@ -28,6 +28,17 @@ export declare function checkpointDurableTurn(session: SessionHost, input: {
|
|
|
28
28
|
readonly model: ModelConfig;
|
|
29
29
|
readonly limits: RunLimitTracker;
|
|
30
30
|
}): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Fold-boundary checkpoint (plan 086 T3). Called once per turn that added folded bodies — never
|
|
33
|
+
* per turn — when the resolved compiler is durable, so a crash after a fold resumes with the
|
|
34
|
+
* ledger and frontier already on disk. Independent of `checkpointPolicy`: the fold is the
|
|
35
|
+
* durability point that matters for a long single run, not the turn boundary.
|
|
36
|
+
*/
|
|
37
|
+
export declare function checkpointDurableFold(session: SessionHost, input: {
|
|
38
|
+
readonly runId: string;
|
|
39
|
+
readonly model: ModelConfig;
|
|
40
|
+
readonly limits: RunLimitTracker;
|
|
41
|
+
}): Promise<void>;
|
|
31
42
|
export declare function persistSucceeded(ctx: RoundContext, loopUsage: Usage | undefined): Promise<AgentRunResult>;
|
|
32
43
|
export declare function cleanupRun(input: {
|
|
33
44
|
session: SessionHost;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Finalize/persist phase of runInternal (plan 059). */
|
|
2
|
-
import { boundedLoopSnapshot, initialAgentRunState, publicState, saveAgentRunState } from "../../agent-run-state.js";
|
|
2
|
+
import { boundedLoopSnapshot, initialAgentRunState, publicState, resolveCheckpointMetadata, saveAgentRunState, } from "../../agent-run-state.js";
|
|
3
3
|
import { AgentRunStateError } from "../../contracts.js";
|
|
4
4
|
import { redactRunLedgerRecord } from "../../redaction.js";
|
|
5
5
|
import { isFlushableRunLedger } from "../../run-ledger.js";
|
|
@@ -10,13 +10,16 @@ export async function persistDurable(session, state) {
|
|
|
10
10
|
if (!durable)
|
|
11
11
|
throw new AgentRunStateError("Durable run state is not configured");
|
|
12
12
|
const withGrant = session.activeToolNames !== undefined ? { ...state, toolNames: session.activeToolNames } : state;
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const persistSessionState = durable.options.persistSessionState === true;
|
|
14
|
+
// Plan 086 T3: durable folding owns its two keys. A run that opted into `durable` writes the
|
|
15
|
+
// fold ledger and its frontier even when the broader session-state bag stays off; a run that
|
|
16
|
+
// did not keeps exactly today's bytes, where the frontier rides `persistSessionState`.
|
|
17
|
+
const attentionSticky = persistSessionState || session.attentionDurable ? session.serializedAttentionSticky() : undefined;
|
|
18
|
+
const attentionFold = session.attentionDurable ? session.serializedAttentionFold() : undefined;
|
|
19
|
+
const sessionState = {
|
|
20
|
+
...(persistSessionState
|
|
21
|
+
? {
|
|
18
22
|
loadedSkillNames: session.loadedSkills.list(),
|
|
19
|
-
...(attentionSticky ? { attentionSticky } : {}),
|
|
20
23
|
...(session.activatedTools.list().length ? { activatedToolNames: session.activatedTools.list() } : {}),
|
|
21
24
|
...(durable.options.includeSkillBodies
|
|
22
25
|
? {
|
|
@@ -25,9 +28,13 @@ export async function persistDurable(session, state) {
|
|
|
25
28
|
: undefined),
|
|
26
29
|
}
|
|
27
30
|
: {}),
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
:
|
|
31
|
+
}
|
|
32
|
+
: {}),
|
|
33
|
+
...(attentionSticky ? { attentionSticky } : {}),
|
|
34
|
+
...(attentionFold ? { attentionFold } : {}),
|
|
35
|
+
};
|
|
36
|
+
const persisted = Object.keys(sessionState).length > 0 ? { ...withGrant, sessionState } : withGrant;
|
|
37
|
+
const metadata = resolveCheckpointMetadata(durable.options.checkpointMetadata) ?? durable.checkpointMetadata;
|
|
31
38
|
const saved = await saveAgentRunState({
|
|
32
39
|
checkpoints: durable.options.checkpoints,
|
|
33
40
|
state: persisted,
|
|
@@ -36,6 +43,7 @@ export async function persistDurable(session, state) {
|
|
|
36
43
|
fencingToken: durable.options.fencingToken,
|
|
37
44
|
redactor: session.activeRedactor,
|
|
38
45
|
maxStateBytes: durable.options.maxStateBytes,
|
|
46
|
+
...(metadata ? { metadata } : {}),
|
|
39
47
|
});
|
|
40
48
|
durable.state = saved.state;
|
|
41
49
|
durable.version = saved.record.version;
|
|
@@ -86,8 +94,25 @@ export async function suspendDurable(session, input) {
|
|
|
86
94
|
* into a resumed run, and loop-local state rides along exactly as it does at suspension.
|
|
87
95
|
*/
|
|
88
96
|
export async function checkpointDurableTurn(session, input) {
|
|
97
|
+
if (session.activeDurable?.options.checkpointPolicy !== "every-turn")
|
|
98
|
+
return;
|
|
99
|
+
await writeRunningCheckpoint(session, input);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Fold-boundary checkpoint (plan 086 T3). Called once per turn that added folded bodies — never
|
|
103
|
+
* per turn — when the resolved compiler is durable, so a crash after a fold resumes with the
|
|
104
|
+
* ledger and frontier already on disk. Independent of `checkpointPolicy`: the fold is the
|
|
105
|
+
* durability point that matters for a long single run, not the turn boundary.
|
|
106
|
+
*/
|
|
107
|
+
export async function checkpointDurableFold(session, input) {
|
|
108
|
+
if (!session.attentionDurable)
|
|
109
|
+
return;
|
|
110
|
+
await writeRunningCheckpoint(session, input);
|
|
111
|
+
}
|
|
112
|
+
/** Shared running-checkpoint write for the turn-boundary and fold-boundary triggers. */
|
|
113
|
+
async function writeRunningCheckpoint(session, input) {
|
|
89
114
|
const durable = session.activeDurable;
|
|
90
|
-
if (durable
|
|
115
|
+
if (!durable)
|
|
91
116
|
return;
|
|
92
117
|
const loop = session.activeLoop;
|
|
93
118
|
const loopState = loop?.snapshot ? boundedLoopSnapshot(loop.name, loop.revision ?? "1", loop.snapshot()) : undefined;
|
|
@@ -216,6 +241,7 @@ export async function cleanupRun(input) {
|
|
|
216
241
|
session.activeLimits?.dispose();
|
|
217
242
|
session.activeToolNames = undefined;
|
|
218
243
|
session.activeLimits = undefined;
|
|
244
|
+
session.activeRecentToolCalls = undefined;
|
|
219
245
|
session.activeLimitOutputBuffer = false;
|
|
220
246
|
session.activeRedactor = undefined;
|
|
221
247
|
session.activeProvider = undefined;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
/** Provider-round phase of runInternal (plan 059). */
|
|
2
|
-
import type { ProviderRequest, ProviderTurnResult, RunOptions, ToolResult, Usage } from "../../contracts.js";
|
|
2
|
+
import type { ModelConfig, ProviderRequest, ProviderTurnResult, RunOptions, ToolResult, Usage } from "../../contracts.js";
|
|
3
3
|
import type { RoundContext, SessionHost } from "./types.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/** Resolve the per-request input cap for turn-budget metadata (plan 087 T1). A model without a
|
|
5
|
+
* derivable cap (or a bad attention setting on an unrelated run) omits the field instead of
|
|
6
|
+
* failing an emitting turn; the attention compiler, when enabled, is the cap authority. */
|
|
7
|
+
export declare function resolveTurnInputCap(session: SessionHost, model: ModelConfig): number | undefined;
|
|
8
|
+
export declare function recordProviderUsage(ctx: RoundContext, turnUsage: Usage | undefined, turn: number, attempt: number, request?: ProviderRequest): Promise<Usage | undefined>;
|
|
9
|
+
/**
|
|
10
|
+
* Plan 096: host middleware may answer the turn deterministically at the `beforeProviderTurn` seam —
|
|
11
|
+
* no provider request, no usage, mandatory provenance. `undefined` sends the turn to the provider
|
|
12
|
+
* unchanged; a malformed answer fails the run closed instead of falling through to the provider.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveDeterministicTurn(session: SessionHost, request: ProviderRequest, runId: string, turn: number, signal: AbortSignal, toolResults?: readonly ToolResult[]): Promise<ProviderTurnResult | undefined>;
|
|
15
|
+
export declare function generateWithRetry(session: SessionHost, request: ProviderRequest, runId: string, options: RunOptions, signal: AbortSignal, requestSecrets?: readonly (string | undefined)[], turn?: number, recordUsage?: (usage: Usage | undefined, turn: number, attempt: number) => Promise<Usage | undefined>, toolResults?: readonly ToolResult[]): Promise<ProviderTurnResult>;
|
|
16
|
+
export declare function generateProviderTurn(session: SessionHost, request: ProviderRequest, runId: string, signal: AbortSignal, secrets?: readonly (string | undefined)[], turn?: number, attempt?: number, recordUsage?: (usage: Usage | undefined, turn: number, attempt: number) => Promise<Usage | undefined>, toolResults?: readonly ToolResult[]): Promise<ProviderTurnResult>;
|