@arnilo/prism 0.0.6 → 0.0.8

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 (54) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +3 -1
  3. package/dist/agent-loops.js +8 -5
  4. package/dist/agent-run-lifecycle.d.ts +28 -0
  5. package/dist/agent-run-lifecycle.js +33 -0
  6. package/dist/agent-run-state.d.ts +53 -0
  7. package/dist/agent-run-state.js +127 -0
  8. package/dist/agents.d.ts +3 -1
  9. package/dist/agents.js +356 -45
  10. package/dist/contracts.d.ts +218 -3
  11. package/dist/contracts.js +4 -0
  12. package/dist/guardrails.d.ts +25 -0
  13. package/dist/guardrails.js +133 -0
  14. package/dist/index.d.ts +15 -3
  15. package/dist/index.js +9 -3
  16. package/dist/input.js +2 -0
  17. package/dist/resources.js +2 -1
  18. package/dist/run-ledger.d.ts +21 -0
  19. package/dist/run-ledger.js +115 -0
  20. package/dist/run-limits.d.ts +34 -0
  21. package/dist/run-limits.js +163 -0
  22. package/dist/secure-agent.d.ts +3 -0
  23. package/dist/secure-agent.js +63 -0
  24. package/dist/tools.d.ts +10 -2
  25. package/dist/tools.js +54 -4
  26. package/docs/a2a.md +61 -42
  27. package/docs/agent-events.md +15 -3
  28. package/docs/agent-loops.md +12 -4
  29. package/docs/agent-session-runtime.md +34 -1
  30. package/docs/credential-storage.md +9 -0
  31. package/docs/database-persistence.md +1 -1
  32. package/docs/evaluations.md +26 -3
  33. package/docs/guardrails.md +75 -0
  34. package/docs/host-security.md +31 -4
  35. package/docs/index.md +17 -14
  36. package/docs/mcp-tools.md +36 -8
  37. package/docs/migration.md +54 -0
  38. package/docs/observability.md +26 -14
  39. package/docs/performance.md +25 -0
  40. package/docs/postgres-persistence.md +1 -0
  41. package/docs/providers/kimi.md +16 -2
  42. package/docs/providers/opencode-go.md +43 -2
  43. package/docs/release-and-install.md +73 -62
  44. package/docs/resource-loading.md +4 -0
  45. package/docs/review-coverage-2026-07-19-phase-3.md +174 -0
  46. package/docs/run-ledger-conformance.md +1 -0
  47. package/docs/runs-and-usage.md +46 -4
  48. package/docs/server.md +5 -2
  49. package/docs/sqlite-persistence.md +1 -0
  50. package/docs/supervisors.md +2 -2
  51. package/docs/tools.md +8 -2
  52. package/docs/web-tools.md +78 -0
  53. package/docs/workflows.md +2 -0
  54. package/package.json +2 -1
package/docs/a2a.md CHANGED
@@ -1,75 +1,94 @@
1
- # A2A interoperability
1
+ # A2A 1.0 interoperability
2
2
 
3
3
  ## What it does
4
4
 
5
- `@arnilo/prism-supervisor` implements a bounded text-only subset of Agent2Agent (A2A) protocol 1.0: Agent Cards, JSON-RPC `SendMessage`, `SendStreamingMessage`, `GetExtendedAgentCard`, SSE task updates, ES256 JWS card signatures, and an explicit remote client.
5
+ `@arnilo/prism-supervisor` implements bounded A2A 1.0 over the JSON-RPC/HTTPS binding. Supported operations: `SendMessage`, `SendStreamingMessage`, `GetTask`, `ListTasks`, `CancelTask`, `SubscribeToTask`, push-notification-config create/get/list/delete, and `GetExtendedAgentCard`. Agent Cards retain explicit ES256 verification. gRPC, HTTP+JSON, discovery registries, automatic JWK/OAuth fetching, and an internal task worker/store are absent.
6
6
 
7
7
  ## When to use it
8
8
 
9
- Use it to expose one explicitly selected Prism agent at an A2A endpoint or call a known remote A2A agent. Do not use it as endpoint discovery, a generic proxy, credential forwarding, or a replacement for local workflows.
9
+ Use it to expose a selected Prism agent or host-owned durable agent/workflow lifecycle to known A2A peers. Use direct `exposure` for backward-compatible text invocation. Supply `tasks` for durable/rich/reconnect operations and `push` only when host persistence and webhook delivery policy already exist.
10
10
 
11
11
  ## Inputs / request
12
12
 
13
- | API/field | Meaning |
14
- | --- | --- |
15
- | `createA2AAgentCard(card)` | Validates/freeze a JSONRPC protocol-1.0 HTTPS text card. |
16
- | `signA2AAgentCard(card, { privateKey, keyId, expiresAt })` | Adds detached-payload ES256 JWS signature using WebCrypto. |
17
- | `verifyA2AAgentCard(card, { publicKey, keyId?, now?, maxAgeMs? })` | Pins ES256/key/expiry and verifies canonical unsigned card. |
18
- | `createA2AHandler({ card, exposure, authorize })` | Web-standard card/JSON-RPC/SSE `Request` to `Response` handler. |
19
- | `createA2AClient({ endpoint, allowedOrigins })` | Explicit HTTPS remote client with optional card verifier/auth callback. |
20
- | `A2ALimits` | Request 64 KiB, response 1 MiB, event 64 KiB, stream 10 MiB/10k events, concurrency 16, timeout 120s, card 64 KiB defaults; finite hard caps apply. |
13
+ ```ts
14
+ const handler = createA2AHandler({
15
+ card,
16
+ exposure: { sessionFactory }, // text fallback
17
+ authorize: authenticateEveryOperation,
18
+ tasks: durableTaskAdapter, // host-owned start/get/list/cancel/subscribe
19
+ push: pushConfigAdapter, // host-owned config persistence/delivery integration
20
+ parts: {
21
+ allowRaw: true,
22
+ allowData: true,
23
+ allowUrl: true,
24
+ validateUrl: validatePinnedPublicHttpsUrl, // validation only; never fetched
25
+ },
26
+ });
27
+ ```
21
28
 
22
- ## Outputs / response / events
29
+ `A2ATaskLifecycle` receives validated messages, exact `A2AAuthorization`, abort signals, bounded pagination, and reconnect cursor. Adapter must map existing durable agent/workflow/checkpoint/persistence operations; Prism creates no worker, queue, task map, or database table. Unknown-owner task/config lookups return `undefined`, producing non-disclosing `TaskNotFoundError` (`-32001`). Missing task/push capability returns `UnsupportedOperationError` (`-32004`).
23
30
 
24
- The handler serves `GET /.well-known/agent-card.json` and its configured POST endpoint. JSON-RPC returns `{ result: { task } }` or a bounded error. Streaming returns backpressure-driven SSE task envelopes. Client `send()` maps a terminal remote task to `AgentRunResult`; `stream()` incrementally yields validated/redacted text artifacts. Client SSE accepts LF, CRLF, mixed blank-line separators, comments/unknown fields, and multiline `data:` joined with LF.
31
+ `A2APart` is an exact one-of:
25
32
 
26
- ## Request/response example
33
+ | Part | Default | Rule |
34
+ | --- | --- | --- |
35
+ | `{ text }` | enabled | bounded UTF-8 text |
36
+ | `{ raw, mediaType?, filename? }` | disabled | strict base64 and decoded-byte cap |
37
+ | `{ data }` | disabled | bounded finite JSON, depth 64/properties 10,000 |
38
+ | `{ url, mediaType?, filename? }` | disabled | credential/fragment-free HTTPS plus required host URL policy; never dereferenced |
27
39
 
28
- ```json
29
- {"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"role":"user","messageId":"m1","parts":[{"text":"Check sources"}]}}}
30
- ```
40
+ Parts, messages, artifacts, histories, metadata, and aggregate responses are untrusted. Rich content remains in A2A task/message/artifact contracts for host mapping; it is never promoted to system instructions or automatically loaded as a Prism resource.
31
41
 
32
42
  ## Implementation example
33
43
 
34
44
  ```ts
35
- import { createA2AClient, createA2AHandler, verifyA2AAgentCard } from "@arnilo/prism-supervisor";
36
-
37
- const handler = createA2AHandler({
38
- card,
39
- exposure: { sessionFactory: ({ ownership }) => agent.createSession({ metadata: ownership }) },
40
- authorize: ({ request }) => authenticate(request),
41
- });
42
-
43
45
  const client = createA2AClient({
44
46
  endpoint: "https://agent.example/a2a/v1",
45
47
  allowedOrigins: ["https://agent.example"],
46
- authorize: () => ({ authorization: `Bearer ${resolveOwnedToken()}` }),
47
- verifyCard: (remoteCard) => verifyA2AAgentCard(remoteCard, { publicKey, keyId: "agent-key" }),
48
+ authorize: ownedAuthHeaders,
49
+ verifyCard: (card) => verifyA2AAgentCard(card, { publicKey, keyId: "agent-key" }),
48
50
  });
51
+ const task = await client.getTask("task-1");
52
+ for await (const event of client.subscribeToTask(task.id, { afterEventId: savedCursor })) persistCursor(event.eventId);
53
+ ```
54
+
55
+ ## Outputs / response / events
56
+
57
+ Streams use ordered SSE frames with `id:` and JSON-RPC `result` containing one `A2ATaskEvent`: full `task`, `statusUpdate`, or `artifactUpdate`. `SubscribeToTask({ id, afterEventId })` passes cursor to durable adapter for authorized bounded replay. Duplicate event IDs are rejected/server-bounded; client de-duplicates repeated IDs. Terminal, `INPUT_REQUIRED`, and `AUTH_REQUIRED` states close streams. String-oriented `client.stream()` reports interrupted states as `ERR_PRISM_A2A_INTERRUPTED`; task APIs preserve status for continuation.
49
58
 
50
- const result = await client.send("Check sources");
59
+ Client APIs:
60
+
61
+ - `send()` / `stream()` preserve text-to-`AgentRunResult` compatibility.
62
+ - `sendMessage()` returns rich/durable `A2ATask`.
63
+ - `getTask()`, `listTasks()`, `cancelTask()`, `subscribeToTask()` operate on durable tasks.
64
+ - `createPushConfig()`, `getPushConfig()`, `listPushConfigs()`, `deletePushConfig()` expose declared push config operations.
65
+
66
+ Every protocol request sends/negotiates `A2A-Version: 1.0`. Client endpoint/card URLs require exact allow-listed HTTPS and `redirect: "error"`. Cards are parsed then optionally verified against host-pinned keys; no key URL is fetched.
67
+
68
+ ## Request/response example
69
+
70
+ ```json
71
+ {"jsonrpc":"2.0","id":1,"method":"SubscribeToTask","params":{"id":"task-1","afterEventId":"event-42"}}
51
72
  ```
52
73
 
53
74
  ## Extension and configuration notes
54
75
 
55
- The package owns no listener or credential store. Mount the handler in a host server and resolve authentication/authorization on every request. Client auth executes only after body/card validation and serialization. Injectable `fetch` supports host transports/tests; redirects are disabled.
76
+ Handler requires `card.capabilities.pushNotifications` to exactly match supplied `push`; mismatch fails construction, preserving signed-card integrity and preventing false capability claims. Streaming remains available for direct text invocation. Push adapter owns exact-owner persistence, signing/auth credentials, and network transport. Host explicitly calls `deliverA2APushEvent()` from its durable update path; helper bounds event, timeout (10s default/60s hard), attempts (1 default/3 hard), and passes stable event ID as idempotency key to host `A2APushDelivery`. It starts no hidden sender and performs no network itself. Config handling validates IDs/count/bytes and requires same explicit URL policy used for URL parts. Returned push configs omit token and authentication credentials.
56
77
 
57
- Only `text` parts are accepted. File/data parts, push notifications, task persistence/query/cancel, gRPC, HTTP+JSON binding, automatic JWK fetching, and endpoint discovery are intentionally absent.
78
+ Defaults/hard caps include: request 64 KiB/1 MiB; response 1/8 MiB; event 64 KiB/1 MiB; stream 10/64 MiB and 10k/100k events; replay 1k/10k events; concurrency 16/256; timeout 120s/30m; IDs 256/4096 B; parts 32/256; part/raw 1/8 MiB; data 256 KiB/4 MiB; artifacts 32/256; history/page 100/1000; cursor 4/16 KiB; push configs 10/100. Hosts may narrow limits.
58
79
 
59
80
  ## Security and performance notes
60
81
 
61
- - Endpoints and card URLs must be HTTPS and exactly origin-allow-listed before fetch; `redirect: "error"` prevents redirect SSRF.
62
- - Treat every remote card, error, task, status, artifact, and SSE frame as untrusted. Shape/count/byte/time limits apply before mapping. Streaming keeps raw stream bytes, current frame bytes, and event count as separate existing limits.
63
- - One fatal streaming UTF-8 decoder is reused across every body chunk and flushed once at EOF. Split multibyte code points are preserved; malformed/truncated UTF-8 fails rather than inserting `U+FFFD` into JSON. A small coalesced line buffer keeps one-byte chunk handling incremental.
64
- - SSE frames require a terminating blank line. A non-whitespace final partial frame, malformed JSON, missing terminal task, failed/canceled task, or any event after a completed task fails with bounded package-owned text. Existing request/response/event/stream/count/timeout hard caps are unchanged.
65
- - Card verification pins `alg=ES256`, optional key ID, issue/expiry, optional maximum age, and canonical unsigned-card payload. Hosts provision trusted public keys; remote `jku` is never fetched automatically.
66
- - Card discovery is public; extended-card and invoke methods call host authorization. Use TLS, rate limits, and replay controls at the host edge.
67
- - Credentials remain in the client auth callback or server authorizer and never enter cards, messages, events, or metrics.
68
- - Offline conformance is authoritative. Live endpoints are optional operator smoke tests.
82
+ - Authorize every operation; lifecycle/push adapters enforce exact owner again at durable storage boundary. Missing and foreign tasks/configs share `-32001`.
83
+ - URL policy must reject private, loopback, link-local, rebound, redirected, or otherwise disallowed destinations. Package never fetches file URLs. Host push delivery must repeat equivalent checks for every attempt/redirect and process event IDs idempotently.
84
+ - Push token/auth credentials are accepted only into host adapter input and removed from protocol reads/responses. Keep them out of task parts, events, telemetry, ledgers, and errors.
85
+ - Known-secret redaction applies before handler JSON/SSE output. Client redacts mapped text/errors. Raw/data/url content remains explicitly untrusted.
86
+ - Canceled/closed streams abort adapter signal, return iterator, clear timeout, and release concurrency slot. Task/push durability and replay retention belong to host adapter and must remain finite.
87
+ - Default tests use in-memory lifecycle/fake fetch only; no public network.
69
88
 
70
89
  ## Related APIs
71
90
 
72
- - [Supervisor delegation](supervisors.md): local child boundary.
73
- - [Web-standard server](server.md): non-A2A Prism routes.
74
- - [Host security](host-security.md): authentication, SSRF, and untrusted-output policy.
75
- - [Agent/session runtime](agent-session-runtime.md): mapped local execution/result.
91
+ - [Supervisor delegation](supervisors.md)
92
+ - [Agent/session runtime](agent-session-runtime.md)
93
+ - [Workflows](workflows.md)
94
+ - [Host security](host-security.md)
@@ -38,11 +38,12 @@ The `AgentEvent` union (grouped by concern):
38
38
 
39
39
  | Group | Variants |
40
40
  | --- | --- |
41
- | Agent lifecycle | `agent_started`, `agent_finished` |
41
+ | Agent lifecycle | `agent_started`, `agent_suspended`, `agent_resumed`, `agent_denied`, `agent_finished` |
42
42
  | Turns | `turn_started`, `turn_finished` |
43
43
  | Provider turns | `provider_turn_started`, `provider_turn_finished` |
44
44
  | Assistant messages | `message_started`, `message_delta`, `message_finished` |
45
45
  | Tool execution | `tool_execution_started`, `tool_execution_progress`, `tool_execution_finished`, `tool_execution_error`, `tool_execution_blocked` |
46
+ | Guardrails | `guardrail_decision` |
46
47
  | Queue/subscribers | `queue_updated`, `event_subscriber_overflow` |
47
48
  | Compaction | `compaction_started`, `compaction_finished` |
48
49
  | Retry | `retry_scheduled` |
@@ -59,6 +60,9 @@ Agent / turn / message events:
59
60
  | --- | --- |
60
61
  | `agent_started` | `sessionId`, `runId` |
61
62
  | `agent_finished` | `sessionId`, `runId`, `usage?: Usage` (aggregate of all usage-bearing provider turns) |
63
+ | `agent_suspended` | `sessionId`, `runId`, redacted `interruption`, checkpoint `version`; no tool side effect has started. |
64
+ | `agent_resumed` | `sessionId`, `runId`, checkpoint `version`. |
65
+ | `agent_denied` | `sessionId`, `runId`, redacted `interruption`, checkpoint `version`; no tool side effect runs. |
62
66
  | `turn_started` / `turn_finished` | `sessionId`, `runId`, `turn: number` |
63
67
  | `message_started` / `message_finished` | `sessionId`, `runId`, `message: Message` |
64
68
  | `message_delta` | `sessionId`, `runId`, `content: ContentBlock` (`tool_call_delta` fragments may appear here for live UI streaming; stored messages use final `tool_call` blocks) |
@@ -75,6 +79,14 @@ Tool execution events:
75
79
  | `tool_execution_error` | `sessionId`, `runId`, `call: ToolCallContent`, `error: ErrorInfo`, `metadata: ToolExecutionMetadata` |
76
80
  | `tool_execution_blocked` | `sessionId`, `runId`, `toolCallId`, `name`, `reason: string`, `error: ErrorInfo`, `metadata: ToolExecutionMetadata` |
77
81
 
82
+ Guardrail events:
83
+
84
+ | Variant | Fields |
85
+ | --- | --- |
86
+ | `guardrail_decision` | `sessionId`, `runId`, optional `toolCallId`/`toolName`, and redacted bounded `record: GuardrailRecord` (`guardrail`, stage, action, reason, metadata). |
87
+
88
+ Guardrails emit their decision before a terminal run error or blocked tool result. Provider-output checks buffer assistant content, and tool-output checks discard blocked raw results before event/ledger/transcript exposure; see [Guardrails](guardrails.md).
89
+
78
90
  Queue / subscriber / compaction / retry / provider events:
79
91
 
80
92
  | Variant | Fields |
@@ -100,7 +112,7 @@ Artifact validation/refinement events (emitted only by `generateValidateReviseLo
100
112
  | `artifact_validation_finished` | `sessionId`, `runId`, `turn`, `attempt`, `result: ArtifactValidation` |
101
113
  | `artifact_revision_started` | `sessionId`, `runId`, `turn`, `attempt`, `failure: ArtifactValidation` |
102
114
  | `artifact_finished` | `sessionId`, `runId`, `turn`, `attempt`, `result: ArtifactValidation` (loop ended successfully) |
103
- | `artifact_failed` | `sessionId`, `runId`, `turn`, `attempt`, `result: ArtifactValidation` (candidate budget exhausted, or `result.metadata.reason === "tool_round_limit"`) |
115
+ | `artifact_failed` | `sessionId`, `runId`, `turn`, `attempt`, `result: ArtifactValidation` (candidate budget exhausted, `result.metadata.reason === "tool_round_limit"`, or `result.metadata.reason === "parse_error"` when the budget was consumed by artifact parse failures) |
104
116
 
105
117
  ### Artifact event ordering
106
118
 
@@ -196,6 +208,6 @@ for await (const event of session.stream("draft", { loop: { strategy: "generate-
196
208
  - [Agent loops](agent-loops.md): `singleShotLoop` and `generateValidateReviseLoop` emit the artifact events.
197
209
  - [Structured output](structured-output.md): `ArtifactValidation` shape threaded through parser/validator/repairer.
198
210
  - [Public contracts](public-contracts.md): full `AgentEvent` union and `ArtifactValidation` contract.
199
- - [Observability](observability.md): `ProviderTurnMetadata`, OpenTelemetry adapter package.
211
+ - [Observability](observability.md): `ProviderTurnMetadata`; optional adapter builds one parented GenAI span tree from metadata-only lifecycle events and ignores message/progress deltas.
200
212
  - [Tools](tools.md): `tool_execution_*` variants.
201
213
  - [Compaction and retry policies](compaction-and-retry.md): `compaction_*` and `retry_scheduled` variants.
@@ -57,7 +57,7 @@ await session.run(input, {
57
57
  parser: hostParser, // optional; default treats assistant text as the value
58
58
  repairer: hostRepairer, // optional; default stringifies validation.errors[].message
59
59
  maxRevisions: 3, // optional; default 3
60
- toolCalls: "bounded", // optional; default "disabled"; uses RunOptions.maxToolRounds
60
+ toolCalls: "bounded", // optional; default "disabled"; uses limits.maxToolRounds
61
61
  },
62
62
  });
63
63
 
@@ -80,7 +80,7 @@ type AgentLoopOptions =
80
80
  readonly parser?: ArtifactParser<unknown>;
81
81
  readonly repairer?: ArtifactRepairer<unknown>;
82
82
  readonly maxRevisions?: number;
83
- /** Default "disabled". "bounded" dispatches sequentially up to RunOptions.maxToolRounds. */
83
+ /** Default "disabled". "bounded" dispatches sequentially up to limits.maxToolRounds. */
84
84
  readonly toolCalls?: "disabled" | "bounded";
85
85
  };
86
86
  ```
@@ -89,7 +89,7 @@ Host callback contracts (all generic over host `T`):
89
89
 
90
90
  | Contract | Shape |
91
91
  | --- | --- |
92
- | `ArtifactParser<T>` | `(text: string, ctx: ArtifactContext) => ArtifactParseResult<T> \| Promise<...>` — parse assistant text to a typed value. |
92
+ | `ArtifactParser<T>` | `(text: string, ctx: ArtifactContext) => ArtifactParseResult<T> \| Promise<...>` — parse assistant text to a typed value. A parse failure (`ok: false` or missing `value`) consumes revision budget exactly like a validation failure: the repairer receives `value: undefined` plus a synthetic failure (`errors[0].message` = the parse error, `metadata.reason: "parse_error"`), and budget exhaustion ends with terminal `artifact_failed`. |
93
93
  | `ArtifactValidator<T>` | `(value: T, ctx: ArtifactContext) => ArtifactValidation \| Promise<...>` — return `{ ok: true }` or `{ ok: false, errors }`. |
94
94
  | `ArtifactRepairer<T>` | `(value: T \| undefined, failure: ArtifactValidation, ctx: ArtifactContext) => AgentInput \| Promise<...>` — build the revision follow-up input. |
95
95
  | `ArtifactValidation` | `{ ok: boolean; errors?: readonly { path?: string; message: string }[]; metadata?: ... }`. |
@@ -109,6 +109,10 @@ Host callback contracts (all generic over host `T`):
109
109
  | `appendMessage(message)` | Appends to the store under the run (redacted). |
110
110
  | `emit(event)` | Emits a redacted `AgentEvent`. |
111
111
 
112
+ ## Durable runs
113
+
114
+ `RunOptions.runState` supports only built-in loop options (`single-shot` and `generate-validate-revise`). A custom `AgentLoopStrategy` has arbitrary in-memory cursor state, so durable configuration rejects it before provider work. Built-in suspension occurs only before an input provider call or immediately before a tool side effect; completed provider turns remain in `SessionStore` history and are not repeated after `resumeAgentRun()`.
115
+
112
116
  ## Outputs / response / events
113
117
 
114
118
  `AgentLoopStrategy.run(ctx)` returns `Promise<Usage | undefined>` as a fallback for custom loops. Core runtime independently accumulates every usage-bearing provider turn in O(turns), persists scoped turn/run rows, and emits `agent_finished` with the aggregate.
@@ -202,7 +206,7 @@ await session.run(input, { loop: twoShotLoop });
202
206
  - `{ strategy: "single-shot" }` resolves to the exported `singleShotLoop`; `{ strategy: "generate-validate-revise", ... }` is mapped by `resolveLoop()` to `generateValidateReviseLoop(opts)`. An unknown `strategy` throws before the first turn. Passing an `AgentLoopStrategy` instance bypasses the options form entirely (custom-loop escape hatch).
203
207
  - The loop is resolved once per run inside `RuntimeAgentSession.run()`, after the usual setup (provider/skills/tools resolution, history rebuild, model-change entry, input append, auto-compaction). The runtime's outer try/catch/finally, run-exclusivity, abort bridging, and subscriber close remain in place around `loop.run(ctx)`.
204
208
  - `LoopContext.assemble(nextInput, toolResults?)` accepts an optional tool-result accumulator so `singleShotLoop` can pass its loop-local results. Bounded artifact tools append results directly to shared history, then assemble the next turn with empty new input; no second transcript path exists.
205
- - `maxToolRounds` bounds both `singleShotLoop` and opt-in bounded artifact tool rounds across the whole run. Artifact mode always dispatches sequentially, regardless of `toolConcurrency`; all dispatches still use existing registry/filter/permission/validator/middleware/redactor/ledger guards.
209
+ - `limits.maxToolRounds` bounds both `singleShotLoop` and opt-in bounded artifact tool rounds across the whole run. Artifact mode always dispatches sequentially, regardless of `toolConcurrency`; all dispatches still use existing registry/filter/permission/validator/middleware/redactor/ledger guards. Deprecated `maxToolRounds` only narrows this limit.
206
210
  - `maxRevisions` (default 3) counts only failed call-free artifact candidates. Bounded artifact runs make at most `1 + maxRevisions + maxToolRounds` provider turns. A tool-round limit is terminal and returns last usage after `artifact_failed`; it does not throw.
207
211
  - A revision cycle appends one assistant draft and one repair user message per revision to the session store, so store entries reflect every attempted draft. The original user input is stored once by the runtime and pushed into loop history once on the first turn. Repair messages are assembled as the next provider `nextInput` and only pushed into live history after that revision request has been generated, so the model never receives a duplicated repair instruction.
208
212
 
@@ -215,6 +219,10 @@ await session.run(input, { loop: twoShotLoop });
215
219
  - The loop is a plain object/factory; no class hierarchy, no background work, no extra dependencies. `LoopContext` is a single object literal of bound arrows built once per run.
216
220
  - The Synapta-free boundary is guarded by tests: `src/` imports no `synapta*` package, and the `Artifact*`/`AgentLoop*`/`LoopContext` contracts contain no `workflow`/`node`/`step` field names. Hosts supply their own schema; no host domain type is imported by `src/`.
217
221
 
222
+ ## Guardrails
223
+
224
+ Built-in loops and custom loops that use `LoopContext.generate()` / `LoopContext.dispatchToolCall()` inherit runtime guardrails. Provider output is checked before a loop appends assistant content; tool stages remain in shared dispatch. Do not call providers or `ToolDefinition.execute()` directly if guardrail enforcement is required; see [Guardrails](guardrails.md).
225
+
218
226
  ## Related APIs
219
227
  - [Agent/session runtime](agent-session-runtime.md): `RuntimeAgentSession.run()` builds the `LoopContext` and delegates to the resolved loop.
220
228
  - [Agent events](agent-events.md): the `artifact_*` event variants and ordering emitted by `generateValidateReviseLoop`.
@@ -5,6 +5,7 @@
5
5
  The agent/session runtime adds the minimal shared SDK surface for running provider turns, dispatching complete host-owned tool calls, and subscribing to session events:
6
6
 
7
7
  - `createAgent(config)`
8
+ - `createSecureAgent(options)` for opt-in fail-closed composition
8
9
  - `createAgentSession(config)`
9
10
  - `agent.createSession(config)`
10
11
  - `session.run(input, options)` → `AgentRunResult`
@@ -17,6 +18,8 @@ The agent/session runtime adds the minimal shared SDK surface for running provid
17
18
  - `session.checkout(leafId?)`
18
19
  - `session.fork(options?)`
19
20
  - `session.clone(options?)`
21
+ - `resumeAgentRun(agent, ref, decision, options)`
22
+ - `createAgentRunLifecycle({ checkpoints, resolveAgent })` for host-selected remote status/resume adapters
20
23
 
21
24
  The runtime streams provider text/tool-call content into `AgentEvent` values. Complete `tool_call` events are dispatched through the active host `ToolRegistry`, then returned as tool-result messages on the next provider turn. When a store is supplied, user, assistant, tool-result, and model-change entries are appended under the current branch leaf. Abort propagation and run exclusivity use native `AbortController`.
22
25
 
@@ -43,7 +46,9 @@ string | Message | readonly Message[]
43
46
 
44
47
  `AgentSessionConfig.store` overrides `AgentConfig.store`; otherwise the session gets a private memory store. `AgentSessionConfig.leafId` selects the branch leaf to resume from.
45
48
 
46
- `RunOptions.model` can override the request model for a run. Model overrides append a `model_change` entry. `AgentConfig.inputLayout` selects the default input assembly layout (`"legacy"` by default, or opt-in `"cache_aware"`); `RunOptions.inputLayout` wins for one run. `AgentConfig.providerOptions`/`RunOptions.providerOptions` supply generic provider request options; `timeoutMs`, `maxRetries`, and `maxRetryDelayMs` are deprecated inert provider-level hints in first-party providers. Use `RunOptions.signal`/host abort controllers for timeouts and `AgentConfig.retry`/`RunOptions.retry` for retry. `AgentConfig.providerRequestPolicies`/`RunOptions.providerRequestPolicies` run before `AIProvider.generate()` and before `provider_request` middleware. `AgentConfig.systemPrompt` and `RunOptions.systemPrompt` add explicit layered system prompt contributions; `RunOptions.systemPrompt: false` disables configured prompt layers for that run while keeping `AgentConfig.instructions` as the base path. `RunOptions.compaction` can enable auto-compaction for that run or use `false` to disable configured auto-compaction. `RunOptions.retry` can enable provider-turn retry for that run or use `false` to disable configured retry. `RunOptions.metadata` is merged with agent/session metadata for assembly, provider requests, and tool contexts. `RunOptions.maxToolRounds` bounds repeated tool turns and defaults to `1`. `RunOptions.signal` is bridged into the per-run abort signal passed to assembly, providers, tools, auto-compaction, and retry backoff.
49
+ `AgentConfig.limits` sets run ceilings; `RunOptions.limits` may only narrow configured agent values. Limits cover turns, provider attempts, tool rounds/calls, wall time, request/response bytes, tokens, and optional single-currency cost. A breach emits one `run_limit_exceeded` event and throws `AgentRunError` with `result.limit`; see [Runs and usage ledger](runs-and-usage.md#run-limits).
50
+
51
+ `RunOptions.model` can override the request model for a run. Model overrides append a `model_change` entry. `AgentConfig.inputLayout` selects the default input assembly layout (`"legacy"` by default, or opt-in `"cache_aware"`); `RunOptions.inputLayout` wins for one run. `AgentConfig.providerOptions`/`RunOptions.providerOptions` supply generic provider request options; `timeoutMs`, `maxRetries`, and `maxRetryDelayMs` are deprecated inert provider-level hints in first-party providers. Use `RunOptions.signal`/host abort controllers for timeouts and `AgentConfig.retry`/`RunOptions.retry` for retry. `AgentConfig.providerRequestPolicies`/`RunOptions.providerRequestPolicies` run before `AIProvider.generate()` and before `provider_request` middleware. `AgentConfig.systemPrompt` and `RunOptions.systemPrompt` add explicit layered system prompt contributions; `RunOptions.systemPrompt: false` disables configured prompt layers for that run while keeping `AgentConfig.instructions` as the base path. `RunOptions.compaction` can enable auto-compaction for that run or use `false` to disable configured auto-compaction. `RunOptions.retry` can enable provider-turn retry for that run or use `false` to disable configured retry. `RunOptions.metadata` is merged with agent/session metadata for assembly, provider requests, and tool contexts. Deprecated `RunOptions.maxToolRounds` narrows `limits.maxToolRounds`. `RunOptions.signal` is bridged into the per-run abort signal passed to assembly, providers, tools, auto-compaction, and retry backoff.
47
52
 
48
53
  ## Outputs / response / events
49
54
 
@@ -160,6 +165,33 @@ await agent.createSession().run("Hi", { model: overrideModel });
160
165
  - Runtime events contain messages/content only; do not put secrets in prompts, metadata, provider events, session entries, or docs examples.
161
166
  - The event broadcaster is in-memory, live-only, and bounded per subscriber by `SubscribeOptions`. It adds no dependency, timer, filesystem/network discovery, worker, or durable queue.
162
167
 
168
+ ## Durable interruption
169
+
170
+ Set `runState` with a host-owned `CheckpointStore`, stable `definitionRevision`, and `interruptBeforeTool: true` to suspend at a persisted pre-side-effect boundary. A suspended result has `status: "suspended"`, a redacted `interruption`, and `runState.version`; it releases session resources before returning.
171
+
172
+ ```ts
173
+ const result = await session.run("Publish draft", {
174
+ runState: { checkpoints, definitionRevision: "2026-07-20.1", interruptBeforeTool: true },
175
+ });
176
+ if (result.status === "suspended") {
177
+ await resumeAgentRun(agent, { runId: result.runId, sessionId: result.sessionId }, {
178
+ decision: "approve", expectedVersion: result.runState!.version!,
179
+ }, { checkpoints, definitionRevision: "2026-07-20.1" });
180
+ }
181
+ ```
182
+
183
+ Resume requires exact checkpoint ownership, version, agent fingerprint, and revision. Prism CAS-claims approval before work, rechecks normal guardrail/permission/validation/limit paths, and marks a pending tool dispatched before its side effect. `createAgentRunLifecycle()` wraps the same core path for server/MCP hosts: adapters pass only authorized ownership, status returns only `{ state, version }`, and `resolveAgent()` supplies current agent/revision. Remote restart requires both checkpoint and session stores to be durable. A crash after that mark is ambiguous and is never replayed automatically; use host tool idempotency keyed by `runId`/`toolCallId` or resolve it manually. Checkpoints contain bounded redacted state plus session/leaf references, never provider objects, callbacks, signals, credentials, or raw secrets. Only built-in loop options are durable; custom `AgentLoopStrategy` rejects before provider work.
184
+
185
+ ## Secure composition
186
+
187
+ `createSecureAgent()` is optional; `createAgent()` remains explicit and backward-compatible. Secure composition requires an ID, non-empty definition revision, exact non-empty ownership, redactor, permission and trust policies, finite explicit limits, a host `ToolArgumentValidator`, non-empty schema for every tool, and checkpoints. It builds a duplicate-error registry, rejects missing schemas, always enables durable pre-tool interruption, and reuses normal provider/request policies without discovery or background work.
188
+
189
+ Per-run options may narrow `limits` and append `guardrails`; they cannot replace secure ownership, redaction, validator, or durable checkpoint policy. Every active tool is trust-checked then permission-checked before validation and its side effect. See [`examples/secure-agent.ts`](../examples/secure-agent.ts).
190
+
191
+ ## Guardrails
192
+
193
+ `AgentConfig.guardrails` applies typed input, output, tool-input, and tool-output checks to every run. `RunOptions.guardrails` appends checks for one run. Input checks run before session append; configured output checks buffer provider content until allowed, so blocked content is never emitted or stored. See [Guardrails](guardrails.md).
194
+
163
195
  ## Related APIs
164
196
 
165
197
  - [Public contracts](public-contracts.md): `Agent`, `AgentSession`, `RunOptions`, and `AgentEvent` contracts.
@@ -172,6 +204,7 @@ await agent.createSession().run("Hi", { model: overrideModel });
172
204
  - [Middleware hooks](middleware-hooks.md): hooks that configured assembly/runtime can run.
173
205
  - [CLI/RPC](cli-rpc.md): terminal and JSONL adapters over this runtime.
174
206
  - [Workflows](workflows.md): optional DAG orchestration that calls `AgentSession.run()` for agent nodes.
207
+ - [A2A interoperability](a2a.md): direct text exposure calls `AgentSession.run()`; durable/rich/reconnect behavior uses host `A2ATaskLifecycle` over existing checkpoints/persistence, never an in-memory runtime cache.
175
208
 
176
209
  `AgentConfig.loop` and `RunOptions.loop` select a replaceable per-run control loop (`singleShotLoop` default, or `generate-validate-revise` with host callbacks); see [Agent loops](agent-loops.md). `RunOptions.loop` wins over `AgentConfig.loop`. Built-in loops emit the same normal turn/message envelope around provider turns, and both add the first run input to live history once after the first provider turn so later turns see the same transcript shape.
177
210
 
@@ -218,9 +218,18 @@ const providers = createOpenAIProviderPackage({ apiKey });
218
218
  - Never log passphrases, derived keys, or decrypted credential payloads.
219
219
  - Live keychain tests are opt-in (`PRISM_TEST_KEYCHAIN=1`); default `npm test` stays offline.
220
220
 
221
+ ## MCP authentication boundary
222
+
223
+ MCP credentials remain host inputs: resolve them before constructing client `requestInit` or inside server `resolveAuthInfo`. Stateful server `resolveIdentity` receives validated SDK auth metadata only to derive a stable non-secret principal ID. Never copy access/refresh tokens into MCP resource/prompt/sampling/elicitation payloads, telemetry, errors, or session bindings; Prism does not refresh or persist MCP OAuth automatically.
224
+
225
+ ## Web adapter credential boundary
226
+
227
+ `@arnilo/prism-web-tools` accepts explicit callbacks or `CredentialResolver`. Brave resolves `subscription_token`; Exa and Firecrawl resolve `api_key` immediately before each fixed-origin request. Keys never enter tool arguments/results, URLs, provider metadata, errors, telemetry, or prompts. Use separate least-privilege credentials and do not forward MCP/provider tokens between adapters.
228
+
221
229
  ## Related APIs
222
230
 
223
231
  - [Credentials and redaction](credentials-and-redaction.md): core resolver helpers and `refreshOAuthCredential()`
232
+ - [Web search, fetch, and extraction](web-tools.md): late-bound Brave/Exa/Firecrawl credentials
224
233
  - [Security/auth/trust](settings-auth-trust-security.md): host-owned settings/credentials boundaries
225
234
  - [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): Plan 056 threat model and conformance matrix rows 7–10
226
235
  - `@arnilo/prism`: `CredentialResolver`, `OAuthCredentialStore`, `createMemoryCredentialStore()`
@@ -241,7 +241,7 @@ Minimum production guidance:
241
241
 
242
242
  - **Branch context:** implement `SessionStore.readBranchPath(query)` with an ancestor query / recursive CTE. Treat `SessionStore.list(sessionId)` as an O(n) development fallback only.
243
243
  - **Cursor pagination:** every `query*` method should honor `cursor`, `limit`, and `order`. Encode cursors from indexed columns such as `(timestamp, id)`, `(started_at, id)`, `(recorded_at, id)`, or `(run_id, sequence)`; never use offset pagination for long sessions.
244
- - **Batch appends:** `SessionStore.append()` is single-entry because the runtime advances one branch leaf at a time. Hosts may batch inside their DB/ledger adapters for `RunLedger` rows, but the adapter must preserve per-run event order and must not acknowledge writes before durable enqueue/commit.
244
+ - **Batch appends:** `SessionStore.append()` stays single-entry because runtime advances one branch leaf at a time. Optional `createBatchedRunLedger()` wraps any ledger with bounded FIFO/backpressure and explicit `write_through`, `flush_on_terminal`, or crash-loss-capable `buffered` acknowledgement semantics; SQLite/PostgreSQL defaults remain direct durable writes.
245
245
  - **Event sequence allocation:** allocate a monotonic `sequence` per `run_id` when inserting `prism_agent_events`. Use it with `run_id` for stable event timeline pagination when timestamps collide.
246
246
  - **Run/event/usage query shapes:** runs page by `(session_id, started_at, id)` or `(branch_id, started_at, id)`; events page by `(run_id, sequence)` or `(session_id, timestamp, id)`; usage pages by `(run_id, recorded_at, id)` or `(session_id, recorded_at, id)`.
247
247
  - **Host-owned sizing:** hosts own connection pools, transaction timeouts, page-size caps, queue/batch size, retention jobs, partitioning, and tenant/account/user isolation. Prism does not guess production limits.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- `@arnilo/prism-evals` adds optional deterministic scorers, immutable datasets, live post-run scoring, and bounded batch experiments over `AgentRunResult`. Scores are finite numbers in `[0, 1]` with optional reason/metadata and linkage to run/session/trace/experiment IDs.
5
+ `@arnilo/prism-evals` adds optional deterministic scorers, immutable datasets, bounded persistence-trace grading, explicit host model judges, pairwise comparisons, CI thresholds, live post-run scoring, and batch experiments over `AgentRunResult`. Scores are finite numbers in `[0, 1]` with optional reason/metadata and linkage to run/session/trace/experiment IDs.
6
6
 
7
7
  ## When to use it
8
8
 
@@ -18,6 +18,10 @@ Use this package when a host needs offline quality checks or sampled live scorin
18
18
  | `runExperiment` | `agent`, dataset, scorers, bounded `concurrency`, optional store/ownership |
19
19
  | `createMemoryEvaluationStore` | optional seed records |
20
20
  | `appendEvaluationFeedback` | `RunFeedbackStore`, `EvaluationStore`, feedback fields, and 1–64 known evaluation IDs |
21
+ | `createPersistenceTraceResolver` | explicit `ProductionPersistenceStore`, exact session/run/ownership, page/byte bounds |
22
+ | `createModelJudge` | host judge callback, stable rubric/version, timeout/attempt/output bounds |
23
+ | `runComparison` | immutable dataset, 2–8 named candidates by default, pairwise scorers |
24
+ | `assertEvaluationThreshold` / `serializeEvaluationReport` | mean/failure/per-scorer gates and bounded redacted JSON |
21
25
 
22
26
  ## Outputs / response / events
23
27
 
@@ -112,11 +116,30 @@ console.log(report.aggregate.meanScore, linked.evaluationIds);
112
116
  - Scorers receive result/item data only. Credentials, tools, and workspace access are not provided unless the host deliberately closes over them.
113
117
  - Records pass through `SecretRedactor` / `secrets` before store append.
114
118
  - Queries filter by ownership scope. Feedback linkage additionally requires tenant plus account/user and the feedback store re-verifies the run.
115
- - Experiment concurrency defaults to `1` and is capped at `32`. Scoring can reference run IDs without duplicating unbounded event payloads.
119
+ - Experiment concurrency defaults to `1` and is capped at `32`. Datasets cap at 10,000 items.
120
+ - Trace reads default to 100 rows × 20 pages with a 4 MiB aggregate cap (hard: 1,000 × 100 and 32 MiB). Repeated/missing cursors, identity drift, ownership drift, and overflow fail closed before scoring.
121
+ - Model judges are host callbacks, not providers: Prism passes rubric/version plus bounded target only—never credential resolvers, tools, or workspace. Defaults are one attempt, 30 seconds, and 16 KiB output; failures become redacted evaluation records.
122
+ - Pairwise candidates are sorted by name, executed once per item, compared in stable item/pair/scorer order, and record ties/failures without choosing a winner. Candidate and scorer outputs have byte caps.
123
+ - `assertEvaluationThreshold()` throws `ERR_PRISM_EVAL_THRESHOLD`; an uncaught error gives CI a non-zero exit. Keep model-judge/live gates credential-gated and outside the network-free default suite. `serializeEvaluationReport()` bounds/redacts checked-in artifacts.
124
+
125
+ ## Trace, judge, comparison, and CI example
126
+
127
+ ```ts
128
+ const traceResolver = createPersistenceTraceResolver(persistence);
129
+ const judge = createModelJudge({
130
+ id: "quality", rubric: "Score factual quality from 0 to 1", rubricVersion: "2026-07-20",
131
+ judge: hostStructuredJudge,
132
+ });
133
+ const evaluations = await scoreRun({ result, scorers: [judge], traceResolver, ownership });
134
+ const comparison = await runComparison({ dataset, candidates: { baseline, candidate }, scorers: [preference] });
135
+ assertEvaluationThreshold(report, { minimumMean: 0.9, maximumFailures: 0 });
136
+ ```
137
+
138
+ `traceResolver` is explicit; no arbitrary run search occurs. `baseline`/`candidate` are host functions returning `AgentRunResult`. See `examples/evaluation-gate.ts` for a network-free gate.
116
139
 
117
140
  ## Related APIs
118
141
 
119
142
  - [Agent/session runtime](agent-session-runtime.md): `AgentRunResult` and `session.run()`
120
143
  - [Runs and usage ledger](runs-and-usage.md): run/session identity for score linkage
121
- - [Observability](observability.md): trace/run metadata hosts may copy into `traceId`
144
+ - [Observability](observability.md): use `onTraceReference` or bounded `traceId(runId)` to supply `ScoreRunOptions.traceId`; evaluation telemetry emits no reason/explanation content
122
145
  - [Release and install](release-and-install.md): optional package install
@@ -0,0 +1,75 @@
1
+ # Guardrails
2
+
3
+ ## What it does
4
+
5
+ Guardrails are typed, fail-closed checks at input, completed provider output, tool input, and raw tool output boundaries. `session.run()` evaluates configured stages through one core runner; `dispatchToolCall()` uses same runner for direct, MCP-server, and workflow tool calls.
6
+
7
+ ## When to use it
8
+
9
+ Use guardrails to block unsafe prompts, model responses, tool arguments, or tool results before their next boundary. Use a redactor for known secrets. Do not treat guardrails as a sandbox, secret detector, permission policy, or validation replacement.
10
+
11
+ ## Inputs / request
12
+
13
+ ```ts
14
+ import type { Guardrail, Guardrails } from "@arnilo/prism";
15
+
16
+ const pii: Guardrail<"input"> = {
17
+ name: "pii",
18
+ stage: "input",
19
+ evaluate: ({ value }) => JSON.stringify(value).includes("SSN")
20
+ ? { action: "tripwire", reason: "pii" }
21
+ : { action: "allow" },
22
+ };
23
+
24
+ const guardrails: Guardrails = { input: [pii], maxConcurrency: 1 };
25
+ ```
26
+
27
+ Set `AgentConfig.guardrails` for every session run or `RunOptions.guardrails` to append checks for one run. `DispatchToolCallOptions.guardrails`, workflow `RunWorkflowOptions.guardrails`, and MCP server `CreatePrismMcpServerOptions.guardrails` apply tool stages to direct calls. A stage has `Guardrail<"input" | "output" | "tool_input" | "tool_output">`, a name, optional revision, and `evaluate(context)` result.
28
+
29
+ Decisions are `allow`, `block`, `tripwire`, or `interrupt`. Evaluation defaults to declaration-order sequential. `maxConcurrency` may be 1–16; records are emitted in declaration order. Thrown or malformed decisions become a fail-closed tripwire. Decision reasons are capped at 4 KiB and metadata at 16 KiB after JSON normalization and optional redaction.
30
+
31
+ ## Outputs / response / events
32
+
33
+ Every evaluated guard produces a redacted `guardrail_decision` `AgentEvent` with a bounded `GuardrailRecord`. Optional OpenTelemetry instrumentation records only controlled stage/action on a short run-child span; guardrail name, reason, and metadata are excluded. An input or output terminal decision rejects the run with `GuardrailError`; `tripwire` stops remaining evaluation. A tool-input or tool-output `block` returns a redacted blocked `ToolResult`; a `tripwire` rejects the enclosing run. `interrupt` is reserved for durable runs and currently fails closed with `ERR_PRISM_GUARDRAIL_INTERRUPT_UNAVAILABLE`.
34
+
35
+ Ordering is fixed:
36
+
37
+ 1. input before session append, compaction, or provider work;
38
+ 2. provider output is privately collected, then output checks run before any assistant message event or persistence;
39
+ 3. tool input runs after tool-call middleware normalization and before lookup, permission, validation, execution policy, and side effect;
40
+ 4. tool output runs after the side effect but before redaction, tool events, ledger rows, transcript append, or next turn.
41
+
42
+ With no output guardrails, provider streaming retains existing behavior. With output guardrails, message events are buffered until the completed provider turn is allowed.
43
+
44
+ ## Request/response example
45
+
46
+ ```json
47
+ {
48
+ "event": {
49
+ "type": "guardrail_decision",
50
+ "record": { "guardrail": "pii", "stage": "input", "action": "tripwire", "reason": "pii" }
51
+ }
52
+ }
53
+ ```
54
+
55
+ ## Implementation example
56
+
57
+ ```ts
58
+ const agent = createAgent({ model, provider, guardrails: { input: [pii], output: [responseGuard] } });
59
+ await agent.createSession().run("Draft reply", { guardrails: { toolInput: [commandGuard] } });
60
+ ```
61
+
62
+ ## Extension and configuration notes
63
+
64
+ Guardrails are callbacks supplied by the host. Prism does not discover, load, retry, or persist callback code. `createSecureAgent()` keeps configured guardrails and only appends run-level checks; it never lets a run remove secure defaults. Custom loops receive guarded `LoopContext.generate()` and `LoopContext.dispatchToolCall()`; host code that directly calls a provider or `ToolDefinition.execute()` is outside the runtime boundary.
65
+
66
+ ## Security and performance notes
67
+
68
+ Output buffering prevents blocked provider content from reaching subscribers, session entries, ledgers, parsers, delegation, or tools. Tool-output checks receive raw results but Prism discards blocked raw output before event, ledger, transcript, or MCP exposure. Redaction replaces exact known values only; it is not general secret detection. Parallel checks receive an abort signal, but callback code must honor it to stop in-flight work.
69
+
70
+ ## Related APIs
71
+
72
+ - [Agent/session runtime](agent-session-runtime.md)
73
+ - [Tools](tools.md)
74
+ - [Agent events](agent-events.md)
75
+ - [Host security](host-security.md)