@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,11 +1,13 @@
|
|
|
1
|
-
import { type PersistedAttentionStickyFrontier } from "./attention-compiler.js";
|
|
2
|
-
import type { Agent, AgentRunInterruption, AgentRunRef, AgentRunState, AgentRunStateOptions, CheckpointRecord, CheckpointStore, JsonValue, Message, ModelConfig, NestedRunRef, OwnershipScope, RunDecision, RunLimitCounters, StickyDecision, ToolCallContent } from "./contracts.js";
|
|
1
|
+
import { type PersistedAttentionFoldLedger, type PersistedAttentionStickyFrontier } from "./attention-compiler.js";
|
|
2
|
+
import type { Agent, AgentRunCheckpointMetadata, AgentRunCheckpointMetadataSource, AgentRunInterruption, AgentRunRef, AgentRunState, AgentRunStateOptions, CheckpointRecord, CheckpointStore, GuardrailRule, JsonValue, Message, ModelConfig, NestedRunRef, OwnershipScope, RunDecision, RunLimitCounters, StickyDecision, ToolCallContent } from "./contracts.js";
|
|
3
3
|
import type { SecretRedactor } from "./redaction.js";
|
|
4
4
|
import { type LoadedSkillBodiesEntry } from "./skill-load.js";
|
|
5
5
|
export declare const AGENT_RUN_STATE_NAMESPACE = "prism.agent-run";
|
|
6
6
|
export declare const AGENT_RUN_STATE_SCHEMA_VERSION: 1;
|
|
7
7
|
export declare const DEFAULT_MAX_AGENT_RUN_STATE_BYTES: number;
|
|
8
8
|
export declare const HARD_MAX_AGENT_RUN_STATE_BYTES: number;
|
|
9
|
+
/** Sidecar metadata ceiling per checkpoint record (not the run-state value). */
|
|
10
|
+
export declare const MAX_AGENT_RUN_METADATA_BYTES: number;
|
|
9
11
|
/** One gated tool call awaiting or holding a decision inside a suspended durable run. */
|
|
10
12
|
export interface PendingToolCall {
|
|
11
13
|
readonly call: ToolCallContent;
|
|
@@ -50,6 +52,13 @@ export interface StoredAgentRunState extends AgentRunState {
|
|
|
50
52
|
/** Plan 074 P3: sticky attention mutations (thinking hashes + tool-call ids), so a durable
|
|
51
53
|
* resume keeps its stubs instead of re-deciding on the first turn. Validated on load. */
|
|
52
54
|
readonly attentionSticky?: PersistedAttentionStickyFrontier;
|
|
55
|
+
/** Plan 086 T3: folded bodies (`attention.compiler.durable`), so a resumed fold re-applies
|
|
56
|
+
* the same stub bytes instead of re-summarizing. Written and restored independently of
|
|
57
|
+
* `persistSessionState`. Validated on load. */
|
|
58
|
+
readonly attentionFold?: PersistedAttentionFoldLedger;
|
|
59
|
+
/** Plan 104 T2: compiled pack refs plus pack-owned state, so a resume re-enforces exactly what
|
|
60
|
+
* the suspended run enforced. Written only with `persistSessionState`; validated on load. */
|
|
61
|
+
readonly guardrailPacks?: PersistedGuardrailPacks;
|
|
53
62
|
};
|
|
54
63
|
/** Per-run allow-list (Task 21). Absent = full registered set (legacy checkpoints). */
|
|
55
64
|
readonly toolNames?: readonly string[];
|
|
@@ -60,17 +69,34 @@ export interface StoredAgentRunState extends AgentRunState {
|
|
|
60
69
|
*/
|
|
61
70
|
readonly checkpointPolicy?: "every-turn";
|
|
62
71
|
/**
|
|
63
|
-
* Set when a terminal state was written by a
|
|
64
|
-
*
|
|
65
|
-
*
|
|
72
|
+
* Set when a terminal state was written by a clean run-end stop that leaves the frontier intact:
|
|
73
|
+
* a `RunOptions.turnPolicy` stop (`host_policy`, plan 084 Task 2) or a stop-hook continuation cap
|
|
74
|
+
* (`hook_limit`, plan 106 R1). The run succeeded but `decision: "continue"` may resume it. Absent
|
|
75
|
+
* on every other state — a naturally finished run is never continuable.
|
|
66
76
|
*/
|
|
67
|
-
readonly stopReason?: "host_policy";
|
|
77
|
+
readonly stopReason?: "host_policy" | "hook_limit";
|
|
68
78
|
}
|
|
69
79
|
/** Session-state caps (plan 015 Task 4): bounded names charged against the run-state byte budget. */
|
|
70
80
|
export declare const MAX_PERSISTED_SKILL_NAMES = 64;
|
|
71
81
|
export declare const MAX_PERSISTED_SKILL_NAME_CHARS = 256;
|
|
72
82
|
/** Plan 041: activated-tool names ride the same budget discipline (cap 128; multiple searches accumulate). */
|
|
73
83
|
export declare const MAX_PERSISTED_ACTIVATED_TOOL_NAMES = 128;
|
|
84
|
+
/** Plan 104 T2: one replayable pack row — the id, the version it was compiled at, and host options. */
|
|
85
|
+
interface PersistedGuardrailPackRef {
|
|
86
|
+
readonly id: string;
|
|
87
|
+
readonly version: number;
|
|
88
|
+
readonly options?: Readonly<Record<string, unknown>>;
|
|
89
|
+
/** Inline pattern rules; closures and `RegExp` patterns never reach a checkpoint (refused at save). */
|
|
90
|
+
readonly rules?: readonly GuardrailRule[];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Plan 104 T2: the checkpoint-side pack block written with `persistSessionState`. Rows replay a
|
|
94
|
+
* registered pack by `id`/`version` or an inline pack by its pattern `rules` (plan 104 T3).
|
|
95
|
+
*/
|
|
96
|
+
export interface PersistedGuardrailPacks {
|
|
97
|
+
readonly packs: readonly PersistedGuardrailPackRef[];
|
|
98
|
+
readonly state?: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
|
|
99
|
+
}
|
|
74
100
|
/** Revision stamps of the built-in loops; custom strategies declare their own `revision`. */
|
|
75
101
|
export declare const BUILT_IN_LOOP_REVISIONS: Readonly<Record<string, string>>;
|
|
76
102
|
/** Validate a strategy snapshot as JSON-compatible and package it for the durable envelope. */
|
|
@@ -81,7 +107,20 @@ export declare function validateRunStateOptions(options: AgentRunStateOptions):
|
|
|
81
107
|
export declare function loadAgentRunState(checkpoints: CheckpointStore, ref: AgentRunRef, ownership?: OwnershipScope): Promise<{
|
|
82
108
|
readonly record: CheckpointRecord;
|
|
83
109
|
readonly state: StoredAgentRunState;
|
|
110
|
+
readonly metadata?: AgentRunCheckpointMetadata;
|
|
84
111
|
}>;
|
|
112
|
+
/** Resolve a host metadata source. A throwing provider fails the checkpoint write (fail closed). */
|
|
113
|
+
export declare function resolveCheckpointMetadata(source: AgentRunCheckpointMetadataSource | undefined): AgentRunCheckpointMetadata | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* Redact + bound a sidecar metadata map for a checkpoint write. Values must be strings;
|
|
116
|
+
* redaction runs first so a replacement marker is still charged against the 4 KiB ceiling.
|
|
117
|
+
*/
|
|
118
|
+
export declare function boundCheckpointMetadata(metadata: AgentRunCheckpointMetadata, redactor?: SecretRedactor): AgentRunCheckpointMetadata;
|
|
119
|
+
/**
|
|
120
|
+
* Read-side normalization (legacy tolerance): absent, oversize, or non-string entries are
|
|
121
|
+
* dropped, never thrown — a malformed sidecar must not block a resume.
|
|
122
|
+
*/
|
|
123
|
+
export declare function readCheckpointMetadata(metadata: unknown): AgentRunCheckpointMetadata | undefined;
|
|
85
124
|
export declare function saveAgentRunState(input: {
|
|
86
125
|
readonly checkpoints: CheckpointStore;
|
|
87
126
|
readonly state: StoredAgentRunState;
|
|
@@ -90,6 +129,7 @@ export declare function saveAgentRunState(input: {
|
|
|
90
129
|
readonly fencingToken?: number;
|
|
91
130
|
readonly redactor?: SecretRedactor;
|
|
92
131
|
readonly maxStateBytes?: number;
|
|
132
|
+
readonly metadata?: AgentRunCheckpointMetadata;
|
|
93
133
|
}): Promise<{
|
|
94
134
|
readonly record: CheckpointRecord;
|
|
95
135
|
readonly state: StoredAgentRunState;
|
|
@@ -112,3 +152,4 @@ export declare function initialAgentRunState(input: {
|
|
|
112
152
|
readonly interruptBeforeTool?: boolean;
|
|
113
153
|
}): StoredAgentRunState;
|
|
114
154
|
export declare function parseAgentRunState(value: unknown, version?: number): StoredAgentRunState;
|
|
155
|
+
export {};
|
package/dist/agent-run-state.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { parseAttentionStickyFrontier } from "./attention-compiler.js";
|
|
2
|
+
import { parseAttentionStickyFrontier, restoreAttentionFoldLedger, } from "./attention-compiler.js";
|
|
3
3
|
import { AgentLoopStateError, AgentRunStateError } from "./contracts.js";
|
|
4
4
|
import { validateLoadedSkillBodies } from "./skill-load.js";
|
|
5
5
|
import { HARD_RUN_TOOL_NAMES } from "./tools.js";
|
|
@@ -7,6 +7,8 @@ export const AGENT_RUN_STATE_NAMESPACE = "prism.agent-run";
|
|
|
7
7
|
export const AGENT_RUN_STATE_SCHEMA_VERSION = 1;
|
|
8
8
|
export const DEFAULT_MAX_AGENT_RUN_STATE_BYTES = 256 * 1024;
|
|
9
9
|
export const HARD_MAX_AGENT_RUN_STATE_BYTES = 1024 * 1024;
|
|
10
|
+
/** Sidecar metadata ceiling per checkpoint record (not the run-state value). */
|
|
11
|
+
export const MAX_AGENT_RUN_METADATA_BYTES = 4 * 1024;
|
|
10
12
|
const MAX_DEPTH = 32;
|
|
11
13
|
const MAX_PROPERTIES = 256;
|
|
12
14
|
/** Session-state caps (plan 015 Task 4): bounded names charged against the run-state byte budget. */
|
|
@@ -14,6 +16,12 @@ export const MAX_PERSISTED_SKILL_NAMES = 64;
|
|
|
14
16
|
export const MAX_PERSISTED_SKILL_NAME_CHARS = 256;
|
|
15
17
|
/** Plan 041: activated-tool names ride the same budget discipline (cap 128; multiple searches accumulate). */
|
|
16
18
|
export const MAX_PERSISTED_ACTIVATED_TOOL_NAMES = 128;
|
|
19
|
+
/** Plan 104 T2: persisted pack refs and state (cap matches `MAX_GUARDRAIL_PACKS`; ids match the pack cap). */
|
|
20
|
+
const MAX_PERSISTED_GUARDRAIL_PACKS = 8;
|
|
21
|
+
const MAX_PERSISTED_GUARDRAIL_PACK_ID_CHARS = 96;
|
|
22
|
+
const MAX_PERSISTED_GUARDRAIL_PACK_RULES = 64;
|
|
23
|
+
/** Per-pack options/state byte ceiling; the whole session state is still bounded by `maxStateBytes`. */
|
|
24
|
+
const MAX_PERSISTED_GUARDRAIL_PACK_BYTES = 8 * 1024;
|
|
17
25
|
/** Revision stamps of the built-in loops; custom strategies declare their own `revision`. */
|
|
18
26
|
export const BUILT_IN_LOOP_REVISIONS = {
|
|
19
27
|
"single-shot": "1",
|
|
@@ -116,10 +124,55 @@ export async function loadAgentRunState(checkpoints, ref, ownership) {
|
|
|
116
124
|
record.value.sessionId !== ref.sessionId) {
|
|
117
125
|
throw new AgentRunStateError("Agent run session mismatch");
|
|
118
126
|
}
|
|
119
|
-
|
|
127
|
+
const metadata = readCheckpointMetadata(record.metadata);
|
|
128
|
+
return { record, state: parseAgentRunState(record.value, record.version), ...(metadata ? { metadata } : {}) };
|
|
129
|
+
}
|
|
130
|
+
/** Resolve a host metadata source. A throwing provider fails the checkpoint write (fail closed). */
|
|
131
|
+
export function resolveCheckpointMetadata(source) {
|
|
132
|
+
return typeof source === "function" ? source() : source;
|
|
133
|
+
}
|
|
134
|
+
function checkpointMetadataBytes(metadata) {
|
|
135
|
+
return Buffer.byteLength(JSON.stringify(metadata), "utf8");
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Redact + bound a sidecar metadata map for a checkpoint write. Values must be strings;
|
|
139
|
+
* redaction runs first so a replacement marker is still charged against the 4 KiB ceiling.
|
|
140
|
+
*/
|
|
141
|
+
export function boundCheckpointMetadata(metadata, redactor) {
|
|
142
|
+
const redacted = redactor?.redact(metadata) ?? metadata;
|
|
143
|
+
if (!redacted || typeof redacted !== "object" || Array.isArray(redacted)) {
|
|
144
|
+
throw new AgentRunStateError("Checkpoint metadata must be an object");
|
|
145
|
+
}
|
|
146
|
+
const bounded = {};
|
|
147
|
+
for (const [key, value] of Object.entries(redacted)) {
|
|
148
|
+
if (typeof value !== "string")
|
|
149
|
+
throw new AgentRunStateError(`Checkpoint metadata value for ${key} must be a string`);
|
|
150
|
+
bounded[key] = value;
|
|
151
|
+
}
|
|
152
|
+
if (checkpointMetadataBytes(bounded) > MAX_AGENT_RUN_METADATA_BYTES) {
|
|
153
|
+
throw new AgentRunStateError(`Checkpoint metadata exceeds ${MAX_AGENT_RUN_METADATA_BYTES} bytes`);
|
|
154
|
+
}
|
|
155
|
+
return Object.freeze(bounded);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Read-side normalization (legacy tolerance): absent, oversize, or non-string entries are
|
|
159
|
+
* dropped, never thrown — a malformed sidecar must not block a resume.
|
|
160
|
+
*/
|
|
161
|
+
export function readCheckpointMetadata(metadata) {
|
|
162
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
|
|
163
|
+
return undefined;
|
|
164
|
+
const bounded = {};
|
|
165
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
166
|
+
if (typeof value === "string")
|
|
167
|
+
bounded[key] = value;
|
|
168
|
+
}
|
|
169
|
+
if (Object.keys(bounded).length === 0 || checkpointMetadataBytes(bounded) > MAX_AGENT_RUN_METADATA_BYTES)
|
|
170
|
+
return undefined;
|
|
171
|
+
return Object.freeze(bounded);
|
|
120
172
|
}
|
|
121
173
|
export async function saveAgentRunState(input) {
|
|
122
174
|
const bounded = boundState(input.redactor?.redact(input.state) ?? input.state, input.maxStateBytes ?? DEFAULT_MAX_AGENT_RUN_STATE_BYTES);
|
|
175
|
+
const metadata = input.metadata === undefined ? undefined : boundCheckpointMetadata(input.metadata, input.redactor);
|
|
123
176
|
const record = await input.checkpoints.saveCheckpoint({
|
|
124
177
|
namespace: AGENT_RUN_STATE_NAMESPACE,
|
|
125
178
|
key: bounded.runId,
|
|
@@ -128,6 +181,7 @@ export async function saveAgentRunState(input) {
|
|
|
128
181
|
fencingToken: input.fencingToken,
|
|
129
182
|
value: bounded,
|
|
130
183
|
category: "agent-run",
|
|
184
|
+
...(metadata ? { metadata } : {}),
|
|
131
185
|
...input.ownership,
|
|
132
186
|
});
|
|
133
187
|
return { record, state: { ...bounded, version: record.version } };
|
|
@@ -222,7 +276,7 @@ export function parseAgentRunState(value, version) {
|
|
|
222
276
|
if (state.checkpointPolicy !== undefined && state.checkpointPolicy !== "every-turn") {
|
|
223
277
|
throw new AgentRunStateError("Malformed agent run checkpoint policy");
|
|
224
278
|
}
|
|
225
|
-
if (state.stopReason !== undefined && state.stopReason !== "host_policy") {
|
|
279
|
+
if (state.stopReason !== undefined && state.stopReason !== "host_policy" && state.stopReason !== "hook_limit") {
|
|
226
280
|
throw new AgentRunStateError("Malformed agent run stop reason");
|
|
227
281
|
}
|
|
228
282
|
// Load bounds against the hard cap, not the default: the configured maxStateBytes is a
|
|
@@ -294,12 +348,106 @@ function validateSessionState(sessionState) {
|
|
|
294
348
|
}
|
|
295
349
|
}
|
|
296
350
|
const attention = sessionState.attentionSticky;
|
|
297
|
-
if (attention === undefined)
|
|
298
|
-
return;
|
|
299
351
|
// Both arrays are capped by the parser, and a malformed frontier is dropped rather than
|
|
300
352
|
// failing the resume: re-deciding a mutation is safe, refusing to resume is not.
|
|
301
|
-
if (parseAttentionStickyFrontier(attention) === undefined) {
|
|
353
|
+
if (attention !== undefined && parseAttentionStickyFrontier(attention) === undefined) {
|
|
302
354
|
throw new AgentRunStateError("Malformed agent run attention frontier");
|
|
303
355
|
}
|
|
356
|
+
// Plan 086 T3: the fold ledger gets the same treatment — malformed entries are dropped by the
|
|
357
|
+
// parser, a malformed shape fails the load rather than the first provider turn.
|
|
358
|
+
const fold = sessionState.attentionFold;
|
|
359
|
+
if (fold !== undefined && restoreAttentionFoldLedger(fold) === undefined) {
|
|
360
|
+
throw new AgentRunStateError("Malformed agent run attention fold ledger");
|
|
361
|
+
}
|
|
362
|
+
validateGuardrailPackState(sessionState.guardrailPacks);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Plan 104 T2/T3: bounds for the persisted pack block. Rows replay a registered pack by id/version or
|
|
366
|
+
* an inline pack by its pattern rules, and state may only name those rows — anything else fails the
|
|
367
|
+
* load, because a dropped pack silently re-allows what it existed to deny. Rule data is re-validated
|
|
368
|
+
* (pattern compile, id/reason caps) by the compiler that replays it; this checks the JSON envelope.
|
|
369
|
+
*/
|
|
370
|
+
function validateGuardrailPackState(value) {
|
|
371
|
+
if (value === undefined)
|
|
372
|
+
return;
|
|
373
|
+
const raw = value;
|
|
374
|
+
if (!raw || typeof raw !== "object" || !Array.isArray(raw.packs)) {
|
|
375
|
+
throw new AgentRunStateError("Malformed agent run guardrail pack state");
|
|
376
|
+
}
|
|
377
|
+
if (raw.packs.length > MAX_PERSISTED_GUARDRAIL_PACKS) {
|
|
378
|
+
throw new AgentRunStateError(`Persisted guardrail packs exceed ${MAX_PERSISTED_GUARDRAIL_PACKS} entries`);
|
|
379
|
+
}
|
|
380
|
+
const ids = new Set();
|
|
381
|
+
for (const row of raw.packs) {
|
|
382
|
+
if (!isPlainObject(row))
|
|
383
|
+
throw new AgentRunStateError("Malformed agent run guardrail pack row");
|
|
384
|
+
const { id, version, options } = row;
|
|
385
|
+
if (typeof id !== "string" || !id.trim() || id.length > MAX_PERSISTED_GUARDRAIL_PACK_ID_CHARS) {
|
|
386
|
+
throw new AgentRunStateError(`Persisted guardrail pack ids must be non-empty strings of at most ${MAX_PERSISTED_GUARDRAIL_PACK_ID_CHARS} chars`);
|
|
387
|
+
}
|
|
388
|
+
if (ids.has(id))
|
|
389
|
+
throw new AgentRunStateError(`Duplicate persisted guardrail pack id "${id}"`);
|
|
390
|
+
ids.add(id);
|
|
391
|
+
if (!Number.isSafeInteger(version) || version < 1) {
|
|
392
|
+
throw new AgentRunStateError(`Persisted guardrail pack "${id}" version must be a positive integer`);
|
|
393
|
+
}
|
|
394
|
+
if (options !== undefined && !isPlainObject(options)) {
|
|
395
|
+
throw new AgentRunStateError(`Persisted guardrail pack "${id}" options must be an object`);
|
|
396
|
+
}
|
|
397
|
+
if (options !== undefined)
|
|
398
|
+
boundPackBytes(options, `Pack "${id}" options`);
|
|
399
|
+
validatePersistedPackRules(id, row);
|
|
400
|
+
}
|
|
401
|
+
if (raw.state === undefined)
|
|
402
|
+
return;
|
|
403
|
+
if (!isPlainObject(raw.state))
|
|
404
|
+
throw new AgentRunStateError("Malformed agent run guardrail pack state");
|
|
405
|
+
for (const [id, state] of Object.entries(raw.state)) {
|
|
406
|
+
if (!ids.has(id))
|
|
407
|
+
throw new AgentRunStateError(`Persisted guardrail pack state names unknown pack "${id}"`);
|
|
408
|
+
if (!isPlainObject(state))
|
|
409
|
+
throw new AgentRunStateError(`Persisted guardrail pack "${id}" state must be an object`);
|
|
410
|
+
boundPackBytes(state, `Pack "${id}" state`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function isPlainObject(value) {
|
|
414
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
415
|
+
}
|
|
416
|
+
/** Plan 104 T3: a persisted inline pack must be pattern data — a closure or `RegExp` cannot restore. */
|
|
417
|
+
function validatePersistedPackRules(id, row) {
|
|
418
|
+
if (row.rules === undefined)
|
|
419
|
+
return;
|
|
420
|
+
if (!Array.isArray(row.rules))
|
|
421
|
+
throw new AgentRunStateError(`Persisted guardrail pack "${id}" rules must be an array`);
|
|
422
|
+
if (row.rules.length === 0 || row.rules.length > MAX_PERSISTED_GUARDRAIL_PACK_RULES) {
|
|
423
|
+
throw new AgentRunStateError(`Persisted guardrail pack "${id}" rules must number 1..${MAX_PERSISTED_GUARDRAIL_PACK_RULES}`);
|
|
424
|
+
}
|
|
425
|
+
for (const rule of row.rules) {
|
|
426
|
+
if (!isPlainObject(rule))
|
|
427
|
+
throw new AgentRunStateError(`Persisted guardrail pack "${id}" rule must be an object`);
|
|
428
|
+
const { id: ruleId, pattern, deny } = rule;
|
|
429
|
+
if (typeof ruleId !== "string" || !ruleId.trim() || ruleId.length > MAX_PERSISTED_GUARDRAIL_PACK_ID_CHARS) {
|
|
430
|
+
throw new AgentRunStateError(`Persisted guardrail pack "${id}" rule ids must be non-empty strings`);
|
|
431
|
+
}
|
|
432
|
+
if (deny !== undefined)
|
|
433
|
+
throw new AgentRunStateError(`Persisted guardrail pack "${id}" rule "${ruleId}" carries a deny predicate`);
|
|
434
|
+
if (pattern !== undefined && typeof pattern !== "string") {
|
|
435
|
+
throw new AgentRunStateError(`Persisted guardrail pack "${id}" rule "${ruleId}" pattern must be a string`);
|
|
436
|
+
}
|
|
437
|
+
boundPackBytes(rule, `Pack "${id}" rule "${ruleId}"`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/** A pack's state must be JSON and under its per-pack ceiling: refuse, never truncate. */
|
|
441
|
+
function boundPackBytes(value, label) {
|
|
442
|
+
let text;
|
|
443
|
+
try {
|
|
444
|
+
text = JSON.stringify(value);
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
throw new AgentRunStateError(`${label} must be JSON serializable`);
|
|
448
|
+
}
|
|
449
|
+
if ((text ? Buffer.byteLength(text) : 0) > MAX_PERSISTED_GUARDRAIL_PACK_BYTES) {
|
|
450
|
+
throw new AgentRunStateError(`${label} exceeds ${MAX_PERSISTED_GUARDRAIL_PACK_BYTES} bytes`);
|
|
451
|
+
}
|
|
304
452
|
}
|
|
305
453
|
//# sourceMappingURL=agent-run-state.js.map
|
|
@@ -3,6 +3,8 @@ import type { AgentEvent, SubscribeOptions } from "../contracts.js";
|
|
|
3
3
|
export declare class EventSubscriber implements AsyncIterable<AgentEvent>, AsyncIterator<AgentEvent> {
|
|
4
4
|
private readonly sessionId;
|
|
5
5
|
private readonly onClose;
|
|
6
|
+
/** Plan 104 T5: run-scoped unless the host opted into `acrossRuns`. */
|
|
7
|
+
readonly acrossRuns: boolean;
|
|
6
8
|
private readonly queue;
|
|
7
9
|
private readonly waiters;
|
|
8
10
|
private readonly maxQueuedEvents;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export class EventSubscriber {
|
|
2
2
|
sessionId;
|
|
3
3
|
onClose;
|
|
4
|
+
/** Plan 104 T5: run-scoped unless the host opted into `acrossRuns`. */
|
|
5
|
+
acrossRuns;
|
|
4
6
|
queue = [];
|
|
5
7
|
waiters = [];
|
|
6
8
|
maxQueuedEvents;
|
|
@@ -12,6 +14,7 @@ export class EventSubscriber {
|
|
|
12
14
|
const maxQueuedEvents = options.maxQueuedEvents ?? 1024;
|
|
13
15
|
this.maxQueuedEvents = Number.isFinite(maxQueuedEvents) ? Math.max(1, Math.floor(maxQueuedEvents)) : 1024;
|
|
14
16
|
this.overflow = options.overflow ?? "close";
|
|
17
|
+
this.acrossRuns = options.acrossRuns === true;
|
|
15
18
|
}
|
|
16
19
|
[Symbol.asyncIterator]() {
|
|
17
20
|
return this;
|
|
@@ -160,6 +160,8 @@ export function createUsageAccumulator() {
|
|
|
160
160
|
const sums = new Map();
|
|
161
161
|
let costCurrency;
|
|
162
162
|
let costCompatible = true;
|
|
163
|
+
let estimated = false;
|
|
164
|
+
let confidence;
|
|
163
165
|
return {
|
|
164
166
|
add(usage) {
|
|
165
167
|
for (const key of ["inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens"]) {
|
|
@@ -181,6 +183,13 @@ export function createUsageAccumulator() {
|
|
|
181
183
|
if (costCompatible)
|
|
182
184
|
sums.set("cost", (sums.get("cost") ?? 0) + usage.cost);
|
|
183
185
|
}
|
|
186
|
+
// Run totals keep estimate provenance (plan 091 T2): one estimated turn
|
|
187
|
+
// labels the total, and the weakest confidence wins.
|
|
188
|
+
if (usage.estimated === true) {
|
|
189
|
+
estimated = true;
|
|
190
|
+
if (usage.confidence !== undefined && (confidence === undefined || usage.confidence === "low"))
|
|
191
|
+
confidence = usage.confidence;
|
|
192
|
+
}
|
|
184
193
|
},
|
|
185
194
|
value() {
|
|
186
195
|
if (sums.size === 0)
|
|
@@ -192,6 +201,11 @@ export function createUsageAccumulator() {
|
|
|
192
201
|
}
|
|
193
202
|
if (costCompatible && sums.has("cost") && costCurrency !== undefined)
|
|
194
203
|
usage.currency = costCurrency;
|
|
204
|
+
if (estimated) {
|
|
205
|
+
usage.estimated = true;
|
|
206
|
+
if (confidence !== undefined)
|
|
207
|
+
usage.confidence = confidence;
|
|
208
|
+
}
|
|
195
209
|
return Object.keys(usage).length > 0 ? usage : undefined;
|
|
196
210
|
},
|
|
197
211
|
};
|