@arnilo/prism 0.5.5 → 0.6.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 (71) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +10 -10
  3. package/dist/agent-approval.js +7 -6
  4. package/dist/agent-loops.js +51 -12
  5. package/dist/agent-session/session.d.ts +1 -0
  6. package/dist/agent-session/session.js +20 -2
  7. package/dist/agent-tool-dispatch.js +5 -4
  8. package/dist/cli-runner.d.ts +8 -1
  9. package/dist/cli-runner.js +97 -7
  10. package/dist/content.d.ts +3 -16
  11. package/dist/content.js +9 -99
  12. package/dist/context-budget.d.ts +12 -1
  13. package/dist/context-budget.js +42 -19
  14. package/dist/contracts-core/agent.d.ts +11 -0
  15. package/dist/contracts-core/agent.js +4 -1
  16. package/dist/extensions.d.ts +18 -1
  17. package/dist/extensions.js +10 -0
  18. package/dist/index.d.ts +6 -6
  19. package/dist/index.js +4 -4
  20. package/dist/input.d.ts +6 -0
  21. package/dist/input.js +12 -1
  22. package/dist/media-types.d.ts +34 -0
  23. package/dist/media-types.js +158 -0
  24. package/dist/pinned-fetch.d.ts +2 -2
  25. package/dist/pinned-fetch.js +11 -12
  26. package/dist/redaction.js +74 -1
  27. package/dist/session-stores.d.ts +11 -0
  28. package/dist/session-stores.js +23 -8
  29. package/docs/acp.md +1 -1
  30. package/docs/ag-ui.md +4 -2
  31. package/docs/agent-events.md +2 -0
  32. package/docs/agent-loops.md +1 -1
  33. package/docs/agent-session-runtime.md +3 -1
  34. package/docs/browser-automation.md +5 -2
  35. package/docs/cli-rpc.md +15 -1
  36. package/docs/contributing.md +37 -0
  37. package/docs/core.md +2 -0
  38. package/docs/document-reader.md +2 -0
  39. package/docs/documents.md +1 -1
  40. package/docs/extension-authoring.md +8 -9
  41. package/docs/extensions.md +13 -1
  42. package/docs/graft.md +29 -5
  43. package/docs/history/release-handoffs.md +33 -0
  44. package/docs/host-security.md +2 -2
  45. package/docs/index.md +33 -17
  46. package/docs/input-and-prompt-assembly.md +4 -4
  47. package/docs/language-intelligence.md +1 -1
  48. package/docs/migrate-to-0.5.md +7 -2
  49. package/docs/migrate-to-0.6.md +89 -0
  50. package/docs/migration.md +30 -0
  51. package/docs/model-registry.md +1 -1
  52. package/docs/multimodal-content.md +1 -1
  53. package/docs/obscura.md +3 -1
  54. package/docs/options-index.md +286 -0
  55. package/docs/peer-dependencies.md +94 -0
  56. package/docs/performance.md +34 -2
  57. package/docs/ponytail.md +2 -0
  58. package/docs/postgres-persistence.md +3 -1
  59. package/docs/provider-conformance.md +1 -1
  60. package/docs/provider-packages.md +21 -21
  61. package/docs/provider-primitives.md +2 -1
  62. package/docs/providers/ai-sdk.md +5 -2
  63. package/docs/public-contracts.md +2 -2
  64. package/docs/release-and-install.md +75 -55
  65. package/docs/server.md +1 -1
  66. package/docs/session-stores.md +3 -1
  67. package/docs/sqlite-persistence.md +2 -0
  68. package/docs/testing.md +38 -0
  69. package/docs/tools.md +1 -1
  70. package/docs/wiki.md +47 -3
  71. package/package.json +5 -5
@@ -131,7 +131,7 @@ Important request shapes:
131
131
  | `SkillRegistry` | Host active skill registry shape: `register()`, `get()`, `resolve()`, and `list()`. |
132
132
  | `CredentialRequest` | Credential lookup request: credential `name`, optional provider id, and metadata. |
133
133
  | `OAuthProvider` | Host/package OAuth callbacks for login, optional refresh, and conversion to a `Credential`. |
134
- | `AgentSessionConfig` | Session creation input: optional id, agent, store, leaf id, and metadata. |
134
+ | `AgentSessionConfig` | Session creation input: optional id, agent, store, leaf id, metadata, and `snapshotCacheTtlMs` (branch-cache TTL; `0` disables). |
135
135
  | `RunOptions` | Per-run overrides: optional abort signal, model, input layout, run limits (incl. `limits.maxToolRounds`), provider options/request policies, system prompt layers, compaction, retry, metadata, skill selection, validate, redactor, and loop. |
136
136
  | `SubscribeOptions` / `SubscriberOverflowPolicy` | Live `AgentEvent` subscriber queue limit and overflow policy: `maxQueuedEvents`, `overflow: "close" \| "drop_oldest" \| "drop_newest"`. |
137
137
  | `resumeAgentRunStream` / `AgentRunResumeStreamOptions` | One durable-run event stream: existing checkpoint/resume options plus `signal` and bounded subscriber options. `AgentRunLifecycle.resumeStream()` adds host capability resolution; no protocol types enter core. Runtime resume validation (0.2.0): every resume entrypoint validates the full input in core before any checkpoint write, tool call, or event — unknown legacy decisions and malformed batches fail closed with `AgentDecisionError` and no side effect; see [Agent/session runtime § Durable interruption](agent-session-runtime.md#durable-interruption). |
@@ -157,7 +157,7 @@ Important request shapes:
157
157
  | `SessionIndex` / `SessionSearchQuery` / `SessionSearchHit` | Bounded optional session search seam (`search` / `SessionStore.searchSessions?`). Filters: workspace (`metadata.workspaceRoot`), time, provider/model, label/summary, optional FTS `query`, ownership. Hits return `sessionId` + optional `leafId` for resume; never credentials. Caps via `resolveSessionSearchQuery` / `DEFAULT_*` / `HARD_MAX_*` session-search constants. |
158
158
  | `contextBudget` / `getContextBudgetReport` / `ContextBudgetError` | Opt-in assembler budget on `AssembleProviderInputOptions`; deterministic eviction; omission report in `ProviderRequest.metadata` (kinds/ids/sizes only). |
159
159
  | `AgentSession.steer` / `SteerOptions` / pending-steer caps | Mid-run enqueue into active run; optional `softInterrupt`; default 8 msgs / 64 KiB UTF-8. |
160
- | `SessionSearchUnsupportedError` / `sessionSearchMode` | Memory opt-out + JSONL; typed throw (not empty success). |
160
+ | `SessionSearchUnsupportedError` / `sessionSearchMode` | Memory opt-out + JSONL; typed throw (not empty success). Memory linear caps are host-overridable via `CreateMemorySessionStoreOptions.search`. |
161
161
  | `BranchRecord` / `BranchQuery` | Branch handle/leaf pointer and query filters (session, name, parent branch, leaf presence). |
162
162
  | `SessionEntryQuery` | Paginated entry filters: `sessionId`, `runId`, `parentId`, `leafId`, `kind`, timestamp range, ownership. |
163
163
  | `RunRecord` / `RunQuery` | Stored run and filters: session, branch, status, timestamps, ownership. |
@@ -5,7 +5,7 @@
5
5
  ## What it does
6
6
 
7
7
 
8
- Prism's current **0.5.x** 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.5.5` 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.6.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.6.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
 
@@ -14,16 +14,16 @@ Core `@arnilo/prism` ships runtime, CLI, templates, and docs. Every code package
14
14
 
15
15
  | package | version | notes |
16
16
  | --- | --- | --- |
17
- | `@arnilo/prism` | 0.5.5 | core — runtime, CLI/RPC, templates, docs |
18
- | `@arnilo/prism-coding-tools` | 0.5.5 | family — /agent, /security, /document-reader, /openapi, /computer-use-linux, /dev, /caveman, /ponytail, /impeccable subpaths |
19
- | `@arnilo/prism-core` | 0.5.5 | family — /runtime, /sessions, /governance, /credentials, /enterprise, /work, /validation subpaths |
20
- | `@arnilo/prism-providers` | 0.5.5 | family — all provider adapters as `/<adapter>` subpaths |
21
- | `@arnilo/prism-acp-agent` | 0.5.5 | capability — ACP adapter |
22
- | `@arnilo/prism-ag-ui` | 0.5.5 | capability — AG-UI/A2A/A2UI adapter |
23
- | `@arnilo/prism-mcp` | 0.5.5 | capability — MCP client/server/OAuth interop |
24
- | `@arnilo/prism-memory` | 0.5.5 | capability — memory plus /rag, /compaction/*, /graft, /wiki subpaths |
25
- | `@arnilo/prism-office` | 0.5.5 | capability — /documents, /sheets, /diagrams subpaths |
26
- | `@arnilo/prism-web-tools` | 0.5.5 | capability — Brave/Exa/Firecrawl plus peer-gated /browser and /obscura subpaths |
17
+ | `@arnilo/prism` | 0.6.0 | core — runtime, CLI/RPC, templates, docs |
18
+ | `@arnilo/prism-coding-tools` | 0.6.0 | family — /agent, /security, /document-reader, /openapi, /computer-use-linux, /dev, /caveman, /ponytail, /impeccable subpaths |
19
+ | `@arnilo/prism-core` | 0.6.0 | family — /runtime, /sessions, /governance, /credentials, /enterprise, /work, /validation subpaths |
20
+ | `@arnilo/prism-providers` | 0.6.0 | family — all provider adapters as `/<adapter>` subpaths |
21
+ | `@arnilo/prism-acp-agent` | 0.6.0 | capability — ACP adapter |
22
+ | `@arnilo/prism-ag-ui` | 0.6.0 | capability — AG-UI/A2A/A2UI adapter |
23
+ | `@arnilo/prism-mcp` | 0.6.0 | capability — MCP client/server/OAuth interop |
24
+ | `@arnilo/prism-memory` | 0.6.0 | capability — memory plus /rag, /compaction/*, /graft, /wiki subpaths |
25
+ | `@arnilo/prism-office` | 0.6.0 | capability — /documents, /sheets, /diagrams subpaths |
26
+ | `@arnilo/prism-web-tools` | 0.6.0 | capability — Brave/Exa/Firecrawl plus peer-gated /browser and /obscura subpaths |
27
27
  <!-- generated:package-truth:inventory end -->
28
28
 
29
29
 
@@ -32,30 +32,30 @@ Core `@arnilo/prism` ships runtime, CLI, templates, and docs. Every code package
32
32
 
33
33
  | adapter package | version |
34
34
  | --- | --- |
35
- | `@arnilo/prism-providers/ai-sdk` | 0.5.5 |
36
- | `@arnilo/prism-providers/alibaba` | 0.5.5 |
37
- | `@arnilo/prism-providers/anthropic` | 0.5.5 |
38
- | `@arnilo/prism-providers/azure` | 0.5.5 |
39
- | `@arnilo/prism-providers/bedrock` | 0.5.5 |
40
- | `@arnilo/prism-providers/clinepass` | 0.5.5 |
41
- | `@arnilo/prism-providers/commandcode` | 0.5.5 |
42
- | `@arnilo/prism-providers/deepseek` | 0.5.5 |
43
- | `@arnilo/prism-providers/google` | 0.5.5 |
44
- | `@arnilo/prism-providers/hyper` | 0.5.5 |
45
- | `@arnilo/prism-providers/kimi` | 0.5.5 |
46
- | `@arnilo/prism-providers/model-discovery` | 0.5.5 |
47
- | `@arnilo/prism-providers/neuralwatt` | 0.5.5 |
48
- | `@arnilo/prism-providers/ollama` | 0.5.5 |
49
- | `@arnilo/prism-providers/openai` | 0.5.5 |
50
- | `@arnilo/prism-providers/opencode-go` | 0.5.5 |
51
- | `@arnilo/prism-providers/openrouter` | 0.5.5 |
52
- | `@arnilo/prism-providers/vertex` | 0.5.5 |
53
- | `@arnilo/prism-providers/xai` | 0.5.5 |
54
- | `@arnilo/prism-providers/zai` | 0.5.5 |
35
+ | `@arnilo/prism-providers/ai-sdk` | 0.6.0 |
36
+ | `@arnilo/prism-providers/alibaba` | 0.6.0 |
37
+ | `@arnilo/prism-providers/anthropic` | 0.6.0 |
38
+ | `@arnilo/prism-providers/azure` | 0.6.0 |
39
+ | `@arnilo/prism-providers/bedrock` | 0.6.0 |
40
+ | `@arnilo/prism-providers/clinepass` | 0.6.0 |
41
+ | `@arnilo/prism-providers/commandcode` | 0.6.0 |
42
+ | `@arnilo/prism-providers/deepseek` | 0.6.0 |
43
+ | `@arnilo/prism-providers/google` | 0.6.0 |
44
+ | `@arnilo/prism-providers/hyper` | 0.6.0 |
45
+ | `@arnilo/prism-providers/kimi` | 0.6.0 |
46
+ | `@arnilo/prism-providers/model-discovery` | 0.6.0 |
47
+ | `@arnilo/prism-providers/neuralwatt` | 0.6.0 |
48
+ | `@arnilo/prism-providers/ollama` | 0.6.0 |
49
+ | `@arnilo/prism-providers/openai` | 0.6.0 |
50
+ | `@arnilo/prism-providers/opencode-go` | 0.6.0 |
51
+ | `@arnilo/prism-providers/openrouter` | 0.6.0 |
52
+ | `@arnilo/prism-providers/vertex` | 0.6.0 |
53
+ | `@arnilo/prism-providers/xai` | 0.6.0 |
54
+ | `@arnilo/prism-providers/zai` | 0.6.0 |
55
55
  <!-- generated:package-truth:providers end -->
56
56
 
57
57
 
58
- 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 an optional `playwright-core` peer for the diagrams live embed. Importing one subpath never evaluates another. The three draft names `@arnilo/prism-documents`/`sheets`/`diagrams` were never published.
58
+ 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.
59
59
 
60
60
  ## When to use it
61
61
 
@@ -82,7 +82,7 @@ Consumers install the core package for the runtime and add first-party packages
82
82
  | Install core + a single provider adapter | `npm install @arnilo/prism @arnilo/prism-providers` (import `@arnilo/prism-providers/openai`) |
83
83
  | 0.0.12 AG-UI (after release) | `npm install @arnilo/prism@0.0.12 @arnilo/prism-ag-ui@0.0.12` |
84
84
  | Install bounded web research tools | `npm install @arnilo/prism @arnilo/prism-web-tools @arnilo/prism-core` |
85
- | Install browser automation tools (Playwright-peer gated `/browser`) | `npm install @arnilo/prism @arnilo/prism-web-tools playwright-core@1.61.0` |
85
+ | Install browser automation tools (Playwright-peer gated `/browser`) | `npm install @arnilo/prism @arnilo/prism-web-tools playwright-core@1.63.0` |
86
86
  | Install Obscura browser-engine tools (host supplies the binary; `/obscura`) | `npm install @arnilo/prism @arnilo/prism-web-tools @arnilo/prism-mcp` |
87
87
  | Install RAG retrieval (memory family `/rag`) | `npm install @arnilo/prism @arnilo/prism-memory` |
88
88
  | Install the Wiki CLI and skills (memory family `/wiki`) | `npm install @arnilo/prism @arnilo/prism-memory` (`npx prism-wiki --help`) |
@@ -143,7 +143,7 @@ A packed tarball contains only public compiled output and release files:
143
143
  - Code packages ship `README.md`, `LICENSE`, and `CHANGELOG.md`; family/profile packages ship `README.md` and `CHANGELOG.md`.
144
144
  - 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`.
145
145
  - `dist/cli.js` and the `bin` link in core.
146
- - **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.5.5.tgz`; family packages produce `arnilo-prism-core-0.5.5.tgz`, `arnilo-prism-coding-tools-0.5.5.tgz`, `arnilo-prism-providers-0.5.5.tgz` (all 19 adapters inside), `arnilo-prism-memory-0.5.5.tgz`, `arnilo-prism-web-tools-0.5.5.tgz`, and `arnilo-prism-office-0.5.5.tgz`; capability packages like `arnilo-prism-mcp-0.5.5.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).
146
+ - **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.6.0.tgz`; family packages produce `arnilo-prism-core-0.6.0.tgz`, `arnilo-prism-coding-tools-0.6.0.tgz`, `arnilo-prism-providers-0.6.0.tgz` (all 19 adapters inside), `arnilo-prism-memory-0.6.0.tgz`, `arnilo-prism-web-tools-0.6.0.tgz`, and `arnilo-prism-office-0.6.0.tgz`; capability packages like `arnilo-prism-mcp-0.6.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).
147
147
 
148
148
  Excluded from every tarball by `files` negation:
149
149
 
@@ -242,6 +242,11 @@ Every side effect carries the run suffix and is cleaned up idempotently (PR clos
242
242
 
243
243
  Frozen by Phase 12 Task 0 in `scripts/phase12-freeze-manifest.json` (schema gate `node --test scripts/phase12-freeze.test.mjs`; docs agreement tripwired in the docs test suite). Any change requires a recorded freeze deviation in plan 012.
244
244
 
245
+ | Runtime | Supported | Measured in CI |
246
+ | --- | --- | --- |
247
+ | 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). |
248
+ | PostgreSQL | 16 (`pgvector/pgvector:pg16`) | `postgres-integration` service container |
249
+
245
250
  ## Extension and configuration notes
246
251
 
247
252
 
@@ -249,7 +254,7 @@ Frozen by Phase 12 Task 0 in `scripts/phase12-freeze-manifest.json` (schema gate
249
254
  - **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.
250
255
  - **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.
251
256
  - **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).
252
- - **Release workflow.** `.github/workflows/release.yml` has six jobs. `verify` runs network-free SDK readiness on Node 24; `node20-compat` builds/imports every public root `exports` default target on Node 20 for declared `engines.node >=20` (docs examples need Node >=22.6 native TypeScript stripping); `postgres-integration` uses `pgvector/pgvector:pg16`; `supply-chain` runs high-severity audit, SPDX/license policy, and tracked-source secret scanning; and tag-only `codeql-release` runs SAST. `publish` runs on `v0.3.0` for the one lockstep cut and on `@arnilo/*@*` package tags afterward; it needs all five gates, preserves clean tagged/version/topological publication, and alone receives `NPM_TOKEN`, `id-token: write`, and `attestations: write`. Before npm publish it packs all current tarballs, generates checksums plus SPDX, scans unpacked public artifacts, creates GitHub attestations for tarballs and SBOM, then retains artifacts for 30 days. Registry state remains the resumable journal. Local `npm run release:dry-run` remains network-free SDK readiness; local PostgreSQL coverage is `PRISM_TEST_POSTGRES_URL=... npm run test:postgres`.
257
+ - **Release workflow.** `.github/workflows/release.yml` has six jobs. `verify` runs network-free SDK readiness on Node 24; `node22-compat` builds/imports every public root `exports` default target on Node 22 for declared `engines.node >=22` (docs examples need Node >=22.6 native TypeScript stripping, inside the ≥22 floor); `postgres-integration` uses `pgvector/pgvector:pg16`; `supply-chain` runs high-severity audit, SPDX/license policy, and tracked-source secret scanning; and tag-only `codeql-release` runs SAST. `publish` runs on `v0.3.0` for the one lockstep cut and on `@arnilo/*@*` package tags afterward; it needs all five gates, preserves clean tagged/version/topological publication, and alone receives `NPM_TOKEN`, `id-token: write`, and `attestations: write`. Before npm publish it packs all current tarballs, generates checksums plus SPDX, scans unpacked public artifacts, creates GitHub attestations for tarballs and SBOM, then retains artifacts for 30 days. Registry state remains the resumable journal. Local `npm run release:dry-run` remains network-free SDK readiness; local PostgreSQL coverage is `PRISM_TEST_POSTGRES_URL=... npm run test:postgres`.
253
258
  - **Protected integration matrix (plan 060).** Required on protected branches; pull requests do not run these jobs (no PR secrets). Connection strings are masked in logs.
254
259
 
255
260
  | Job | Workflow | Cadence | Evidence |
@@ -292,17 +297,17 @@ Frozen by Phase 12 Task 0 in `scripts/phase12-freeze-manifest.json` (schema gate
292
297
  - 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.
293
298
  - 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.
294
299
  - **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.
295
- - **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`), receives no provider/npm/OIDC secrets, and uploads only a redacted aggregate status artifact (7-day retention).
300
+ - **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).
296
301
  - **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.
297
302
  - **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.
298
- - **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) → batched approval → OpenAPI side effect with idempotency → artifact upload + signed delivery, with policy-deny and hash-mismatch fail-closed injections. 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`).
303
+ - **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`).
299
304
  - **Protected restart-recovery leg (plan 012 Task 4).** `scripts/phase12-restart-recovery.test.mjs` (run by `npm run test:postgres` after the Phase 7 suite) spawns two real processes against one PostgreSQL schema: replica A runs a durable agent, suspends on a batched tool approval, appends durable events and is then SIGKILLed by the driver; replica B reconnects and resumes. Operators re-run the leg with `PRISM_TEST_POSTGRES_URL="postgresql://…" npm run test:postgres` against a disposable PostgreSQL 16 (e.g. `pgvector/pgvector:pg16`). Without the URL the gate records a named `BLOCKED GATE` failure instead of skipping. Reconnect p95 and 16-worker append contention p95 are asserted against the frozen `reconnectP95Ms` / `pointOpP95Ms` ceilings; set `PRISM_PHASE12_RECORD_EVIDENCE=1` to refresh the checked-in evidence file `scripts/phase12-restart-recovery.json`.
300
- - **Offline test budget.** The default `npm test` (no `PRISM_LIVE_PROVIDER_TESTS`) is pinned at **< 60s on Node 20** with a measured local baseline of ~45s (build ~18s + network-free tests/workspace tests/packaging smoke ~27s). Plan 057 retired the historical `phase11-freeze` … `phase34-freeze`/`phase30-release` gate files from the default suite (17 files, 247 tests) — they stay in the repo as immutable release evidence and remain audit-runnable standalone via `node --test scripts/<file>.test.mjs`, with their self-wiring assertions flipped to assert non-wiring so the retirement cannot silently regress. Release/security gates (`release-gate`, `tooling-gate`, `budget-gate`, `phase23-quality-gates`, `phase8–11` conformance) stay in the run. The full CI `sdk:ready` gate runs on Node 24 because docs tests execute `examples/*.ts` via native TypeScript stripping. `npm run sdk:ready` also runs typecheck, pack dry-run, and the coverage summary, so it is allowed to exceed the `npm test` budget while remaining network-free. `npm run test:coverage` additionally runs the combined coverage summary (`npm run coverage:summary`, ~25s local: core + each workspace suite once with `--experimental-test-coverage`; measured total ~70s on Node 24) — additive reporting only, the core gate stays the only hard threshold. The CI `sdk:ready` step has `timeout-minutes: 5` as a hang backstop; the separate Node 20 compatibility step has `timeout-minutes: 3`. The budget was raised from 30s after the default suite grew to include every first-party package, offline install smoke, packaging guards, docs examples, and workspace tests; optimize before raising it again.
305
+ - **Offline test budget.** The default `npm test` (no `PRISM_LIVE_PROVIDER_TESTS`) is pinned at **< 60s** with a measured local baseline of ~45s (build ~18s + network-free tests/workspace tests/packaging smoke ~27s; the 0.5.x baselines were measured on Node 20, the current ones on Node 24). Plan 057 retired the historical `phase11-freeze` … `phase34-freeze`/`phase30-release` gate files from the default suite (17 files, 247 tests) — they stay in the repo as immutable release evidence and remain audit-runnable standalone via `node --test scripts/<file>.test.mjs`, with their self-wiring assertions flipped to assert non-wiring so the retirement cannot silently regress. Their criteria are content-verified, not filesystem-verified (plan 071 Tasks 5 and 10): capture ordering compares the baselines' recorded `captured` dates instead of file mtimes, phase markers resolve against the live file plus the frozen lineage (the phase plan of record and `docs/history/`) so a living doc that is rewritten between releases cannot fail an audit gate (a marker set that is checked this way is declared in the gate's own manifest — `phase26-freeze-manifest.json` `lineageCheckedFiles` — so the fallback stays bounded to the files that need it), a path retired since the freeze resolves to its recorded archive instead of being hash-compared (`status: "retired"` + `retiredTo` in the baseline, with the Task 0 `sha256` kept as history), and a version literal is asserted as a *transition* (`!`-prefixed marker: the pre-cut literal must be gone) rather than an equality that the next release sweep would invalidate. Release/security gates (`release-gate`, `tooling-gate`, `budget-gate`, `phase23-quality-gates`, `phase8–11` conformance) stay in the run. The full CI `sdk:ready` gate runs on Node 24 because docs tests execute `examples/*.ts` via native TypeScript stripping. `npm run sdk:ready` also runs typecheck, pack dry-run, and the coverage summary, so it is allowed to exceed the `npm test` budget while remaining network-free. `npm run test:coverage` additionally runs the combined coverage summary (`npm run coverage:summary`, ~25s local: core + each workspace suite once with `--experimental-test-coverage`; measured total ~70s on Node 24) — additive reporting only, the core gate stays the only hard threshold. The CI `sdk:ready` step has `timeout-minutes: 30` as a hang backstop; the separate Node 22 compatibility job has `timeout-minutes: 10`. The budget was raised from 30s after the default suite grew to include every first-party package, offline install smoke, packaging guards, docs examples, and workspace tests; optimize before raising it again.
301
306
 
302
307
  ### GitHub Actions pipeline (0.0.27+)
303
308
 
304
309
 
305
- `.github/workflows/release.yml` is the single pipeline: **push to `main`** runs CI (`verify` = `npm run sdk:ready`, `node20-compat`, `postgres-integration`, `supply-chain`), **`v0.3.0` or `@arnilo/*@*` package tags** additionally run `codeql-release` and the `publish` job (deterministic `release:publish` in dependency order with provenance attestation). `security.yml` adds CodeQL/dependency-review/SBOM on push and PR; `live-canaries.yml` and `sandbox-browser.yml` are scheduled. All actions are SHA-pinned (2026-08-06 fix: CodeQL pins were invalid 404 refs and `workflow_dispatch` was missing — re-verified every pin against its upstream repo). Prerequisites outside the repo: Actions enabled in repository settings, and the `NPM_TOKEN` secret (with `id-token: write` for provenance). To re-cut a tag after a fix commit, delete and recreate it (`git push origin :v0.0.28 && git push origin v0.0.28`) so the tag creation event fires.
310
+ `.github/workflows/release.yml` is the single pipeline: **push to `main`** runs CI (`verify` = `npm run sdk:ready`, `node22-compat`, `postgres-integration`, `supply-chain`), **`v0.3.0` or `@arnilo/*@*` package tags** additionally run `codeql-release` and the `publish` job (deterministic `release:publish` in dependency order with provenance attestation). `security.yml` adds CodeQL/dependency-review/SBOM on push and PR; `live-canaries.yml` and `sandbox-browser.yml` are scheduled. All actions are SHA-pinned (2026-08-06 fix: CodeQL pins were invalid 404 refs and `workflow_dispatch` was missing — re-verified every pin against its upstream repo). Prerequisites outside the repo: Actions enabled in repository settings, and the `NPM_TOKEN` secret (with `id-token: write` for provenance). To re-cut a tag after a fix commit, delete and recreate it (`git push origin :v0.0.28 && git push origin v0.0.28`) so the tag creation event fires.
306
311
 
307
312
  ## Formatting, linting, and coverage
308
313
 
@@ -314,7 +319,7 @@ Prism uses one tool for formatting and linting — [Biome](https://biomejs.dev)
314
319
  | `npm run lint` | `biome lint .` — fails on any lint error (warnings are non-fatal). |
315
320
  | `npm run format:check` | `biome format .` — fails if any file is unformatted. |
316
321
  | `npm run format` | `biome format --write .` — normalizes formatting in place. |
317
- | `npm run test:coverage` | `node --test --experimental-test-coverage` over the core suite with enforced minimums: **lines 60%**, **functions 70%**, **branches 75%** (current baseline ≈ 90.5 / 84.2 / 90.6), then `scripts/coverage-summary.mjs` + the `phase23-coverage` gate. Excludes `__tests__/`, `node_modules/`, `scripts/`, and `packages/` from the core report. |
322
+ | `npm run test:coverage` | `node --test --experimental-test-coverage` over the core suite with enforced minimums: **lines 60%**, **functions 70%**, **branches 75%** (current baseline ≈ 92.0 / 85.0 / 92.6), then `scripts/coverage-summary.mjs` + the `phase23-coverage` gate. Excludes `__tests__/`, `node_modules/`, `scripts/`, and `packages/` from the core report. |
318
323
 
319
324
  All four gates run inside `npm run sdk:ready` (after `typecheck`, before `pack:dry-run`). A few rules are disabled in `biome.json` because they are false positives for this codebase: `noControlCharactersInRegex` and `noAssignInExpressions` (security/redaction code intentionally matches control characters and uses `while ((m = re.exec(…)))` loops), `noShadowRestrictedNames`, `noThenProperty` (the workflow DSL has a legitimate `then` branch field), `noExplicitAny`, `noVoidTypeReturn`, and `useYield`. Raise the coverage thresholds in `package.json` `test:coverage` as the baseline climbs.
320
325
 
@@ -326,13 +331,14 @@ Workspace coverage rows used to include the symlinked root core `dist/` (workspa
326
331
  | Fact | Value |
327
332
  | --- | --- |
328
333
  | Workspace include filter | `--test-coverage-include=dist/**` per package (package-local denominator) |
329
- | Per-package gate | `lines >= threshold` from `scripts/coverage-thresholds.json` (frozen 2026-08-14 = recompute − 3pp, two runs were byte-identical); branches/functions recorded, not gated |
330
- | Protected exceptions | `@arnilo/prism-core/sessions/postgres`, `@arnilo/prism-core/enterprise/postgres`, `@arnilo/prism-memory`, `@arnilo/prism-core/sessions/nats` — durable legs need `PRISM_TEST_POSTGRES_URL` or a real NATS server; plus `@arnilo/prism-coding-tools/security` — native-sandbox legs probe `unshare --net` (NETNS) and skip on CI runners (host runs exercise them); exempt from the gate, reported separately with the reason |
331
- | Artifact | `scripts/coverage-summary.json` (gitignored, CI-retained): per-package `lines`/`branches`/`functions`/`denominatorFiles`/`threshold`/`pass`/`protectedException` + `belowThreshold` |
334
+ | 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 |
335
+ | 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 |
336
+ | 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 |
337
+ | 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) |
332
338
  | 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 |
333
339
  | Overrides | `PRISM_COVERAGE_THRESHOLDS`, `PRISM_COVERAGE_ARTIFACT` (used by the gate regression) |
334
340
 
335
- A new workspace package must add an evidence-based threshold entry (or a `protectedException` reason) to `scripts/coverage-thresholds.json` before `test:coverage` passes.
341
+ A new workspace package must add an evidence-based threshold entry (or a `protectedException` reason) to `scripts/coverage-thresholds.json` before `test:coverage` passes, and a removed package's row must be pruned in the same change (the retired-row assertion in `scripts/phase23-coverage.test.mjs` fails otherwise). Recapture the rows from a `scripts/coverage-summary.json` written by two back-to-back runs; never hand-edit a percentage. A failing package is self-describing: the summary prints the child's redacted output tail under the row (repo root and home become `<repo>`/`<home>`, credential-shaped env values are redacted through `createSecretRedactor` — names only, never values), and the same `tail` lands in the artifact, so a bare `(suite failed)` is never the whole story (`scripts/coverage-failure.mjs`, unit-tested by the gate).
336
342
 
337
343
  ### Release evidence and protected skips
338
344
 
@@ -350,6 +356,14 @@ Surfaces: core `npm test` (counts and the skip total come from the latest `phase
350
356
 
351
357
  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.
352
358
 
359
+ Protected legs fail closed through one convention (`scripts/blocked-gate.mjs`): a leg that cannot run prints exactly one canonical record and exits non-zero (test-style legs fail as a test failure), never a passing skip:
360
+
361
+ ```text
362
+ BLOCKED GATE <id> requires=<names> evidence=<surface> hint=<how to unblock>
363
+ ```
364
+
365
+ `node scripts/blocked-gate.mjs` audits the same registry — which protected legs cannot run with the current environment, which of them a release profile owns (their evidence surface decides the gate), and which are documented gaps that never block. `scripts/blocked-gate.test.mjs` (in-chain) asserts the registry, the record shape, and the fail-closed exits; `scripts/phase27-freeze-manifest.json` freezes the record template.
366
+
353
367
  ### Quality-gate reports and the Biome baseline
354
368
 
355
369
 
@@ -382,43 +396,46 @@ Major dependency upgrades are **isolated, compatibility-tested changes — never
382
396
 
383
397
  **Current third-party upgrade surface** (internal `@arnilo/prism-*` ranges are version-managed by the release tooling, not dependency upgrades; the core `@arnilo/prism` package has **zero** runtime dependencies, asserted by `core-boundaries.test.ts`):
384
398
 
399
+ Development type packages track the **declared** engines floor, not the machine that builds them: `@types/node` stays on the oldest supported Node line (`^22.20.0` while `engines.node` is `>=22`), so an API that the floor cannot provide fails the build instead of compiling clean against a newer type surface. Raising the floor is a support-matrix change (freeze manifest + CI legs + `docs/migration.md`), never a side effect of bumping the types package.
400
+
385
401
  | Dependency | Range | Resolved (lockfile) | Used by |
386
402
  | --- | --- | --- | --- |
387
403
  | `typescript` (dev) | `^7.0.2` | 7.0.2 | root build |
388
- | `@types/node` (dev) | `^26.1.1` | 26.1.1 | root build |
389
- | `@biomejs/biome` (dev) | `^2.5.5` | 2.5.5 | lint/format (Task 6) |
404
+ | `@types/node` (dev) | `^22.20.0` | 22.20.2 | root build (pinned to the `engines.node` floor) |
405
+ | `@biomejs/biome` (dev) | `^2.5.13` | 2.5.13 | lint/format |
390
406
  | `diff` | `^9.0.0` | 9.0.0 | `@arnilo/prism-coding-tools/agent` |
391
- | `pg` | `^8.22.0` | 8.22.0 | `@arnilo/prism-memory`, `@arnilo/prism-core/sessions/postgres` |
392
- | `better-sqlite3` | `^12.11.1` | 12.11.1 | `@arnilo/prism-core/sessions/sqlite` |
407
+ | `pg` | `^8.23.0` | 8.23.0 | `@arnilo/prism-memory`, `@arnilo/prism-core/sessions/postgres` |
408
+ | `better-sqlite3` | `^13.0.3` | 13.0.3 | `@arnilo/prism-core/sessions/sqlite` |
393
409
  | `ajv` | `^8.17.1` | 8.20.0 | `@arnilo/prism-core/validation/json-schema` |
394
- | `zod` | `^4.4.3` | 4.4.3 | `@arnilo/prism-mcp` |
410
+ | `zod` | `^4.6.2` | 4.6.2 | `@arnilo/prism-mcp` |
395
411
  | `@napi-rs/keyring` | `^1.3.0` | 1.3.0 | `@arnilo/prism-core/credentials/node` |
396
- | `@modelcontextprotocol/sdk` | `1.29.0` | 1.29.0 | `@arnilo/prism-mcp` |
412
+ | `@modelcontextprotocol/client` | `2.0.0` | 2.0.0 | `@arnilo/prism-mcp` |
413
+ | `@modelcontextprotocol/server` | `2.0.0` | 2.0.0 | `@arnilo/prism-mcp` |
397
414
  | `@ag-ui/core` | `0.0.59` | 0.0.59 | `@arnilo/prism-ag-ui` |
398
- | `@agentclientprotocol/sdk` | `1.3.0` | 1.3.0 | `@arnilo/prism-ag-ui` |
415
+ | `@agentclientprotocol/sdk` | `1.4.0` | 1.4.0 | `@arnilo/prism-ag-ui`, `@arnilo/prism-acp-agent` |
399
416
 
400
417
  **Recorded compatibility matrix (2026-07-26, release 0.0.16):**
401
418
 
402
419
  | Leg | Node | Result |
403
420
  | --- | --- | --- |
404
421
  | Full SDK readiness (`npm run sdk:ready`: typecheck, lint, format, test, coverage, pack, release:gate) | 24.18.0 (current) | ✅ green — 1312/1312 tests, lint 0 errors, format clean, coverage 64/72/79 vs 60/70/75 thresholds. |
405
- | Build toolchain (`tsc` 7.0.2, `biome` 2.5.5) | 20.20.2 (LTS iron) | ✅ both run under Node 20. |
422
+ | Build toolchain (`tsc` 7.0.2, `biome` 2.5.13) | 20.20.2 (LTS iron) | ✅ both run under Node 20. |
406
423
  | Public surface import smoke (all 21 root `exports` default targets) | 20.20.2 | ✅ all import cleanly. |
407
424
  | Full core test suite | 20.20.2 | 1311/1312 — the single failure is `examples_demos_run_to_completion_and_emit_no_secret`, which executes `examples/*.ts` via Node's native TypeScript stripping (Node 22.6+). This is a test-harness capability, not an SDK runtime incompatibility, and is exactly why CI scopes Node 20 to build + import smoke. |
408
425
 
409
- **CI enforcement** (`.github/workflows/release.yml`): the `verify` job runs `npm run sdk:ready` on Node 24; `node20-compat` runs `npm ci`, `npm run build`, and the public-import smoke on Node 20; `supply-chain` runs audit, SPDX/license checks, SBOM, and source-secret scans; `publish` `needs:` all of `verify`, `node20-compat`, `postgres-integration`, `codeql-release`, and `supply-chain`, so nothing publishes unless every leg — including the audit/SBOM gates — passes.
426
+ **CI enforcement** (`.github/workflows/release.yml`): the `verify` job runs `npm run sdk:ready` on Node 24; `node22-compat` runs `npm ci`, `npm run build`, and the public-import smoke on Node 22; `supply-chain` runs audit, SPDX/license checks, SBOM, and source-secret scans; `publish` `needs:` all of `verify`, `node22-compat`, `postgres-integration`, `codeql-release`, and `supply-chain`, so nothing publishes unless every leg — including the audit/SBOM gates — passes.
410
427
 
411
428
  **Process for a major-upgrade PR:** (1) bump exactly one dependency major in its own branch; (2) `npm run sdk:ready` green; (3) packed-install evidence (`npm run pack:dry-run`, or a scratch `npm install <tarball>` import smoke for native deps like `better-sqlite3`); (4) review lockfile churn line-by-line; (5) the `supply-chain` job supplies audit/SBOM; (6) confirm no build-time regression beyond measured noise on the matrix above; (7) merge separately from any feature work.
412
429
 
413
430
  ## Release checklist
414
431
 
415
432
 
416
- Every release gate maps to an exact enforcement test or command, so the checklist is executable rather than manual. Run `npm run sdk:ready` for the full local SDK readiness gate: `npm run typecheck`, `npm run lint`, `npm run format:check`, network-free `npm test`, `npm run test:coverage`, `npm run pack:dry-run`, and `npm run release:gate`. `npm run release:dry-run` is an alias for the same gate. The GitHub Actions `verify` job runs `npm ci` and `npm run sdk:ready` on Node 24; `node20-compat` runs `npm ci`, `npm run build`, and public export imports on Node 20; `postgres-integration` runs the opt-in PostgreSQL adapter suite against a CI Postgres service.
433
+ Every release gate maps to an exact enforcement test or command, so the checklist is executable rather than manual. Run `npm run sdk:ready` for the full local SDK readiness gate: `npm run typecheck`, `npm run lint`, `npm run format:check`, network-free `npm test`, `npm run test:coverage`, `npm run pack:dry-run`, and `npm run release:gate`. `npm run release:dry-run` is an alias for the same gate. The GitHub Actions `verify` job runs `npm ci` and `npm run sdk:ready` on Node 24; `node22-compat` runs `npm ci`, `npm run build`, and public export imports on Node 22; `postgres-integration` runs the opt-in PostgreSQL adapter suite against a CI Postgres service.
417
434
 
418
435
  | Gate | Enforcement |
419
436
  | --- | --- |
420
437
  | Docs coverage for persistence/runtime/migration surfaces | `docs.test.ts` enrolls every API page in `apiPages` (heading + index-link + bare-specifier + secret-scan checks); dedicated section assertions pin `database-persistence.md`, `runs-and-usage.md`, `session-stores-and-branching.md`, `migration.md`, `agent-definitions.md`, `performance.md`, and the Phase 41 `external_app_example_*` / `phase41_external_app_surfaces_*` gates. |
421
- | Package exports/subpaths resolve to built output | `public-export-contract.test.ts` asserts every `exports`/`main`/`types`/`bin` target resolves to a built file under `dist/` with a sibling `.d.ts` (`dist/index.js` + `dist/index.d.ts` for a root package), and no target escapes `dist/` (no `src/` or `examples/` leak). CI `node20-compat` also imports every public root `exports` default target on Node 20. |
438
+ | Package exports/subpaths resolve to built output | `public-export-contract.test.ts` asserts every `exports`/`main`/`types`/`bin` target resolves to a built file under `dist/` with a sibling `.d.ts` (`dist/index.js` + `dist/index.d.ts` for a root package), and no target escapes `dist/` (no `src/` or `examples/` leak). CI `node22-compat` also imports every public root `exports` default target on Node 22. |
422
439
  | Public-API drift | `public-export-contract.test.ts` `phase39_public_protocol_exports_and_types_do_not_drift` pins the runtime protocol (`providerToolCallDelta`, `ToolCallDeltaContent`), the `/testing/provider-conformance` subpath shape, and the observational-memory runtime `.d.ts` surface. |
423
440
  | 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`. |
424
441
  | 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. |
@@ -427,10 +444,13 @@ Every release gate maps to an exact enforcement test or command, so the checklis
427
444
  | 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. |
428
445
  | 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. |
429
446
  | 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. |
447
+ | Release version claims have one source | `currentVersion()` (`scripts/package-truth.mjs`) reads the root manifest, and `scripts/version-literal-gate.test.mjs` fails when any surface that claims the release version disagrees with it: all 10 manifests, every internal `@arnilo/*` caret range, `package-lock.json`, the `src/index.ts` version constant, the `docs/index.md` current-line banner, the `release.yml` tag list, and the generated `scripts/package-truth.json`. Each stale surface is named in one pre-flight (with a positive control for a half-finished cut), and the release-line suites (`packaging.test.ts`, `docs.test.ts`, `release.test.ts`, `phase24-truth`, `phase34-freeze`) derive their expectations from the manifest instead of pinning the cut. |
430
448
  | Pre-publish compatibility gates | `release:gate` (in `sdk:ready`) fails on removed/changed `.d.ts` exports vs `scripts/compat-baseline/` (unless `--allow-break` + migration note), version-range/lockfile drift, and tarball deny-list violations (`plans/`, `code-reviews/`, `docs/review-coverage-*`, `*.map`, `__tests__/`); unit-tested in `scripts/release-gate.test.mjs`. |
431
449
  | 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. |
432
450
  | 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`. |
433
451
  | 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. |
452
+ | 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. |
453
+ | 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. |
434
454
  | 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). |
435
455
  | 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). |
436
456
 
package/docs/server.md CHANGED
@@ -8,7 +8,7 @@ No listener starts on import. Empty `agents`/`workflows` maps expose nothing. Au
8
8
 
9
9
  ## When to use it
10
10
 
11
- Use it when a Node 20, serverless, worker, or framework host already speaks Web `Request`/`Response` and needs a small Prism API boundary. Wrap it in the platform's native adapter rather than adding Express, Fastify, Hono, Koa, Nest, or Next to Prism.
11
+ Use it when a Node 22, serverless, worker, or framework host already speaks Web `Request`/`Response` and needs a small Prism API boundary. Wrap it in the platform's native adapter rather than adding Express, Fastify, Hono, Koa, Nest, or Next to Prism.
12
12
 
13
13
  Use `AgentSession` or workflow APIs directly for in-process applications. Do not treat this package as an auth provider, user database, firewall, durable agent-result store, or public listener.
14
14
 
@@ -31,7 +31,7 @@ Public helpers:
31
31
  | Helper | Purpose |
32
32
  | --- | --- |
33
33
  | `createSessionEntry(options)` | Build a `SessionEntry` with generated `id`/`timestamp` when omitted. |
34
- | `createMemorySessionStore(initialEntries?, options?)` | Built-in in-memory `SessionStore`. `options.sessionSearchMode`: `"linear"` (default) or `"unsupported"` (throws `SessionSearchUnsupportedError`). |
34
+ | `createMemorySessionStore(initialEntries?, options?: CreateMemorySessionStoreOptions)` | Built-in in-memory `SessionStore`. `options.sessionSearchMode`: `"linear"` (default) or `"unsupported"` (throws `SessionSearchUnsupportedError`); `options.search` may override the linear scan caps (`maxLinearSessions` / `maxLinearEntries` / `maxLinearBytes`), each bounded by its `HARD_MAX_SESSION_SEARCH_LINEAR_*` value and validated at construction (`TypeError` below 1 or above the hard cap). |
35
35
  | `resolveSessionSearchQuery(query)` | Validate/clamp search limits (page, query bytes, snippet, cursor, linear/FTS caps). |
36
36
  | `SessionIndex` | Narrow search seam (`search(query)`); adapters may expose this instead of `SessionStore.searchSessions`. |
37
37
  | `getSessionBranchEntries(entries, options)` | Return root-to-leaf entries for a leaf id (sync array path). |
@@ -124,6 +124,8 @@ const page = await store.searchSessions!({
124
124
  limit: 20,
125
125
  });
126
126
  // Opt out: createMemorySessionStore([], { sessionSearchMode: "unsupported" })
127
+ // Raise the in-process scan caps for a small but large-query session set (defaults are the contract caps):
128
+ const wide = createMemorySessionStore([], { search: { maxLinearSessions: 5_000, maxLinearEntries: 50_000 } });
127
129
  ```
128
130
 
129
131
  Finite caps (defaults / hard): page 20/100; query string 4 KiB/16 KiB; snippet 512 B/4 KiB; cursor 1 KiB/4 KiB; memory linear sessions 1000/5000, entries 10000/50000, bytes 8 MiB/64 MiB; DB FTS candidates 1000/5000. Overflow fails closed via `resolveSessionSearchQuery`. See [Phase 6 evidence](_evidence/review-coverage-2026-07-22-phase-6.md).
@@ -1,5 +1,7 @@
1
1
  # SQLite persistence
2
2
 
3
+ > **Optional peer install:** `better-sqlite3` — see [Optional peer dependencies](peer-dependencies.md).
4
+
3
5
  ## What it does
4
6
 
5
7
  The optional `@arnilo/prism-core/sessions/sqlite` package ships a production-oriented SQLite adapter that implements:
@@ -0,0 +1,38 @@
1
+ # Test layout and isolation
2
+
3
+ ## What it does
4
+
5
+ Documents how the hermetic suite runs, which stage a new suite belongs to, and the isolation rules that keep tracked fixtures byte-identical between runs. Live and credentialed tiers are separate — see [Live and end-to-end testing](live-testing.md).
6
+
7
+ ## When to use it
8
+
9
+ - Adding or moving a suite: pick its stage and follow the scratch-root rule below.
10
+ - Investigating a report that a test run modified tracked files or scaffolded directories in the repository.
11
+
12
+ ## Running the suite
13
+
14
+ `npm test` delegates to `scripts/run-all-tests.mjs`, which runs five stages and reports every stage even when an earlier one fails:
15
+
16
+ | stage | contents |
17
+ | :--- | :--- |
18
+ | build | `npm run build` (all workspaces) |
19
+ | root suites | `dist/__tests__/*.test.js` |
20
+ | gate suites | `scripts/*.test.mjs` — the protection, truth, benchmark, journey, and conformance gates listed in `GATE_FILES` (`scripts/run-all-tests.mjs`) |
21
+ | build race | `scripts/phase23-build-race.test.mjs` |
22
+ | workspace suites | `npm run test --workspaces --if-present` |
23
+
24
+ Protected-environment legs (Postgres, PTY, NATS, live credentials) are not part of `npm test`; they fail closed with one canonical `BLOCKED GATE <id> requires=<names> evidence=<surface> hint=<how to unblock>` record and a non-zero exit when their infrastructure is absent (registry and audit: `node scripts/blocked-gate.mjs`). Retired phase freeze/release gates live in `scripts/` for audit but are deliberately kept out of the chain.
25
+
26
+ ## Isolation rules
27
+
28
+ - **Scratch roots come from the OS.** A suite that writes anything creates its root with `mkdtempSync(join(tmpdir(), "prism-…"))` and removes it in `after()`. Never rely on `process.cwd()` for write targets: the same suite runs with different working directories (workspace stage vs. root stage), so a cwd-relative root silently writes into the repository.
29
+ - **Pass explicit roots.** Wiki, memory, and store helpers default `workspaceRoot` to `process.cwd()`; suites pass their scratch root (and a `wikiRoot` relative to it) instead of accepting the default.
30
+ - **Tracked fixtures stay byte-identical.** `packages/memory/.wiki/` is a tracked wiki fixture and `docs/` is a tracked corpus. `scripts/wiki-scratch-isolation.test.mjs` runs the wiki suites from the package and from the repository root and fails if the tracked fixture hashes change, if a new file appears inside the fixture, if `<repo>/.wiki/` is scaffolded, or if the old cwd-relative scratch directories reappear.
31
+ - **Gates never write inside the repository.** A gate asserts against tracked content and spawns suites in temporary directories only. A gate that spawns `node --test` must strip `NODE_TEST_CONTEXT`/`NODE_TEST_WORKER_ID` from the child environment (an inherited value makes the nested runner skip every file and still exit 0) and assert the child reported a non-zero pass count.
32
+ - **Wait by polling, not by sleeping.** Async browser state (download quarantine, idle reaping) is not awaitable from the outside — `manager.ts` settles it on a fire-and-forget listener promise — so a fixed sleep is a race that loses under CPU load and fails the assertion for a reason unrelated to the behavior under test. Suites poll observable state through `waitFor(read, ok, label, { timeoutMs, intervalMs })` in `packages/web-tools/src/browser/__tests__/wait-for.ts`, which returns as soon as the state appears and otherwise throws naming the label and the last observed value. Fixed sleeps remain only where real elapsed time is the subject of the test (idle TTLs).
33
+
34
+ ## Related APIs
35
+
36
+ - [Live and end-to-end testing](live-testing.md): live matrix, credential scoping, skip-not-fail contract.
37
+ - [Coverage gates](release-and-install.md): per-package line thresholds and the functional-surface baseline.
38
+ - `scripts/run-all-tests.mjs` — the stage table, `STAGES` and `effectiveTestChain()` exports.
package/docs/tools.md CHANGED
@@ -216,7 +216,7 @@ By default tools without `parameters` skip schema validation (`missingSchema: "a
216
216
 
217
217
  ### Parallel tool execution (single-shot loop)
218
218
 
219
- Opt in through `loop.toolConcurrency` on `AgentConfig` / `RunOptions` (single-shot strategy only). Default is `1` (sequential). Independent calls from one provider turn run concurrently up to the limit; transcript rows and `appendMessage` stay in original call order. Each call still uses `dispatchToolCall` (permission, validation, abort signal). If a worker throws or the run aborts, workers stop claiming new calls, already-claimed calls settle, buffered tool-result rows are not appended, and the first failure is rethrown. Already-claimed side effects are not rolled back; the shared abort signal is still passed to each dispatch. The round-level `chargeToolRound` approval gate runs before any worker starts. See [Agent loops](agent-loops.md).
219
+ Opt in through `loop.toolConcurrency` on `AgentConfig` / `RunOptions` (single-shot strategy only). Default is `1` (sequential). Independent calls from one provider turn run concurrently up to the limit; transcript rows and `appendMessage` stay in original call order. Each call still uses `dispatchToolCall` (permission, validation, abort signal). If a worker throws or the run aborts, workers stop claiming new calls, already-claimed calls settle, and rows are then persisted in call order before the first failure is rethrown: finished calls keep their real results, the call that threw gets an error row carrying that failure, and calls the batch never started get a `tool_call_not_dispatched` error row — a stopped batch never leaves `tool_call` ids without a `tool_result` (run-level suspension errors are exempt: their resume machinery appends the real result). Already-claimed side effects are not rolled back; the shared abort signal is still passed to each dispatch. The round-level `chargeToolRound` approval gate runs before any worker starts. See [Agent loops](agent-loops.md).
220
220
 
221
221
  ```ts
222
222
  await session.run(input, {
package/docs/wiki.md CHANGED
@@ -17,7 +17,7 @@ It integrates Tobias Lütke's [`qmd`](https://github.com/tobi/qmd) on-device hyb
17
17
 
18
18
  The Karpathy LLM Wiki pattern is structured into 3 distinct tiers:
19
19
 
20
- 1. **Raw Sources (Immutable)**: Source code files, design docs, transcripts, journals, and Markdown notes. Raw sources are strictly read-only and never mutated.
20
+ 1. **Raw Sources (Immutable)**: Source code files, design docs, transcripts, journals, and Markdown notes. Raw sources are strictly read-only and never mutated. New external material arrives through the ingest staging area (`raw/ingest/<utc>-<slug>/`): an immutable `source.*` original plus a UTF-8 `extract.md` the maintainer skill files into the wiki.
21
21
  2. **Compiled Wiki (`.wiki/`)**: Persistent, cross-linked Markdown documents containing synthesized architecture models, entity descriptions, decision records, and line-anchored claims.
22
22
  3. **Schema & Protocols (`SCHEMA.md`)**: Operational guidelines governing OKF v0.2 emission, entity categorization, citation rules (`file:///path#Lxx-Lyy`), catalog indexing (`index.md`), and chronological change logging (`log.md`).
23
23
 
@@ -27,7 +27,7 @@ The Karpathy LLM Wiki pattern is structured into 3 distinct tiers:
27
27
 
28
28
  | Field | Type | Required | Default | Description |
29
29
  | :--- | :--- | :--- | :--- | :--- |
30
- | `wikiRoot` | `string` | No | `".wiki"` | Path to the compiled wiki directory. |
30
+ | `wikiRoot` | `string` | No | `".wiki"` | Path to the compiled wiki directory; a relative path is resolved against `workspaceRoot`, an absolute path is used as-is. |
31
31
  | `rawRoots` | `readonly string[]` | No | `["."]` | Directories containing raw source files (code, notes, docs). |
32
32
  | `profile` | `"codebase" \| "pkm" \| "hybrid" \| "auto"` | No | `"auto"` | Operating strategy for parsing and symbol indexing. |
33
33
  | `qmdPath` | `string` | No | `"qmd"` | Path or executable name for the `qmd` CLI binary. |
@@ -39,12 +39,14 @@ The Karpathy LLM Wiki pattern is structured into 3 distinct tiers:
39
39
  - `wiki_search`: `{ query: string, mode?: "search" | "vsearch" | "query", maxResults?: number }`
40
40
  - `wiki_read_page`: `{ pagePath: string }` — `pagePath` must resolve inside the wiki root (lexical + `fs.realpath` containment). Traversal (sibling-prefix, `..`, absolute paths) and symlinks pointing outside the wiki throw an access-denied error; a missing contained page returns `found: false`.
41
41
  - `wiki_record_insight`: `{ title: string, content: string, category?: "decision" | "concept" | "entity" }` — title and content must be non-empty; titles are capped at 200 characters, content at 65,536 bytes, and control characters/newlines in titles are collapsed to spaces so titles cannot inject Markdown headings, index entries, or log entries.
42
+ - `wiki_ingest`: `{ text?: string, path?: string, url?: string, title?: string }` — stages one raw source and returns a filing brief for the `wiki-maintainer` skill (see `/wiki-ingest` below). Exactly one of `text`/`path`/`url` must be provided; `url` requires a host `fetchUrl` hook.
42
43
 
43
44
  ### Slash Commands
44
45
 
45
46
  - `/wiki-init`: Scaffolds `.wiki/`, instantiates `SCHEMA.md`, `index.md`, and `log.md`, deploys skills, and adds the `qmd` collection.
46
47
  - `/wiki-refresh`: Detects modified source files via SHA-256 Merkle diffing, compiles updates to affected entity pages, reconciles contradictions in `log.md`, and runs `qmd update`.
47
48
  - `/wiki-lint`: Checks OKF frontmatter (`type`, ISO `generated.at`), leftover `[[wikilinks]]`, unresolved relative markdown links, dead line anchors, and orphan pages.
49
+ - `/wiki-ingest`: `{ text?, path?, url?, title? }` — stages one external source into `raw/ingest/<utc>-<slug>/` (`source.*` original + `extract.md`), then returns a brief (staged paths, extract preview, source URL when applicable, Karpathy filing checklist). When the host injects `drivers`, the command calls `drivers.startRun(brief, { activeSkills: ["wiki-maintainer"] })` so the maintainer skill files the source into the wiki; without drivers it stages only and reports `runStarted: false`. Results are labeled `metadata.trust: "untrusted_external"`.
48
50
 
49
51
  ### Standalone CLI Commands
50
52
 
@@ -60,12 +62,49 @@ npx prism-wiki lint
60
62
 
61
63
  # Search wiki from terminal
62
64
  npx prism-wiki search "How does authentication work?" --mode query
65
+
66
+ # Stage an external source for the wiki
67
+ npx prism-wiki ingest --path notes/paper.pdf --title "Paper"
68
+
69
+ # `--url` is a usage error in the standalone CLI:
70
+ # the wiki package never fetches — URL ingest needs a host fetchUrl hook
71
+ npx prism-wiki ingest --url https://example.com/rfc.pdf # → exit 1
63
72
  ```
64
73
 
65
74
  ## Outputs / response / events
66
75
 
67
76
  - `wiki_search` returns a structured markdown payload containing section breadcrumbs, conceptual summaries, and clickable source line links (`file:///path#Lxx-Lyy`).
68
77
  - Lifecycle commands return status objects (`{ status: "initialized" | "refreshed" | "clean", ok: boolean }`).
78
+ - `wiki_ingest` / `/wiki-ingest` return the staged paths, the (capped) extract preview, and a filing brief; `value.runStarted` reports whether a driver run was started.
79
+
80
+ ### `ingestWikiSource(input, options)`
81
+
82
+ The staging primitive behind `/wiki-ingest`, `wiki_ingest`, and the CLI. Accepts `{ text?, path?, bytes?, url?, filename?, title? }` (precedence `path` > `bytes` > `url` > `text`) and returns `{ id, rawDir, sourcePath, extractPath, mediaType?, extract, truncated, url? }` (`url` present only for URL-staged sources).
83
+
84
+ | Input | Parse behavior |
85
+ | :--- | :--- |
86
+ | Text-like files and `text` | Decoded as UTF-8 (RAG text/markdown/html parsers) |
87
+ | Uncompressed PDF | Parsed by the RAG PDF parser (bounded pages/bytes) |
88
+ | Compressed PDF / DOCX | Throws a named error unless the host supplies `options.extractDocument` (e.g. wire `createDocumentReader()` from `@arnilo/prism-coding-tools/document-reader`) |
89
+ | `url` | `assertSsrfAllowedUrl` runs first (private/link-local hosts rejected before any fetch); then the host `fetchUrl` hook supplies the bytes/text — missing or empty hook output fails closed. Staged filename comes from the hook, the URL extension (`doc.pdf`), or `source.md` |
90
+ | Images | Staged as-is; stub extract points at the staged `source.*` — no OCR; view the file |
91
+ | Unknown binary | Fails closed unless `extractDocument` claims it |
92
+
93
+ Caps: 32 MiB per staged input, 2 MiB per extract. `path` must resolve inside the workspace root (realpath containment). `log.md` gains an `**Ingested**` entry only when the wiki root exists.
94
+
95
+ Wiring a `fetchUrl` hook (the wiki package ships no HTTP client — hosts bring their own, e.g. Obscura):
96
+
97
+ ```ts
98
+ import { runObscuraCli, validateObscuraWebUrl } from "@arnilo/prism-web-tools/obscura";
99
+
100
+ const wiki = createWikiExtension({
101
+ fetchUrl: async ({ url }) => {
102
+ validateObscuraWebUrl(url); // host-side SSRF gate of its own
103
+ const run = await runObscuraCli({ command: "obscura", args: ["fetch", url, "--dump", "markdown"] });
104
+ return { text: run.stdout, filename: "source.md" };
105
+ },
106
+ });
107
+ ```
69
108
 
70
109
  ## Request/response example
71
110
 
@@ -135,12 +174,17 @@ Emitted `.wiki/` trees are [OKF v0.2](https://github.com/GoogleCloudPlatform/ope
135
174
 
136
175
  ## Extension and configuration notes
137
176
 
138
- - The wiki subpath registers tools (`wiki_search`, `wiki_read_page`, `wiki_record_insight`), commands (`wiki-init`, `wiki-refresh`, `wiki-lint`), skills (`wiki-maintainer`, `wiki-searcher`), and instruction injectors (`wiki-guidance`) into Prism registries.
177
+ - The wiki subpath registers tools (`wiki_search`, `wiki_read_page`, `wiki_record_insight`, `wiki_ingest`), commands (`wiki-init`, `wiki-refresh`, `wiki-lint`, `wiki-ingest`), skills (`wiki-maintainer`, `wiki-searcher`), and instruction injectors (`wiki-guidance`) into Prism registries.
139
178
  - It operates with zero core modifications and can be used with any `@arnilo/prism` agent.
140
179
  - `qmd` is optional but recommended. When `@tobilu/qmd` is not installed, the search engine falls back to catalog matching against `index.md`.
141
180
 
181
+ ## Ingest filing rules (Karpathy/OKF)
182
+
183
+ After staging, the `wiki-maintainer` skill files the source: read `.wiki/SCHEMA.md` and `index.md` first, read the staged `extract.md` (and view `source.*` for images/PDFs), integrate claims into existing entity/concept/decision pages — create pages only for genuinely new concepts — then emit OKF v0.2 frontmatter (`type` required; `sources[].resource` pointing at the staged `source.*` — for URL-staged sources the original URL is also legitimate; `generated.by: prism-wiki/ingest`), add per-claim footnotes keyed to `sources[].id` for source-specific claims, update `index.md`, and prepend an `**Ingested**` entry to `log.md`. Never copy raw bodies into wiki pages; one source per ingest; contradictions update the existing page and are logged. The same rules ship in every scaffolded `SCHEMA.md` (`## Ingest Protocol`).
184
+
142
185
  ## Security and performance notes
143
186
 
187
+ - **Ingest boundaries**: the wiki package never fetches — `url` inputs require a host `fetchUrl` hook, and `assertSsrfAllowedUrl` rejects private/link-local hosts before the hook runs; the standalone CLI rejects `--url` with a usage error. `path` inputs are contained inside the workspace root via realpath; extracts are untrusted data (`trust: "untrusted_external"`), never instructions; the raw layer stays read-only for the LLM; images get a stub extract (no OCR).
144
188
  - **Source Immutability**: Raw source files are read-only and never modified by wiki operations.
145
189
  - **Subprocess Safety**: All `qmd` subprocess calls use argument arrays (`execFile`) to prevent shell injection.
146
190
  - **Path Containment**: Wiki and raw source paths are confined to the workspace root; directory traversal (`../`) is rejected.