@arnilo/prism 0.7.0 → 0.8.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 (99) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +12 -11
  3. package/dist/agent-approval.d.ts +4 -0
  4. package/dist/agent-approval.js +5 -1
  5. package/dist/agent-run-lifecycle.js +29 -5
  6. package/dist/agent-run-state.d.ts +13 -1
  7. package/dist/agent-run-state.js +12 -2
  8. package/dist/agent-session/helpers.js +6 -1
  9. package/dist/agent-session/session/assemble.js +126 -5
  10. package/dist/agent-session/session/persist.d.ts +16 -0
  11. package/dist/agent-session/session/persist.js +58 -2
  12. package/dist/agent-session/session/provider-round.d.ts +3 -3
  13. package/dist/agent-session/session/provider-round.js +12 -6
  14. package/dist/agent-session/session/tool-round.js +4 -1
  15. package/dist/agent-session/session/types.d.ts +12 -1
  16. package/dist/agent-session/session.d.ts +2 -0
  17. package/dist/agent-session/session.js +2 -0
  18. package/dist/checkpoints.js +7 -11
  19. package/dist/contracts-core/content.d.ts +5 -0
  20. package/dist/contracts-core/loop.d.ts +42 -0
  21. package/dist/contracts-core/run-limits.d.ts +2 -0
  22. package/dist/contracts-protocol.d.ts +15 -3
  23. package/dist/contracts-run-state.d.ts +26 -5
  24. package/dist/evidence-grounding.d.ts +29 -0
  25. package/dist/evidence-grounding.js +162 -0
  26. package/dist/host-composition.d.ts +13 -0
  27. package/dist/host-composition.js +33 -2
  28. package/dist/index.d.ts +6 -2
  29. package/dist/index.js +3 -1
  30. package/dist/provider-events.d.ts +3 -1
  31. package/dist/provider-events.js +2 -2
  32. package/dist/providers/transport.d.ts +3 -1
  33. package/dist/providers/transport.js +36 -0
  34. package/dist/redaction.js +18 -2
  35. package/dist/run-bundle.d.ts +89 -0
  36. package/dist/run-bundle.js +149 -0
  37. package/dist/testing/state-concurrency-conformance.js +5 -12
  38. package/docs/ag-ui.md +5 -0
  39. package/docs/agent-loops.md +33 -0
  40. package/docs/agent-session-runtime.md +4 -3
  41. package/docs/coding-security.md +1 -0
  42. package/docs/coding-tools.md +0 -1
  43. package/docs/compaction-observational-memory.md +1 -1
  44. package/docs/connected-apps.md +116 -0
  45. package/docs/context-and-skills.md +13 -0
  46. package/docs/core.md +1 -1
  47. package/docs/diagrams.md +6 -6
  48. package/docs/document-reader.md +9 -9
  49. package/docs/documents.md +32 -11
  50. package/docs/durable-runs.md +87 -0
  51. package/docs/enterprise-postgres-state.md +4 -0
  52. package/docs/execution-timeline.md +6 -0
  53. package/docs/guardrails.md +33 -0
  54. package/docs/history/079-messaging-primitive-review.md +391 -0
  55. package/docs/history/080-messaging-followon-primitive-review.md +234 -0
  56. package/docs/history/081-connected-apps-primitive-review.md +74 -0
  57. package/docs/history/083-prism-work-primitive-review.md +84 -0
  58. package/docs/history/084-primitive-review.md +96 -0
  59. package/docs/history/085-honesty-and-cut-primitive-review.md +91 -0
  60. package/docs/history/README.md +5 -0
  61. package/docs/history/release-handoffs.md +38 -0
  62. package/docs/host-compositions.md +8 -6
  63. package/docs/host-security.md +2 -2
  64. package/docs/index.md +47 -29
  65. package/docs/live-testing.md +5 -3
  66. package/docs/mcp-tools.md +1 -0
  67. package/docs/messaging-channel-operations.md +166 -0
  68. package/docs/messaging-channels.md +150 -0
  69. package/docs/migrate-to-0.8.md +124 -0
  70. package/docs/migration.md +30 -0
  71. package/docs/model-registry.md +12 -2
  72. package/docs/openapi-tools.md +1 -1
  73. package/docs/operations.md +1 -3
  74. package/docs/options-index.md +34 -2
  75. package/docs/peer-dependencies.md +6 -6
  76. package/docs/postgres-persistence.md +1 -1
  77. package/docs/provider-layer.md +2 -2
  78. package/docs/provider-packages.md +20 -20
  79. package/docs/providers/neuralwatt.md +5 -1
  80. package/docs/rag.md +1 -1
  81. package/docs/release-and-install.md +52 -46
  82. package/docs/run-bundle.md +92 -0
  83. package/docs/runs-and-usage.md +14 -0
  84. package/docs/server.md +2 -0
  85. package/docs/sheets.md +9 -9
  86. package/docs/signal-channel.md +112 -0
  87. package/docs/speech.md +5 -1
  88. package/docs/sqlite-persistence.md +1 -1
  89. package/docs/telegram-channel.md +157 -0
  90. package/docs/testing.md +2 -2
  91. package/docs/wiki.md +1 -1
  92. package/docs/work-artifacts-and-review.md +1 -1
  93. package/docs/work-connectors.md +9 -9
  94. package/docs/work-sandbox.md +115 -0
  95. package/docs/work-tools.md +38 -16
  96. package/package.json +5 -3
  97. package/templates/business-worker/manifest.json +2 -1
  98. package/templates/business-worker/src/agent.ts.tmpl +1 -1
  99. package/templates/business-worker/src/tests/agent.test.ts.tmpl +1 -1
@@ -22,8 +22,8 @@ One row per declaration. `Unlocks` names the subpath whose import reaches the pe
22
22
  | `zod` | `^3.25.0 \|\| ^4.0.0` | no | `@arnilo/prism-ag-ui` | `./acp` | `npm i zod` | no |
23
23
  | `@nanonets/graft` | `^0.16.0 \|\| ^0.18.0` | yes | `@arnilo/prism-memory` | `./graft` | `npm i @nanonets/graft` | no |
24
24
  | `@dietrichgebert/ponytail` | `^4.9.0` | yes | `@arnilo/prism-coding-tools` | `./ponytail` | `npm i @dietrichgebert/ponytail` | no |
25
- | `mammoth` | `^1.8.0` | yes | `@arnilo/prism-coding-tools` | `./document-reader` | `npm i mammoth` | no |
26
- | `pdf-parse` | `^2.4.5` | yes | `@arnilo/prism-coding-tools` | `./document-reader` | `npm i pdf-parse` | no |
25
+ | `mammoth` | `^1.8.0` | yes | `@arnilo/prism-work` | `./document-reader` | `npm i mammoth` | no |
26
+ | `pdf-parse` | `^2.4.5` | yes | `@arnilo/prism-work` | `./document-reader` | `npm i pdf-parse` | no |
27
27
  | `e2b` | `2.49.1` | yes | `@arnilo/prism-coding-tools` | `./security` | `npm i e2b@2.49.1` | yes |
28
28
  | `better-sqlite3` | `^13.0.3` | yes | `@arnilo/prism-core` | `./sessions/sqlite`, `./governance/prompts` | `npm i better-sqlite3` | no |
29
29
  | `pg` | `^8.23.0` | yes | `@arnilo/prism-core` | `./sessions/postgres`, `./enterprise/postgres`, `./governance/prompts` | `npm i pg` | yes |
@@ -51,7 +51,7 @@ Two peers are pinned to an exact version instead of a range, because the pin is
51
51
  - **No secrets are read by the peers.** Prism resolves credentials through host providers and redacts them at the boundary; peers only ever receive a resolved connection string or model object. See [Credentials and redaction](credentials-and-redaction.md) and [Host security guide](host-security.md).
52
52
  - **Nothing is installed implicitly.** Optional peers are never auto-installed by npm; a missing one fails closed at the call site with a typed error naming the peer and the subpath. Required peers (today only `zod`) are installed by npm with the package.
53
53
 
54
- Test-only dependencies are *not* peers. `playwright-core` appears in `@arnilo/prism-office` as a devDependency only, because the office diagrams embed takes a host-supplied iframe and the sole consumer is the gated live draw.io conformance test.
54
+ Test-only dependencies are *not* peers. `playwright-core` appears in `@arnilo/prism-work` as a devDependency only, because the office diagrams embed takes a host-supplied iframe and the sole consumer is the gated live draw.io conformance test.
55
55
 
56
56
  ## Implementation example
57
57
 
@@ -66,7 +66,7 @@ npm i @arnilo/prism-core pg
66
66
  npm i @arnilo/prism-core @nats-io/transport-node @nats-io/jetstream
67
67
 
68
68
  # Document reader: pick the parser you need (both are independent)
69
- npm i @arnilo/prism-coding-tools pdf-parse mammoth
69
+ npm i @arnilo/prism-work pdf-parse mammoth
70
70
  ```
71
71
 
72
72
  ```ts
@@ -80,7 +80,7 @@ const tools = await createBrowserTools({ browser });
80
80
  ## Extension and configuration notes
81
81
 
82
82
  - A peer is an *implementation the host owns*. When a peer's default wiring is not what you want, pass your own implementation instead of installing theirs: the document reader accepts host parsers (`createReadTool({ documentReader })`), the memory `/graft` resolver accepts an explicit package root, and the browser surfaces accept a host `Browser`.
83
- - Subpaths that need a peer isolate that import, so importing another subpath of the same package never evaluates it. The office family is the extreme case: zero peers, because it takes structural inputs.
83
+ - Subpaths that need a peer isolate that import, so importing another subpath of the same package never evaluates it. The office family is the extreme case: zero peers, because it takes structural inputs. `@arnilo/prism-channels` also has no third-party peers: Telegram uses native `fetch`, and signal-cli is a host-operated binary rather than an npm peer.
84
84
  - Adding a peer to a Prism package is a release-gated change: the declaration must be optional unless a hard dependency's own peer forces it (the `zod` case), and exact pins must come with a version-gate or compatibility rationale.
85
85
 
86
86
  ## Security and performance notes
@@ -93,4 +93,4 @@ const tools = await createBrowserTools({ browser });
93
93
 
94
94
  - [Release and install](release-and-install.md): install profiles that pair with each peer.
95
95
  - [Configuration options index](options-index.md): the option surfaces each peer unlocks.
96
- - Package-level detail: [Coding tools](coding-tools.md), [Core runtime](core.md), [Session stores](session-stores.md), [Browser automation](browser-automation.md), [Document reader](document-reader.md), [Graft](graft.md), [Ponytail](ponytail.md), [Provider packages](provider-packages.md).
96
+ - Package-level detail: [Coding tools](coding-tools.md), [Core runtime](core.md), [Session stores](session-stores.md), [Browser automation](browser-automation.md), [Document reader](document-reader.md), [Graft](graft.md), [Ponytail](ponytail.md), [Provider packages](provider-packages.md), [Messaging channels](messaging-channels.md).
@@ -60,7 +60,7 @@ Hosts own TLS (`ssl` in `poolConfig`), credentials, connection limits, and backu
60
60
  | `RunLedger.append*` | Inserts run/event/tool/usage rows; events receive monotonic per-run `sequence` values. |
61
61
  | `events` | Durable `AgentEventSource`; `LISTEN`/`NOTIFY` only wakes exact owned indexed reads, while polling remains recovery fallback. |
62
62
  | `ProductionPersistenceStore.query*` | Parameterized cursor pagination on indexed columns with tenant/account/user filters. |
63
- | `checkpoints` | Generic versioned `CheckpointStore` backed by `prism_checkpoints`; ownership, CAS/fencing checks, bounded pagination, and workflow suspended/denied/schedule/state/replay values without a schema migration. |
63
+ | `checkpoints` | Generic versioned `CheckpointStore` backed by `prism_checkpoints`; ownership, CAS/fencing checks, bounded pagination, and workflow suspended/denied/schedule/state/replay values without a schema migration. A load or delete under a non-matching ownership scope reads as absent and a cross-scope write fails as a generic `ERR_PRISM_CHECKPOINT_CONFLICT` (plan 080 Task 3) — no ownership-shaped existence oracle. |
64
64
  | `leases` | Atomic `LeaseStore` backed by `prism_leases`; database-clock expiry, opaque renew/release token, monotonic takeover fence. |
65
65
  | `close()` | Ends the pool when the adapter created it from `connectionString`. |
66
66
 
@@ -127,7 +127,7 @@ const agent = createAgent({ model: { provider: own.id, model: "demo" }, provider
127
127
 
128
128
  - Registry `resolve()` returns the matching provider/model or throws before any provider `generate()` call.
129
129
  - Provider event helpers return plain `ProviderEvent` objects.
130
- - `providerError()` converts unknown errors to redacted `ErrorInfo` through `errorToErrorInfo()` and preserves safe string/number `code` fields for retry classification.
130
+ - `providerError()` converts unknown errors to redacted `ErrorInfo`, preserves safe string/number `code` fields for retry classification, and stamps advisory `failureClass` (`quota`, `auth`, `rate_limited`, `transient`, `permanent`, or `unknown`) from already-captured status/body evidence. It never changes retry behavior or exposes response bodies/headers; see [Runs and usage ledger](runs-and-usage.md#provider-failure-classes).
131
131
  - `createMockProvider()` returns an `AIProvider` whose `generate()` yields the scripted events in order and checks `request.signal?.aborted` before each event.
132
132
  - The agent/session runtime passes its per-run abort signal as `ProviderRequest.signal`. `ProviderRequestOptions.structuredOutput` requests provider-native JSON-schema output when the model declares `capabilities.structuredOutput`; unsupported models fail before fetch. Timeouts are host-owned: pass `RunOptions.signal`/host abort controllers; retries are runtime-owned via `AgentConfig.retry`/`RunOptions.retry`. Provider-level timeout/retry hints were removed in 0.1.5.
133
133
 
@@ -200,7 +200,7 @@ for await (const event of resolvedProvider.generate({
200
200
  - `createMockProvider()` uses scripted events only: no timers, credentials, SDKs, or network.
201
201
  - Do not hide real secrets in mock event fixtures. If an error event must include secret-like text, use fake placeholders and redaction helpers.
202
202
  - `providerError(error, secrets)` only redacts the provided secret values. It is not a general secret scanner.
203
- - Providers may set safe `ErrorInfo.code` values such as `429`, `503`, or `ETIMEDOUT`; retry policy code treats them as classification hints, not trusted provider metadata.
203
+ - Providers may set safe `ErrorInfo.code` values such as `429`, `503`, or `ETIMEDOUT`; retry policy code treats them as classification hints, not trusted provider metadata. The shared `classifyProviderFailure()` transport helper maps those already-captured values to advisory run outcome metadata; `unknown` is always the fallback.
204
204
 
205
205
  ## Related APIs
206
206
 
@@ -25,26 +25,26 @@ Do not use provider packages as a package manager, credential store, env loader,
25
25
 
26
26
  | adapter package | version |
27
27
  | --- | --- |
28
- | `@arnilo/prism-providers/ai-sdk` | 0.7.0 |
29
- | `@arnilo/prism-providers/alibaba` | 0.7.0 |
30
- | `@arnilo/prism-providers/anthropic` | 0.7.0 |
31
- | `@arnilo/prism-providers/azure` | 0.7.0 |
32
- | `@arnilo/prism-providers/bedrock` | 0.7.0 |
33
- | `@arnilo/prism-providers/clinepass` | 0.7.0 |
34
- | `@arnilo/prism-providers/commandcode` | 0.7.0 |
35
- | `@arnilo/prism-providers/deepseek` | 0.7.0 |
36
- | `@arnilo/prism-providers/google` | 0.7.0 |
37
- | `@arnilo/prism-providers/hyper` | 0.7.0 |
38
- | `@arnilo/prism-providers/kimi` | 0.7.0 |
39
- | `@arnilo/prism-providers/model-discovery` | 0.7.0 |
40
- | `@arnilo/prism-providers/neuralwatt` | 0.7.0 |
41
- | `@arnilo/prism-providers/ollama` | 0.7.0 |
42
- | `@arnilo/prism-providers/openai` | 0.7.0 |
43
- | `@arnilo/prism-providers/opencode-go` | 0.7.0 |
44
- | `@arnilo/prism-providers/openrouter` | 0.7.0 |
45
- | `@arnilo/prism-providers/vertex` | 0.7.0 |
46
- | `@arnilo/prism-providers/xai` | 0.7.0 |
47
- | `@arnilo/prism-providers/zai` | 0.7.0 |
28
+ | `@arnilo/prism-providers/ai-sdk` | 0.8.0 |
29
+ | `@arnilo/prism-providers/alibaba` | 0.8.0 |
30
+ | `@arnilo/prism-providers/anthropic` | 0.8.0 |
31
+ | `@arnilo/prism-providers/azure` | 0.8.0 |
32
+ | `@arnilo/prism-providers/bedrock` | 0.8.0 |
33
+ | `@arnilo/prism-providers/clinepass` | 0.8.0 |
34
+ | `@arnilo/prism-providers/commandcode` | 0.8.0 |
35
+ | `@arnilo/prism-providers/deepseek` | 0.8.0 |
36
+ | `@arnilo/prism-providers/google` | 0.8.0 |
37
+ | `@arnilo/prism-providers/hyper` | 0.8.0 |
38
+ | `@arnilo/prism-providers/kimi` | 0.8.0 |
39
+ | `@arnilo/prism-providers/model-discovery` | 0.8.0 |
40
+ | `@arnilo/prism-providers/neuralwatt` | 0.8.0 |
41
+ | `@arnilo/prism-providers/ollama` | 0.8.0 |
42
+ | `@arnilo/prism-providers/openai` | 0.8.0 |
43
+ | `@arnilo/prism-providers/opencode-go` | 0.8.0 |
44
+ | `@arnilo/prism-providers/openrouter` | 0.8.0 |
45
+ | `@arnilo/prism-providers/vertex` | 0.8.0 |
46
+ | `@arnilo/prism-providers/xai` | 0.8.0 |
47
+ | `@arnilo/prism-providers/zai` | 0.8.0 |
48
48
  <!-- generated:package-truth:providers end -->
49
49
 
50
50
 
@@ -95,7 +95,7 @@ after resolved fields so per-call values and overrides win.
95
95
  | --- | --- |
96
96
  | Provider stream | Prism text, thinking (`delta.reasoning_content` → `providerThinkingDelta`), tool-call delta/final, `usage`, `done`, redacted `error` with HTTP-status `code` for retry classification. |
97
97
  | Block preservation | Text, thinking, assistant `tool_call` → `tool_calls`, `tool_result` → role `tool` messages, images when `capabilities.input` includes `"image"`. |
98
- | Model catalog | Featured aliases declare provider id, display name, context limit, text/image input support, tools, reasoning/fast variants, streaming, implicit cache, and NeuralWatt JSON-mode compat metadata where documented. |
98
+ | Model catalog | Featured aliases declare provider id, display name, context limit, text/image input support, tools, reasoning/fast variants, streaming, implicit cache, NeuralWatt JSON-mode compat metadata where documented, and conformance-derived `toolCallStrictness: "strict"`. |
99
99
  | Pricing | Static aliases do not guess rates. Exact per-alias input/output/cache-read prices are advertised by NeuralWatt's `/v1/models` response and mapped by `listNeuralWattModels()` when present. |
100
100
  | SSE comments | `: energy` / `: cost` comment lines are parsed by `neuralWattEventsWithTelemetry()` into `neuralwatt:telemetry` events; the standard `neuralWattEvents()` stream (used by `generate()`) tolerates them without spurious events. |
101
101
  | `[DONE]` | Terminates the stream; final `providerDone(usage)` always emitted on a clean stream. |
@@ -292,6 +292,10 @@ prior tool turns through a multi-turn loop:
292
292
  the stringified `tool_result` — matching the OpenAI requirement that a tool result
293
293
  follows the call that produced it. `tool_result` blocks must appear in `role: "tool"
294
294
  messages; `tool_call` blocks must be the only content on their assistant message.
295
+ - **Catalog evidence.** Curated aliases carry `capabilities.toolCallStrictness: "strict"`
296
+ because the network-free conformance fixtures cover parallel indexed calls,
297
+ schema-shaped arguments, and empty `{}` arguments. This is adapter evidence, not a
298
+ provider SLA: hosts still validate every call. See [tool-call coverage](../_evidence/toolcall-coverage-2026-09-17.md).
295
299
 
296
300
  ### Energy and cost telemetry
297
301
 
package/docs/rag.md CHANGED
@@ -172,7 +172,7 @@ const found = await retrieveContext("leave balance", {
172
172
  - `createRagContextProvider()` derives its query from latest user text by default; pass a fixed string or callback for host-controlled query generation.
173
173
  - `createResourceDocumentLoader({ loader })` calls one host-owned `ResourceLoader`; it scans nothing and performs no filesystem or network I/O itself. Pass the host's permission/trust context to that loader.
174
174
  - `createWebFetchDocumentLoader({ fetcher })` accepts an already-configured `@arnilo/prism-web-tools` fetch adapter. It never opens a socket, rejects file/local/private/IP-literal URLs, and carries normalized citation/trust metadata forward. The fetch adapter still owns DNS/SSRF policy.
175
- - `pdfParser` is deliberately limited to bounded, uncompressed PDF text. Provide a host parser through `Parser` for compressed, scanned, or complex PDFs; do not silently index partial text. Hosts that need OCR wrap `createMistralOcrParser` from `@arnilo/prism-coding-tools/document-reader` — it is never the default parser and never runs unless the host passes it to `replaceDocument({ parser })`.
175
+ - `pdfParser` is deliberately limited to bounded, uncompressed PDF text. Provide a host parser through `Parser` for compressed, scanned, or complex PDFs; do not silently index partial text. Hosts that need OCR wrap `createMistralOcrParser` from `@arnilo/prism-work/document-reader` — it is never the default parser and never runs unless the host passes it to `replaceDocument({ parser })`.
176
176
  - Package is available directly or via the `@arnilo/prism-memory` family tarball; installation does not create an embedder, vector store, loader, parser, or context provider.
177
177
 
178
178
  ## Security and performance notes
@@ -5,27 +5,28 @@
5
5
  ## What it does
6
6
 
7
7
 
8
- Prism's current **0.7.0** line has **10 publishable manifests**: the root `@arnilo/prism` core package plus **9 workspace packages** — **19 provider adapters** (19 provider adapter subpaths inside the `@arnilo/prism-providers` family), 3 `prism-*` family/profile packages, and 6 capability packages. (Generated by `node scripts/package-truth.mjs` → `scripts/package-truth.json` — the manifest-derived single source for counts, provider membership, umbrella closures, and profile closures.) The last lockstep cut was 0.3.0; Decision B now publishes changed packages independently inside `^0.3.0` — the plan 039 changed-package cut moved root `@arnilo/prism` and every plan-035+ changed package to **0.3.1**, and the plan 050 changed-package cut moved root plus four changed packages to **0.3.2**; the plan 041-044 changed-package cut moves root to **0.3.3** with `@arnilo/prism-memory@0.3.2` (composite recall scoring), `@arnilo/prism-evals@0.3.1` (trace-to-dataset curation), the three session-store packages at **0.3.1** (run-ledger `promptVersion` provenance), and the initial `@arnilo/prism-prompts@0.0.1` (independent opt-in, outside `prism-all`); plan 054 consolidation then folded `@arnilo/prism-browser` and `@arnilo/prism-obscura` into the `@arnilo/prism-web-tools` family as `/browser` and `/obscura` subpaths, folded `@arnilo/prism-rag`, both compaction strategies, `@arnilo/prism-graft`, and `@arnilo/prism-wiki` into the `@arnilo/prism-memory` family as `/rag`, `/compaction/llm`, `/compaction/observational-memory`, `/graft`, and `/wiki` subpaths (deleting the `@arnilo/prism-compaction` profile), and folded all 17 `@arnilo/prism-provider-*` packages into the `@arnilo/prism-providers` family as `/<adapter>` subpaths (Azure/Bedrock/Vertex stop being special all-only manifests); independent publication continues inside `^0.3.0` ranges (which satisfy 0.3.1, 0.3.2, and 0.3.3). This page describes how they are packed, what each tarball contains, how to install them, the required non-optional **caret** `@arnilo/prism@^0.7.0` peer range, the release workflow, and the offline test budget. The measurable 1.0 readiness gates (command-per-gate) live in [`0.1.0-readiness.md`](history/./0.1.0-readiness.md).
8
+ Prism's current **0.8.0** line has **11 publishable manifests**: the root `@arnilo/prism` core package plus **10 workspace packages** — **19 provider adapters** (19 provider adapter subpaths inside the `@arnilo/prism-providers` family), 4 `prism-*` family packages, and 6 capability packages. (Generated by `node scripts/package-truth.mjs` → `scripts/package-truth.json` — the manifest-derived single source for counts, provider membership, umbrella closures, and profile closures.) The last lockstep cut was 0.3.0; Decision B now publishes changed packages independently inside `^0.3.0` — the plan 039 changed-package cut moved root `@arnilo/prism` and every plan-035+ changed package to **0.3.1**, and the plan 050 changed-package cut moved root plus four changed packages to **0.3.2**; the plan 041-044 changed-package cut moves root to **0.3.3** with `@arnilo/prism-memory@0.3.2` (composite recall scoring), `@arnilo/prism-evals@0.3.1` (trace-to-dataset curation), the three session-store packages at **0.3.1** (run-ledger `promptVersion` provenance), and the initial `@arnilo/prism-prompts@0.0.1` (independent opt-in, outside `prism-all`); plan 054 consolidation then folded `@arnilo/prism-browser` and `@arnilo/prism-obscura` into the `@arnilo/prism-web-tools` family as `/browser` and `/obscura` subpaths, folded `@arnilo/prism-rag`, both compaction strategies, `@arnilo/prism-graft`, and `@arnilo/prism-wiki` into the `@arnilo/prism-memory` family as `/rag`, `/compaction/llm`, `/compaction/observational-memory`, `/graft`, and `/wiki` subpaths (deleting the `@arnilo/prism-compaction` profile), and folded all 17 `@arnilo/prism-provider-*` packages into the `@arnilo/prism-providers` family as `/<adapter>` subpaths (Azure/Bedrock/Vertex stop being special all-only manifests); independent publication continues inside `^0.3.0` ranges (which satisfy 0.3.1, 0.3.2, and 0.3.3). This page describes how they are packed, what each tarball contains, how to install them, the required non-optional **caret** `@arnilo/prism@^0.8.0` peer range, the release workflow, and the offline test budget. The measurable 1.0 readiness gates (command-per-gate) live in [`0.1.0-readiness.md`](history/./0.1.0-readiness.md).
9
9
 
10
10
  Core `@arnilo/prism` ships runtime, CLI, templates, and docs. Every code package has a required `@arnilo/prism` peer inside the Decision B window — the caret current spec is `@arnilo/prism@^0.3.3` and every declared window peer satisfies it: packages republishing in the plan 050 cut carry `^0.3.2`; the plan 039 set keeps `^0.3.1`; unchanged packages keep their `^0.3.0` peer; profiles are pure manifests. The plan 050 republished set declares the required `@arnilo/prism@^0.3.2` peer; the plan 041-044 republished set keeps its existing `^0.3.0` window peer; unchanged packages keep their prior window. Installation activates no provider, listener, database, browser, credential, or tool capability.
11
11
 
12
- The **0.6.0 and 0.7.0 lockstep cuts** each moved all ten manifests together: the current declared peer is `@arnilo/prism@^0.7.0` on every package, and `release.mjs` lockstep mode fails closed on any internal range that merely satisfies the cut version instead of matching it. The independent-publication history above (0.3.x, 0.4.x, 0.5.x) describes how the line grew when packages moved separately.
12
+ The **0.8.0 lockstep cut** moved all **eleven** manifests together: the current declared peer is `@arnilo/prism@^0.8.0` on every package, and `release.mjs` lockstep mode fails closed on any internal range that merely satisfies the cut version instead of matching it. The **0.6.0 and 0.7.0 lockstep cuts** each moved the then-ten manifests together. The independent-publication history above (0.3.x, 0.4.x, 0.5.x) describes how the line grew when packages moved separately.
13
13
 
14
14
  <!-- generated:package-truth:inventory begin -->
15
- **10 publishable manifests** — root `@arnilo/prism` plus 9 workspace packages (3 `prism-*` family packages, 6 capability packages). Generated by `node scripts/package-truth.mjs --emit-docs` — do not hand-edit.
15
+ **11 publishable manifests** — root `@arnilo/prism` plus 10 workspace packages (4 `prism-*` family packages, 6 capability packages). Generated by `node scripts/package-truth.mjs --emit-docs` — do not hand-edit.
16
16
 
17
17
  | package | version | notes |
18
18
  | --- | --- | --- |
19
- | `@arnilo/prism` | 0.7.0 | core — runtime, CLI/RPC, templates, docs |
20
- | `@arnilo/prism-coding-tools` | 0.7.0 | family — /agent, /security, /document-reader, /openapi, /computer-use-linux, /dev, /caveman, /ponytail, /impeccable subpaths |
21
- | `@arnilo/prism-core` | 0.7.0 | family — /runtime, /sessions, /governance, /credentials, /enterprise, /work, /validation subpaths |
22
- | `@arnilo/prism-providers` | 0.7.0 | family — all provider adapters as `/<adapter>` subpaths |
23
- | `@arnilo/prism-acp-agent` | 0.7.0 | capability — ACP adapter |
24
- | `@arnilo/prism-ag-ui` | 0.7.0 | capability — AG-UI/A2A/A2UI adapter |
25
- | `@arnilo/prism-mcp` | 0.7.0 | capability — MCP client/server/OAuth interop |
26
- | `@arnilo/prism-memory` | 0.7.0 | capability — memory plus /rag, /compaction/*, /fabric, /graft, /wiki subpaths |
27
- | `@arnilo/prism-office` | 0.7.0 | capability — /documents, /sheets, /diagrams subpaths |
28
- | `@arnilo/prism-web-tools` | 0.7.0 | capability — Brave/Exa/Firecrawl plus peer-gated /browser and /obscura subpaths |
19
+ | `@arnilo/prism` | 0.8.0 | core — runtime, CLI/RPC, templates, docs |
20
+ | `@arnilo/prism-channels` | 0.8.0 | family — transport-neutral messaging runtime, durable journal, pairing and one-use approvals; official /telegram (private DMs, opt-in granted groups/topics) and experimental pinned signal-cli /signal |
21
+ | `@arnilo/prism-coding-tools` | 0.8.0 | family — /agent, /security, /openapi, /computer-use-linux, /dev, /caveman, /ponytail, /impeccable subpaths |
22
+ | `@arnilo/prism-core` | 0.8.0 | family — /runtime, /sessions, /governance, /credentials, /enterprise, /validation subpaths |
23
+ | `@arnilo/prism-providers` | 0.8.0 | family — all provider adapters as `/<adapter>` subpaths |
24
+ | `@arnilo/prism-acp-agent` | 0.8.0 | capability — ACP adapter |
25
+ | `@arnilo/prism-ag-ui` | 0.8.0 | capability — AG-UI/A2A/A2UI adapter |
26
+ | `@arnilo/prism-mcp` | 0.8.0 | capability — MCP client/server/OAuth interop |
27
+ | `@arnilo/prism-memory` | 0.8.0 | capability — memory plus /rag, /compaction/*, /fabric, /graft, /wiki subpaths |
28
+ | `@arnilo/prism-web-tools` | 0.8.0 | capability — Brave/Exa/Firecrawl plus peer-gated /browser and /obscura subpaths |
29
+ | `@arnilo/prism-work` | 0.8.0 | capability — /connectors, /documents, /sheets, /diagrams, /document-reader, /sandbox, /skills, /tools subpaths |
29
30
  <!-- generated:package-truth:inventory end -->
30
31
 
31
32
 
@@ -34,30 +35,30 @@ The **0.6.0 and 0.7.0 lockstep cuts** each moved all ten manifests together: the
34
35
 
35
36
  | adapter package | version |
36
37
  | --- | --- |
37
- | `@arnilo/prism-providers/ai-sdk` | 0.7.0 |
38
- | `@arnilo/prism-providers/alibaba` | 0.7.0 |
39
- | `@arnilo/prism-providers/anthropic` | 0.7.0 |
40
- | `@arnilo/prism-providers/azure` | 0.7.0 |
41
- | `@arnilo/prism-providers/bedrock` | 0.7.0 |
42
- | `@arnilo/prism-providers/clinepass` | 0.7.0 |
43
- | `@arnilo/prism-providers/commandcode` | 0.7.0 |
44
- | `@arnilo/prism-providers/deepseek` | 0.7.0 |
45
- | `@arnilo/prism-providers/google` | 0.7.0 |
46
- | `@arnilo/prism-providers/hyper` | 0.7.0 |
47
- | `@arnilo/prism-providers/kimi` | 0.7.0 |
48
- | `@arnilo/prism-providers/model-discovery` | 0.7.0 |
49
- | `@arnilo/prism-providers/neuralwatt` | 0.7.0 |
50
- | `@arnilo/prism-providers/ollama` | 0.7.0 |
51
- | `@arnilo/prism-providers/openai` | 0.7.0 |
52
- | `@arnilo/prism-providers/opencode-go` | 0.7.0 |
53
- | `@arnilo/prism-providers/openrouter` | 0.7.0 |
54
- | `@arnilo/prism-providers/vertex` | 0.7.0 |
55
- | `@arnilo/prism-providers/xai` | 0.7.0 |
56
- | `@arnilo/prism-providers/zai` | 0.7.0 |
38
+ | `@arnilo/prism-providers/ai-sdk` | 0.8.0 |
39
+ | `@arnilo/prism-providers/alibaba` | 0.8.0 |
40
+ | `@arnilo/prism-providers/anthropic` | 0.8.0 |
41
+ | `@arnilo/prism-providers/azure` | 0.8.0 |
42
+ | `@arnilo/prism-providers/bedrock` | 0.8.0 |
43
+ | `@arnilo/prism-providers/clinepass` | 0.8.0 |
44
+ | `@arnilo/prism-providers/commandcode` | 0.8.0 |
45
+ | `@arnilo/prism-providers/deepseek` | 0.8.0 |
46
+ | `@arnilo/prism-providers/google` | 0.8.0 |
47
+ | `@arnilo/prism-providers/hyper` | 0.8.0 |
48
+ | `@arnilo/prism-providers/kimi` | 0.8.0 |
49
+ | `@arnilo/prism-providers/model-discovery` | 0.8.0 |
50
+ | `@arnilo/prism-providers/neuralwatt` | 0.8.0 |
51
+ | `@arnilo/prism-providers/ollama` | 0.8.0 |
52
+ | `@arnilo/prism-providers/openai` | 0.8.0 |
53
+ | `@arnilo/prism-providers/opencode-go` | 0.8.0 |
54
+ | `@arnilo/prism-providers/openrouter` | 0.8.0 |
55
+ | `@arnilo/prism-providers/vertex` | 0.8.0 |
56
+ | `@arnilo/prism-providers/xai` | 0.8.0 |
57
+ | `@arnilo/prism-providers/zai` | 0.8.0 |
57
58
  <!-- generated:package-truth:providers end -->
58
59
 
59
60
 
60
- Core ships `dist`, docs, templates, and `CHANGELOG.md`; code packages ship compiled output, README, license, and changelog. Family/profile packages ship manifest, README, and changelog. `@arnilo/prism-providers` is the unified provider family: all provider adapters ship as `dist/<adapter>` subpaths in one tarball (Azure/Bedrock/Vertex included), with the required `@arnilo/prism` peer as the only dependency and `@ai-sdk/provider` an optional peer of `/ai-sdk`. `@arnilo/prism-core` provides the unified runtime, sessions, governance, credentials, enterprise persistence, and work integration family package. `@arnilo/prism-web-tools` provides the unified web tools family: root Brave/Exa/Firecrawl research tools plus `/browser` (Playwright-peer gated) and `/obscura` (host-binary + MCP gated) subpaths. `@arnilo/prism-memory` provides the unified memory and context family: root working/vector memory plus `/rag` (with `/rag/loaders` and `/rag/parsers`), `/compaction/llm`, `/compaction/observational-memory`, `/graft` (`@nanonets/graft` optional-peer gated), and `/wiki` subpaths, including the `prism-wiki` bin and bundled skills. `@arnilo/prism-coding-tools/dev` ships the loopback dev inspector — the `prism-dev` bin, the `prism dev` CLI composition, and the `/dev/cli` export the core CLI delegates to for `prism dev` (plan 040 Tasks 4–5); dev tooling is developer-time only and must never be the production API boundary. `@arnilo/prism-core/governance/prompts` (plan 042) is the versioned prompt registry: an explicit host opt-in with no first-party package depending on it — unlike `@arnilo/prism-memory` (a family member) and `@arnilo/prism-core/governance/evals` (used by the promotion helper as an optional peer). `@arnilo/prism-office` (plan 054 Task 8, absorbing plans 051–053) is the unified office family: `/documents`, `/sheets`, and `/diagrams` subpaths in one tarball with exact-pinned `@office-open/{docx,xlsx,pptx,xml}` regular dependencies and no optional peer (the diagrams embed is browser-agnostic; `playwright-core` stays a devDependency for the gated live draw.io test). Importing one subpath never evaluates another. The three draft names `@arnilo/prism-documents`/`sheets`/`diagrams` were never published.
61
+ Core ships `dist`, docs, templates, and `CHANGELOG.md`; code packages ship compiled output, README, license, and changelog. Family/profile packages ship manifest, README, and changelog. `@arnilo/prism-providers` is the unified provider family: all provider adapters ship as `dist/<adapter>` subpaths in one tarball (Azure/Bedrock/Vertex included), with the required `@arnilo/prism` peer as the only dependency and `@ai-sdk/provider` an optional peer of `/ai-sdk`. `@arnilo/prism-core` provides the unified runtime, sessions, governance, credentials, and enterprise persistence family package. `@arnilo/prism-channels` provides the transport-neutral messaging runtime, durable journal and pairing surface; it has only the required `@arnilo/prism` peer, while its SQLite/PostgreSQL conformance drivers are development-only. `@arnilo/prism-web-tools` provides the unified web tools family: root Brave/Exa/Firecrawl research tools plus `/browser` (Playwright-peer gated) and `/obscura` (host-binary + MCP gated) subpaths. `@arnilo/prism-memory` provides the unified memory and context family: root working/vector memory plus `/rag` (with `/rag/loaders` and `/rag/parsers`), `/compaction/llm`, `/compaction/observational-memory`, `/graft` (`@nanonets/graft` optional-peer gated), and `/wiki` subpaths, including the `prism-wiki` bin and bundled skills. `@arnilo/prism-coding-tools/dev` ships the loopback dev inspector — the `prism-dev` bin, the `prism dev` CLI composition, and the `/dev/cli` export the core CLI delegates to for `prism dev` (plan 040 Tasks 4–5); dev tooling is developer-time only and must never be the production API boundary. `@arnilo/prism-core/governance/prompts` (plan 042) is the versioned prompt registry: an explicit host opt-in with no first-party package depending on it — unlike `@arnilo/prism-memory` (a family member) and `@arnilo/prism-core/governance/evals` (used by the promotion helper as an optional peer). `@arnilo/prism-work` is the unified work family: `/connectors`, `/documents`, `/sheets`, `/diagrams`, `/document-reader`, `/sandbox`, `/skills`, and `/tools` subpaths in one tarball. Office dependencies are exact-pinned; `/document-reader` keeps `mammoth` and `pdf-parse` optional and fail-closed, while `playwright-core` remains a devDependency for gated draw.io testing. Importing `/connectors` never evaluates document dependencies.
61
62
 
62
63
  ## When to use it
63
64
 
@@ -74,6 +75,7 @@ Consumers install the core package for the runtime and add first-party packages
74
75
  | Install core only | `npm install @arnilo/prism` |
75
76
  | Scaffold a minimal project | `npx --package @arnilo/prism prism init my-agent [--provider openai] [--template <name>] [--list-templates] [--with-workflows] [--with-evals]` |
76
77
  | Install core runtime & persistence family | `npm install @arnilo/prism @arnilo/prism-core` |
78
+ | Install messaging channels | `npm install @arnilo/prism @arnilo/prism-channels` |
77
79
  | Install core + all provider adapters | `npm install @arnilo/prism @arnilo/prism-providers` (import `@arnilo/prism-providers/<adapter>`) |
78
80
  | Install minimal runtime (replaces `@arnilo/prism`) | `npm install @arnilo/prism @arnilo/prism-core @arnilo/prism-memory` |
79
81
  | Install compaction strategies only | `npm install @arnilo/prism @arnilo/prism-memory` |
@@ -89,9 +91,11 @@ Consumers install the core package for the runtime and add first-party packages
89
91
  | Install RAG retrieval (memory family `/rag`) | `npm install @arnilo/prism @arnilo/prism-memory` |
90
92
  | Install the Wiki CLI and skills (memory family `/wiki`) | `npm install @arnilo/prism @arnilo/prism-memory` (`npx prism-wiki --help`) |
91
93
  | Install the Graft context-graph bridge (`/graft`, host supplies the CLI) | `npm install @arnilo/prism @arnilo/prism-memory` (+ host-installed `@nanonets/graft`) |
92
- | Install document/spreadsheet/presentation engine | `npm install @arnilo/prism @arnilo/prism-office` (import `@arnilo/prism-office/documents`) |
93
- | Install spreadsheet and CSV data engine | `npm install @arnilo/prism @arnilo/prism-office` (import `@arnilo/prism-office/sheets`) |
94
- | Install draw.io embed client & diagram engine | `npm install @arnilo/prism @arnilo/prism-office` (import `@arnilo/prism-office/diagrams`) |
94
+ | Install work connectors | `npm install @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/connectors`) |
95
+ | Install document/spreadsheet/presentation engine | `npm install @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/documents`) |
96
+ | Install spreadsheet and CSV data engine | `npm install @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/sheets`) |
97
+ | Install draw.io embed client & diagram engine | `npm install @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/diagrams`) |
98
+ | Install bounded document reader | `npm install @arnilo/prism @arnilo/prism-work` (optional `pdf-parse mammoth`; import `@arnilo/prism-work/document-reader`) |
95
99
  | Build everything (core + workspaces) | `npm run build` |
96
100
  | Delete all build output (explicit one-shot, see build notes) | `npm run clean` |
97
101
  | Run the default (network-free) test suite | `npm test` |
@@ -145,7 +149,7 @@ A packed tarball contains only public compiled output and release files:
145
149
  - Code packages ship `README.md`, `LICENSE`, and `CHANGELOG.md`; family/profile packages ship `README.md` and `CHANGELOG.md`.
146
150
  - The core tarball additionally ships the full `docs/` directory (the docs hub), `templates/init/`, and the `templates/` gallery (e.g. `deep-research`) used by `prism init`.
147
151
  - `dist/cli.js` and the `bin` link in core.
148
- - **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.7.0.tgz`; family packages produce `arnilo-prism-core-0.7.0.tgz`, `arnilo-prism-coding-tools-0.7.0.tgz`, `arnilo-prism-providers-0.7.0.tgz` (all 19 adapters inside), `arnilo-prism-memory-0.7.0.tgz`, `arnilo-prism-web-tools-0.7.0.tgz`, and `arnilo-prism-office-0.7.0.tgz`; capability packages like `arnilo-prism-mcp-0.7.0.tgz` carry their own package version. Independent-package tags carry their own version. The CLI bin name `prism` is unaffected by the package name (`npx prism` still works; npm allows the bin field to differ from the package name).
152
+ - **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.8.0.tgz`; family packages produce `arnilo-prism-core-0.8.0.tgz`, `arnilo-prism-coding-tools-0.8.0.tgz`, `arnilo-prism-providers-0.8.0.tgz` (all 19 adapters inside), `arnilo-prism-channels-0.8.0.tgz`, `arnilo-prism-memory-0.8.0.tgz`, and `arnilo-prism-web-tools-0.8.0.tgz`; capability packages like `arnilo-prism-mcp-0.8.0.tgz` and `arnilo-prism-work-0.8.0.tgz` carry their own package version. Independent-package tags carry their own version. The CLI bin name `prism` is unaffected by the package name (`npx prism` still works; npm allows the bin field to differ from the package name).
149
153
 
150
154
  Excluded from every tarball by `files` negation:
151
155
 
@@ -246,13 +250,13 @@ Frozen by Phase 12 Task 0 in `scripts/phase12-freeze-manifest.json` (schema gate
246
250
 
247
251
  | Runtime | Supported | Measured in CI |
248
252
  | --- | --- | --- |
249
- | Node | 22, 24 (`engines.node >=22`) | `verify` runs the full `sdk:ready` gate on Node 24; `node22-compat` builds and imports every public root `exports` target on Node 22. Node 20 support was dropped in 0.6.0 (`dev-006`; Node 20 reached upstream end-of-life 2026-04-30); 0.7.0 keeps the same floor. |
253
+ | Node | 22, 24 (`engines.node >=22`) | `verify` runs the full `sdk:ready` gate on Node 24; `node22-compat` builds and imports every public root `exports` target on Node 22. Node 20 support was dropped in 0.6.0 (`dev-006`; Node 20 reached upstream end-of-life 2026-04-30); 0.8.0 keeps the same floor. |
250
254
  | PostgreSQL | 16 (`pgvector/pgvector:pg16`) | `postgres-integration` service container |
251
255
 
252
256
  ## Extension and configuration notes
253
257
 
254
258
 
255
- - **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional **caret** `@arnilo/prism@^0.7.0` peer (the lockstep 0.7.0 cut rewrote every internal range; the version-literal gate rejects a declared range that only satisfies the cut version) (`peerDependenciesMeta` must not mark `@arnilo/prism` optional; other peers such as `playwright-core` may be optional). **Peer-version policy (plan 030, Decision B — independent packages):** internal ranges stay inside the caret window of the cut they shipped in, so a package may patch independently while consumers remain on a compatible 0.x line. A package outside that window is refused by the release gate until the next coordinated peer bump. Inside the workspace each package also declares `"@arnilo/prism": "file:../.."` in `devDependencies` so `npm install` resolves the peer locally; that devDependency is stripped from consumer installs and is not a runtime dependency.
259
+ - **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional **caret** `@arnilo/prism@^0.8.0` peer (the lockstep 0.8.0 cut rewrote every internal range; the version-literal gate rejects a declared range that only satisfies the cut version) (`peerDependenciesMeta` must not mark `@arnilo/prism` optional; other peers such as `playwright-core` may be optional). **Peer-version policy (plan 030, Decision B — independent packages):** internal ranges stay inside the caret window of the cut they shipped in, so a package may patch independently while consumers remain on a compatible 0.x line. A package outside that window is refused by the release gate until the next coordinated peer bump. Inside the workspace each package also declares `"@arnilo/prism": "file:../.."` in `devDependencies` so `npm install` resolves the peer locally; that devDependency is stripped from consumer installs and is not a runtime dependency.
256
260
  - **Public access.** All 56 manifests (root + 55 workspace packages: 49 code packages + 6 pure-manifest family/profile packages — the 10 `prism-*` family/profile set is the 6 pure-manifest profiles plus the 4 code packages `prism-caveman`, `prism-impeccable`, `prism-openapi-tools`, `prism-ponytail`) declare `"publishConfig": { "access": "public" }`; the publisher also passes `--access public` explicitly because scoped packages otherwise default to restricted on first publish.
257
261
  - **Shipped vs repository docs.** The npm tarball ships `docs/` pages linked from `docs/index.md` (public API, security, migration, providers, install). It excludes `docs/_evidence/` (per-phase evidence freezes, including `release-0.2.7-evidence.md`), `docs/release-*-evidence.md`, and `docs/api-page-template.md`. Those files remain in git for audit. `dist/__tests__` and `*.map` stay excluded.
258
262
  - **Map retention knob.** Source maps are emitted locally but stripped from tarballs by `!dist/**/*.map`. Removing that `files` negation ships maps in releases (larger tarballs, better consumer stack traces).
@@ -288,6 +292,8 @@ Frozen by Phase 12 Task 0 in `scripts/phase12-freeze-manifest.json` (schema gate
288
292
  - `NEURALWATT_API_KEY` for `@arnilo/prism-providers/neuralwatt`
289
293
  - `OPENCODE_API_KEY` for `@arnilo/prism-providers/opencode-go`
290
294
  - `PRISM_LIVE_WEB=1` — gates `@arnilo/prism-web-tools` restricted live tests; provider calls additionally require `PRISM_BRAVE_SEARCH_TOKEN`, `PRISM_EXA_API_KEY`, or `PRISM_FIRECRAWL_API_KEY`. Run `npm run test:live -w @arnilo/prism-web-tools`; default tests use injected fake fetch only.
295
+ - `PRISM_LIVE_TELEGRAM=1` — gates `@arnilo/prism-channels` Telegram live probe (`TELEGRAM_BOT_TOKEN`, optional chat id). Skips without credentials; never creates contacts or accounts. Run `npm run test:live -w @arnilo/prism-channels`.
296
+ - `PRISM_LIVE_SIGNAL=1` — gates `@arnilo/prism-channels` Signal live probe (`PRISM_LIVE_SIGNAL_SOCKET`, `PRISM_LIVE_SIGNAL_ACCOUNT`, `PRISM_LIVE_SIGNAL_TERMS_VERSION`, optional recipient UUID). Skips without credentials; never creates contacts or accounts.
291
297
  - `PRISM_TEST_PLAYWRIGHT=1` or `PRISM_LIVE_PLAYWRIGHT=1` — gates `@arnilo/prism-web-tools/browser` protected Playwright adversarial matrix (`npm run test:live -w @arnilo/prism-web-tools/browser`). Host must supply a pinned Chromium binary via `playwright-core`. Default tests use fake Playwright APIs only; enabled but missing browser fails closed.
292
298
  - `PRISM_TEST_DOCKER_SANDBOX=1` — gates `@arnilo/prism-coding-tools/security` protected Docker matrix. Requires host-preloaded digest-pinned `PRISM_TEST_DOCKER_IMAGE` and absolute `PRISM_TEST_DOCKER_BIN` (optional `PRISM_TEST_DOCKER_USER`). Prism never pulls/builds the image during default tests. Missing prerequisites fail closed when the gate is enabled; disabled gate skips safely.
293
299
  - `PRISM_LIVE_CANARIES=1` — gates `scripts/live-canary.mjs`, used only by scheduled/manual `.github/workflows/live-canaries.yml` in protected `live-canaries` environment. It requires provider endpoint/key/model, MCP endpoint/token, A2A endpoint/token, and Brave token environment entries; performs four probes plus at most one MCP session DELETE; caps provider output at one token, each response at 64 KiB, each request at 15 seconds (30 seconds hard), and emits only aggregate kind/status/code/duration. Disabled gate skips before network; enabled but incomplete configuration fails closed.
@@ -299,7 +305,7 @@ Frozen by Phase 12 Task 0 in `scripts/phase12-freeze-manifest.json` (schema gate
299
305
  - Provider live tests read the API key from the env only when both gates are set; the key is used as a bearer token and never logged. `assertNoSecretLeak` verifies the key value does not appear in any streamed event. The compaction placeholders still carry no real credentials.
300
306
  - Enforced by `network-free-guard.test.ts` (default suite stays network-free) and by source-scanning meta-tests that assert each `live.test.ts` keeps its `skip:` guard.
301
307
  - **Supply-chain workflows.** `.github/workflows/security.yml` runs CodeQL JavaScript/TypeScript SAST, PR-only dependency review, `npm audit`, SPDX 2.3 generation, exact license allow/deny policy, tracked-source plus unpacked-tarball credential-pattern scans, and seven-day SBOM retention. Dependabot opens bounded weekly npm and GitHub Actions updates. Every third-party action uses a full immutable revision; workflows never use `pull_request_target`. GitHub repository secret scanning/push protection and required-check branch rules remain repository settings because GitHub provides no equivalent checked-in workflow toggle; enable `security / codeql`, `security / supply-chain`, PR dependency review, and release checks on protected branches.
302
- - **Sandbox/browser protected workflow.** `.github/workflows/sandbox-browser.yml` is scheduled/manual only in protected `sandbox-browser` environment. It runs network-free adversarial eval fixtures by default, optionally enables digest-pinned Docker and Playwright gates via repository variables (`PRISM_TEST_DOCKER_IMAGE`, `PRISM_ENABLE_PLAYWRIGHT_GATE`), plus Obscura (`PRISM_ENABLE_OBSCURA_GATE`, `PRISM_OBSCURA_BIN`) and draw.io (`PRISM_ENABLE_DRAWIO_GATE`, `PRISM_TEST_DRAWIO_URL`) legs whose suites live in `@arnilo/prism-web-tools` and `@arnilo/prism-office` — the draw.io leg runs through `scripts/live-matrix.mjs` with `PRISM_LIVE_FILTER=office/drawio-live`; it receives no provider/npm/OIDC secrets, and uploads only a redacted aggregate status artifact (7-day retention).
308
+ - **Sandbox/browser protected workflow.** `.github/workflows/sandbox-browser.yml` is scheduled/manual only in protected `sandbox-browser` environment. It runs network-free adversarial eval fixtures by default, optionally enables digest-pinned Docker and Playwright gates via repository variables (`PRISM_TEST_DOCKER_IMAGE`, `PRISM_ENABLE_PLAYWRIGHT_GATE`), plus Obscura (`PRISM_ENABLE_OBSCURA_GATE`, `PRISM_OBSCURA_BIN`) and draw.io (`PRISM_ENABLE_DRAWIO_GATE`, `PRISM_TEST_DRAWIO_URL`) legs whose suites live in `@arnilo/prism-web-tools` and `@arnilo/prism-work` — the draw.io leg runs through `scripts/live-matrix.mjs` with `PRISM_LIVE_FILTER=work/drawio-live`; it receives no provider/npm/OIDC secrets, and uploads only a redacted aggregate status artifact (7-day retention).
303
309
  - **Release attestations.** Tag publication uses GitHub OIDC with only `contents: read`, `id-token: write`, and `attestations: write` at the publish job. `actions/attest-build-provenance` attests every `.tgz` and `sbom.spdx.json` before npm publication; npm still receives `--provenance`. Verify downloaded attestations with GitHub CLI and npm signatures on the release host.
304
310
  - **Install smoke is offline.** The install-smoke test packs core + every package into a temp dir and installs tarballs with `--offline --no-audit --no-fund` into a fresh project. External dependencies are satisfied from the lockfile-backed npm cache prepared by `npm ci`; any attempted uncached registry fetch fails the gate.
305
311
  - **Packed-install e2e journeys (plan 012 Task 3).** `scripts/e2e-enterprise-journey.test.mjs` and `scripts/e2e-coding-journey.test.mjs` pack the first-party packages for their journey, install the exact tarballs into a fresh consumer project, and run the journey script inside that consumer — public exports only, no workspace-relative resolution (asserted per run). The **enterprise journey** composes OIDC identity → OPA policy decision (durable ledger) → agent run with durable events (memory, or real PostgreSQL when `PRISM_TEST_POSTGRES_URL` is set **and the `pg` peer is installed in that consumer**) → batched approval → OpenAPI side effect with idempotency → artifact upload + signed delivery, with policy-deny and hash-mismatch fail-closed injections. The durable leg is peer-gated and never assumed from the env alone: with `PRISM_TEST_POSTGRES_URL` ambient but `pg` unresolvable (the default, since `pg` is a peer of `@arnilo/prism-core` and the consumer installs only Prism tarballs), the fixture prints `SKIP durable postgres leg: …` and runs the memory event source instead of dying with `ERR_MODULE_NOT_FOUND`; the test reports that line as a TAP diagnostic and asserts it, so the skip can never be silent. Scope the env to the phase that needs it (`PRISM_TEST_POSTGRES_URL=… npm run test:postgres`, or `phase release:gate` in `.github/workflows/release.yml`) rather than exporting it globally. The **coding journey** composes an ACP editor session (init capability negotiation, session new + load/resume) → bounded coding tools (git-aware list/search, glob, read-before-write write, delete, move) → sandboxed process session → forge handoff with idempotent PR creation, with execution-policy and read-before-write denial paths. Each fixture asserts the installed version matches the packed manifest graph and stays within the frozen `e2eJourneyFixtureMsCeiling` (120 s in `scripts/phase12-freeze-manifest.json`).
@@ -333,10 +339,10 @@ Workspace coverage rows used to include the symlinked root core `dist/` (workspa
333
339
  | Fact | Value |
334
340
  | --- | --- |
335
341
  | Workspace include filter | `--test-coverage-include=dist/**` per package (package-local denominator) |
336
- | Workspace discovery | any `*.test.js` under `packages/<name>/dist/**`, nested layouts included (`@arnilo/prism-acp-agent` builds to `dist/src/__tests__`, `@arnilo/prism-office` to `dist/<area>/__tests__`) — all 9 workspace packages are measured |
342
+ | Workspace discovery | any `*.test.js` under `packages/<name>/dist/**`, nested layouts included (`@arnilo/prism-acp-agent` builds to `dist/src/__tests__`, `@arnilo/prism-work` to `dist/<area>/__tests__`) — all 10 workspace packages are measured and artifact keys must match their live manifest names |
337
343
  | Per-package gate | `lines >= threshold` from `scripts/coverage-thresholds.json` (recaptured 2026-09-11 = min of two back-to-back runs − 3pp; the two runs differed by ≤ 0.02pp); branches/functions recorded, not gated. `phase23-coverage` also fails when a row names a package that is not in the live workspace graph — retired rows must be pruned |
338
344
  | Protected exceptions | `@arnilo/prism-core` only (durable postgres/NATS legs need `PRISM_TEST_POSTGRES_URL` / `PRISM_TEST_NATS_URL`); exempt from the gate and reported separately with the reason. Env- or capability-gated legs elsewhere (memory postgres, coding-tools native sandbox, provider live legs) skip as protected skips but their packages stay gated on the measured run |
339
- | Artifact | `scripts/coverage-summary.json` (gitignored, CI-retained): per-package `lines`/`branches`/`functions`/`denominatorFiles`/`threshold`/`pass`/`protectedException` + `belowThreshold`; a row whose child failed additionally carries `status`/`exitCode`/`tail` (a redacted tail of the child's output) |
345
+ | Artifact | `scripts/coverage-summary.json` (gitignored, CI-retained): its package-key set must exactly equal live workspace `package.json` names; each row carries `lines`/`branches`/`functions`/`denominatorFiles`/`threshold`/`pass`/`protectedException` + `belowThreshold`; a row whose child failed additionally carries `status`/`exitCode`/`tail` (a redacted tail of the child's output) |
340
346
  | Fail-closed | a non-protected package below its threshold, a suite failure, or a run producing no coverage data exits non-zero; a missing threshold entry is a config error |
341
347
  | Overrides | `PRISM_COVERAGE_THRESHOLDS`, `PRISM_COVERAGE_ARTIFACT` (used by the gate regression) |
342
348
 
@@ -354,7 +360,7 @@ A new workspace package must add an evidence-based threshold entry (or a `protec
354
360
  | `protected` | a documented, permitted gap with a reason (+ required env where applicable) | pass, always visible |
355
361
  | `blocked` | a required release surface cannot be attested (required env absent, or evidence missing) | **fail closed** — `release.mjs gate` refuses to release |
356
362
 
357
- Surfaces: core `npm test` (counts and the skip total come from the latest `phase*-baseline.json` `exitGate.counts` — currently 33 protected/live skips, a frozen floor), `security:threat-suites`, every workspace suite (evidence from `scripts/coverage-summary.json` of the same run; `protectedException` packages are named with their reason), `test:postgres` durable conformance (**required**: `PRISM_TEST_POSTGRES_URL` must be set when `release:gate` runs — the release workflow's verify job declares it on the `release:gate` phase only, so the env never leaks into the env-gated docs demo / durable integration suites of `npm test`, and the `postgres-integration` job runs the suite against a real server; a local release must set it too, exactly like the phase-22 release profile), `test:nats` real JetStream legs (protected, 0.3.0), the `PRISM_LIVE_PROVIDER_TESTS` provider legs (protected, per package), and the four live canaries from `scripts/live-canary.mjs` (provider/MCP/A2A/web; run by the scheduled `live-canaries` workflow with real credentials — recorded `protected`, never `pass`). The manifest cross-references the latest baseline's `exitGate`/`protectedEvidence` so per-phase records stay the source of truth.
363
+ Surfaces: core `npm test` (counts and the skip total come from the latest `phase*-baseline.json` `exitGate.counts` — currently 33 protected/live skips, a frozen floor), `security:threat-suites`, every workspace suite (evidence from `scripts/coverage-summary.json` of the same run; `protectedException` packages are named with their reason), `test:postgres` durable conformance (**required**: `PRISM_TEST_POSTGRES_URL` must be set when `release:gate` runs, and gitignored `scripts/postgres-evidence.json` must be written by a successful `npm run test:postgres` at the current `git rev-parse HEAD`; a phase baseline or stale evidence is blocked. The wrapper records only `gitHead`, capture time, and TAP counts — never a DSN. The release workflow's verify job declares the env on the `release:gate` phase only, so it never leaks into env-gated docs demos / durable integration suites of `npm test`; the `postgres-integration` job runs the suite against a real server; a local release must set it too), `test:nats` real JetStream legs (protected, 0.3.0), the `PRISM_LIVE_PROVIDER_TESTS` provider legs (protected, per package), and the four live canaries from `scripts/live-canary.mjs` (provider/MCP/A2A/web; run by the scheduled `live-canaries` workflow with real credentials — recorded `protected`, never `pass`). The manifest cross-references the latest baseline's `exitGate`/`protectedEvidence` so per-phase records stay the source of truth.
358
364
 
359
365
  Override `PRISM_RELEASE_EVIDENCE` to redirect the manifest (used by the gate regression). The manifest is gitignored and CI-retained (`release-evidence` artifact). A release cannot ship with a required env absent and unexplained — the operator sees every blocked surface in the retained manifest.
360
366
 
@@ -442,7 +448,7 @@ Every release gate maps to an exact enforcement test or command, so the checklis
442
448
  | Root SDK export surface freeze | `public-export-contract.test.ts` `root export surface is frozen` snapshots every value and type export of `src/index.ts` (107 value + 69 type) so any add/remove is a deliberate test update; `every frozen value export resolves at runtime` rebuilds `dist/index.js` and asserts each value export is present (catches build drift), and `every frozen type export appears in the built type declarations` asserts each type export is in `dist/index.d.ts`. |
443
449
  | Examples compile and are listed; runnable demos execute | `npm run typecheck` runs `tsc -p examples --noEmit`; `docs.test.ts` checks every `examples/*.ts` file is listed in `examples/README.md`, then runs demos offline and scans output for secrets. |
444
450
  | Examples run to completion with no secret leakage | `docs.test.ts` `examples_demos_run_to_completion_and_emit_no_secret` runs each demo (Node strips TypeScript types natively) with exit-0 and real-secret scans; `external_app_example_*` pins the DB-backed adapter reference exercising the `RunLedger`, branch-handle checkout, fork, and prior-run resume. |
445
- | Tarball excludes built tests, source maps, and source | `packaging.test.ts` rejects `dist/__tests__/`, `*.map`, `src/`, `plans/`, and internal files; confirms every package ships README/changelog (and code packages ship LICENSE), core ships docs + CLI, and every export target exists. Profile manifests (`prism-base`/`prism-code`/`prism-sdk`/`prism-all`) are retired; hosts install explicit family packages. `@arnilo/prism-office` ships `/documents`, `/sheets`, `/diagrams`. |
451
+ | Tarball excludes built tests, source maps, and source | `packaging.test.ts` rejects `dist/__tests__/`, `*.map`, `src/`, `plans/`, and internal files; confirms every package ships README/changelog (and code packages ship LICENSE), core ships docs + CLI, and every export target exists. Profile manifests (`prism-base`/`prism-code`/`prism-sdk`/`prism-all`) are retired; hosts install explicit family packages. `@arnilo/prism-work` ships isolated `/connectors`, `/documents`, `/sheets`, `/diagrams`, and `/document-reader` subpaths. |
446
452
  | NeuralWatt package/docs/examples release gate | `packaging.test.ts` pins `@arnilo/prism-providers/neuralwatt` package exports/type declarations and `@arnilo/prism-providers` family membership; `docs.test.ts` asserts `docs/index.md` links `providers/neuralwatt.md` and `provider-caching.md`, and that `examples/cache-aware-prompt-assembly.ts` plus `examples/neuralwatt-agent-run.ts` exist and are listed. |
447
453
  | Enterprise PostgreSQL package/docs/example gate | Packaging/install/public-contract tests include `@arnilo/prism-core/enterprise/postgres`; `docs.test.ts` pins its API page, four-store migration/ownership/unknown-outcome/async-router guidance, and `examples/enterprise-postgres-state.ts`; `npm run test:postgres` exercises migration, restart, contention, and cleanup with an explicit database URL. |
448
454
  | Version graph and resumable publication | `release.test.ts` covers exact package/lock/range validation, topological order, registry collisions, dry-run, interrupted reports/resume, clean tagged git state, provenance/public/tag arguments, and token-safe errors. `release:check` and `release:publish` derive the workspace graph without a manual package list. |
@@ -451,7 +457,7 @@ Every release gate maps to an exact enforcement test or command, so the checklis
451
457
  | Legacy registry markers (plan 054 Task 7) | `scripts/phase54-legacy-registry.mjs --dry-run` verifies every retired name's final published version exists and `latest` is unchanged, and that each deprecation URL anchor exists in `docs/migrate-to-0.4.md`, without mutating the registry; `--apply --confirm` pre-flights all 54 entries and fails closed (zero mutations) on any mismatch, then idempotently adds the `legacy` dist-tag and `<0.4.0` deprecation warning (already-correct entries skipped; per-entry status in `release-artifacts/legacy-registry-plan.json` for safe resume). `packaging.test.ts` asserts the generated plan covers all 54 retired names with uniform messages and valid guide anchors; the offline fixture suite `scripts/phase54-legacy-registry.test.mjs` proves the dry-run/apply/resume behavior without network or tokens. |
452
458
  | Formatting, linting, and coverage thresholds | `npm run lint` and `npm run format:check` run Biome (single root `biome.json`, workspaces inherit) and fail on any lint error or unformatted file; `npm run test:coverage` uses Node's built-in `--experimental-test-coverage` with enforced minimums (lines 60 / functions 70 / branches 75) and no third-party service. All three run inside `sdk:ready`. |
453
459
  | Supply-chain and live-canary policy | `supply-chain-security.test.ts` verifies SPDX allow/deny behavior, bounded source/artifact secret detection, credential-free canary reports, timeout/redacted failures, immutable action revisions, no `pull_request_target`, protected live environment, attestation paths, and publish dependency on `supply-chain`; CI adds CodeQL and PR dependency review. |
454
- | Release secret scan covers the tracked release set | `scripts/phase27-release.test.mjs` enumerates `git ls-files` (plus built `packages/prism-core/dist` when present) and passes that explicit list to the unchanged `scanSecrets` — the same tracked set CI scans (`git ls-files -z \| xargs -0 node scripts/scan-secrets.mjs` in `release.yml`/`security.yml`), so no untracked or gitignored working-tree file can fail the gate or mask a tracked finding; a bare `node scripts/scan-secrets.mjs <path>` still walks everything for pre-commit use, and the gate reports the mode, file count, and untracked-but-unignored findings as a note without failing on them. |
460
+ | Release secret scan covers the tracked release set | `scripts/phase27-release.test.mjs` enumerates `git ls-files` (plus built `packages/prism-core/dist` when present) and passes that explicit list to the unchanged `scanSecrets` — the same tracked set CI scans (`git ls-files -z \| xargs -0 node scripts/scan-secrets.mjs` in `release.yml`/`security.yml`), so no untracked or gitignored working-tree file can fail the gate or mask a tracked finding; a bare `node scripts/scan-secrets.mjs <path>` still walks everything for pre-commit use except the two local-only credential names `.gitignore` already excludes (`scripts/live.env`, `*.local.env`), and the gate reports the mode, file count, and untracked-but-unignored findings as a note without failing on them. |
455
461
  | Workflow and script references resolve | `scripts/workflow-liveness.test.mjs` resolves every `-w <pkg>` / `--workspace <pkg>` target in `.github/workflows/*.yml` against the live workspace inventory and every named npm script against that package's manifest (root scripts included for a bare `npm run x`), resolves every `@arnilo/*` specifier in `scripts/**/*.mjs` against the live package and its `exports` subpaths, and rejects any `uses:` reference that is not a full 40-hex commit SHA — the drift class that left `sandbox-browser.yml` building four retired packages, `scripts/fixtures/phase26-coding-journey.mjs` packing `packages/coding-agent`/`-security` and `scripts/benchmark-scenarios/phase11-auth.mjs` importing `@arnilo/prism-openapi-tools`/`-server` after plan 054 folded them, with a positive control for each. |
456
462
  | Network-free + offline test budget | `network-free-guard.test.ts` keeps the default suite network-free; budget pinned `< 60s` (measured baseline above). Install-smoke is offline (`--offline --no-audit --no-fund`, zero registry fetches). |
457
463
  | Core security invariants reaffirmed | Runtime/docs tests hold the trust boundary: **no built-in app tools** (hosts register tools; the core ships only the mock provider and contract helpers), **no hidden provider/credential globals** (providers/credentials are host-owned `AgentConfig` fields, resolved via explicit `providerSource`/`CredentialResolver`), **no auto package discovery** (provider/tool/skill packages are opt-in and individually installed; contribution discovery is realpath-contained and emits inert envelopes the host registers), and **no secret persistence in core** (redaction applies before any `RunLedger`/`SessionStore` append; the ledger gate asserts each message event is written exactly once and redacted). |
@@ -0,0 +1,92 @@
1
+ # Effective run bundle snapshots
2
+
3
+ `snapshotRunBundle()` answers one question for a host harness: *what exactly ran?* It projects the inputs a
4
+ run resolves to — prompt contributions, skills, tools, guardrails, loop, limits, model, storage kinds — into
5
+ frozen JSON with one stable digest, so a harness registry can pin the bundle it evaluated and diff a later run
6
+ against it. It is the inspectable half of the durable-run fingerprint: same inputs, named fields, one hash.
7
+
8
+ ## What it does
9
+
10
+ - Reads only in-process configuration: `agent` (plus optional `AgentSessionConfig` and `RunOptions` overrides).
11
+ - Returns frozen JSON with `schemaVersion`, a `sha256:` `digest`, and the durable `fingerprint` it corresponds to.
12
+ - Never opens a socket, never reads a store, never resolves a credential, and never emits a store connection
13
+ string — only its kind and durability.
14
+ - Never emits bodies: prompt and skill instructions are digests, tool parameters are digests.
15
+ - Is synchronous, in-memory, and O(contributions): a 100-tool agent snapshots in well under a millisecond.
16
+
17
+ `digest` is SHA-256 over the canonicalized, redacted snapshot (the `digest` field itself excluded), prefixed
18
+ `sha256:`. Tool parameter schemas go through `canonicalizeJsonSchema()` first, so key order and `required`
19
+ ordering cannot fake a change. Identical configuration produces an identical digest; any listed contribution
20
+ change — a tool, a schema, a skill body, a guardrail revision, a limit, `thinkingLevel`, the loop revision, a
21
+ request policy, a store kind — produces a different one.
22
+
23
+ ## When to use it
24
+
25
+ - Pin the bundle in a harness/eval registry next to the run or timeline id, then diff digests across releases.
26
+ - Explain a durable-resume failure: `fingerprint` is the value compare-and-set against stored run state, and
27
+ the snapshot shows *which* field moved.
28
+ - Feed a release manifest or a support bundle: it is JSON, bounded (512 KiB), and secret-free by construction.
29
+
30
+ Do not use it as a substitute for `inspectHostComposition()` (that inspects a whole composition's readiness,
31
+ sandbox isolation, and credential references) or as a policy decision — it is a report, not a guard.
32
+
33
+ ## Inputs
34
+
35
+ | Input | Purpose |
36
+ | --- | --- |
37
+ | `agent` | The agent to inspect; `agent.config` supplies tools, skills, guardrails, prompt, loop, limits, model. |
38
+ | `config` | Optional `AgentSessionConfig`: its `store` wins over `agent.config.store` for the reported session-store kind. |
39
+ | `run` | Optional `RunOptions`: run-level overrides (`limit`s, `thinkingLevel`, `systemPrompt`, `guardrails`, `loop`, `toolNames`, `attentionCompiler`, `providerRequestPolicies`, `runState.definitionRevision`, `effectStore`). |
40
+ | `memory` | Optional memory store instance. Only its kind and durability are read — never its contents. |
41
+
42
+ ## Output
43
+
44
+ ```ts
45
+ const bundle = snapshotRunBundle({ agent, run: { limits: { maxTurns: 12 }, toolNames: ["search"] } });
46
+
47
+ bundle.schemaVersion; // 1
48
+ bundle.digest; // "sha256:1ddd…" — pin this
49
+ bundle.fingerprint; // agentFingerprint() for durable resume
50
+ bundle.agent; // { id, definitionRevision }
51
+ bundle.systemPrompt; // { disabled, instructionsDigest, contributions: [{ id, mode, source, digest }] }
52
+ bundle.skills; // [{ name, instructionsDigest, toolNames }]
53
+ bundle.tools; // [{ name, schemaDigest, exclusive, effect }] — run.toolNames already applied
54
+ bundle.guardrails; // [{ name, stage, revision }]
55
+ bundle.loop; // { strategy, revision }
56
+ bundle.limits; // resolved ResolvedRunLimits
57
+ bundle.model; // { provider, model }
58
+ bundle.storage; // { sessionStore, checkpoints, effectStore, memory } → { kind, durable }
59
+ ```
60
+
61
+ `tools` is the *effective* set: `RunOptions.toolNames` narrowing is applied, and an unknown name fails closed
62
+ exactly as it would during the run. `storage.*.kind` comes from the store's declared `kind` or constructor name
63
+ and is reduced to a plain token (`[a-z0-9_.-]`, ≤64 chars); anything URL-shaped is reported as `custom`, so a
64
+ connection string can never reach a pinned artifact.
65
+
66
+ ## Example
67
+
68
+ ```ts
69
+ import { createAgent, snapshotRunBundle } from "@arnilo/prism";
70
+
71
+ const agent = createAgent({ model: { provider: "anthropic", model: "claude-sonnet-4-5" }, /* … */ });
72
+ const bundle = snapshotRunBundle({ agent, run: { thinkingLevel: "high" } });
73
+
74
+ const pinned = bundle.digest; // store with the harness artifact
75
+ const next = snapshotRunBundle({ agent: changedAgent });
76
+ if (next.digest !== pinned) reportFields(next, pinned); // hosts diff by field, not by digest alone
77
+ ```
78
+
79
+ ## Redaction and limits
80
+
81
+ Every string field is passed through the host `SecretRedactor` (`RunOptions.redactor` ?? `AgentConfig.redactor`)
82
+ before hashing and before returning, so redaction is part of the pinned digest. The snapshot refuses to exceed
83
+ 512 KiB — a bundle that large is a host wiring bug, not something to retain — and throws `TypeError` rather than
84
+ truncating. There is no network path, no store read, and no credential resolution in this function; a store that
85
+ throws on every method still snapshots fine.
86
+
87
+ ## Related APIs
88
+
89
+ - [`agentFingerprint()`](durable-runs.md): the durable-resume identity this snapshot projects.
90
+ - [`inspectHostComposition()`](host-compositions.md): composition readiness, storage durability, sandbox isolation.
91
+ - [`ExecutionTimeline`](execution-timeline.md): what a run *did*; the snapshot is what it was *configured* with.
92
+ - [`RunRecord`](runs-and-usage.md): the ledger row a snapshotted run leaves behind.
@@ -65,6 +65,18 @@ Prism charges turns before assembly, provider attempts before generation, reques
65
65
 
66
66
  `createRunLimitTracker()` and `resolveRunLimits()` are public for adapters that need the same validation and accounting semantics. Workflow agent nodes forward `RunWorkflowOptions.limits`; supervisor delegation narrows its step/tool/token/timeout budget into core limits; MCP tool calls use a per-call tracker.
67
67
 
68
+ ## Clean stops and stop reasons
69
+
70
+ A run can end without an error but also without the model finishing its thought: a host `RunOptions.turnPolicy.stop`, a `turnPolicy.maxTurns` cap, or a loop ceiling. `AgentRunResult.stopReason` names that outcome — `"host_policy"` for a host policy stop, `"turn_limit"`, `"token_limit"`, or `"refusal"` for loop ceilings — with `turnPolicy.stop`'s own string in `stopDetail`. A natural end carries neither field, so hosts that only care about "did it stop early?" check truthiness. The same values ride the emitted `agent_finished` event (as `finishReason`/`stopDetail`), the finish `RunRecord`, and the projected [Execution Timeline](execution-timeline.md).
71
+
72
+ A `host_policy` stop is terminal for the run yet resumable: with `runState: { checkpointPolicy: "every-turn" }` the stopped state keeps its frontier, and `resumeAgentRun(..., { decision: "continue" })` picks the loop up at the boundary. Every other terminal state is final. See [Agent loops § Turn policy](agent-loops.md#turn-policy).
73
+
74
+ ## Provider failure classes
75
+
76
+ Provider-originated failures carry advisory `ErrorInfo.failureClass` on the failed `AgentRunResult`, terminal `RunRecord`, error events, and any `ToolResult.error` that already carries that `ErrorInfo`. Values are `"quota"`, `"auth"`, `"rate_limited"`, `"transient"`, `"permanent"`, and `"unknown"`. The classifier uses an already-captured HTTP status plus bounded error body: quota-shaped `429` responses (for example `GoUsageLimitError`) are `"quota"`; other `429` values are `"rate_limited"`; `401`/`403` are `"auth"`; `5xx` and known network codes such as `ECONNRESET` are `"transient"`; other `4xx` values are `"permanent"`; anything else is `"unknown"`.
77
+
78
+ This field is outcome metadata, not a retry control. Existing retry policy, attempt limits, and fail-closed behavior continue to use `ErrorInfo.code` exactly as before. Prism records no provider headers or response bodies beyond the existing redacted error message.
79
+
68
80
  ## Durable run state
69
81
 
70
82
  `RunOptions.runState` writes a bounded, versioned checkpoint only at a safe interruption boundary. Its counters and absolute deadline resume with the run, while transcript history stays in `SessionStore` by session/leaf reference. `AgentRunResult.runState` exposes only redacted identity/status/version data; `interruption` excludes tool arguments. See [Agent/session runtime](agent-session-runtime.md#durable-interruption).
@@ -86,6 +98,8 @@ The adapter receives these record shapes:
86
98
  | `status` | `queued` \| `running` \| `suspended` \| `denied` \| `succeeded` \| `failed` \| `aborted`. |
87
99
  | `startedAt` / `finishedAt` | ISO timestamps. |
88
100
  | `abortReason` | Set when status is `aborted`. |
101
+ | `stopReason` | Why the loop stopped cleanly instead of reaching a natural end: `host_policy` (`RunOptions.turnPolicy.stop`), `turn_limit`, `token_limit`, or `refusal`. Absent on a natural end. |
102
+ | `stopDetail` | Host stop detail from `turnPolicy.stop` (≤256 bytes, redacted). |
89
103
  | `error` | `ErrorInfo` when status is `failed`. |
90
104
  | `tenantId` / `accountId` / `userId` | From active ownership scope. |
91
105
 
package/docs/server.md CHANGED
@@ -264,6 +264,8 @@ A2A routes are not added to `createPrismHandler()`. Install `@arnilo/prism-core/
264
264
  - [MCP client and server exposure](mcp-tools.md): selected MCP capabilities and web-standard MCP transport.
265
265
  - [Host security guide](host-security.md): remote-boundary checklist.
266
266
  - [A2A interoperability](a2a.md): separately mounted A2A 1.0 handler/client.
267
+ - [Telegram channel](telegram-channel.md): separately mounted Telegram Web `Request`/`Response` webhook handler; host owns fixed HTTPS route and TLS.
268
+ - [Signal channel (experimental)](signal-channel.md): private Unix-socket manual receive for an externally supervised signal-cli daemon; host owns socket, account, accepted-use policy and supervision.
267
269
  - [Obscura browser engine](obscura.md): optional binary-backed generic tools for hosted agents.
268
270
  - [Conversations](conversations.md): durable user-scoped conversation service, replay, branches, export, deletion.
269
271
  - [Work artifacts and review](work-artifacts-and-review.md): durable artifact review service, revisions, approvals, authorized expiring delivery links.