@arnilo/prism 0.7.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.
Files changed (182) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +12 -11
  3. package/dist/agent-approval.d.ts +15 -2
  4. package/dist/agent-approval.js +5 -1
  5. package/dist/agent-event-source.d.ts +9 -1
  6. package/dist/agent-event-source.js +10 -3
  7. package/dist/agent-loops.js +7 -4
  8. package/dist/agent-run-lifecycle.d.ts +15 -1
  9. package/dist/agent-run-lifecycle.js +91 -10
  10. package/dist/agent-run-state.d.ts +34 -2
  11. package/dist/agent-run-state.js +68 -6
  12. package/dist/agent-session/helpers.js +20 -1
  13. package/dist/agent-session/session/assemble.js +250 -27
  14. package/dist/agent-session/session/persist.d.ts +27 -0
  15. package/dist/agent-session/session/persist.js +94 -12
  16. package/dist/agent-session/session/provider-round.d.ts +14 -4
  17. package/dist/agent-session/session/provider-round.js +197 -25
  18. package/dist/agent-session/session/tool-round.js +24 -2
  19. package/dist/agent-session/session/types.d.ts +36 -2
  20. package/dist/agent-session/session.d.ts +40 -4
  21. package/dist/agent-session/session.js +78 -5
  22. package/dist/attention-compiler.d.ts +51 -2
  23. package/dist/attention-compiler.js +282 -21
  24. package/dist/cache-helpers.d.ts +4 -2
  25. package/dist/cache-helpers.js +8 -6
  26. package/dist/checkpoint-restore.d.ts +45 -0
  27. package/dist/checkpoint-restore.js +54 -0
  28. package/dist/checkpoints.js +7 -11
  29. package/dist/context-budget.d.ts +2 -1
  30. package/dist/context-budget.js +24 -2
  31. package/dist/contracts-core/agent.d.ts +30 -0
  32. package/dist/contracts-core/attention.d.ts +95 -0
  33. package/dist/contracts-core/content.d.ts +15 -0
  34. package/dist/contracts-core/guardrail-packs.d.ts +41 -0
  35. package/dist/contracts-core/guardrail-packs.js +2 -0
  36. package/dist/contracts-core/loop.d.ts +42 -0
  37. package/dist/contracts-core/provider.d.ts +25 -0
  38. package/dist/contracts-core/run-limits.d.ts +21 -0
  39. package/dist/contracts-core/session.d.ts +23 -5
  40. package/dist/contracts-core/session.js +21 -2
  41. package/dist/contracts-core/usage.d.ts +40 -0
  42. package/dist/contracts-core/usage.js +8 -0
  43. package/dist/contracts-core.d.ts +2 -0
  44. package/dist/contracts-core.js +2 -0
  45. package/dist/contracts-protocol.d.ts +90 -4
  46. package/dist/contracts-run-state.d.ts +82 -6
  47. package/dist/evidence-grounding.d.ts +29 -0
  48. package/dist/evidence-grounding.js +162 -0
  49. package/dist/guardrail-packs/coding-standard.d.ts +3 -0
  50. package/dist/guardrail-packs/coding-standard.js +63 -0
  51. package/dist/guardrail-packs/destructive-commands.d.ts +3 -0
  52. package/dist/guardrail-packs/destructive-commands.js +46 -0
  53. package/dist/guardrail-packs/errors.d.ts +7 -0
  54. package/dist/guardrail-packs/errors.js +9 -0
  55. package/dist/guardrail-packs/index.d.ts +4 -0
  56. package/dist/guardrail-packs/index.js +15 -0
  57. package/dist/guardrail-packs/secrets-hygiene.d.ts +3 -0
  58. package/dist/guardrail-packs/secrets-hygiene.js +23 -0
  59. package/dist/guardrail-packs/types.d.ts +16 -0
  60. package/dist/guardrail-packs/types.js +2 -0
  61. package/dist/guardrail-packs/validation-respect.d.ts +3 -0
  62. package/dist/guardrail-packs/validation-respect.js +53 -0
  63. package/dist/guardrails.d.ts +20 -1
  64. package/dist/guardrails.js +268 -0
  65. package/dist/host-composition.d.ts +13 -0
  66. package/dist/host-composition.js +33 -2
  67. package/dist/index.d.ts +19 -10
  68. package/dist/index.js +11 -6
  69. package/dist/input.d.ts +8 -1
  70. package/dist/input.js +68 -6
  71. package/dist/middleware.d.ts +37 -2
  72. package/dist/middleware.js +41 -0
  73. package/dist/node/session-store-jsonl.js +18 -3
  74. package/dist/observability.js +6 -0
  75. package/dist/provider-events.d.ts +11 -3
  76. package/dist/provider-events.js +62 -4
  77. package/dist/providers/openai-compatible.js +6 -3
  78. package/dist/providers/transport.d.ts +3 -1
  79. package/dist/providers/transport.js +36 -0
  80. package/dist/redaction.js +18 -2
  81. package/dist/run-bundle.d.ts +89 -0
  82. package/dist/run-bundle.js +150 -0
  83. package/dist/run-limits.d.ts +11 -1
  84. package/dist/run-limits.js +46 -0
  85. package/dist/session-stores.d.ts +12 -1
  86. package/dist/session-stores.js +21 -4
  87. package/dist/testing/agent-event-source-conformance.js +41 -2
  88. package/dist/testing/prefix-stability-conformance.d.ts +30 -0
  89. package/dist/testing/prefix-stability-conformance.js +104 -0
  90. package/dist/testing/session-store-conformance.d.ts +3 -2
  91. package/dist/testing/session-store-conformance.js +48 -0
  92. package/dist/testing/state-concurrency-conformance.js +5 -12
  93. package/dist/tools.d.ts +5 -0
  94. package/dist/tools.js +11 -3
  95. package/dist/usage-estimation.d.ts +29 -0
  96. package/dist/usage-estimation.js +79 -0
  97. package/docs/ag-ui.md +5 -0
  98. package/docs/agent-events.md +68 -1
  99. package/docs/agent-loops.md +33 -0
  100. package/docs/agent-session-runtime.md +5 -3
  101. package/docs/attention-compiler.md +89 -8
  102. package/docs/coding-agent-tools.md +1 -1
  103. package/docs/coding-security.md +1 -0
  104. package/docs/coding-tools.md +0 -1
  105. package/docs/compaction-and-retry.md +1 -1
  106. package/docs/compaction-observational-memory.md +34 -7
  107. package/docs/connected-apps.md +116 -0
  108. package/docs/context-and-skills.md +13 -0
  109. package/docs/core.md +1 -1
  110. package/docs/diagrams.md +6 -6
  111. package/docs/document-reader.md +9 -9
  112. package/docs/documents.md +32 -11
  113. package/docs/durable-runs.md +129 -0
  114. package/docs/embeddings.md +5 -0
  115. package/docs/enterprise-postgres-state.md +4 -0
  116. package/docs/evaluations.md +5 -0
  117. package/docs/execution-timeline.md +84 -1
  118. package/docs/guardrails.md +71 -2
  119. package/docs/history/079-messaging-primitive-review.md +391 -0
  120. package/docs/history/080-messaging-followon-primitive-review.md +234 -0
  121. package/docs/history/081-connected-apps-primitive-review.md +74 -0
  122. package/docs/history/083-prism-work-primitive-review.md +84 -0
  123. package/docs/history/084-primitive-review.md +96 -0
  124. package/docs/history/085-honesty-and-cut-primitive-review.md +91 -0
  125. package/docs/history/README.md +5 -0
  126. package/docs/history/release-handoffs.md +38 -0
  127. package/docs/host-compositions.md +8 -6
  128. package/docs/host-security.md +2 -2
  129. package/docs/index.md +66 -29
  130. package/docs/input-and-prompt-assembly.md +3 -3
  131. package/docs/knowledge-sync.md +4 -0
  132. package/docs/live-testing.md +5 -3
  133. package/docs/mcp-tools.md +1 -0
  134. package/docs/messaging-channel-operations.md +166 -0
  135. package/docs/messaging-channels.md +150 -0
  136. package/docs/middleware-hooks.md +38 -2
  137. package/docs/migrate-to-0.8.md +124 -0
  138. package/docs/migrate-to-0.9.md +210 -0
  139. package/docs/migration.md +43 -0
  140. package/docs/model-registry.md +12 -2
  141. package/docs/multi-agent-patterns.md +25 -2
  142. package/docs/node-jsonl-session-store.md +7 -1
  143. package/docs/observability.md +7 -3
  144. package/docs/openapi-tools.md +1 -1
  145. package/docs/operations.md +1 -3
  146. package/docs/options-index.md +36 -3
  147. package/docs/peer-dependencies.md +6 -6
  148. package/docs/policy-and-audit.md +13 -1
  149. package/docs/postgres-persistence.md +1 -1
  150. package/docs/prefix-stability-conformance.md +93 -0
  151. package/docs/provider-caching.md +4 -4
  152. package/docs/provider-conformance.md +16 -0
  153. package/docs/provider-layer.md +2 -2
  154. package/docs/provider-packages.md +20 -20
  155. package/docs/providers/neuralwatt.md +5 -1
  156. package/docs/public-contracts.md +2 -2
  157. package/docs/rag.md +102 -4
  158. package/docs/release-and-install.md +55 -47
  159. package/docs/run-bundle.md +92 -0
  160. package/docs/runs-and-usage.md +57 -6
  161. package/docs/scoped-agent-memory.md +262 -0
  162. package/docs/server.md +2 -0
  163. package/docs/session-store-conformance.md +1 -2
  164. package/docs/session-stores.md +17 -17
  165. package/docs/sheets.md +9 -9
  166. package/docs/signal-channel.md +112 -0
  167. package/docs/speech.md +5 -1
  168. package/docs/sqlite-persistence.md +1 -1
  169. package/docs/supervisors.md +32 -12
  170. package/docs/telegram-channel.md +157 -0
  171. package/docs/testing.md +2 -2
  172. package/docs/tools.md +17 -0
  173. package/docs/wiki.md +1 -1
  174. package/docs/work-artifacts-and-review.md +1 -1
  175. package/docs/work-connectors.md +9 -9
  176. package/docs/work-sandbox.md +115 -0
  177. package/docs/work-tools.md +38 -16
  178. package/docs/workflows.md +5 -0
  179. package/package.json +9 -3
  180. package/templates/business-worker/manifest.json +2 -1
  181. package/templates/business-worker/src/agent.ts.tmpl +1 -1
  182. package/templates/business-worker/src/tests/agent.test.ts.tmpl +1 -1
@@ -1,4 +1,6 @@
1
- import type { AgentLoopOptions, AgentLoopStrategy, ArtifactValidation, CompactionOptions, ContentBlock, ErrorInfo, GuardrailRecord, Guardrails, InstructionInjector, JsonObject, Message, ModelConfig, OwnershipScope, ProviderRequestOptions, ProviderRequestPolicy, ProviderResolver, RetryOptions, RunLimitBreach, RunLimits, Skill, SubscriberOverflowPolicy, SystemPromptConfig, ToolCallAuthority, ToolCallContent, Usage } from "./contracts-core.js";
1
+ import type { AgentLoopOptions, AgentLoopStrategy, ArtifactValidation, BudgetAxisUsage, BudgetConsumedCounters, CompactionOptions, ContentBlock, ErrorInfo, GuardrailRecord, Guardrails, InstructionInjector, JsonObject, Message, ModelConfig, OwnershipScope, ProviderRequestOptions, ProviderRequestPolicy, ProviderResolver, ProviderStopReason, RetryOptions, RunLimitBreach, RunLimitName, RunLimits, Skill, SubscriberOverflowPolicy, SystemPromptConfig, ToolCallAuthority, ToolCallContent, ToolCallSummary, TurnBudgets, TurnPolicyOptions, Usage } from "./contracts-core.js";
2
+ import type { CacheUsageReport } from "./cache-helpers.js";
3
+ import type { CheckpointRestoreAudit } from "./checkpoint-restore.js";
2
4
  import type { AgentRunInterruption, AgentRunStateOptions } from "./contracts-run-state.js";
3
5
  import type { SecretRedactor } from "./redaction.js";
4
6
  import type { ToolValidator } from "./tools.js";
@@ -28,6 +30,7 @@ export type ProviderEvent = {
28
30
  } | {
29
31
  readonly type: "done";
30
32
  readonly usage?: Usage;
33
+ readonly stopReason?: ProviderStopReason;
31
34
  } | {
32
35
  readonly type: "error";
33
36
  readonly error: ErrorInfo;
@@ -108,6 +111,10 @@ export interface RunOptions {
108
111
  readonly toolNames?: readonly string[];
109
112
  /** Tools disclosure: "all" (default) sends every active tool schema; "search" sends top-k + the generated `search_tools` tool. */
110
113
  readonly toolsDisclosure?: import("./tool-search.js").ToolsDisclosure;
114
+ /** Per-turn restrictive allow-list over the run grant. Overrides `AgentConfig.toolNarrowing`. */
115
+ readonly toolNarrowing?: import("./contracts-core/agent.js").ToolNarrowing;
116
+ /** Opt-in: tools hidden this turn stay callable by name (default off). Overrides agent config. */
117
+ readonly allowHiddenToolCalls?: true;
111
118
  readonly toolsSearch?: import("./tool-search.js").ToolsSearchOptions;
112
119
  /** Opt-in projection-only fold for aged large tool results in provider view; store untouched. */
113
120
  readonly toolResultFold?: import("./tool-result-fold.js").ToolResultFoldOptions;
@@ -125,6 +132,11 @@ export interface RunOptions {
125
132
  readonly runState?: AgentRunStateOptions;
126
133
  /** Prompt provenance: copied verbatim onto this run's start and finish ledger records. */
127
134
  readonly promptVersion?: PromptVersionRef;
135
+ /**
136
+ * Host turn policy (plan 084 Task 2): a clean turn cap and/or a synchronous stop callback
137
+ * evaluated before every provider request. Omitted → no callback runs.
138
+ */
139
+ readonly turnPolicy?: TurnPolicyOptions;
128
140
  }
129
141
  export interface ProviderTurnMetadata {
130
142
  readonly providerId: string;
@@ -135,6 +147,17 @@ export interface ProviderTurnMetadata {
135
147
  readonly httpStatus?: number;
136
148
  readonly rateLimitRemaining?: number;
137
149
  readonly rateLimitResetMs?: number;
150
+ /** Why the provider turn stopped (plan 087 T1); present on `provider_turn_finished` only. */
151
+ readonly stopReason?: ProviderStopReason;
152
+ /** Effective budget state at turn end (plan 087 T1); present on `provider_turn_finished` only. */
153
+ readonly budgets?: TurnBudgets;
154
+ /** Provider-reported cache usage and derived hit rate; absent when cache usage is unknown. */
155
+ readonly cache?: CacheUsageReport;
156
+ /** Effective tool menu this turn. Names hashed in request order; never includes args. */
157
+ readonly tools?: {
158
+ readonly count: number;
159
+ readonly idsHash: string;
160
+ };
138
161
  }
139
162
  export interface ToolExecutionMetadata {
140
163
  readonly durationMs: number;
@@ -170,8 +193,20 @@ export interface DelegatedAgentStep {
170
193
  readonly label?: string;
171
194
  };
172
195
  }
173
- export type AgentFinishReason = "turn_limit" | "token_limit" | "refusal";
174
- export type AgentEvent = {
196
+ /** Why a run stopped cleanly. `host_policy` is a `RunOptions.turnPolicy` stop; the rest are loop ceilings (F4). */
197
+ export type AgentFinishReason = "turn_limit" | "token_limit" | "refusal" | "host_policy";
198
+ /**
199
+ * Origin of an agent event forwarded from a delegated child (supervisor child-event passthrough).
200
+ * Present only on child events routed onto a parent stream; absent on a session's own events.
201
+ */
202
+ export interface ChildEventOrigin {
203
+ readonly childId: string;
204
+ readonly delegationId: string;
205
+ /** Delegation depth: 1 is a direct child of the hosting supervisor. */
206
+ readonly depth: number;
207
+ }
208
+ /** Payload union of every agent event; the exported `AgentEvent` adds the optional child origin tag. */
209
+ type AgentEventPayload = {
175
210
  readonly type: "agent_started";
176
211
  readonly sessionId: string;
177
212
  readonly runId: string;
@@ -180,8 +215,10 @@ export type AgentEvent = {
180
215
  readonly sessionId: string;
181
216
  readonly runId: string;
182
217
  readonly usage?: Usage;
183
- /** Why the loop stopped, when a limit/ceiling ended the run cleanly (F4). Absent = natural end. */
218
+ /** Why the loop stopped, when a limit/ceiling or a host turn policy ended the run cleanly (F4). Absent = natural end. */
184
219
  readonly finishReason?: AgentFinishReason;
220
+ /** Host stop detail from `TurnPolicyOptions.stop` (≤256 bytes, redacted). Present only with `finishReason: "host_policy"`. */
221
+ readonly stopDetail?: string;
185
222
  } | {
186
223
  readonly type: "agent_suspended";
187
224
  readonly sessionId: string;
@@ -193,6 +230,8 @@ export type AgentEvent = {
193
230
  readonly sessionId: string;
194
231
  readonly runId: string;
195
232
  readonly version: number;
233
+ /** Plan 094 Task 3: audit of the external-state restore hooks that ran before this claim. */
234
+ readonly restore?: CheckpointRestoreAudit;
196
235
  } | {
197
236
  readonly type: "agent_denied";
198
237
  readonly sessionId: string;
@@ -209,6 +248,17 @@ export type AgentEvent = {
209
248
  readonly sessionId: string;
210
249
  readonly runId: string;
211
250
  readonly turn: number;
251
+ } | {
252
+ /**
253
+ * Host middleware completed this turn without a provider request (plan 096). No `usage` field:
254
+ * a deterministic turn has no provider cost, so accounting must never zero-fill one.
255
+ */
256
+ readonly type: "deterministic_turn";
257
+ readonly sessionId: string;
258
+ readonly runId: string;
259
+ readonly turn: number;
260
+ /** Answering middleware id (provenance); ids only, never free host code. */
261
+ readonly middleware: string;
212
262
  } | {
213
263
  readonly type: "provider_turn_started";
214
264
  readonly sessionId: string;
@@ -273,6 +323,13 @@ export type AgentEvent = {
273
323
  readonly reason: string;
274
324
  readonly error: ErrorInfo;
275
325
  readonly metadata: ToolExecutionMetadata;
326
+ } | {
327
+ /** Host `toolNarrowing` asked for names outside the run grant; those names were dropped. */
328
+ readonly type: "tool_narrowing_clamped";
329
+ readonly sessionId: string;
330
+ readonly runId: string;
331
+ readonly turn: number;
332
+ readonly dropped: readonly string[];
276
333
  } | {
277
334
  readonly type: "guardrail_decision";
278
335
  readonly sessionId: string;
@@ -285,6 +342,16 @@ export type AgentEvent = {
285
342
  readonly sessionId: string;
286
343
  readonly runId: string;
287
344
  readonly breach: RunLimitBreach;
345
+ } | {
346
+ /** Terminal attribution for a run that died on a run limit (plan 087 T2): which axis fired,
347
+ * counters at exhaustion, how close the other axes were, and hashes of recent tool calls. */
348
+ readonly type: "budget_exhausted";
349
+ readonly sessionId: string;
350
+ readonly runId: string;
351
+ readonly limit: RunLimitName;
352
+ readonly consumed: BudgetConsumedCounters;
353
+ readonly closestOtherAxes: readonly BudgetAxisUsage[];
354
+ readonly recentToolCalls: readonly ToolCallSummary[];
288
355
  } | {
289
356
  readonly type: "queue_updated";
290
357
  readonly sessionId: string;
@@ -375,6 +442,14 @@ export type AgentEvent = {
375
442
  readonly attempt: number;
376
443
  readonly result: ArtifactValidation;
377
444
  };
445
+ /**
446
+ * One agent event. `child` is set only when the event was forwarded from a delegated child
447
+ * (e.g. supervisor `report: "stream"` passthrough), so hosts can route it onto a parent stream
448
+ * without per-event-type special cases. It never replaces the event's own `sessionId`/`runId`.
449
+ */
450
+ export type AgentEvent = AgentEventPayload & {
451
+ readonly child?: ChildEventOrigin;
452
+ };
378
453
  export type ToolEffectKind = "none" | "local_mutation" | "external_mutation";
379
454
  export type ToolEffectIdempotency = "none" | "optional" | "required" | "tool_managed" | "unsupported";
380
455
  /** Static or validated-argument classification of one tool call's side-effect behavior. */
@@ -538,6 +613,10 @@ export interface RunRecord extends OwnershipScope {
538
613
  readonly startedAt: string;
539
614
  readonly finishedAt?: string;
540
615
  readonly abortReason?: string;
616
+ /** Present when the loop stopped on a ceiling or host policy instead of a natural end. */
617
+ readonly stopReason?: AgentFinishReason;
618
+ /** Host stop detail from `TurnPolicyOptions.stop` (≤256 bytes, redacted). */
619
+ readonly stopDetail?: string;
541
620
  readonly error?: ErrorInfo;
542
621
  readonly metadata?: Readonly<Record<string, unknown>>;
543
622
  /** Provenance ref copied from `RunOptions.promptVersion` when the host supplied one. */
@@ -676,4 +755,11 @@ export interface ProviderTurnResult {
676
755
  readonly messageId?: string;
677
756
  readonly started: boolean;
678
757
  readonly usage?: Usage;
758
+ /**
759
+ * Provenance for turns that did not come from the provider (plan 096):
760
+ * `{ deterministic: { middleware } }`. Copied onto the assistant `Message.metadata`, so it
761
+ * serializes with the transcript and survives replay. Absent for provider turns.
762
+ */
763
+ readonly metadata?: Readonly<Record<string, unknown>>;
679
764
  }
765
+ export {};
@@ -1,5 +1,6 @@
1
- import type { AgentSessionCloneOptions, AgentSessionForkOptions, CheckpointStore, CompactionOptions, CompactionResult, ContentBlock, ErrorInfo, JsonObject, JsonValue, Message, ModelConfig, OwnershipScope, RunLimitBreach, SessionEntry, SubscribeOptions, ToolCallContent, Usage } from "./contracts-core.js";
2
- import type { AgentEvent, RunOptions, ToolEffectKind } from "./contracts-protocol.js";
1
+ import type { AgentSessionCloneOptions, AgentSessionForkOptions, CheckpointRecord, CheckpointStore, CompactionOptions, CompactionResult, ContentBlock, ContextMeter, ErrorInfo, JsonObject, JsonValue, Message, ModelConfig, OwnershipScope, RunLimitBreach, SessionEntry, SubscribeOptions, ToolCallContent, Usage } from "./contracts-core.js";
2
+ import type { AgentEvent, AgentFinishReason, RunOptions, ToolEffectKind } from "./contracts-protocol.js";
3
+ import type { CheckpointRestoreHook } from "./checkpoint-restore.js";
3
4
  export type AgentRunStatus = "succeeded" | "failed" | "aborted" | "suspended" | "denied";
4
5
  export type AgentRunInterruptionKind = "input_guardrail" | "tool_approval" | "elicitation";
5
6
  export type ApprovalOutcome = "allow_once" | "allow_for_run" | "reject_once" | "reject_for_run";
@@ -139,12 +140,38 @@ export declare const HARD_MAX_ACTION_CONSTRAINTS = 64;
139
140
  export declare const MAX_ATTRIBUTION_DEPTH = 8;
140
141
  export declare const MAX_ACTION_CONSTRAINT_BYTES: number;
141
142
  export declare const HARD_MAX_ACTION_CONSTRAINT_BYTES: number;
143
+ /**
144
+ * Opaque host sidecar pinned to one checkpoint *record* (git commit, document version,
145
+ * workspace fingerprint) — never part of the run-state value, so it costs no `maxStateBytes`
146
+ * budget and is invisible to state parsing. Bounded to `MAX_AGENT_RUN_METADATA_BYTES` (4 KiB)
147
+ * and redacted like the state value at every write.
148
+ */
149
+ export type AgentRunCheckpointMetadata = Readonly<Record<string, string>>;
150
+ /** Host source for checkpoint sidecar metadata: a fixed map or a live provider resolved per write. */
151
+ export type AgentRunCheckpointMetadataSource = AgentRunCheckpointMetadata | (() => AgentRunCheckpointMetadata | undefined);
142
152
  export interface AgentRunStateOptions {
143
153
  readonly checkpoints: CheckpointStore;
144
154
  /** Host-authored immutable revision required for durable runs. */
145
155
  readonly definitionRevision: string;
156
+ /**
157
+ * Durable checkpoint cadence (plan 084 Task 1). `"decision"` (default) persists only on
158
+ * suspension and terminal status. `"every-turn"` additionally persists a running-state
159
+ * checkpoint at each provider-turn boundary — after the previous turn's tool results are in
160
+ * the session store, before the next provider request — so a host process that dies mid-run
161
+ * can `resumeAgentRun(..., { decision: "continue" })` from the last turn instead of re-running
162
+ * the investigation. Costs one bounded, redacted checkpoint write per provider turn; the
163
+ * policy is recorded in the checkpoint, so a later resume keeps checkpointing without the host
164
+ * repeating the option.
165
+ */
166
+ readonly checkpointPolicy?: "decision" | "every-turn";
146
167
  /** Suspend every tool call before its side effect. */
147
168
  readonly interruptBeforeTool?: boolean;
169
+ /**
170
+ * Sidecar metadata written with every checkpoint of this run (and carried into a resumed
171
+ * run). A provider is resolved at each checkpoint write, so a host closure can pin state
172
+ * that moves mid-run (e.g. the current git commit). Absent = records stay byte-identical.
173
+ */
174
+ readonly checkpointMetadata?: AgentRunCheckpointMetadataSource;
148
175
  readonly maxStateBytes?: number;
149
176
  readonly fencingToken?: number;
150
177
  /** Enables sticky auto-apply when a nested suspension first surfaces during this run. */
@@ -183,17 +210,41 @@ export interface AgentRunState {
183
210
  }
184
211
  export interface AgentRunResume {
185
212
  readonly expectedVersion: number;
186
- /** Legacy single-approval path; `approve` allows all pending once, `deny` terminates the run denied. */
187
- readonly decision?: "approve" | "deny";
213
+ /**
214
+ * Legacy decision path. `approve` allows all pending decisions once; `deny` terminates the run
215
+ * as `denied`; `continue` resumes a running-state `"every-turn"` checkpoint that has no pending
216
+ * decisions (crash recovery). A suspended run still requires `approve`/`deny` or a decision
217
+ * batch — `continue` never bypasses an approval gate and is a host-API-only action.
218
+ */
219
+ readonly decision?: "approve" | "deny" | "continue";
188
220
  /** Batch decision path; exactly one of decision/decisions. Applied as one atomic CAS transition. */
189
221
  readonly decisions?: readonly RunDecision[];
190
222
  }
223
+ /**
224
+ * Checkpoint handed to a restore hook (plan 094 Task 3). `checkpoint.value` is the raw stored
225
+ * run-state value; `metadata` is the redacted, bounded sidecar map hosts write via
226
+ * `AgentRunStateOptions.checkpointMetadata`.
227
+ */
228
+ export interface AgentCheckpointRestoreContext {
229
+ readonly runId: string;
230
+ readonly sessionId: string;
231
+ /** Version of the checkpoint being claimed; a hook may pass it to an external system's own CAS. */
232
+ readonly version: number;
233
+ /** State being claimed: `running` for crash recovery, `suspended` for a decision resume. */
234
+ readonly status: AgentRunStatus | "running";
235
+ readonly metadata?: AgentRunCheckpointMetadata;
236
+ readonly checkpoint: CheckpointRecord;
237
+ }
238
+ /** Host code restoring one external layer before a durable resume applies. */
239
+ export type AgentCheckpointRestoreHook = CheckpointRestoreHook<AgentCheckpointRestoreContext>;
191
240
  export interface AgentRunResumeOptions {
192
241
  readonly checkpoints: CheckpointStore;
193
242
  /** Current host-authored revision; must exactly match the checkpoint. */
194
243
  readonly definitionRevision: string;
195
244
  readonly ownership?: OwnershipScope;
196
245
  readonly fencingToken?: number;
246
+ /** Host abort for the resume: checked between steps and threaded into the resumed provider/tool turn. */
247
+ readonly signal?: AbortSignal;
197
248
  /** Routes root decisions for nested-run approvals back to the child (e.g. supervisor). */
198
249
  readonly resumeNestedRun?: ResumeNestedRun;
199
250
  /**
@@ -206,10 +257,23 @@ export interface AgentRunResumeOptions {
206
257
  readonly persistSessionState?: boolean;
207
258
  /** Opt-in (plan 018 Task 6): restore persisted loaded-skill bodies (requires `persistSessionState` too). */
208
259
  readonly includeSkillBodies?: boolean;
260
+ /**
261
+ * Checkpoint sidecar metadata for the claim write (and the resumed run's later checkpoints).
262
+ * Absent = the record's existing metadata is preserved unchanged.
263
+ */
264
+ readonly checkpointMetadata?: AgentRunCheckpointMetadataSource;
265
+ /**
266
+ * Plan 094 Task 3: external-state restore hooks. Every hook must succeed (sequentially, each
267
+ * within `restoreHookTimeoutMs`) before the claim write and the conversation restore apply;
268
+ * the first failure throws `CheckpointRestoreError` naming the hook and leaves the checkpoint
269
+ * suspended. Hosts that register hooks on the lifecycle instead pass them once there.
270
+ */
271
+ readonly restoreHooks?: readonly AgentCheckpointRestoreHook[];
272
+ /** Per-hook restore ceiling in ms; defaults to `DEFAULT_CHECKPOINT_RESTORE_TIMEOUT_MS`. */
273
+ readonly restoreHookTimeoutMs?: number;
209
274
  }
210
- /** Bounded, abortable options for `resumeAgentRunStream()`. */
275
+ /** Bounded live-event options for `resumeAgentRunStream()`; `signal` is inherited from the base resume options. */
211
276
  export interface AgentRunResumeStreamOptions extends AgentRunResumeOptions, SubscribeOptions {
212
- readonly signal?: AbortSignal;
213
277
  }
214
278
  export interface AgentRunRef {
215
279
  readonly runId: string;
@@ -218,6 +282,8 @@ export interface AgentRunRef {
218
282
  export interface AgentRunStatusResult {
219
283
  readonly state: AgentRunState;
220
284
  readonly version: number;
285
+ /** Checkpoint sidecar metadata; absent when the record carries none (or carries only malformed entries). */
286
+ readonly metadata?: AgentRunCheckpointMetadata;
221
287
  }
222
288
  export declare class AgentRunStateError extends Error {
223
289
  readonly code = "ERR_PRISM_AGENT_RUN_STATE";
@@ -251,6 +317,10 @@ export interface AgentRunResult {
251
317
  readonly error?: ErrorInfo;
252
318
  /** String form of the abort reason when `status` is `"aborted"`. */
253
319
  readonly abortReason?: string;
320
+ /** Present when the loop stopped on a ceiling or host turn policy instead of a natural end. */
321
+ readonly stopReason?: AgentFinishReason;
322
+ /** Host stop detail from `TurnPolicyOptions.stop` (≤256 bytes, redacted). */
323
+ readonly stopDetail?: string;
254
324
  /** Present for durable suspended/terminal runs. Payload is redacted and bounded. */
255
325
  readonly runState?: AgentRunState;
256
326
  /** Present only while awaiting an operator decision. */
@@ -298,6 +368,12 @@ export interface AgentSession {
298
368
  abort(reason?: unknown): void;
299
369
  entries(): Promise<readonly SessionEntry[]>;
300
370
  checkout(leafId?: string): Promise<void>;
371
+ /**
372
+ * Context-fill read (plan 091 T2): latest provider turn's input tokens
373
+ * (reported or labeled estimate) plus the resolved per-request cap, run input
374
+ * budget, and used ratio. Before any provider turn it estimates stored history.
375
+ */
376
+ contextMeter(): ContextMeter;
301
377
  fork(options?: AgentSessionForkOptions): AgentSession;
302
378
  clone(options?: AgentSessionCloneOptions): Promise<AgentSession>;
303
379
  }
@@ -0,0 +1,29 @@
1
+ import type { Guardrail, ToolResult } from "./contracts.js";
2
+ export interface ClaimGroundingEvidence {
3
+ readonly value: number;
4
+ /** Refer to this governed figure as `[evidence:<ref>]` immediately after a claim. */
5
+ readonly ref?: string;
6
+ }
7
+ export interface ClaimGroundingEvidenceExtractorContext {
8
+ readonly sessionId: string;
9
+ readonly runId: string;
10
+ readonly metadata: Readonly<Record<string, unknown>>;
11
+ readonly toolResults: readonly ToolResult[];
12
+ }
13
+ /** Supplies host-governed figures without coupling this primitive to a host store or package. */
14
+ export type ClaimGroundingEvidenceExtractor = (context: ClaimGroundingEvidenceExtractorContext) => readonly ClaimGroundingEvidence[];
15
+ export interface ClaimGroundingGuardrailOptions {
16
+ /** False makes the returned guardrail a no-op. */
17
+ readonly requireEvidenceForNumbers: boolean;
18
+ /** Defaults to same-run tool results. A host extractor may return its own governed figures. */
19
+ readonly evidenceSources?: "tool_results" | ClaimGroundingEvidenceExtractor;
20
+ /** Defaults to block. Flag emits an allow record with `metadata.violation: true`. */
21
+ readonly onViolation?: "block" | "flag";
22
+ /** Exact numeric equality by default; rounded accepts half the final printed unit. */
23
+ readonly tolerance?: "exact" | "rounded";
24
+ }
25
+ /**
26
+ * Deterministic output guardrail for figures that must be grounded in same-run tool results
27
+ * or host-governed evidence. It never calls a provider, store, or extractor asynchronously.
28
+ */
29
+ export declare function createClaimGroundingGuardrail(options: ClaimGroundingGuardrailOptions): Guardrail<"output">;
@@ -0,0 +1,162 @@
1
+ const MAX_EVIDENCE_FIGURES = 4096;
2
+ const MAX_EVIDENCE_DEPTH = 16;
3
+ const MAX_EVIDENCE_NODES = 16 * 1024;
4
+ const MAX_EVIDENCE_TEXT_CHARS = 128 * 1024;
5
+ const MAX_CLAIM_CHARS = 128;
6
+ const MAX_CITATION_DISTANCE = 96;
7
+ const EVIDENCE_CITATION = /\[evidence:([a-zA-Z0-9._:-]{1,128})\]/g;
8
+ const NUMERIC_CLAIM = /(?<![\p{L}\p{N}_])~?[$€£¥]?[-+]?(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?[kKmMbB]?%?(?![\p{L}\p{N}_])/gu;
9
+ /**
10
+ * Deterministic output guardrail for figures that must be grounded in same-run tool results
11
+ * or host-governed evidence. It never calls a provider, store, or extractor asynchronously.
12
+ */
13
+ export function createClaimGroundingGuardrail(options) {
14
+ const resolved = resolveOptions(options);
15
+ return {
16
+ name: "claim-grounding",
17
+ stage: "output",
18
+ revision: "1",
19
+ evaluate(context) {
20
+ if (!resolved.requireEvidenceForNumbers)
21
+ return { action: "allow" };
22
+ const figures = evidenceFigures(resolved.evidenceSources, context);
23
+ const refs = new Set(figures.flatMap((figure) => (figure.ref === undefined ? [] : [figure.ref])));
24
+ for (const claim of outputClaims(context.value.content)) {
25
+ const block = context.value.content[claim.contentIndex];
26
+ if (matchesEvidence(claim, figures, resolved.tolerance) || (block?.type === "text" && citesEvidence(block.text, claim.end, refs)))
27
+ continue;
28
+ const metadata = {
29
+ violation: true,
30
+ claim: claim.text.slice(0, MAX_CLAIM_CHARS),
31
+ contentIndex: claim.contentIndex,
32
+ start: claim.end - claim.text.length,
33
+ end: claim.end,
34
+ };
35
+ return resolved.onViolation === "block"
36
+ ? { action: "block", reason: "claim_ungrounded", metadata }
37
+ : { action: "allow", reason: "claim_ungrounded", metadata };
38
+ }
39
+ return { action: "allow" };
40
+ },
41
+ };
42
+ }
43
+ function resolveOptions(options) {
44
+ if (!options || typeof options.requireEvidenceForNumbers !== "boolean")
45
+ throw new TypeError("Claim grounding requireEvidenceForNumbers must be boolean");
46
+ const evidenceSources = options.evidenceSources ?? "tool_results";
47
+ if (evidenceSources !== "tool_results" && typeof evidenceSources !== "function")
48
+ throw new TypeError('Claim grounding evidenceSources must be "tool_results" or an extractor');
49
+ const onViolation = options.onViolation ?? "block";
50
+ if (onViolation !== "block" && onViolation !== "flag")
51
+ throw new TypeError('Claim grounding onViolation must be "block" or "flag"');
52
+ const tolerance = options.tolerance ?? "exact";
53
+ if (tolerance !== "exact" && tolerance !== "rounded")
54
+ throw new TypeError('Claim grounding tolerance must be "exact" or "rounded"');
55
+ return { requireEvidenceForNumbers: options.requireEvidenceForNumbers, evidenceSources, onViolation, tolerance };
56
+ }
57
+ function evidenceFigures(source, context) {
58
+ if (source !== "tool_results")
59
+ return source({
60
+ sessionId: context.sessionId,
61
+ runId: context.runId,
62
+ metadata: context.metadata,
63
+ toolResults: context.toolResults ?? [],
64
+ })
65
+ .filter(validFigure)
66
+ .slice(0, MAX_EVIDENCE_FIGURES);
67
+ const figures = [];
68
+ const budget = { chars: MAX_EVIDENCE_TEXT_CHARS, nodes: MAX_EVIDENCE_NODES };
69
+ for (const result of context.toolResults ?? []) {
70
+ if (result.error)
71
+ continue;
72
+ const ref = `tool:${result.toolCallId}`;
73
+ collectFigures(result.value, figures, ref, new WeakSet(), 0, budget);
74
+ for (const block of result.content ?? [])
75
+ if (block.type === "text")
76
+ collectTextFigures(block.text, figures, ref, budget);
77
+ }
78
+ return figures;
79
+ }
80
+ function validFigure(value) {
81
+ return typeof value?.value === "number" && Number.isFinite(value.value) && (value.ref === undefined || citationRef(value.ref));
82
+ }
83
+ function collectFigures(value, figures, ref, seen = new WeakSet(), depth = 0, budget = { chars: MAX_EVIDENCE_TEXT_CHARS, nodes: MAX_EVIDENCE_NODES }) {
84
+ if (figures.length >= MAX_EVIDENCE_FIGURES || depth > MAX_EVIDENCE_DEPTH || budget.nodes-- < 1)
85
+ return;
86
+ if (typeof value === "number") {
87
+ if (Number.isFinite(value))
88
+ figures.push({ value, ref });
89
+ return;
90
+ }
91
+ if (typeof value === "string") {
92
+ collectTextFigures(value, figures, ref, budget);
93
+ return;
94
+ }
95
+ if (!value || typeof value !== "object" || seen.has(value))
96
+ return;
97
+ seen.add(value);
98
+ if (Array.isArray(value)) {
99
+ for (const item of value)
100
+ collectFigures(item, figures, ref, seen, depth + 1, budget);
101
+ return;
102
+ }
103
+ for (const item of Object.values(value))
104
+ collectFigures(item, figures, ref, seen, depth + 1, budget);
105
+ }
106
+ function collectTextFigures(text, figures, ref, budget) {
107
+ const bounded = text.slice(0, budget.chars);
108
+ budget.chars -= bounded.length;
109
+ for (const claim of claims(bounded, 0)) {
110
+ if (figures.length >= MAX_EVIDENCE_FIGURES)
111
+ return;
112
+ figures.push({ value: claim.value, ref });
113
+ }
114
+ }
115
+ function outputClaims(content) {
116
+ const out = [];
117
+ for (let index = 0; index < content.length; index += 1) {
118
+ const block = content[index];
119
+ if (block?.type !== "text")
120
+ continue;
121
+ out.push(...claims(block.text, index));
122
+ }
123
+ return out;
124
+ }
125
+ function claims(text, contentIndex) {
126
+ const out = [];
127
+ NUMERIC_CLAIM.lastIndex = 0;
128
+ for (let match = NUMERIC_CLAIM.exec(text); match; match = NUMERIC_CLAIM.exec(text)) {
129
+ const token = match[0];
130
+ const parsed = parseNumber(token);
131
+ if (parsed === undefined)
132
+ continue;
133
+ out.push({ text: token, value: parsed.value, tolerance: parsed.tolerance, end: match.index + token.length, contentIndex });
134
+ }
135
+ return out;
136
+ }
137
+ function parseNumber(token) {
138
+ const compact = token.replace(/^~?[$€£¥]?[-+]?/, "").replace(/%$/, "");
139
+ const suffix = compact.at(-1)?.toLowerCase();
140
+ const multiplier = suffix === "k" ? 1_000 : suffix === "m" ? 1_000_000 : suffix === "b" ? 1_000_000_000 : 1;
141
+ const numberText = (multiplier === 1 ? compact : compact.slice(0, -1)).replace(/,/g, "");
142
+ const value = Number(numberText) * multiplier;
143
+ if (!Number.isFinite(value))
144
+ return undefined;
145
+ const fraction = numberText.split(".")[1]?.length ?? 0;
146
+ return { value, tolerance: 0.5 * multiplier * 10 ** -fraction };
147
+ }
148
+ function matchesEvidence(claim, figures, tolerance) {
149
+ return figures.some((figure) => Math.abs(figure.value - claim.value) <= (tolerance === "rounded" ? claim.tolerance : 0));
150
+ }
151
+ function citesEvidence(text, end, refs) {
152
+ const nearby = text.slice(end, end + MAX_CITATION_DISTANCE);
153
+ EVIDENCE_CITATION.lastIndex = 0;
154
+ for (let match = EVIDENCE_CITATION.exec(nearby); match; match = EVIDENCE_CITATION.exec(nearby))
155
+ if (refs.has(match[1] ?? ""))
156
+ return true;
157
+ return false;
158
+ }
159
+ function citationRef(value) {
160
+ return /^[a-zA-Z0-9._:-]{1,128}$/.test(value);
161
+ }
162
+ //# sourceMappingURL=evidence-grounding.js.map
@@ -0,0 +1,3 @@
1
+ import type { GuardrailPackDefinition } from "./types.js";
2
+ /** Canned coding hygiene: file mutations confined to configured roots, test files read-only. */
3
+ export declare const codingStandardPack: GuardrailPackDefinition;
@@ -0,0 +1,63 @@
1
+ import { resolve as resolvePath, sep } from "node:path";
2
+ import { GuardrailPackError } from "./errors.js";
3
+ /** File-mutating prism coding tool names (plan 092 Task 1: `shell`/`read` are not mutations). */
4
+ const MUTATING_TOOLS = ["write", "edit", "delete", "move"];
5
+ const PATH_ARGS = ["path", "paths", "from", "to"];
6
+ const TEST_FILE_PATTERN = /(^|[\\/])(?:__tests__|tests?|specs?)[\\/]|\.(?:test|spec)\.[cm]?[jt]sx?$/;
7
+ const MAX_ROOTS = 16;
8
+ function pathStrings(args) {
9
+ const paths = [];
10
+ for (const key of PATH_ARGS) {
11
+ const value = args[key];
12
+ if (typeof value === "string")
13
+ paths.push(value);
14
+ else if (Array.isArray(value))
15
+ paths.push(...value.filter((item) => typeof item === "string"));
16
+ }
17
+ return paths;
18
+ }
19
+ function readRoots(value, cwd) {
20
+ if (value === undefined)
21
+ return [cwd];
22
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_ROOTS) {
23
+ throw new GuardrailPackError(`coding-standard options.roots must be a non-empty string array (max ${MAX_ROOTS})`);
24
+ }
25
+ return value.map((root) => {
26
+ if (typeof root !== "string" || !root.trim())
27
+ throw new GuardrailPackError("coding-standard options.roots entries must be non-empty strings");
28
+ return resolvePath(cwd, root);
29
+ });
30
+ }
31
+ /** Lazy containment: no symlink resolution (a link inside a root can still point out); execution policy/sandbox remains the hard boundary. */
32
+ function outsideRoots(candidate, roots, cwd) {
33
+ const resolved = resolvePath(cwd, candidate);
34
+ return !roots.some((root) => resolved === root || resolved.startsWith(root.endsWith(sep) ? root : `${root}${sep}`));
35
+ }
36
+ /** Canned coding hygiene: file mutations confined to configured roots, test files read-only. */
37
+ export const codingStandardPack = {
38
+ id: "coding-standard",
39
+ version: 1,
40
+ description: "Restricts file mutation to configured workspace roots and blocks test-file rewrites.",
41
+ build(options) {
42
+ const cwd = resolvePath(typeof options.cwd === "string" ? options.cwd : process.cwd());
43
+ const roots = readRoots(options.roots, cwd);
44
+ return {
45
+ rules: [
46
+ {
47
+ id: "no-unrelated-file-edits",
48
+ tool: MUTATING_TOOLS,
49
+ reason: "File edits are restricted to the configured workspace roots",
50
+ deny: (args) => pathStrings(args).some((candidate) => outsideRoots(candidate, roots, cwd)),
51
+ },
52
+ {
53
+ id: "no-test-rewrites",
54
+ tool: MUTATING_TOOLS,
55
+ pattern: TEST_FILE_PATTERN,
56
+ argPath: PATH_ARGS,
57
+ reason: "Test files are read-only under this pack",
58
+ },
59
+ ],
60
+ };
61
+ },
62
+ };
63
+ //# sourceMappingURL=coding-standard.js.map
@@ -0,0 +1,3 @@
1
+ import type { GuardrailPackDefinition } from "./types.js";
2
+ /** Canned destructive shell/SQL patterns on the `shell` tool's `command` argument. */
3
+ export declare const destructiveCommandsPack: GuardrailPackDefinition;
@@ -0,0 +1,46 @@
1
+ /** Shell commands that destroy data or history. Denies `--force-with-lease` too: both rewrite remote history. */
2
+ const DESTRUCTIVE_COMMANDS = [
3
+ {
4
+ id: "no-recursive-force-delete",
5
+ pattern: /\brm\s+(?:[^\n;&|]*?\s)?-(?=[a-z]*r)(?=[a-z]*f)[a-z]+/i,
6
+ reason: "Recursive force delete is not allowed",
7
+ },
8
+ {
9
+ id: "no-long-flag-force-delete",
10
+ pattern: /\brm\s+[^\n;&|]*--recursive\b[^\n;&|]*--force\b|\brm\s+[^\n;&|]*--force\b[^\n;&|]*--recursive\b/i,
11
+ reason: "Recursive force delete is not allowed",
12
+ },
13
+ {
14
+ id: "no-force-push",
15
+ pattern: /\bgit\s+push\b[^\n;&|]*(?:--force\b|(?:^|\s)-f(?:\s|$))/i,
16
+ reason: "Force push is not allowed",
17
+ },
18
+ {
19
+ id: "no-destructive-sql",
20
+ pattern: /\b(?:drop|truncate)\s+table\b/i,
21
+ reason: "Destructive SQL is not allowed",
22
+ },
23
+ {
24
+ id: "no-device-overwrite",
25
+ pattern: /\bmkfs(?:\.\w+)?\b|\bdd\b[^\n;&|]*\bof=\/dev\//i,
26
+ reason: "Raw device overwrite is not allowed",
27
+ },
28
+ ];
29
+ /** Canned destructive shell/SQL patterns on the `shell` tool's `command` argument. */
30
+ export const destructiveCommandsPack = {
31
+ id: "destructive-commands",
32
+ version: 1,
33
+ description: "Blocks destructive shell and SQL commands (recursive force delete, force push, drop table, device overwrite).",
34
+ build() {
35
+ return {
36
+ rules: DESTRUCTIVE_COMMANDS.map(({ id, pattern, reason }) => ({
37
+ id,
38
+ tool: "shell",
39
+ pattern,
40
+ argPath: "command",
41
+ reason,
42
+ })),
43
+ };
44
+ },
45
+ };
46
+ //# sourceMappingURL=destructive-commands.js.map
@@ -0,0 +1,7 @@
1
+ /** Guardrail pack config/compile error (plan 092 Task 2). Config mistakes fail closed at compile time. */
2
+ export declare class GuardrailPackError extends Error {
3
+ readonly code = "ERR_PRISM_GUARDRAIL_PACK";
4
+ constructor(message: string, options?: {
5
+ readonly cause?: unknown;
6
+ });
7
+ }
@@ -0,0 +1,9 @@
1
+ /** Guardrail pack config/compile error (plan 092 Task 2). Config mistakes fail closed at compile time. */
2
+ export class GuardrailPackError extends Error {
3
+ code = "ERR_PRISM_GUARDRAIL_PACK";
4
+ constructor(message, options) {
5
+ super(message, options);
6
+ this.name = "GuardrailPackError";
7
+ }
8
+ }
9
+ //# sourceMappingURL=errors.js.map