@arnilo/prism 0.8.0 → 0.10.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 +62 -1
- package/README.md +13 -12
- package/dist/agent-approval.d.ts +17 -2
- package/dist/agent-approval.js +15 -6
- 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 +82 -11
- package/dist/agent-run-state.d.ts +47 -6
- package/dist/agent-run-state.js +154 -6
- package/dist/agent-session/event-subscriber.d.ts +2 -0
- package/dist/agent-session/event-subscriber.js +3 -0
- package/dist/agent-session/helpers.js +14 -0
- package/dist/agent-session/session/assemble.js +281 -32
- package/dist/agent-session/session/persist.d.ts +11 -0
- package/dist/agent-session/session/persist.js +48 -16
- package/dist/agent-session/session/provider-round.d.ts +14 -4
- package/dist/agent-session/session/provider-round.js +226 -19
- package/dist/agent-session/session/tool-round.d.ts +2 -2
- package/dist/agent-session/session/tool-round.js +78 -6
- package/dist/agent-session/session/types.d.ts +44 -3
- package/dist/agent-session/session.d.ts +100 -5
- package/dist/agent-session/session.js +224 -13
- 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 +13 -1
- package/dist/context-budget.js +57 -4
- package/dist/contracts-core/agent.d.ts +52 -1
- package/dist/contracts-core/attention.d.ts +95 -0
- package/dist/contracts-core/content.d.ts +10 -0
- package/dist/contracts-core/extensions.d.ts +3 -0
- package/dist/contracts-core/guardrail-packs.d.ts +46 -0
- package/dist/contracts-core/guardrail-packs.js +2 -0
- package/dist/contracts-core/loop.d.ts +36 -0
- package/dist/contracts-core/provider.d.ts +30 -0
- package/dist/contracts-core/run-limits.d.ts +29 -1
- 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 +81 -5
- package/dist/contracts-run-state.d.ts +91 -2
- package/dist/contributions.d.ts +2 -1
- package/dist/contributions.js +1 -0
- package/dist/extensions.d.ts +15 -1
- package/dist/extensions.js +68 -0
- 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 +26 -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 +69 -0
- package/dist/guardrails.d.ts +61 -1
- package/dist/guardrails.js +377 -0
- package/dist/index.d.ts +16 -11
- package/dist/index.js +10 -7
- 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.d.ts +6 -1
- package/dist/run-bundle.js +5 -1
- package/dist/run-limits.d.ts +11 -1
- package/dist/run-limits.js +59 -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 +59 -0
- package/dist/testing/prefix-stability-conformance.js +172 -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 +21 -6
- package/dist/usage-estimation.d.ts +29 -0
- package/dist/usage-estimation.js +79 -0
- package/docs/agent-events.md +75 -4
- package/docs/agent-session-runtime.md +10 -6
- package/docs/attention-compiler.md +89 -8
- package/docs/caveman.md +1 -1
- package/docs/coding-agent-tools.md +1 -1
- package/docs/compaction-and-retry.md +1 -1
- package/docs/compaction-llm.md +2 -0
- package/docs/compaction-observational-memory.md +54 -7
- package/docs/durable-runs.md +46 -3
- package/docs/embeddings.md +9 -0
- package/docs/evaluations.md +5 -0
- package/docs/execution-timeline.md +79 -1
- package/docs/extensions.md +20 -3
- package/docs/guardrails.md +50 -4
- package/docs/hooks.md +282 -0
- package/docs/index.md +37 -15
- package/docs/input-and-prompt-assembly.md +4 -4
- package/docs/instruction-injection.md +1 -0
- package/docs/knowledge-sync.md +4 -0
- package/docs/live-testing.md +3 -1
- package/docs/memory-fabric.md +28 -0
- package/docs/middleware-hooks.md +90 -4
- package/docs/migrate-to-0.9.md +210 -0
- package/docs/migration.md +26 -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 +4 -1
- package/docs/policy-and-audit.md +26 -1
- package/docs/prefix-stability-conformance.md +143 -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 +3 -2
- package/docs/rag.md +188 -3
- package/docs/release-and-install.md +45 -40
- package/docs/runs-and-usage.md +56 -10
- package/docs/scoped-agent-memory.md +270 -0
- package/docs/scoped-memory.md +138 -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 +18 -1
- package/docs/wiki.md +4 -2
- package/docs/workflows.md +5 -0
- package/package.json +8 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** session (0.2.5 plan 025 Task 1 split). Moved verbatim from agent-session.ts; public surface unchanged behind the barrel. */
|
|
2
|
-
import { ActiveDurableRun } from "../agent-approval.js";
|
|
3
|
-
import type { PendingToolCall, StoredAgentRunState } from "../agent-run-state.js";
|
|
4
|
-
import { type AttentionStickyFrontier, type PersistedAttentionStickyFrontier } from "../attention-compiler.js";
|
|
5
|
-
import type { Agent, AgentEvent, AgentRunResult, AgentRunState, AgentRunStateOptions, AgentSession, AgentSessionConfig, AIProvider, CompactionOptions, CompactionResult, ErrorInfo, Message, OwnershipScope, PendingDecision, PromptVersionRef, ProviderRequest, RunDecision, RunOptions, SessionEntry, Skill, SteerOptions, SubscribeOptions, ToolDefinition, ToolEffectStore, Usage } from "../contracts.js";
|
|
2
|
+
import { ActiveDurableRun, ActiveDurableRunExtras } from "../agent-approval.js";
|
|
3
|
+
import type { PendingToolCall, PersistedGuardrailPacks, StoredAgentRunState } from "../agent-run-state.js";
|
|
4
|
+
import { type AttentionFoldLedger, type AttentionStickyFrontier, type PersistedAttentionFoldLedger, type PersistedAttentionStickyFrontier } from "../attention-compiler.js";
|
|
5
|
+
import type { Agent, AgentEvent, AgentRunResult, AgentRunState, AgentRunStateOptions, AgentSession, AgentSessionConfig, AIProvider, CompactionOptions, CompactionResult, ContextMeter, ErrorInfo, GuardrailPackRef, Guardrails, Message, OwnershipScope, PendingDecision, PromptVersionRef, ProviderRequest, RunDecision, RunOptions, SessionEntry, Skill, SteerOptions, SubscribeOptions, ToolCallSummary, ToolDefinition, ToolEffectStore, Usage } from "../contracts.js";
|
|
6
6
|
import type { AgentIdentity } from "../identity.js";
|
|
7
7
|
import type { AgentInput } from "../input.js";
|
|
8
8
|
import type { RunLimitTracker } from "../run-limits.js";
|
|
@@ -14,6 +14,10 @@ export declare class RuntimeAgentSession implements AgentSession {
|
|
|
14
14
|
private readonly metadata?;
|
|
15
15
|
private readonly store;
|
|
16
16
|
private readonly subscribers;
|
|
17
|
+
/** Plan 106 R2: `session_start` is dispatched at the first run start, once per runtime session. */
|
|
18
|
+
private sessionOpened;
|
|
19
|
+
/** Plan 106 R2: `close()` dispatches `session_shutdown` and closes subscribers exactly once. */
|
|
20
|
+
private closed;
|
|
17
21
|
private currentLeafId?;
|
|
18
22
|
private history;
|
|
19
23
|
private activeRun?;
|
|
@@ -30,9 +34,27 @@ export declare class RuntimeAgentSession implements AgentSession {
|
|
|
30
34
|
activeIdentity?: AgentIdentity;
|
|
31
35
|
private activeIdempotencyKey?;
|
|
32
36
|
private activeGuardrails?;
|
|
37
|
+
/** Plan 092 Task 2: guardrail packs compiled once in the constructor; merged into every run's `activeGuardrails`. */
|
|
38
|
+
packGuardrails?: Guardrails;
|
|
39
|
+
/** Plan 104 Task 3: `ask` rules as the durable charge-time gate (a match records `interrupt`). */
|
|
40
|
+
packAskGate?: Guardrails;
|
|
41
|
+
/** Plan 104 Task 3: the same `ask` rules as plain blocks for a run that cannot suspend. */
|
|
42
|
+
packAskBlocks?: Guardrails;
|
|
43
|
+
/** Original pack refs, carried into `fork()`/`clone()` so a branch cannot silently lose its policy. */
|
|
44
|
+
private packRefs?;
|
|
45
|
+
/** Plan 104 Task 2: compiled refs + live pack state, replaced by `restoreGuardrailPacks` on resume. */
|
|
46
|
+
private compiledGuardrailPacks?;
|
|
33
47
|
activeMetadata?: Readonly<Record<string, unknown>>;
|
|
34
48
|
activePromptVersion?: PromptVersionRef;
|
|
35
49
|
activeLimits?: RunLimitTracker;
|
|
50
|
+
/** Plan 091 T2: input tokens of the latest provider turn plus whether the provider reported
|
|
51
|
+
* them; set by the usage seam, read by `contextMeter()`. */
|
|
52
|
+
activeInputMeter?: {
|
|
53
|
+
readonly tokens: number;
|
|
54
|
+
readonly source: "reported" | "estimated";
|
|
55
|
+
};
|
|
56
|
+
/** Bounded last-N tool-call summaries of the active run (plan 087 T2): ids, names, arg hashes. */
|
|
57
|
+
activeRecentToolCalls?: ToolCallSummary[];
|
|
36
58
|
activeLimitOutputBuffer: boolean;
|
|
37
59
|
activeDurable?: ActiveDurableRun;
|
|
38
60
|
activeLoop?: import("../contracts.js").AgentLoopStrategy;
|
|
@@ -43,6 +65,8 @@ export declare class RuntimeAgentSession implements AgentSession {
|
|
|
43
65
|
}>;
|
|
44
66
|
activeLoopTurn: number;
|
|
45
67
|
private readonly loadedSkills;
|
|
68
|
+
/** Run-owned only: loaded bodies and URI resources retain first insertion order within one provider loop. */
|
|
69
|
+
readonly tailSegments: Map<string, Message>;
|
|
46
70
|
/** Tools activated via `search_tools` this session (plan 041); names-only in persistence. */
|
|
47
71
|
readonly activatedTools: import("../tool-search.js").ActiveToolSet;
|
|
48
72
|
/** Plan 018 Task 6 (closeout `checkpoint-bodies`): persisted exact instructions, registry-independent. */
|
|
@@ -62,6 +86,17 @@ export declare class RuntimeAgentSession implements AgentSession {
|
|
|
62
86
|
/** Plan 074 P3: restore a frontier validated at checkpoint load, so a resumed run keeps its
|
|
63
87
|
* stubs instead of re-deciding its first turn from the ratio. */
|
|
64
88
|
restoreAttentionSticky(persisted: PersistedAttentionStickyFrontier): void;
|
|
89
|
+
/** Session-owned folded bodies (plan 086 T3); created on first use like the frontier, so a
|
|
90
|
+
* compiler-off session allocates nothing. */
|
|
91
|
+
private attentionFold?;
|
|
92
|
+
/** Set per run from the resolved compiler: `durable: true` opts the fold ledger and its
|
|
93
|
+
* frontier into checkpoints even when `persistSessionState` is off. */
|
|
94
|
+
attentionDurable: boolean;
|
|
95
|
+
attentionFoldFor(): AttentionFoldLedger;
|
|
96
|
+
/** Plan 086 T3: bounded ledger snapshot for a durable checkpoint; `undefined` before any fold. */
|
|
97
|
+
serializedAttentionFold(): PersistedAttentionFoldLedger | undefined;
|
|
98
|
+
/** Plan 086 T3: adopt a ledger validated at checkpoint load, so a resumed fold is byte-identical. */
|
|
99
|
+
restoreAttentionFold(ledger: AttentionFoldLedger): void;
|
|
65
100
|
/** Plan 015 Task 4: re-add persisted loaded-skill names (names only; bodies re-resolve on demand). */
|
|
66
101
|
restoreLoadedSkills(names: readonly string[]): void;
|
|
67
102
|
/** Plan 041: re-add persisted activated-tool names (names only; inert for absent tools). */
|
|
@@ -70,22 +105,77 @@ export declare class RuntimeAgentSession implements AgentSession {
|
|
|
70
105
|
clearActivatedTools(): void;
|
|
71
106
|
/** Plan 018 Task 6: restore persisted loaded-skill bodies (already validated fail-closed at load). */
|
|
72
107
|
restoreLoadedSkillBodies(bodies: readonly LoadedSkillBodiesEntry[]): void;
|
|
108
|
+
/** Plan 104 T2: the refs this session actually enforces (restored ones after a resume). */
|
|
109
|
+
get guardrailPackRefs(): readonly GuardrailPackRef[] | undefined;
|
|
110
|
+
/** Plan 104 T2: pack refs + live pack-owned state for a durable checkpoint (opt-in with `persistSessionState`). */
|
|
111
|
+
serializedGuardrailPackState(): PersistedGuardrailPacks | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* Plan 104 T2: recompile checkpoint packs before the resumed run's first turn. `state` present
|
|
114
|
+
* (even empty) marks a restore, so unknown ids, version mismatches, and codec-less state fail
|
|
115
|
+
* closed as `AgentRunStateError` — never a session that silently enforces less than it did.
|
|
116
|
+
*/
|
|
117
|
+
restoreGuardrailPacks(refs: readonly GuardrailPackRef[], state?: Readonly<Record<string, unknown>>): void;
|
|
73
118
|
private ledgerChain;
|
|
74
119
|
private ledgerFailure;
|
|
75
120
|
private snapshotGeneration;
|
|
76
121
|
private snapshotCache?;
|
|
77
122
|
private readonly snapshotCacheTtlMs;
|
|
123
|
+
/**
|
|
124
|
+
* Plan 103 T4: identity-keyed meter cache. Holds only the last public meter value
|
|
125
|
+
* plus the identity of everything the cold read consumed (`snapshotGeneration`,
|
|
126
|
+
* leaf, active meter/limits, and the history array reference + length, which catches
|
|
127
|
+
* in-place `history.push` during a run) — never history content, never an estimator.
|
|
128
|
+
*/
|
|
129
|
+
private meterCache?;
|
|
78
130
|
constructor(config: AgentSessionConfig & {
|
|
79
131
|
readonly agent: Agent;
|
|
80
132
|
});
|
|
81
133
|
get leafId(): string | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* Context-fill read (plan 091 T2): the latest provider turn's input tokens —
|
|
136
|
+
* provider-reported when it reported, else a labeled estimate — plus the
|
|
137
|
+
* per-request cap and cumulative run input budget, resolved exactly as
|
|
138
|
+
* `provider_turn_finished.budgets` resolves them. Before any provider turn in
|
|
139
|
+
* this session it estimates stored history, so a non-reporting model still
|
|
140
|
+
* shows a working meter instead of zero. Never billing; estimates are labeled.
|
|
141
|
+
*
|
|
142
|
+
* Plan 103 T4: reads are cached until the history generation, leaf, history
|
|
143
|
+
* length, or active-run identity changes, so a per-frame poll pays one estimate
|
|
144
|
+
* per mutation instead of one per read. The cached value is frozen and is
|
|
145
|
+
* identical (`===`) to the previous read while nothing changed.
|
|
146
|
+
*/
|
|
147
|
+
contextMeter(): ContextMeter;
|
|
148
|
+
/** Cold path of `contextMeter()`: one estimate over stored history plus cap/budget resolution. */
|
|
149
|
+
private measureContextMeter;
|
|
150
|
+
/**
|
|
151
|
+
* Live events for this session. A run-scoped subscriber (the default) is closed when the run ends,
|
|
152
|
+
* suspends, or is denied; `SubscribeOptions.acrossRuns: true` keeps one subscriber open across runs
|
|
153
|
+
* of the same session until the host closes it, the session tears it down, or its bounded queue
|
|
154
|
+
* overflows under the default policy. Subscribe before `run()`; the consumer loop and `run()` must
|
|
155
|
+
* run concurrently, since events are only emitted during a live run.
|
|
156
|
+
*/
|
|
82
157
|
subscribe(options?: SubscribeOptions): AsyncIterable<AgentEvent>;
|
|
158
|
+
private createSubscriber;
|
|
83
159
|
run(input: AgentInput, options?: RunOptions): Promise<AgentRunResult>;
|
|
84
160
|
steer(input: AgentInput, options?: SteerOptions): void;
|
|
85
|
-
resumeDurable(state: StoredAgentRunState, runState: AgentRunStateOptions, ownership?: OwnershipScope, signal?: AbortSignal, decisions?: ReadonlyMap<string, RunDecision
|
|
161
|
+
resumeDurable(state: StoredAgentRunState, runState: AgentRunStateOptions, ownership?: OwnershipScope, signal?: AbortSignal, decisions?: ReadonlyMap<string, RunDecision>, extras?: ActiveDurableRunExtras): Promise<AgentRunResult>;
|
|
86
162
|
recordDurableResumption(runId: string, interruption: import("../contracts.js").AgentRunInterruption, version: number, ownership?: OwnershipScope): Promise<void>;
|
|
87
163
|
recordDurableDenial(runId: string, interruption: import("../contracts.js").AgentRunInterruption, version: number, ownership?: OwnershipScope): Promise<void>;
|
|
88
164
|
private runInternal;
|
|
165
|
+
/**
|
|
166
|
+
* Plan 106 R2: dispatch `session_start` once per session, at its first run start (including the
|
|
167
|
+
* first run of a session rebuilt from a durable checkpoint). The run assembler awaits it right
|
|
168
|
+
* after `agent_started`/`agent_resumed`, so session-scoped provisioning is done before the first
|
|
169
|
+
* turn while the runtime's synchronous emit burst stays intact. Middleware error policy decides
|
|
170
|
+
* whether a failure surfaces or becomes an `extension_error` event.
|
|
171
|
+
*/
|
|
172
|
+
openSession(runId: string): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* Plan 106 R2: session teardown. Dispatches `session_shutdown` middleware once (idempotent) and
|
|
175
|
+
* then closes every subscriber, run-scoped and `acrossRuns` alike. Call it after the active run
|
|
176
|
+
* settles; `closeSubscribers()` remains the subscriber-only seam.
|
|
177
|
+
*/
|
|
178
|
+
close(): Promise<void>;
|
|
89
179
|
prompt(input: string, options?: RunOptions): Promise<AgentRunResult>;
|
|
90
180
|
stream(input: AgentInput, options?: RunOptions & SubscribeOptions): AsyncGenerator<AgentEvent>;
|
|
91
181
|
buildRunResult(input: {
|
|
@@ -115,6 +205,11 @@ export declare class RuntimeAgentSession implements AgentSession {
|
|
|
115
205
|
resolveRunProvider(options: RunOptions): void;
|
|
116
206
|
resolveRunSkills(options: RunOptions, tools: readonly ToolDefinition[]): readonly Skill[];
|
|
117
207
|
emit(event: AgentEvent): void;
|
|
208
|
+
/**
|
|
209
|
+
* Run end (finish, suspension, or denial): closes the run-scoped subscribers only. Subscribers that
|
|
210
|
+
* opted into `SubscribeOptions.acrossRuns` stay open for the next run of this session.
|
|
211
|
+
*/
|
|
212
|
+
closeRunSubscribers(): void;
|
|
118
213
|
closeSubscribers(): void;
|
|
119
214
|
drainLedger(): Promise<void>;
|
|
120
215
|
applyPendingSteers(runId: string, metadata: Readonly<Record<string, unknown>>, signal: AbortSignal): Promise<boolean>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/** session (0.2.5 plan 025 Task 1 split). Moved verbatim from agent-session.ts; public surface unchanged behind the barrel. */
|
|
2
2
|
import { policyList } from "../agent-tool-dispatch.js";
|
|
3
|
-
import { createAttentionStickyFrontier, resolveInputCap, restoreAttentionStickyFrontier, serializeAttentionStickyFrontier, } from "../attention-compiler.js";
|
|
3
|
+
import { createAttentionFoldLedger, createAttentionStickyFrontier, resolveInputCap, restoreAttentionStickyFrontier, serializeAttentionFoldLedger, serializeAttentionStickyFrontier, } from "../attention-compiler.js";
|
|
4
4
|
import { createDefaultCompactionStrategy, isCompactionEntryData } from "../compaction.js";
|
|
5
|
-
import { estimateAssemblyTokens, estimateTextTokens } from "../context-budget.js";
|
|
6
|
-
import { DEFAULT_MAX_PENDING_STEER_BYTES, DEFAULT_MAX_PENDING_STEERS, DEFAULT_SNAPSHOT_CACHE_TTL_MS, HARD_MAX_SNAPSHOT_CACHE_TTL_MS, resolveShouldCompact, } from "../contracts.js";
|
|
7
|
-
import { GuardrailError, runGuardrails } from "../guardrails.js";
|
|
5
|
+
import { estimateAssemblyTokens, estimateMessageTokens, estimateTextTokens } from "../context-budget.js";
|
|
6
|
+
import { AgentRunStateError, DEFAULT_MAX_PENDING_STEER_BYTES, DEFAULT_MAX_PENDING_STEERS, DEFAULT_SNAPSHOT_CACHE_TTL_MS, HARD_MAX_SNAPSHOT_CACHE_TTL_MS, resolveShouldCompact, } from "../contracts.js";
|
|
7
|
+
import { compileGuardrailPacksWithState, GuardrailError, GuardrailPackError, runGuardrails } from "../guardrails.js";
|
|
8
8
|
import { applyDefaultProviderRequestOptions, createProviderRequestPolicyChain, normalizeProviderRequestPolicyResult, } from "../provider-request-policy.js";
|
|
9
9
|
import { redactAgentEvent, redactProviderRequest, redactRunLedgerRecord, redactSecrets, redactSessionEntry } from "../redaction.js";
|
|
10
10
|
import { createMemorySessionStore, createSessionEntry, getSessionBranchEntries, rebuildSessionContext } from "../session-stores.js";
|
|
@@ -23,6 +23,10 @@ export class RuntimeAgentSession {
|
|
|
23
23
|
metadata;
|
|
24
24
|
store;
|
|
25
25
|
subscribers = new Set();
|
|
26
|
+
/** Plan 106 R2: `session_start` is dispatched at the first run start, once per runtime session. */
|
|
27
|
+
sessionOpened = false;
|
|
28
|
+
/** Plan 106 R2: `close()` dispatches `session_shutdown` and closes subscribers exactly once. */
|
|
29
|
+
closed = false;
|
|
26
30
|
currentLeafId;
|
|
27
31
|
history = [];
|
|
28
32
|
activeRun;
|
|
@@ -39,9 +43,24 @@ export class RuntimeAgentSession {
|
|
|
39
43
|
activeIdentity;
|
|
40
44
|
activeIdempotencyKey;
|
|
41
45
|
activeGuardrails;
|
|
46
|
+
/** Plan 092 Task 2: guardrail packs compiled once in the constructor; merged into every run's `activeGuardrails`. */
|
|
47
|
+
packGuardrails;
|
|
48
|
+
/** Plan 104 Task 3: `ask` rules as the durable charge-time gate (a match records `interrupt`). */
|
|
49
|
+
packAskGate;
|
|
50
|
+
/** Plan 104 Task 3: the same `ask` rules as plain blocks for a run that cannot suspend. */
|
|
51
|
+
packAskBlocks;
|
|
52
|
+
/** Original pack refs, carried into `fork()`/`clone()` so a branch cannot silently lose its policy. */
|
|
53
|
+
packRefs;
|
|
54
|
+
/** Plan 104 Task 2: compiled refs + live pack state, replaced by `restoreGuardrailPacks` on resume. */
|
|
55
|
+
compiledGuardrailPacks;
|
|
42
56
|
activeMetadata;
|
|
43
57
|
activePromptVersion;
|
|
44
58
|
activeLimits;
|
|
59
|
+
/** Plan 091 T2: input tokens of the latest provider turn plus whether the provider reported
|
|
60
|
+
* them; set by the usage seam, read by `contextMeter()`. */
|
|
61
|
+
activeInputMeter;
|
|
62
|
+
/** Bounded last-N tool-call summaries of the active run (plan 087 T2): ids, names, arg hashes. */
|
|
63
|
+
activeRecentToolCalls;
|
|
45
64
|
activeLimitOutputBuffer = false;
|
|
46
65
|
activeDurable;
|
|
47
66
|
activeLoop;
|
|
@@ -49,6 +68,8 @@ export class RuntimeAgentSession {
|
|
|
49
68
|
activeGatedRound;
|
|
50
69
|
activeLoopTurn = 1;
|
|
51
70
|
loadedSkills = createLoadedSkillSet();
|
|
71
|
+
/** Run-owned only: loaded bodies and URI resources retain first insertion order within one provider loop. */
|
|
72
|
+
tailSegments = new Map();
|
|
52
73
|
/** Tools activated via `search_tools` this session (plan 041); names-only in persistence. */
|
|
53
74
|
activatedTools = createActiveToolSet();
|
|
54
75
|
/** Plan 018 Task 6 (closeout `checkpoint-bodies`): persisted exact instructions, registry-independent. */
|
|
@@ -77,6 +98,24 @@ export class RuntimeAgentSession {
|
|
|
77
98
|
restoreAttentionSticky(persisted) {
|
|
78
99
|
this.attentionSticky = restoreAttentionStickyFrontier(persisted);
|
|
79
100
|
}
|
|
101
|
+
/** Session-owned folded bodies (plan 086 T3); created on first use like the frontier, so a
|
|
102
|
+
* compiler-off session allocates nothing. */
|
|
103
|
+
attentionFold;
|
|
104
|
+
/** Set per run from the resolved compiler: `durable: true` opts the fold ledger and its
|
|
105
|
+
* frontier into checkpoints even when `persistSessionState` is off. */
|
|
106
|
+
attentionDurable = false;
|
|
107
|
+
attentionFoldFor() {
|
|
108
|
+
this.attentionFold ??= createAttentionFoldLedger();
|
|
109
|
+
return this.attentionFold;
|
|
110
|
+
}
|
|
111
|
+
/** Plan 086 T3: bounded ledger snapshot for a durable checkpoint; `undefined` before any fold. */
|
|
112
|
+
serializedAttentionFold() {
|
|
113
|
+
return this.attentionFold && this.attentionFold.bodies.size > 0 ? serializeAttentionFoldLedger(this.attentionFold) : undefined;
|
|
114
|
+
}
|
|
115
|
+
/** Plan 086 T3: adopt a ledger validated at checkpoint load, so a resumed fold is byte-identical. */
|
|
116
|
+
restoreAttentionFold(ledger) {
|
|
117
|
+
this.attentionFold = ledger;
|
|
118
|
+
}
|
|
80
119
|
/** Plan 015 Task 4: re-add persisted loaded-skill names (names only; bodies re-resolve on demand). */
|
|
81
120
|
restoreLoadedSkills(names) {
|
|
82
121
|
for (const name of names)
|
|
@@ -98,11 +137,51 @@ export class RuntimeAgentSession {
|
|
|
98
137
|
for (const entry of bodies)
|
|
99
138
|
this.loadedSkills.add(entry.name);
|
|
100
139
|
}
|
|
140
|
+
/** Plan 104 T2: the refs this session actually enforces (restored ones after a resume). */
|
|
141
|
+
get guardrailPackRefs() {
|
|
142
|
+
return this.packRefs;
|
|
143
|
+
}
|
|
144
|
+
/** Plan 104 T2: pack refs + live pack-owned state for a durable checkpoint (opt-in with `persistSessionState`). */
|
|
145
|
+
serializedGuardrailPackState() {
|
|
146
|
+
const compiled = this.compiledGuardrailPacks;
|
|
147
|
+
if (!compiled || compiled.packs.length === 0)
|
|
148
|
+
return undefined;
|
|
149
|
+
const state = compiled.snapshotState();
|
|
150
|
+
return { packs: compiled.packs, ...(state ? { state } : {}) };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Plan 104 T2: recompile checkpoint packs before the resumed run's first turn. `state` present
|
|
154
|
+
* (even empty) marks a restore, so unknown ids, version mismatches, and codec-less state fail
|
|
155
|
+
* closed as `AgentRunStateError` — never a session that silently enforces less than it did.
|
|
156
|
+
*/
|
|
157
|
+
restoreGuardrailPacks(refs, state) {
|
|
158
|
+
let compiled;
|
|
159
|
+
try {
|
|
160
|
+
compiled = compileGuardrailPacksWithState(refs, undefined, state ?? {});
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error instanceof GuardrailPackError)
|
|
164
|
+
throw new AgentRunStateError(`Cannot restore guardrail packs: ${error.message}`);
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
this.compiledGuardrailPacks = compiled;
|
|
168
|
+
this.packGuardrails = compiled.guardrails;
|
|
169
|
+
this.packAskGate = compiled.askGate;
|
|
170
|
+
this.packAskBlocks = compiled.askBlocks;
|
|
171
|
+
this.packRefs = refs;
|
|
172
|
+
}
|
|
101
173
|
ledgerChain = Promise.resolve();
|
|
102
174
|
ledgerFailure;
|
|
103
175
|
snapshotGeneration = 0;
|
|
104
176
|
snapshotCache;
|
|
105
177
|
snapshotCacheTtlMs;
|
|
178
|
+
/**
|
|
179
|
+
* Plan 103 T4: identity-keyed meter cache. Holds only the last public meter value
|
|
180
|
+
* plus the identity of everything the cold read consumed (`snapshotGeneration`,
|
|
181
|
+
* leaf, active meter/limits, and the history array reference + length, which catches
|
|
182
|
+
* in-place `history.push` during a run) — never history content, never an estimator.
|
|
183
|
+
*/
|
|
184
|
+
meterCache;
|
|
106
185
|
constructor(config) {
|
|
107
186
|
this.id = config.id ?? randomId("session");
|
|
108
187
|
this.agent = config.agent;
|
|
@@ -110,11 +189,89 @@ export class RuntimeAgentSession {
|
|
|
110
189
|
this.store = config.store ?? config.agent.config.store ?? createMemorySessionStore();
|
|
111
190
|
this.currentLeafId = config.leafId;
|
|
112
191
|
this.snapshotCacheTtlMs = resolveSnapshotCacheTtlMs(config.snapshotCacheTtlMs);
|
|
192
|
+
this.compiledGuardrailPacks = compileGuardrailPacksWithState(config.guardrailPacks);
|
|
193
|
+
this.packGuardrails = this.compiledGuardrailPacks.guardrails;
|
|
194
|
+
this.packAskGate = this.compiledGuardrailPacks.askGate;
|
|
195
|
+
this.packAskBlocks = this.compiledGuardrailPacks.askBlocks;
|
|
196
|
+
this.packRefs = config.guardrailPacks;
|
|
197
|
+
const usageEstimation = config.agent.config.usageEstimation;
|
|
198
|
+
if (usageEstimation !== undefined && usageEstimation !== "fallback" && usageEstimation !== "off" && usageEstimation !== "strict") {
|
|
199
|
+
throw new TypeError('usageEstimation must be "fallback", "off", or "strict"');
|
|
200
|
+
}
|
|
113
201
|
}
|
|
114
202
|
get leafId() {
|
|
115
203
|
return this.currentLeafId;
|
|
116
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Context-fill read (plan 091 T2): the latest provider turn's input tokens —
|
|
207
|
+
* provider-reported when it reported, else a labeled estimate — plus the
|
|
208
|
+
* per-request cap and cumulative run input budget, resolved exactly as
|
|
209
|
+
* `provider_turn_finished.budgets` resolves them. Before any provider turn in
|
|
210
|
+
* this session it estimates stored history, so a non-reporting model still
|
|
211
|
+
* shows a working meter instead of zero. Never billing; estimates are labeled.
|
|
212
|
+
*
|
|
213
|
+
* Plan 103 T4: reads are cached until the history generation, leaf, history
|
|
214
|
+
* length, or active-run identity changes, so a per-frame poll pays one estimate
|
|
215
|
+
* per mutation instead of one per read. The cached value is frozen and is
|
|
216
|
+
* identical (`===`) to the previous read while nothing changed.
|
|
217
|
+
*/
|
|
218
|
+
contextMeter() {
|
|
219
|
+
const cached = this.meterCache;
|
|
220
|
+
if (cached &&
|
|
221
|
+
cached.leafId === this.currentLeafId &&
|
|
222
|
+
cached.generation === this.snapshotGeneration &&
|
|
223
|
+
cached.meter === this.activeInputMeter &&
|
|
224
|
+
cached.limits === this.activeLimits &&
|
|
225
|
+
cached.history === this.history &&
|
|
226
|
+
cached.historyLength === this.history.length) {
|
|
227
|
+
return cached.value;
|
|
228
|
+
}
|
|
229
|
+
const value = Object.freeze(this.measureContextMeter());
|
|
230
|
+
this.meterCache = {
|
|
231
|
+
leafId: this.currentLeafId,
|
|
232
|
+
generation: this.snapshotGeneration,
|
|
233
|
+
meter: this.activeInputMeter,
|
|
234
|
+
limits: this.activeLimits,
|
|
235
|
+
history: this.history,
|
|
236
|
+
historyLength: this.history.length,
|
|
237
|
+
value,
|
|
238
|
+
};
|
|
239
|
+
return value;
|
|
240
|
+
}
|
|
241
|
+
/** Cold path of `contextMeter()`: one estimate over stored history plus cap/budget resolution. */
|
|
242
|
+
measureContextMeter() {
|
|
243
|
+
const model = this.agent.config.model;
|
|
244
|
+
const inputTokens = this.activeInputMeter?.tokens ?? estimateMessageTokens(this.history, model.model).tokens;
|
|
245
|
+
const source = this.activeInputMeter?.source ?? "estimated";
|
|
246
|
+
let inputCap;
|
|
247
|
+
try {
|
|
248
|
+
const setting = this.agent.config.attentionCompiler;
|
|
249
|
+
const options = typeof setting === "object" && setting !== null ? setting : undefined;
|
|
250
|
+
inputCap = resolveInputCap(options ? { maxInputTokens: options.maxInputTokens, reserveTokens: options.reserveTokens } : {}, model);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
inputCap = undefined; // undialed model: omit instead of throwing a state read
|
|
254
|
+
}
|
|
255
|
+
const runInputBudget = this.activeLimits?.limits.maxInputTokens ?? undefined;
|
|
256
|
+
return {
|
|
257
|
+
inputTokens,
|
|
258
|
+
source,
|
|
259
|
+
...(inputCap === undefined ? {} : { inputCap }),
|
|
260
|
+
...(runInputBudget == null ? {} : { runInputBudget }),
|
|
261
|
+
...(inputCap === undefined ? {} : { usedRatio: inputTokens / inputCap }),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Live events for this session. A run-scoped subscriber (the default) is closed when the run ends,
|
|
266
|
+
* suspends, or is denied; `SubscribeOptions.acrossRuns: true` keeps one subscriber open across runs
|
|
267
|
+
* of the same session until the host closes it, the session tears it down, or its bounded queue
|
|
268
|
+
* overflows under the default policy. Subscribe before `run()`; the consumer loop and `run()` must
|
|
269
|
+
* run concurrently, since events are only emitted during a live run.
|
|
270
|
+
*/
|
|
117
271
|
subscribe(options = {}) {
|
|
272
|
+
return this.createSubscriber(options);
|
|
273
|
+
}
|
|
274
|
+
createSubscriber(options) {
|
|
118
275
|
const subscriber = new EventSubscriber(this.id, options, () => this.subscribers.delete(subscriber));
|
|
119
276
|
this.subscribers.add(subscriber);
|
|
120
277
|
return subscriber;
|
|
@@ -147,12 +304,13 @@ export class RuntimeAgentSession {
|
|
|
147
304
|
this.pendingSoftInterrupt = true;
|
|
148
305
|
}
|
|
149
306
|
}
|
|
150
|
-
async resumeDurable(state, runState, ownership, signal, decisions) {
|
|
307
|
+
async resumeDurable(state, runState, ownership, signal, decisions, extras) {
|
|
151
308
|
return this.runInternal(state.input ?? [], { runState, ownership, signal }, state.runId, {
|
|
152
309
|
options: runState,
|
|
153
310
|
state,
|
|
154
311
|
version: state.version,
|
|
155
312
|
decisions,
|
|
313
|
+
...extras,
|
|
156
314
|
});
|
|
157
315
|
}
|
|
158
316
|
async recordDurableResumption(runId, interruption, version, ownership) {
|
|
@@ -167,7 +325,7 @@ export class RuntimeAgentSession {
|
|
|
167
325
|
this.activeLedger = undefined;
|
|
168
326
|
this.activeOwnership = undefined;
|
|
169
327
|
this.activeRedactor = undefined;
|
|
170
|
-
this.
|
|
328
|
+
this.closeRunSubscribers();
|
|
171
329
|
}
|
|
172
330
|
}
|
|
173
331
|
async recordDurableDenial(runId, interruption, version, ownership) {
|
|
@@ -182,25 +340,58 @@ export class RuntimeAgentSession {
|
|
|
182
340
|
this.activeLedger = undefined;
|
|
183
341
|
this.activeOwnership = undefined;
|
|
184
342
|
this.activeRedactor = undefined;
|
|
185
|
-
this.
|
|
343
|
+
this.closeRunSubscribers();
|
|
186
344
|
}
|
|
187
345
|
}
|
|
188
346
|
async runInternal(input, options, runId, resumed) {
|
|
189
347
|
return executeRun(asSessionHost(this), input, options, runId, resumed);
|
|
190
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Plan 106 R2: dispatch `session_start` once per session, at its first run start (including the
|
|
351
|
+
* first run of a session rebuilt from a durable checkpoint). The run assembler awaits it right
|
|
352
|
+
* after `agent_started`/`agent_resumed`, so session-scoped provisioning is done before the first
|
|
353
|
+
* turn while the runtime's synchronous emit burst stays intact. Middleware error policy decides
|
|
354
|
+
* whether a failure surfaces or becomes an `extension_error` event.
|
|
355
|
+
*/
|
|
356
|
+
async openSession(runId) {
|
|
357
|
+
if (this.sessionOpened)
|
|
358
|
+
return;
|
|
359
|
+
this.sessionOpened = true;
|
|
360
|
+
await this.agent.config.middleware?.run("session_start", { sessionId: this.id, runId });
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Plan 106 R2: session teardown. Dispatches `session_shutdown` middleware once (idempotent) and
|
|
364
|
+
* then closes every subscriber, run-scoped and `acrossRuns` alike. Call it after the active run
|
|
365
|
+
* settles; `closeSubscribers()` remains the subscriber-only seam.
|
|
366
|
+
*/
|
|
367
|
+
async close() {
|
|
368
|
+
if (this.closed)
|
|
369
|
+
return;
|
|
370
|
+
this.closed = true;
|
|
371
|
+
try {
|
|
372
|
+
await this.agent.config.middleware?.run("session_shutdown", { sessionId: this.id });
|
|
373
|
+
}
|
|
374
|
+
finally {
|
|
375
|
+
this.closeSubscribers();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
191
378
|
prompt(input, options) {
|
|
192
379
|
return this.run(input, options);
|
|
193
380
|
}
|
|
194
381
|
async *stream(input, options = {}) {
|
|
195
382
|
const { maxQueuedEvents, overflow, ...runOptions } = options;
|
|
196
|
-
const
|
|
383
|
+
const subscriber = this.createSubscriber({ maxQueuedEvents, overflow });
|
|
197
384
|
let runOwnedId;
|
|
198
385
|
let settled = false;
|
|
386
|
+
// This subscription is stream()'s own, so it does not depend on the run-end close: settling the
|
|
387
|
+
// run closes it too, which also unblocks the consumer loop when the run fails before it ever
|
|
388
|
+
// emits (a pre-flight validation rejection returns before run-end cleanup).
|
|
199
389
|
const runPromise = this.run(input, runOptions).finally(() => {
|
|
200
390
|
settled = true;
|
|
391
|
+
subscriber.close();
|
|
201
392
|
});
|
|
202
393
|
try {
|
|
203
|
-
for await (const event of
|
|
394
|
+
for await (const event of subscriber) {
|
|
204
395
|
if ("runId" in event && typeof event.runId === "string") {
|
|
205
396
|
if (runOwnedId === undefined && event.type === "agent_started")
|
|
206
397
|
runOwnedId = event.runId;
|
|
@@ -212,6 +403,7 @@ export class RuntimeAgentSession {
|
|
|
212
403
|
await runPromise;
|
|
213
404
|
}
|
|
214
405
|
finally {
|
|
406
|
+
subscriber.close();
|
|
215
407
|
if (!settled) {
|
|
216
408
|
this.abort(new Error("stream consumer closed"));
|
|
217
409
|
await runPromise.catch(() => undefined);
|
|
@@ -241,7 +433,10 @@ export class RuntimeAgentSession {
|
|
|
241
433
|
async compact(options = {}) {
|
|
242
434
|
if (this.activeRun)
|
|
243
435
|
throw new Error("Agent session already has an active run");
|
|
244
|
-
|
|
436
|
+
const result = await this.compactBranch(options, undefined, options.signal, "manual");
|
|
437
|
+
// Plan 091 T2: history changed, so a pre-compaction meter reading would overstate the context.
|
|
438
|
+
this.activeInputMeter = undefined;
|
|
439
|
+
return result;
|
|
245
440
|
}
|
|
246
441
|
abort(reason) {
|
|
247
442
|
this.activeRun?.abort(reason);
|
|
@@ -264,6 +459,7 @@ export class RuntimeAgentSession {
|
|
|
264
459
|
store: this.store,
|
|
265
460
|
leafId: options.leafId ?? this.currentLeafId,
|
|
266
461
|
metadata: this.metadata,
|
|
462
|
+
...(this.packRefs ? { guardrailPacks: this.packRefs } : {}),
|
|
267
463
|
});
|
|
268
464
|
}
|
|
269
465
|
async clone(options = {}) {
|
|
@@ -286,6 +482,7 @@ export class RuntimeAgentSession {
|
|
|
286
482
|
store: this.store,
|
|
287
483
|
leafId: branch.length ? remap.get(branch[branch.length - 1].id) : undefined,
|
|
288
484
|
metadata: this.metadata,
|
|
485
|
+
...(this.packRefs ? { guardrailPacks: this.packRefs } : {}),
|
|
289
486
|
});
|
|
290
487
|
}
|
|
291
488
|
branchReader() {
|
|
@@ -347,6 +544,15 @@ export class RuntimeAgentSession {
|
|
|
347
544
|
});
|
|
348
545
|
}
|
|
349
546
|
}
|
|
547
|
+
/**
|
|
548
|
+
* Run end (finish, suspension, or denial): closes the run-scoped subscribers only. Subscribers that
|
|
549
|
+
* opted into `SubscribeOptions.acrossRuns` stay open for the next run of this session.
|
|
550
|
+
*/
|
|
551
|
+
closeRunSubscribers() {
|
|
552
|
+
for (const subscriber of this.subscribers)
|
|
553
|
+
if (!subscriber.acrossRuns)
|
|
554
|
+
subscriber.close();
|
|
555
|
+
}
|
|
350
556
|
closeSubscribers() {
|
|
351
557
|
for (const subscriber of this.subscribers)
|
|
352
558
|
subscriber.close();
|
|
@@ -457,10 +663,15 @@ export class RuntimeAgentSession {
|
|
|
457
663
|
signal,
|
|
458
664
|
};
|
|
459
665
|
this.emit({ type: "compaction_started", sessionId: this.id, runId });
|
|
460
|
-
|
|
666
|
+
// Plan 106 R3: pre-compaction seam — the strategy compacts exactly the context this returns.
|
|
667
|
+
const requested = (await this.agent.config.middleware?.run("compaction_request", context)) ?? context;
|
|
668
|
+
let result = await strategy.compact(requested);
|
|
461
669
|
result = { ...result, summary: redactSecrets(result.summary, secrets) };
|
|
462
|
-
const payload = (await this.agent.config.middleware?.run("compaction", {
|
|
463
|
-
context,
|
|
670
|
+
const payload = (await this.agent.config.middleware?.run("compaction", {
|
|
671
|
+
context: requested,
|
|
672
|
+
result,
|
|
673
|
+
})) ?? {
|
|
674
|
+
context: requested,
|
|
464
675
|
result,
|
|
465
676
|
};
|
|
466
677
|
result = { ...payload.result, summary: redactSecrets(payload.result.summary, secrets) };
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* frozen prefix. The assembly branch lives in `input.ts`; opt-in agent wiring (Task 5) is not
|
|
6
6
|
* part of this module. */
|
|
7
7
|
import { type ContextBudgetMessageGroups } from "./context-budget.js";
|
|
8
|
-
import type { AttentionCompiler, AttentionCompilerContext, AttentionCompilerOptions, AttentionCompilerSetting, AttentionInputCapOptions, AttentionReport, ContextBlock, Skill, ToolDefinition } from "./contracts.js";
|
|
8
|
+
import type { AttentionCompiler, AttentionCompilerContext, AttentionCompilerOptions, AttentionCompilerSetting, AttentionInputCapOptions, AttentionReport, AttentionTrigger, AttentionTriggerDecision, AttentionTriggerState, ContextBlock, Skill, ToolDefinition } from "./contracts.js";
|
|
9
9
|
import { type CompactionTrigger } from "./contracts-core/compaction.js";
|
|
10
10
|
import type { SecretRedactor } from "./redaction.js";
|
|
11
11
|
import { type ResolvedToolResultFoldOptions } from "./tool-result-fold.js";
|
|
@@ -25,13 +25,29 @@ export declare function resolveAttentionReserveTokens(value: unknown): number;
|
|
|
25
25
|
/** C2: host `maxInputTokens` wins; otherwise `contextWindow - (maxOutputTokens ?? 0) - reserve`.
|
|
26
26
|
* Shared with the `input_ratio` compaction trigger (Task 4). Throws when no cap can be derived. */
|
|
27
27
|
export declare function resolveInputCap(options?: AttentionInputCapOptions, model?: AttentionCompilerContext["model"]): number;
|
|
28
|
+
/**
|
|
29
|
+
* Build the frozen per-turn state the axes read: the compiler supplies the cap and the run budget
|
|
30
|
+
* (so a caller cannot silently drop the budget and fall back to the input cap), the caller
|
|
31
|
+
* supplies what only the turn knows.
|
|
32
|
+
*/
|
|
33
|
+
export declare function attentionTriggerState(compiler: Pick<AttentionCompiler, "inputCap" | "runInputBudget">, turn: {
|
|
34
|
+
readonly estimatedInputTokens: number;
|
|
35
|
+
readonly runInputTokens?: number;
|
|
36
|
+
readonly turn?: number;
|
|
37
|
+
}): AttentionTriggerState;
|
|
38
|
+
/**
|
|
39
|
+
* Evaluate the fold axes in order against one turn's state; the first axis that fires wins, and
|
|
40
|
+
* its `targetTokens` (when it has one) is what the stages fold to. Called at most twice per turn —
|
|
41
|
+
* once at turn start, once after the stages — so a host predicate never runs per row.
|
|
42
|
+
*/
|
|
43
|
+
export declare function evaluateAttentionTrigger(axes: readonly AttentionTrigger[], state: AttentionTriggerState): AttentionTriggerDecision;
|
|
28
44
|
/** Validate compiler options + resolve the input cap. No provider I/O; unknown or
|
|
29
45
|
* unresolvable input throws here rather than on the first turn. */
|
|
30
46
|
export declare function createAttentionCompiler(options?: AttentionCompilerOptions, context?: AttentionCompilerContext): AttentionCompiler;
|
|
31
47
|
/** Resolve the run's compiler from the agent setting plus an optional run overlay, validating
|
|
32
48
|
* both eagerly (no provider I/O) so a typo fails at run start, not on some later turn (C12).
|
|
33
49
|
* Returns `undefined` when the compiler is off — the assembly path then allocates nothing. */
|
|
34
|
-
export declare function resolveRunAttentionCompiler(agent: AttentionCompilerSetting | undefined, run: AttentionCompilerSetting | undefined, model: AttentionCompilerContext["model"]): AttentionCompiler | undefined;
|
|
50
|
+
export declare function resolveRunAttentionCompiler(agent: AttentionCompilerSetting | undefined, run: AttentionCompilerSetting | undefined, model: AttentionCompilerContext["model"], runInputBudget?: number | null): AttentionCompiler | undefined;
|
|
35
51
|
/** Caller-owned sticky frontier: what this session leaf already mutated (C10). Mutations are
|
|
36
52
|
* monotonic, so a stubbed call stays stubbed and stripped thinking stays stripped even on a
|
|
37
53
|
* later under-ratio turn — restoring either would rewrite the prompt-cache prefix. */
|
|
@@ -53,9 +69,15 @@ export interface AttentionCompileOptions {
|
|
|
53
69
|
* decide fold-eligibility. Omitted → the deterministic stub and `keepLast` alone. */
|
|
54
70
|
readonly fold?: ResolvedToolResultFoldOptions;
|
|
55
71
|
readonly frontier?: AttentionStickyFrontier;
|
|
72
|
+
/** Session-owned folded bodies (plan 086 T3): a body stored here is re-applied instead of
|
|
73
|
+
* re-summarized, so a sticky row stays byte-identical and the host `summarize` runs once per
|
|
74
|
+
* row instead of once per turn. Omit for one-shot assemblies. */
|
|
75
|
+
readonly attentionFold?: AttentionFoldLedger;
|
|
56
76
|
readonly redactor?: SecretRedactor;
|
|
57
77
|
readonly signal?: AbortSignal;
|
|
58
78
|
readonly turn?: number;
|
|
79
|
+
/** Run input tokens already charged this run; the `run_input_ratio` axis projects onto it. */
|
|
80
|
+
readonly runInputTokens?: number;
|
|
59
81
|
readonly sessionId?: string;
|
|
60
82
|
readonly runId?: string;
|
|
61
83
|
}
|
|
@@ -90,6 +112,33 @@ export declare function serializeAttentionStickyFrontier(frontier: AttentionStic
|
|
|
90
112
|
export declare function parseAttentionStickyFrontier(value: unknown): PersistedAttentionStickyFrontier | undefined;
|
|
91
113
|
/** Rebuild a frontier from a restored snapshot; callers hold the session that owns it. */
|
|
92
114
|
export declare function restoreAttentionStickyFrontier(persisted: PersistedAttentionStickyFrontier): AttentionStickyFrontier;
|
|
115
|
+
/** Session-owned folded bodies, keyed by tool call id. A row is summarized once: every later
|
|
116
|
+
* turn re-applies the stored body, so a sticky row stays byte-identical for the provider cache
|
|
117
|
+
* and the host `summarize` is not called again for it. Bodies are already redacted and capped by
|
|
118
|
+
* the fold that produced them, so nothing here needs redaction at persistence time. */
|
|
119
|
+
export interface AttentionFoldLedger {
|
|
120
|
+
/** toolCallId → stub body (never the payload); insertion order is fold order, oldest first. */
|
|
121
|
+
readonly bodies: Map<string, string>;
|
|
122
|
+
}
|
|
123
|
+
export declare function createAttentionFoldLedger(): AttentionFoldLedger;
|
|
124
|
+
/** Serialized fold ledger (plan 086 T3). Stub bodies only — no payload, and every entry was
|
|
125
|
+
* already capped and redacted when it was folded, so a durable resume restores it verbatim. */
|
|
126
|
+
export interface PersistedAttentionFoldLedger {
|
|
127
|
+
readonly v: 1;
|
|
128
|
+
/** Oldest-first, so a restored ledger is the tail of the folds the session made. */
|
|
129
|
+
readonly bodies: readonly {
|
|
130
|
+
readonly id: string;
|
|
131
|
+
readonly body: string;
|
|
132
|
+
}[];
|
|
133
|
+
}
|
|
134
|
+
/** Bounded snapshot of a live ledger; caller-owned (the runtime persists it, the compiler never
|
|
135
|
+
* writes anywhere). */
|
|
136
|
+
export declare function serializeAttentionFoldLedger(ledger: AttentionFoldLedger): PersistedAttentionFoldLedger;
|
|
137
|
+
/** Validate a persisted ledger from an untrusted store (plan 086 T3). Malformed *entries* are
|
|
138
|
+
* dropped one by one — a body the compiler cannot trust simply re-summarizes on the next turn —
|
|
139
|
+
* while a malformed *shape* yields `undefined` so the caller starts from an empty ledger.
|
|
140
|
+
* Never throws: a resume must not fail because a checkpoint was hand-edited. */
|
|
141
|
+
export declare function restoreAttentionFoldLedger(value: unknown): AttentionFoldLedger | undefined;
|
|
93
142
|
/** Consecutive `truncated` turns that arm compaction by default. */
|
|
94
143
|
export declare const DEFAULT_ATTENTION_TRUNCATION_THRESHOLD = 2;
|
|
95
144
|
export interface AttentionTruncationTriggerOptions {
|