@arnilo/prism 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +11 -11
  3. package/dist/agent-approval.d.ts +11 -2
  4. package/dist/agent-event-source.d.ts +9 -1
  5. package/dist/agent-event-source.js +10 -3
  6. package/dist/agent-loops.js +7 -4
  7. package/dist/agent-run-lifecycle.d.ts +15 -1
  8. package/dist/agent-run-lifecycle.js +63 -6
  9. package/dist/agent-run-state.d.ts +22 -2
  10. package/dist/agent-run-state.js +57 -5
  11. package/dist/agent-session/helpers.js +14 -0
  12. package/dist/agent-session/session/assemble.js +126 -24
  13. package/dist/agent-session/session/persist.d.ts +11 -0
  14. package/dist/agent-session/session/persist.js +37 -11
  15. package/dist/agent-session/session/provider-round.d.ts +14 -4
  16. package/dist/agent-session/session/provider-round.js +185 -19
  17. package/dist/agent-session/session/tool-round.js +20 -1
  18. package/dist/agent-session/session/types.d.ts +25 -2
  19. package/dist/agent-session/session.d.ts +38 -4
  20. package/dist/agent-session/session.js +76 -5
  21. package/dist/attention-compiler.d.ts +51 -2
  22. package/dist/attention-compiler.js +282 -21
  23. package/dist/cache-helpers.d.ts +4 -2
  24. package/dist/cache-helpers.js +8 -6
  25. package/dist/checkpoint-restore.d.ts +45 -0
  26. package/dist/checkpoint-restore.js +54 -0
  27. package/dist/context-budget.d.ts +2 -1
  28. package/dist/context-budget.js +24 -2
  29. package/dist/contracts-core/agent.d.ts +30 -0
  30. package/dist/contracts-core/attention.d.ts +95 -0
  31. package/dist/contracts-core/content.d.ts +10 -0
  32. package/dist/contracts-core/guardrail-packs.d.ts +41 -0
  33. package/dist/contracts-core/guardrail-packs.js +2 -0
  34. package/dist/contracts-core/provider.d.ts +25 -0
  35. package/dist/contracts-core/run-limits.d.ts +19 -0
  36. package/dist/contracts-core/session.d.ts +23 -5
  37. package/dist/contracts-core/session.js +21 -2
  38. package/dist/contracts-core/usage.d.ts +40 -0
  39. package/dist/contracts-core/usage.js +8 -0
  40. package/dist/contracts-core.d.ts +2 -0
  41. package/dist/contracts-core.js +2 -0
  42. package/dist/contracts-protocol.d.ts +76 -2
  43. package/dist/contracts-run-state.d.ts +56 -1
  44. package/dist/guardrail-packs/coding-standard.d.ts +3 -0
  45. package/dist/guardrail-packs/coding-standard.js +63 -0
  46. package/dist/guardrail-packs/destructive-commands.d.ts +3 -0
  47. package/dist/guardrail-packs/destructive-commands.js +46 -0
  48. package/dist/guardrail-packs/errors.d.ts +7 -0
  49. package/dist/guardrail-packs/errors.js +9 -0
  50. package/dist/guardrail-packs/index.d.ts +4 -0
  51. package/dist/guardrail-packs/index.js +15 -0
  52. package/dist/guardrail-packs/secrets-hygiene.d.ts +3 -0
  53. package/dist/guardrail-packs/secrets-hygiene.js +23 -0
  54. package/dist/guardrail-packs/types.d.ts +16 -0
  55. package/dist/guardrail-packs/types.js +2 -0
  56. package/dist/guardrail-packs/validation-respect.d.ts +3 -0
  57. package/dist/guardrail-packs/validation-respect.js +53 -0
  58. package/dist/guardrails.d.ts +20 -1
  59. package/dist/guardrails.js +268 -0
  60. package/dist/index.d.ts +14 -9
  61. package/dist/index.js +9 -6
  62. package/dist/input.d.ts +8 -1
  63. package/dist/input.js +68 -6
  64. package/dist/middleware.d.ts +37 -2
  65. package/dist/middleware.js +41 -0
  66. package/dist/node/session-store-jsonl.js +18 -3
  67. package/dist/observability.js +6 -0
  68. package/dist/provider-events.d.ts +8 -2
  69. package/dist/provider-events.js +60 -2
  70. package/dist/providers/openai-compatible.js +6 -3
  71. package/dist/run-bundle.js +2 -1
  72. package/dist/run-limits.d.ts +11 -1
  73. package/dist/run-limits.js +46 -0
  74. package/dist/session-stores.d.ts +12 -1
  75. package/dist/session-stores.js +21 -4
  76. package/dist/testing/agent-event-source-conformance.js +41 -2
  77. package/dist/testing/prefix-stability-conformance.d.ts +30 -0
  78. package/dist/testing/prefix-stability-conformance.js +104 -0
  79. package/dist/testing/session-store-conformance.d.ts +3 -2
  80. package/dist/testing/session-store-conformance.js +48 -0
  81. package/dist/tools.d.ts +5 -0
  82. package/dist/tools.js +11 -3
  83. package/dist/usage-estimation.d.ts +29 -0
  84. package/dist/usage-estimation.js +79 -0
  85. package/docs/agent-events.md +68 -1
  86. package/docs/agent-session-runtime.md +1 -0
  87. package/docs/attention-compiler.md +89 -8
  88. package/docs/coding-agent-tools.md +1 -1
  89. package/docs/compaction-and-retry.md +1 -1
  90. package/docs/compaction-observational-memory.md +33 -6
  91. package/docs/durable-runs.md +42 -0
  92. package/docs/embeddings.md +5 -0
  93. package/docs/evaluations.md +5 -0
  94. package/docs/execution-timeline.md +78 -1
  95. package/docs/guardrails.md +38 -2
  96. package/docs/index.md +32 -13
  97. package/docs/input-and-prompt-assembly.md +3 -3
  98. package/docs/knowledge-sync.md +4 -0
  99. package/docs/middleware-hooks.md +38 -2
  100. package/docs/migrate-to-0.9.md +210 -0
  101. package/docs/migration.md +13 -0
  102. package/docs/multi-agent-patterns.md +25 -2
  103. package/docs/node-jsonl-session-store.md +7 -1
  104. package/docs/observability.md +7 -3
  105. package/docs/options-index.md +2 -1
  106. package/docs/policy-and-audit.md +13 -1
  107. package/docs/prefix-stability-conformance.md +93 -0
  108. package/docs/provider-caching.md +4 -4
  109. package/docs/provider-conformance.md +16 -0
  110. package/docs/provider-packages.md +20 -20
  111. package/docs/public-contracts.md +2 -2
  112. package/docs/rag.md +101 -3
  113. package/docs/release-and-install.md +39 -37
  114. package/docs/runs-and-usage.md +43 -6
  115. package/docs/scoped-agent-memory.md +262 -0
  116. package/docs/session-store-conformance.md +1 -2
  117. package/docs/session-stores.md +17 -17
  118. package/docs/supervisors.md +32 -12
  119. package/docs/tools.md +17 -0
  120. package/docs/workflows.md +5 -0
  121. package/package.json +5 -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, TurnPolicyOptions, 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;
@@ -140,6 +147,17 @@ export interface ProviderTurnMetadata {
140
147
  readonly httpStatus?: number;
141
148
  readonly rateLimitRemaining?: number;
142
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
+ };
143
161
  }
144
162
  export interface ToolExecutionMetadata {
145
163
  readonly durationMs: number;
@@ -177,7 +195,18 @@ export interface DelegatedAgentStep {
177
195
  }
178
196
  /** Why a run stopped cleanly. `host_policy` is a `RunOptions.turnPolicy` stop; the rest are loop ceilings (F4). */
179
197
  export type AgentFinishReason = "turn_limit" | "token_limit" | "refusal" | "host_policy";
180
- export type AgentEvent = {
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 = {
181
210
  readonly type: "agent_started";
182
211
  readonly sessionId: string;
183
212
  readonly runId: string;
@@ -201,6 +230,8 @@ export type AgentEvent = {
201
230
  readonly sessionId: string;
202
231
  readonly runId: string;
203
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;
204
235
  } | {
205
236
  readonly type: "agent_denied";
206
237
  readonly sessionId: string;
@@ -217,6 +248,17 @@ export type AgentEvent = {
217
248
  readonly sessionId: string;
218
249
  readonly runId: string;
219
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;
220
262
  } | {
221
263
  readonly type: "provider_turn_started";
222
264
  readonly sessionId: string;
@@ -281,6 +323,13 @@ export type AgentEvent = {
281
323
  readonly reason: string;
282
324
  readonly error: ErrorInfo;
283
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[];
284
333
  } | {
285
334
  readonly type: "guardrail_decision";
286
335
  readonly sessionId: string;
@@ -293,6 +342,16 @@ export type AgentEvent = {
293
342
  readonly sessionId: string;
294
343
  readonly runId: string;
295
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[];
296
355
  } | {
297
356
  readonly type: "queue_updated";
298
357
  readonly sessionId: string;
@@ -383,6 +442,14 @@ export type AgentEvent = {
383
442
  readonly attempt: number;
384
443
  readonly result: ArtifactValidation;
385
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
+ };
386
453
  export type ToolEffectKind = "none" | "local_mutation" | "external_mutation";
387
454
  export type ToolEffectIdempotency = "none" | "optional" | "required" | "tool_managed" | "unsupported";
388
455
  /** Static or validated-argument classification of one tool call's side-effect behavior. */
@@ -688,4 +755,11 @@ export interface ProviderTurnResult {
688
755
  readonly messageId?: string;
689
756
  readonly started: boolean;
690
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>>;
691
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";
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
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,6 +140,15 @@ 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. */
@@ -156,6 +166,12 @@ export interface AgentRunStateOptions {
156
166
  readonly checkpointPolicy?: "decision" | "every-turn";
157
167
  /** Suspend every tool call before its side effect. */
158
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;
159
175
  readonly maxStateBytes?: number;
160
176
  readonly fencingToken?: number;
161
177
  /** Enables sticky auto-apply when a nested suspension first surfaces during this run. */
@@ -204,6 +220,23 @@ export interface AgentRunResume {
204
220
  /** Batch decision path; exactly one of decision/decisions. Applied as one atomic CAS transition. */
205
221
  readonly decisions?: readonly RunDecision[];
206
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>;
207
240
  export interface AgentRunResumeOptions {
208
241
  readonly checkpoints: CheckpointStore;
209
242
  /** Current host-authored revision; must exactly match the checkpoint. */
@@ -224,6 +257,20 @@ export interface AgentRunResumeOptions {
224
257
  readonly persistSessionState?: boolean;
225
258
  /** Opt-in (plan 018 Task 6): restore persisted loaded-skill bodies (requires `persistSessionState` too). */
226
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;
227
274
  }
228
275
  /** Bounded live-event options for `resumeAgentRunStream()`; `signal` is inherited from the base resume options. */
229
276
  export interface AgentRunResumeStreamOptions extends AgentRunResumeOptions, SubscribeOptions {
@@ -235,6 +282,8 @@ export interface AgentRunRef {
235
282
  export interface AgentRunStatusResult {
236
283
  readonly state: AgentRunState;
237
284
  readonly version: number;
285
+ /** Checkpoint sidecar metadata; absent when the record carries none (or carries only malformed entries). */
286
+ readonly metadata?: AgentRunCheckpointMetadata;
238
287
  }
239
288
  export declare class AgentRunStateError extends Error {
240
289
  readonly code = "ERR_PRISM_AGENT_RUN_STATE";
@@ -319,6 +368,12 @@ export interface AgentSession {
319
368
  abort(reason?: unknown): void;
320
369
  entries(): Promise<readonly SessionEntry[]>;
321
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;
322
377
  fork(options?: AgentSessionForkOptions): AgentSession;
323
378
  clone(options?: AgentSessionCloneOptions): Promise<AgentSession>;
324
379
  }
@@ -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
@@ -0,0 +1,4 @@
1
+ import type { GuardrailPackDefinition } from "./types.js";
2
+ export declare const BUILT_IN_GUARDRAIL_PACKS: ReadonlyMap<string, GuardrailPackDefinition>;
3
+ /** Built-in pack ids, in registry order. */
4
+ export declare const BUILT_IN_GUARDRAIL_PACK_IDS: readonly string[];
@@ -0,0 +1,15 @@
1
+ /** Built-in guardrail pack registry (plan 092 Task 2). Definitions are pure data + pure factories. */
2
+ import { codingStandardPack } from "./coding-standard.js";
3
+ import { destructiveCommandsPack } from "./destructive-commands.js";
4
+ import { secretsHygienePack } from "./secrets-hygiene.js";
5
+ import { validationRespectPack } from "./validation-respect.js";
6
+ const DEFINITIONS = [
7
+ codingStandardPack,
8
+ destructiveCommandsPack,
9
+ validationRespectPack,
10
+ secretsHygienePack,
11
+ ];
12
+ export const BUILT_IN_GUARDRAIL_PACKS = new Map(DEFINITIONS.map((definition) => [definition.id, definition]));
13
+ /** Built-in pack ids, in registry order. */
14
+ export const BUILT_IN_GUARDRAIL_PACK_IDS = DEFINITIONS.map((definition) => definition.id);
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ import type { GuardrailPackDefinition } from "./types.js";
2
+ /** Canned secret hygiene: any tool argument carrying credential-shaped material is denied. */
3
+ export declare const secretsHygienePack: GuardrailPackDefinition;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Known credential shapes, all quantifier-bounded so a hostile argument string cannot blow up the regex.
3
+ * Prism redaction matches exact known values only (plan 092 Task 1), so these patterns ship with the pack.
4
+ */
5
+ const SECRET_PATTERN = /(?:sk-[A-Za-z0-9]{16,}|gh[pousr]_[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----|eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.|xox[baprs]-[A-Za-z0-9-]{10,})/;
6
+ /** Canned secret hygiene: any tool argument carrying credential-shaped material is denied. */
7
+ export const secretsHygienePack = {
8
+ id: "secrets-hygiene",
9
+ version: 1,
10
+ description: "Blocks tool calls whose arguments carry secret-shaped material (API keys, tokens, private keys).",
11
+ build() {
12
+ return {
13
+ rules: [
14
+ {
15
+ id: "no-secret-material-in-arguments",
16
+ pattern: SECRET_PATTERN,
17
+ reason: "Tool arguments contain secret-shaped material",
18
+ },
19
+ ],
20
+ };
21
+ },
22
+ };
23
+ //# sourceMappingURL=secrets-hygiene.js.map
@@ -0,0 +1,16 @@
1
+ /** Internal guardrail-pack definition types (plan 092 Task 2). Not SDK surface; hosts configure packs via `AgentSessionConfig.guardrailPacks`. */
2
+ import type { GuardrailRule, GuardrailRuleContext } from "../contracts-core/guardrail-packs.js";
3
+ import type { ToolResult } from "../contracts-protocol.js";
4
+ /** What a pack's `build` returns: restrictive rules plus an optional pure result observer. */
5
+ export interface GuardrailPackRules {
6
+ readonly rules: readonly GuardrailRule[];
7
+ /** Records tool results into pack-local state (never denies); a throw fails closed as a guardrail tripwire. */
8
+ readonly observe?: (state: Record<string, unknown>, result: ToolResult, context: GuardrailRuleContext) => void;
9
+ }
10
+ export interface GuardrailPackDefinition {
11
+ readonly id: string;
12
+ readonly version: number;
13
+ readonly description: string;
14
+ /** Pure factory: options in, rules out. No session access, no I/O. */
15
+ readonly build: (options: Readonly<Record<string, unknown>>) => GuardrailPackRules;
16
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,3 @@
1
+ import type { GuardrailPackDefinition } from "./types.js";
2
+ /** Canned "respect failed validation": once a validation tool result fails, file mutations are denied until one passes. */
3
+ export declare const validationRespectPack: GuardrailPackDefinition;
@@ -0,0 +1,53 @@
1
+ import { GuardrailPackError } from "./errors.js";
2
+ const MUTATING_TOOLS = ["write", "edit", "delete", "move"];
3
+ /** Validation-style tool names. `shell` is opt-in (`validationTools`): a non-zero shell exit is a failure signal too. */
4
+ const DEFAULT_VALIDATION_TOOLS = ["test", "run_tests", "validate", "validation", "lint", "typecheck", "check"];
5
+ const MAX_VALIDATION_TOOLS = 16;
6
+ /** A tool result failed when it carries an error, or reported a non-zero `exitCode` (the shell tool's shape). */
7
+ function resultFailed(result) {
8
+ if (result.error !== undefined)
9
+ return true;
10
+ const value = result.value;
11
+ if (!value || typeof value !== "object" || !("exitCode" in value))
12
+ return false;
13
+ const exitCode = value.exitCode;
14
+ return typeof exitCode === "number" && exitCode !== 0;
15
+ }
16
+ function readValidationTools(value) {
17
+ if (value === undefined)
18
+ return DEFAULT_VALIDATION_TOOLS;
19
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_VALIDATION_TOOLS) {
20
+ throw new GuardrailPackError(`validation-respect options.validationTools must be a non-empty string array (max ${MAX_VALIDATION_TOOLS})`);
21
+ }
22
+ return value.map((name) => {
23
+ if (typeof name !== "string" || !name.trim()) {
24
+ throw new GuardrailPackError("validation-respect options.validationTools entries must be non-empty strings");
25
+ }
26
+ return name;
27
+ });
28
+ }
29
+ /** Canned "respect failed validation": once a validation tool result fails, file mutations are denied until one passes. */
30
+ export const validationRespectPack = {
31
+ id: "validation-respect",
32
+ version: 1,
33
+ description: "Denies file mutations after a failed validation tool result until a later validation passes.",
34
+ build(options) {
35
+ const validationTools = readValidationTools(options.validationTools);
36
+ return {
37
+ observe(state, result, context) {
38
+ if (!validationTools.includes(context.toolName))
39
+ return;
40
+ state.validationFailed = resultFailed(result) ? context.toolName : undefined;
41
+ },
42
+ rules: [
43
+ {
44
+ id: "no-mutation-after-failed-validation",
45
+ tool: MUTATING_TOOLS,
46
+ reason: "A previous validation result failed; fix it before mutating files",
47
+ deny: (_args, context) => context.state.validationFailed !== undefined,
48
+ },
49
+ ],
50
+ };
51
+ },
52
+ };
53
+ //# sourceMappingURL=validation-respect.js.map
@@ -1,6 +1,10 @@
1
- import type { AgentEvent, GuardrailContext, GuardrailRecord, GuardrailStage, Guardrails, GuardrailValue } from "./contracts.js";
1
+ import type { AgentEvent, GuardrailContext, GuardrailPackRef, GuardrailRecord, GuardrailStage, Guardrails, GuardrailValue } from "./contracts.js";
2
+ export { GuardrailPackError } from "./guardrail-packs/errors.js";
2
3
  import type { SecretRedactor } from "./redaction.js";
3
4
  export declare const MAX_GUARDRAIL_CONCURRENCY = 16;
5
+ /** Guardrail pack compile bounds (plan 092 Task 2). All ceilings are config-shape limits, not runtime budgets. */
6
+ export declare const MAX_GUARDRAIL_PACKS = 8;
7
+ export declare const MAX_GUARDRAIL_PACK_RULES = 64;
4
8
  export declare class GuardrailError extends Error {
5
9
  readonly code: string;
6
10
  readonly record: GuardrailRecord;
@@ -23,3 +27,18 @@ export interface GuardrailRunResult {
23
27
  /** Evaluate one typed stage. Default is declaration-order sequential; bounded parallel mode still reports declaration order. */
24
28
  export declare function runGuardrails<S extends GuardrailStage>(options: RunGuardrailsOptions<S>): Promise<GuardrailRunResult>;
25
29
  export declare function assertGuardrailsAllowed(result: GuardrailRunResult): void;
30
+ /** One `guardrail:<stage>` identity row of a compiled pack, for run-bundle fingerprints. */
31
+ export interface GuardrailPackRow {
32
+ readonly name: string;
33
+ readonly stage: "tool_input" | "tool_output";
34
+ readonly revision: string | null;
35
+ }
36
+ /**
37
+ * Compiles `guardrailPacks` config onto the existing tool interception seams: one `tool_input`
38
+ * guardrail per rule (`name = pack:<pack>/<rule>`), plus one `tool_output` recorder for packs that
39
+ * observe results. Compiled once per session — patterns are compiled here, never per tool call.
40
+ * Throws `GuardrailPackError` on malformed config (fail closed); returns `undefined` when unset.
41
+ */
42
+ export declare function compileGuardrailPacks(refs: readonly GuardrailPackRef[] | undefined, registry?: ReadonlyMap<string, import("./guardrail-packs/types.js").GuardrailPackDefinition>): Guardrails | undefined;
43
+ /** Stable identity rows for the same config `compileGuardrailPacks` accepts (no state, no guardrails built). */
44
+ export declare function describeGuardrailPacks(refs: readonly GuardrailPackRef[] | undefined, registry?: ReadonlyMap<string, import("./guardrail-packs/types.js").GuardrailPackDefinition>): readonly GuardrailPackRow[];