@arnilo/prism 0.4.0 → 0.5.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 (171) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/README.md +23 -20
  3. package/dist/agent-run-state.d.ts +1 -2
  4. package/dist/agent-run-state.js +0 -3
  5. package/dist/agent-session/session/assemble.d.ts +6 -0
  6. package/dist/agent-session/session/assemble.js +391 -0
  7. package/dist/agent-session/session/persist.d.ts +28 -0
  8. package/dist/agent-session/session/persist.js +166 -0
  9. package/dist/agent-session/session/provider-round.d.ts +6 -0
  10. package/dist/agent-session/session/provider-round.js +231 -0
  11. package/dist/agent-session/session/tool-round.d.ts +31 -0
  12. package/dist/agent-session/session/tool-round.js +473 -0
  13. package/dist/agent-session/session/types.d.ts +115 -0
  14. package/dist/agent-session/session/types.js +5 -0
  15. package/dist/agent-session/session.d.ts +49 -43
  16. package/dist/agent-session/session.js +11 -1177
  17. package/dist/capture.d.ts +63 -0
  18. package/dist/capture.js +67 -0
  19. package/dist/cli-init.d.ts +18 -2
  20. package/dist/cli-init.js +2 -7
  21. package/dist/cli-runner.d.ts +2 -2
  22. package/dist/cli-runner.js +45 -9
  23. package/dist/content.d.ts +3 -3
  24. package/dist/content.js +3 -1
  25. package/dist/contracts-core/agent.d.ts +2 -0
  26. package/dist/contracts-core/batch.d.ts +97 -0
  27. package/dist/contracts-core/batch.js +65 -0
  28. package/dist/contracts-core/content.d.ts +72 -1
  29. package/dist/contracts-core/embeddings.d.ts +30 -0
  30. package/dist/contracts-core/embeddings.js +17 -0
  31. package/dist/contracts-core/images.d.ts +60 -0
  32. package/dist/contracts-core/images.js +17 -0
  33. package/dist/contracts-core/moderation.d.ts +46 -0
  34. package/dist/contracts-core/moderation.js +34 -0
  35. package/dist/contracts-core/speech.d.ts +39 -0
  36. package/dist/contracts-core/speech.js +17 -0
  37. package/dist/contracts-core/transcription.d.ts +48 -0
  38. package/dist/contracts-core/transcription.js +17 -0
  39. package/dist/contracts-core/video.d.ts +61 -0
  40. package/dist/contracts-core/video.js +17 -0
  41. package/dist/contracts-core.d.ts +7 -0
  42. package/dist/contracts-core.js +7 -0
  43. package/dist/index.d.ts +5 -3
  44. package/dist/index.js +4 -3
  45. package/dist/node/agent-definitions.d.ts +1 -8
  46. package/dist/node/agent-definitions.js +0 -34
  47. package/dist/node/settings.d.ts +0 -1
  48. package/dist/node/settings.js +0 -5
  49. package/dist/pinned-fetch.js +29 -3
  50. package/dist/provider-events.js +3 -4
  51. package/dist/providers/media.d.ts +1 -2
  52. package/dist/providers/media.js +1 -4
  53. package/dist/rpc.d.ts +1 -1
  54. package/dist/rpc.js +4 -4
  55. package/dist/testing/provider-conformance.d.ts +114 -5
  56. package/dist/testing/provider-conformance.js +342 -0
  57. package/dist/testing/tool-effect-store-conformance.d.ts +0 -1
  58. package/dist/testing/tool-effect-store-conformance.js +0 -3
  59. package/dist/thinking.d.ts +48 -9
  60. package/dist/thinking.js +134 -8
  61. package/docs/0.1.0-readiness.md +3 -3
  62. package/docs/a2a.md +2 -2
  63. package/docs/acp.md +3 -3
  64. package/docs/ag-ui-adoption.md +1 -1
  65. package/docs/ag-ui.md +1 -2
  66. package/docs/agent-definitions.md +1 -1
  67. package/docs/agent-events.md +5 -5
  68. package/docs/agent-identity.md +13 -2
  69. package/docs/audit-export.md +3 -3
  70. package/docs/batch-jobs.md +120 -0
  71. package/docs/cli-rpc.md +20 -9
  72. package/docs/coding-agent-tools.md +19 -19
  73. package/docs/coding-review-and-diagnostics.md +2 -2
  74. package/docs/coding-security.md +4 -4
  75. package/docs/coding-workspaces.md +2 -2
  76. package/docs/computer-use-linux.md +13 -2
  77. package/docs/context-and-skills.md +1 -1
  78. package/docs/conversations.md +4 -4
  79. package/docs/credential-storage.md +11 -7
  80. package/docs/credentials-and-redaction.md +1 -1
  81. package/docs/data-classification.md +1 -1
  82. package/docs/database-persistence.md +4 -4
  83. package/docs/dev-inspector.md +6 -6
  84. package/docs/device-adapters.md +2 -2
  85. package/docs/diagrams.md +1 -1
  86. package/docs/document-reader.md +6 -6
  87. package/docs/documents.md +5 -4
  88. package/docs/embeddings.md +112 -0
  89. package/docs/enterprise-postgres-state.md +7 -7
  90. package/docs/evaluations.md +8 -8
  91. package/docs/extensions.md +3 -3
  92. package/docs/forge-integration.md +3 -3
  93. package/docs/graft.md +2 -2
  94. package/docs/guardrails.md +1 -1
  95. package/docs/host-security.md +15 -15
  96. package/docs/image-generation.md +129 -0
  97. package/docs/impeccable.md +5 -3
  98. package/docs/index.md +60 -33
  99. package/docs/indexed-code-search.md +2 -2
  100. package/docs/language-intelligence.md +4 -4
  101. package/docs/live-testing.md +126 -0
  102. package/docs/mcp-tools.md +43 -12
  103. package/docs/middleware-hooks.md +1 -1
  104. package/docs/migrate-to-0.4.md +3 -3
  105. package/docs/migrate-to-0.5.md +122 -0
  106. package/docs/migration.md +29 -1
  107. package/docs/model-registry.md +38 -0
  108. package/docs/model-routing.md +5 -5
  109. package/docs/moderation.md +117 -0
  110. package/docs/multi-agent-patterns.md +4 -4
  111. package/docs/multimodal-content.md +26 -2
  112. package/docs/obscura.md +2 -2
  113. package/docs/observability.md +32 -7
  114. package/docs/openapi-tools.md +13 -3
  115. package/docs/operations.md +11 -0
  116. package/docs/performance.md +7 -7
  117. package/docs/persistence-credentials-multimodality-primitives.md +6 -6
  118. package/docs/policy-and-audit.md +17 -7
  119. package/docs/ponytail.md +1 -1
  120. package/docs/postgres-persistence.md +5 -5
  121. package/docs/process-sessions.md +2 -2
  122. package/docs/prompt-registry.md +7 -7
  123. package/docs/provider-caching.md +4 -0
  124. package/docs/provider-conformance.md +23 -1
  125. package/docs/provider-packages.md +39 -3
  126. package/docs/provider-primitives.md +1 -1
  127. package/docs/provider-request-policies.md +1 -1
  128. package/docs/providers/ai-sdk.md +15 -3
  129. package/docs/providers/alibaba.md +5 -1
  130. package/docs/providers/anthropic.md +4 -0
  131. package/docs/providers/azure.md +17 -1
  132. package/docs/providers/bedrock.md +15 -0
  133. package/docs/providers/clinepass.md +4 -0
  134. package/docs/providers/commandcode.md +253 -0
  135. package/docs/providers/deepseek.md +4 -0
  136. package/docs/providers/google.md +4 -0
  137. package/docs/providers/hyper.md +284 -0
  138. package/docs/providers/kimi.md +4 -0
  139. package/docs/providers/neuralwatt.md +4 -0
  140. package/docs/providers/ollama.md +15 -0
  141. package/docs/providers/openai-compatible.md +4 -0
  142. package/docs/providers/openai.md +4 -0
  143. package/docs/providers/opencode-go.md +4 -0
  144. package/docs/providers/openrouter.md +5 -1
  145. package/docs/providers/vertex.md +16 -0
  146. package/docs/providers/xai.md +4 -0
  147. package/docs/providers/zai.md +4 -0
  148. package/docs/rag.md +26 -4
  149. package/docs/release-and-install.md +103 -46
  150. package/docs/resource-loading.md +1 -1
  151. package/docs/runs-and-usage.md +14 -2
  152. package/docs/server.md +5 -5
  153. package/docs/settings-auth-trust-security.md +7 -5
  154. package/docs/sheets.md +2 -2
  155. package/docs/speech.md +126 -0
  156. package/docs/sqlite-persistence.md +4 -4
  157. package/docs/supervisors.md +3 -3
  158. package/docs/thinking-and-reasoning.md +93 -60
  159. package/docs/tool-conformance.md +1 -1
  160. package/docs/tool-execution-primitives.md +8 -8
  161. package/docs/tools.md +4 -4
  162. package/docs/web-tools.md +1 -1
  163. package/docs/wiki.md +1 -1
  164. package/docs/work-artifacts-and-review.md +17 -6
  165. package/docs/work-connectors.md +4 -4
  166. package/docs/work-tools.md +5 -5
  167. package/docs/workflow-orchestration-primitives.md +11 -11
  168. package/docs/workflows.md +5 -5
  169. package/package.json +11 -8
  170. package/templates/init/providers.json +24 -8
  171. package/docs/antigravity-agent.md +0 -207
@@ -0,0 +1,126 @@
1
+ # Live and end-to-end testing
2
+
3
+ ## What it does
4
+
5
+ Prism ships three network-free test tiers by default (`npm test`, per-package `node --test` suites, and packed-consumer journeys) plus an opt-in **live matrix** that runs the same public surface against real credentials. This page documents how to run the live matrix, which credentials each suite needs, and the guarantees the harness gives you: a missing credential skips its suite (never fails), and secrets never enter the repo, logs, or reports.
6
+
7
+ ## When to use it
8
+
9
+ - Before a release: prove every public export subpath still works over a real wire or real process, not just against fakes.
10
+ - After adding a provider or protocol adapter: add a live suite, register it in `scripts/live-matrix.json`, and the coverage gate + doc-check keep the docs honest.
11
+ - In CI: scheduled canaries probe deployed endpoints; strict mode is available for release gates.
12
+
13
+ ## Coverage definition (what "100%" means)
14
+
15
+ - **Functional-surface 100%:** every public export subpath across the ten first-party packages (98 subpaths, tracked in `scripts/e2e-coverage.json`) is exercised by at least one suite — a hermetic unit test, a real-wire live test, a real-binary leg, or the full-surface packed journey (`scripts/e2e-full-surface.test.mjs`). The gate is `node scripts/e2e-coverage-gate.mjs` (baseline mode fails only on regressions).
16
+ - **Line-ratchet caveat:** the per-package line-coverage thresholds in `scripts/coverage-thresholds.json` are measured by the network-free `npm test` run only. Live suites intentionally do not contribute to line coverage; 100% functional-surface coverage does not mean 100% line coverage of every branch under live conditions.
17
+
18
+ ## Running the matrix
19
+
20
+ ```sh
21
+ npm run test:live # run active suites whose credentials are present
22
+ node scripts/live-matrix.mjs --check # validate manifest + per-suite skip/run table (no spawns)
23
+ ```
24
+
25
+ | knob | effect |
26
+ |---|---|
27
+ | `PRISM_LIVE_ENV_FILE` | credential file, loaded with Node `--env-file` semantics. Default: `scripts/live.env` when present. Copy `scripts/live.env.example` to start. |
28
+ | `PRISM_LIVE_FILTER=<sub>` | only suites whose id contains `<sub>` |
29
+ | `PRISM_LIVE_STRICT=1` | any skip fails the **run** (exit 1), not the suite — for release gates |
30
+ | `PRISM_LIVE_DRY_RUN=1` | accounting only, no spawns |
31
+ | `PRISM_LIVE_CONCURRENCY=n` | parallel suites (default 1, sequential) |
32
+ | `PRISM_LIVE_SUITE_TIMEOUT_MS` | per-suite kill timer (default 600000) |
33
+
34
+ ### Skip-not-fail contract
35
+
36
+ A suite whose required credentials are absent is **skipped with a reason**, never failed. `scripts/live-matrix.json` encodes the contract per suite (`requires` all-of, `requiresAny` any-of, `optional` extras); `resolveSuiteState()` is the pure implementation the runner and the `--check` table share. Strict mode inverts the semantics deliberately: with `PRISM_LIVE_STRICT=1` a skip fails the run, because a release gate wants proof the credentials were present and the wires were exercised.
37
+
38
+ ### Model selection
39
+
40
+ Every provider suite reads `PRISM_LIVE_<PROVIDER>_MODEL` to pin the model under test (multi-probe suites suffix `_CHAT_MODEL` / `_MESSAGES_MODEL` / `_GPT_MODEL`). Each manifest row records the wired env var and its cost-bounded default, so cost stays predictable. `wired: false` means the suite does not read that env var yet.
41
+
42
+ ### Report
43
+
44
+ Each run writes `docs/_evidence/live-matrix-report.{json,md}`: per-suite status/duration/detail, totals, a filter/strict/dry-run annotation, and an e2e surface-coverage summary. The report contains env var **names** and model ids — never secret values.
45
+
46
+ ## Credential matrix
47
+
48
+ Set only the rows you want to run; everything else skips. Least-privilege scope is mandatory per row: issue the narrowest credential that satisfies the named probe. Credentials live in `scripts/live.env` (gitignored) or `~/.config/prism/live.env` (out-of-repo), never in the repository, test fixtures, or CI logs; the journey suites assert the `assertNoSecretLeak` convention (from `@arnilo/prism/testing/*`) over every transcript they produce, and the manifest validator rejects secret-shaped values.
49
+
50
+ <!-- generated:live-matrix:start -->
51
+ | suite | status | required credentials | model override | least-privilege scope | cost |
52
+ |---|---|---|---|---|---|
53
+ | `providers/openai` | active | `PRISM_LIVE_PROVIDER_TESTS` + `OPENAI_API_KEY` | `PRISM_LIVE_OPENAI_MODEL` (default `gpt-5.1`) | Project-scoped chat-completion key; least privilege: restrict to chat models. | 2 requests (1 text, 1 tool-call) on the configured model. |
54
+ | `providers/anthropic` | active | `PRISM_LIVE_PROVIDER_TESTS` + `ANTHROPIC_API_KEY` | `PRISM_LIVE_ANTHROPIC_MODEL` (default `claude-haiku-4-5`) | Chat-completion key. | 2 requests. |
55
+ | `providers/google` | active | `PRISM_LIVE_PROVIDER_TESTS`; any of: `GEMINI_API_KEY` / `GOOGLE_API_KEY` | `PRISM_LIVE_GOOGLE_MODEL` (default `gemini-2.5-flash-lite`) | Gemini API key (either var). | 2 requests. |
56
+ | `providers/alibaba` | active | `PRISM_LIVE_PROVIDER_TESTS` + `PRISM_LIVE_DASHSCOPE_KEY` | `PRISM_LIVE_DASHSCOPE_MODEL` (default `text-embedding-v4`) | DashScope key. | 2 requests (embedding/roundtrip class). |
57
+ | `providers/clinepass` | active | `PRISM_LIVE_PROVIDER_TESTS` + `CLINE_API_KEY` | `PRISM_LIVE_CLINEPASS_MODEL` (default `cline-pass/deepseek-v4-flash`) | Cline provider key. | 2 requests. |
58
+ | `providers/commandcode` | active | `PRISM_LIVE_PROVIDER_TESTS` + `COMMAND_CODE_API_KEY` | `PRISM_LIVE_COMMANDCODE_CHAT_MODEL` (default `Qwen/Qwen3.8-Flash`), `PRISM_LIVE_COMMANDCODE_MESSAGES_MODEL` (default `claude-haiku-4-5-20251001`), `PRISM_LIVE_COMMANDCODE_GPT_MODEL` (default `gpt-5.6-luna`) | Command Code key; probes three cheap routed models. | 3-4 requests on cheap models. |
59
+ | `providers/deepseek` | active | `PRISM_LIVE_PROVIDER_TESTS` + `DEEPSEEK_API_KEY` | `PRISM_LIVE_DEEPSEEK_MODEL` (default `deepseek-v4-flash`) | DeepSeek key. | 2 requests on flash-class model. |
60
+ | `providers/hyper` | active | `PRISM_LIVE_PROVIDER_TESTS` + `HYPER_API_KEY` | `PRISM_LIVE_HYPER_CHAT_MODEL` (default `deepseek-v4-pro`), `PRISM_LIVE_HYPER_MESSAGES_MODEL` (default `qwen3.6-plus`) | Hyper key; second model probes the exact-prefix cache. | ~4 requests incl. ~1 KiB prefix cache write; sub-cent. |
61
+ | `providers/kimi` | active | `PRISM_LIVE_PROVIDER_TESTS` + `KIMI_API_KEY` | `PRISM_LIVE_KIMI_MODEL` (default `kimi-for-coding`) | Kimi coding-plan credential. | 2 requests. |
62
+ | `providers/neuralwatt` | active | `PRISM_LIVE_PROVIDER_TESTS` + `NEURALWATT_API_KEY` | `PRISM_LIVE_NEURALWATT_MODEL` (default `glm-5.2`) | NeuralWatt key. | 2 requests. |
63
+ | `providers/opencode-go` | active | `PRISM_LIVE_PROVIDER_TESTS` + `OPENCODE_API_KEY` | `PRISM_LIVE_OPENCODE_MODEL` (default `mimo-v2.5`) | OpenCode Go key. | 2 requests. |
64
+ | `providers/openrouter` | active | `PRISM_LIVE_PROVIDER_TESTS` + `OPENROUTER_API_KEY` | `PRISM_LIVE_OPENROUTER_MODEL` (default `anthropic/claude-sonnet-4`) | OpenRouter key (any routed model id). | 2 requests. |
65
+ | `providers/xai` | active | `PRISM_LIVE_PROVIDER_TESTS` + `XAI_API_KEY`; optional: `PRISM_LIVE_XAI_OAUTH` | `PRISM_LIVE_XAI_MODEL` (default `grok-4.6`) | xAI key; OAuth leg additionally needs PRISM_LIVE_XAI_OAUTH=1. | 2-3 requests. |
66
+ | `providers/zai` | active | `PRISM_LIVE_PROVIDER_TESTS` + `ZAI_API_KEY` | `PRISM_LIVE_ZAI_MODEL` (default `glm-5.2`) | Z.ai key. | 2 requests. |
67
+ | `web-tools/brave` | active | `PRISM_LIVE_WEB` + `PRISM_BRAVE_SEARCH_TOKEN` | — | Brave Search token, least-privilege plan; 1 result per query. | 1 search request. |
68
+ | `web-tools/exa` | active | `PRISM_LIVE_WEB` + `PRISM_EXA_API_KEY` | — | Exa key; 1 result per query. | 1 search request. |
69
+ | `web-tools/firecrawl` | active | `PRISM_LIVE_WEB` + `PRISM_FIRECRAWL_API_KEY` | — | Firecrawl key; bounded 64 KiB markdown fetch. | 1 fetch request. |
70
+ | `web-tools/browser-live` | active | any of: `PRISM_LIVE_PLAYWRIGHT` / `PRISM_TEST_PLAYWRIGHT` | — | Real Chromium, no secret; needs Playwright browsers installed. | Local browser session, no API spend. |
71
+ | `web-tools/obscura-live` | active | `PRISM_LIVE_OBSCURA` + `PRISM_OBSCURA_BIN` | — | Local obscura CLI binary; suite fails closed if flag set without binary. | Local process, no API spend. |
72
+ | `memory/observational-live` | active | `PRISM_LIVE_OBSERVATIONAL_MEMORY_TESTS` + `OPENAI_API_KEY` | `PRISM_LIVE_OPENAI_MODEL` (not wired yet) | Reuses the OpenAI key as the compaction worker provider. | A few small summarization requests. |
73
+ | `memory/compaction-llm-live` | active | `PRISM_LIVE_COMPACTION_TESTS` | — | Stub leg today: live summary-provider checks are wired by plans/064 Task 6 (provider key + model env TBD there). | n/a until wired. |
74
+ | `office/libreoffice-golden` | active | `PRISM_TEST_LIBREOFFICE` | — | Local LibreOffice binary renders golden documents; no secret. | Local process, no API spend. |
75
+ | `office/drawio-live` | active | any of: `PRISM_LIVE_DRAWIO_URL` / `PRISM_TEST_DRAWIO_URL` | — | Operator-hosted drawio export service URL (not a secret). | 1-2 export requests to your own service. |
76
+ | `core/postgres` | active | `PRISM_TEST_POSTGRES_URL` | — | Throwaway PostgreSQL database URL (sessions + enterprise + event-source + memory vector legs). | Local/container DB, no API spend. |
77
+ | `core/nats` | active | `PRISM_TEST_NATS_URL` | — | NATS server URL with JetStream enabled. | Local/container server, no API spend. |
78
+ | `coding-tools/docker-sandbox` | active | `PRISM_TEST_DOCKER_SANDBOX` + `PRISM_TEST_DOCKER_BIN` + `PRISM_TEST_DOCKER_IMAGE` + `PRISM_TEST_DOCKER_USER` | — | Local Docker daemon + pinned minimal sandbox image; no secret. | Local containers, no API spend. |
79
+ | `core/keychain` | active | `PRISM_TEST_KEYCHAIN` | — | Real OS keychain; writes throwaway test entries only. | Local, no API spend. |
80
+ | `acp/client-smoke` | active | `PRISM_TEST_ACP_CLIENT` | — | Real ACP SDK client over stdio in a subprocess; sandboxed, policy never disabled. | Local process, no API spend. |
81
+ | `canaries/deployed` | active | `PRISM_LIVE_CANARIES`; optional: `PRISM_CANARY_TIMEOUT_MS` `PRISM_CANARY_REPORT` | — | Deployed prism provider/MCP/A2A endpoints; script itself validates all PRISM_CANARY_* URL/token vars and credential-free HTTPS. | 1-4 bounded requests (64 KiB JSON cap) against your deployments. |
82
+ | `providers/azure` | active | `PRISM_LIVE_PROVIDER_TESTS` + `AZURE_OPENAI_ENDPOINT` + `AZURE_OPENAI_API_KEY` + `PRISM_LIVE_AZURE_MODEL` | `PRISM_LIVE_AZURE_MODEL` (default `gpt-5.1`) | Azure OpenAI resource key. | 3-4 requests on the configured deployment. |
83
+ | `providers/bedrock` | active | `PRISM_LIVE_PROVIDER_TESTS` + `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` + `AWS_REGION` | `PRISM_LIVE_BEDROCK_MODEL` (default `us.anthropic.claude-haiku-4-5-20251001-v1:0`) | AWS access key + secret (SigV4). | 3-4 requests on haiku-class model. |
84
+ | `providers/vertex` | active | `PRISM_LIVE_PROVIDER_TESTS` + `GOOGLE_VERTEX_PROJECT` + `PRISM_VERTEX_ACCESS_TOKEN` | `PRISM_LIVE_VERTEX_MODEL` (default `gemini-2.5-flash`) | Pre-minted Vertex bearer token (e.g. gcloud auth print-access-token). | 3-4 requests on flash-class model. |
85
+ | `providers/ollama` | active | `PRISM_LIVE_PROVIDER_TESTS` + `OLLAMA_BASE_URL` | `PRISM_LIVE_OLLAMA_MODEL` (default `(first model served by OLLAMA_BASE_URL)`) | No credential for local ollama serve; Ollama Cloud key optional via provider options. | 3-4 requests on the first locally pulled model. |
86
+ | `providers/ai-sdk` | active | `PRISM_LIVE_PROVIDER_TESTS` + `OPENAI_API_KEY` | `PRISM_LIVE_AISDK_MODEL` (default `gpt-5.1`) | Reuses OPENAI_API_KEY through the real @ai-sdk/openai provider. | 3 requests on gpt-5.1. |
87
+ | `providers/model-discovery` | active | `PRISM_LIVE_PROVIDER_TESTS`; any of: `OPENAI_API_KEY` / `GEMINI_API_KEY` | — | Reuses OPENAI_API_KEY or GEMINI_API_KEY for a real listing request. | 2 GET /models requests (second cached in TTL). |
88
+ | `cli/live-journey` | planned | `PRISM_LIVE_PROVIDER_TESTS` + `OPENAI_API_KEY` | — | Packed CLI: init/provider-add/print/json/rpc over a real provider; transcript secret-scanned. | 1 pack + install, offline scaffold tests, <=3 wire prompts on the selected provider model (wire legs skip on 401/403). |
89
+ | `memory/rag-rerankers-live` | active | any of: `PRISM_TEST_TEI_RERANKER_URL` / `PRISM_TEST_HOSTED_RERANK_URL`; optional: `PRISM_TEST_HOSTED_RERANK_URL` | `PRISM_LIVE_TEI_RERANKER_MODEL` (default `(endpoint default model)`), `PRISM_LIVE_HOSTED_RERANK_MODEL` (default `(endpoint default model)`) | Real TEI / OpenAI-compatible rerank endpoints; each leg self-skips when its endpoint env is unset. | 1 rerank request per configured endpoint (≤2 total). |
90
+ | `coding-tools/openapi-live` | active | `PRISM_LIVE_OPENAPI_TOOLS` | — | Real public OpenAPI 3.1 spec (warnely.com) + real GET tool calls; no credential. | 3 HTTP requests against example.com-class public hosts (plan budget ≤5). |
91
+ | `coding-tools/computer-use-live` | active | `PRISM_TEST_COMPUTER_USE` + `PRISM_COMPUTER_USE_BIN` | — | Real host computer-use-linux MCP binary over stdio; real tool inventory + one bounded read-only screenshot. | Local desktop only; ≤30s ceiling. |
92
+ | `mcp/client-smoke` | active | `PRISM_TEST_MCP_CLIENT` | — | Real @modelcontextprotocol/client SDK over a real stdio subprocess serving createPrismMcpServer. | Local only; ≤30s. |
93
+ | `core/opa-live` | active | `PRISM_TEST_OPA_URL` | — | Operator-hosted OPA REST endpoint (optionally token-gated). | <=3 decision requests to the operator OPA endpoint; fail-closed probe never reaches the wire. |
94
+ | `core/oidc-live` | active | `PRISM_TEST_OIDC_ISSUER` + `PRISM_TEST_OIDC_AUDIENCE` + `PRISM_TEST_OIDC_TOKEN` | — | Real IdP issuer/JWKS + short-lived test bearer token. | 1 JWKS fetch (cached) + 3 local verify calls; no token endpoint traffic. |
95
+ | `core/webhooks-live` | active | `PRISM_TEST_WEBHOOK_URL` | — | Operator-controlled signed webhook receiver. | <=3 webhook deliveries (1 signed target + 1 loopback retry receiver). |
96
+ | `core/artifact-bodies-s3-live` | active | `PRISM_TEST_S3_ENDPOINT` + `PRISM_TEST_S3_KEY` + `PRISM_TEST_S3_SECRET` + `PRISM_TEST_S3_BUCKET` | — | Dedicated throwaway S3-compatible bucket. | <=5 S3 requests (put/get/presign/delete x2). |
97
+ | `cli/journey` | active | `PRISM_LIVE_PROVIDER_TESTS`; any of: `OPENAI_API_KEY` / `OPENROUTER_API_KEY` / `KIMI_API_KEY` / `ZAI_API_KEY` / `OPENCODE_API_KEY` / `NEURALWATT_API_KEY` / `DASHSCOPE_API_KEY` / `OLLAMA_API_KEY` | — | First init-catalog provider credential present in the environment. | 4-5 one-shot requests on the default catalog model. |
98
+ | `memory/postgres` | active | `PRISM_TEST_POSTGRES_URL` | — | Postgres memory store + pgvector index round-trips on the operator database. | Bounded insert/query cycles against the configured Postgres. |
99
+ | `memory/graft` | active | — (hermetic leg) | — | Graft upstream CLI child-process protocol (real binary spawns against the in-repo fixture graft bin). | Hermetic; no network. |
100
+ | `memory/wiki` | active | — (hermetic leg) | — | Wiki lifecycle over real fs trees (init/refresh/lint/search fallback; qmd child-process client degrades without the binary). | Hermetic; no network. |
101
+ | `coding-tools/lsp-forge` | active | — (hermetic leg) | — | LSP/language-intelligence + forge suites: real child-process spawns over the real LSP/forge wire protocols against fixture binaries. | Hermetic; no network. |
102
+ | `ag-ui/conformance` | active | — (hermetic leg) | — | AG-UI + ACP conformance suites: real-event replay over the acp/a2a/ag-ui protocol surfaces (fixture agents, real event-source wire semantics). | Hermetic; no network. |
103
+ | `prism-providers/conformance` | active | — (hermetic leg) | — | Plan-065 machine-checked thinking coverage: every first-party reasoning catalog model declares capabilities.thinkingLevels + a compat.thinkingFamily stamp and emits a legal effort field on the wire (14 catalogs walked hermetically). | free |
104
+ <!-- generated:live-matrix:end -->
105
+
106
+ ## Strict CI workflow
107
+
108
+ The scheduled [`live-canaries` workflow](../.github/workflows/live-canaries.yml) (Tuesdays 05:43 UTC, plus `workflow_dispatch`) runs four restricted protocol canaries — provider, MCP, A2A, and Brave search — from a GitHub `live-canaries` environment, with every value supplied through repository secrets and a 15 s per-canary timeout. It runs `scripts/live-canary.mjs` (gated on `PRISM_LIVE_CANARIES=1`, which requires **all** canary credentials together so A2A never runs standalone) and uploads the status report as a workflow artifact. The full live matrix runs on a weekly schedule (and on demand) via the [`live-matrix` workflow](../.github/workflows/live-matrix.yml): strict-mode filtered run (default `providers/opencode-go`, the credentialed suites) with the report + e2e-coverage evidence uploaded as artifacts; operators also run it from a credentialed checkout with `npm run test:live`, optionally `PRISM_LIVE_STRICT=1` when gating a release.
109
+
110
+ ## Security and performance notes
111
+
112
+ - Credentials never enter the repo, test fixtures, logs, or reports; only env var names and model ids are persisted. `assertNoSecretLeak` (exported from `@arnilo/prism/testing/*`) runs over live transcripts, and secret-shaped values in `scripts/live-matrix.json` are rejected by the manifest validator.
113
+ - Suite costs are bounded and recorded per row; provider model defaults are the cheapest cost-bounded models that satisfy each probe.
114
+ - Per-suite `PRISM_LIVE_SUITE_TIMEOUT_MS` kill timers and opt-in `PRISM_LIVE_CONCURRENCY` parallelism keep a stuck wire from stalling the run.
115
+ - Packaging tests run under `scripts/with-build-lock.mjs` so parallel suites never race the TypeScript build.
116
+
117
+ ## Extension and configuration notes
118
+
119
+ - Add a suite: write the env-gated test (skip-not-fail), register it in `scripts/live-matrix.json` (`id`, `package`, `status`, `source`, `command`, `cwd`, `requires`/`requiresAny`, model wiring, least-privilege `scope`, `cost`), then run `node scripts/generate-live-docs.mjs --write` to refresh the table above. `scripts/live-doc-check.test.mjs` fails `npm test` when the doc drifts.
120
+ - Add a credential: extend `scripts/live.env.example` and the relevant manifest `requires` list. Prefer `requiresAny` when a probe accepts several interchangeable providers.
121
+
122
+ ## Related APIs
123
+
124
+ - `@arnilo/prism/testing/*` conformance helpers (`assertProviderStreamConforms`, `assertNoSecretLeak`): the assertion vocabulary live suites reuse.
125
+ - `scripts/e2e-coverage-gate.mjs` + `scripts/e2e-coverage.json`: the functional-surface coverage ledger this matrix satisfies.
126
+ - [Provider conformance](provider-conformance.md): the network-free contract checks every provider adapter must pass before a live suite is worth running.
package/docs/mcp-tools.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- `@arnilo/prism-mcp` has two explicit directions. Its client bridge connects hosts to remote [Model Context Protocol](https://modelcontextprotocol.io) servers and maps discovered tools to ordinary `ToolDefinition`s. Its server API registers selected Prism `ToolDefinition` and `CommandDefinition` values on the official SDK `McpServer`, with required authorization and a bounded optional Web-standard Streamable HTTP handler. The package pins `@modelcontextprotocol/sdk` **1.30.0** (MCP protocol negotiation remains SDK-owned) and adds no MCP branch to core Prism.
5
+ `@arnilo/prism-mcp` has two explicit directions. Its client bridge connects hosts to remote [Model Context Protocol](https://modelcontextprotocol.io) servers and maps discovered tools to ordinary `ToolDefinition`s. Its server API registers selected Prism `ToolDefinition` and `CommandDefinition` values on the official SDK `McpServer`, with required authorization and a bounded optional Web-standard Streamable HTTP handler. The package pins the modular TypeScript SDK v2 packages `@modelcontextprotocol/client` and `@modelcontextprotocol/server` **2.0.0** (MCP protocol negotiation remains SDK-owned; the 2026-07-28 adoption is tracked in plan 063) and adds no MCP branch to core Prism.
6
6
 
7
7
  Primary API:
8
8
 
@@ -32,19 +32,25 @@ const app = await bridge.apps!.readResource("ui://weather/card");
32
32
 
33
33
  `bridge.apps` exposes reviewed UI metadata, linked bounded `ui://` HTML, and same-server app tools for a host renderer/proxy; it never creates an iframe or executes HTML.
34
34
 
35
+ **Conformance and verification record (plan 063 task 7):** the official `@modelcontextprotocol/conformance` suite runs against the dual-era serving stack via `node scripts/mcp-conformance-2026.mjs` — the 20 expressible scenarios pass and the 14 scenarios requiring surface Prism does not expose (server-initiated sampling/elicitation/logging/progress from tool callbacks, `resources/subscribe`, completion capability, session-based SSE polling, non-text tool content blocks) are recorded as documented boundaries in `scripts/mcp-conformance-2026-baseline.yaml`. The CLI publishes scenarios only up to spec version 2025-11-25; the run is repeated when 2026-07-28 scenarios ship upstream. Measured on the loopback fixture (single process): legacy connect ~60ms, auto connect (with the one discovery probe) ~40ms, pinned modern connect ~20ms, steady-state bridge tool call ~4ms, uncached list walk ~5ms, cached list refresh ~2ms — the only added connect cost versus 1.x is the single negotiation probe plus SDK codec work. Security regression coverage lives in the package suites: malformed envelopes/headers, auth mix-up (`ERR_PRISM_MCP_OAUTH_ORIGIN`), SSRF/DNS-rebinding, oversized JSON/schema/results, MRTR round/replay, subscription exhaustion caps, cross-principal cache/session isolation, timeouts, cancellation, and redaction.
36
+
37
+ **Extension status (revalidated 2026-09-05 against the modular SDK v2, plan 063 task 6):** MCP Apps (`io.modelcontextprotocol/ui`) is the only extension Prism negotiates — on both the 2025 legacy handshake and 2026-07-28 (modern capabilities ride per request in `params._meta`); resource reads/pagination now go through SDK v2 `listResources`/`readResource` with the same per-descriptor byte bounds, item caps, cursor-loop detection, and linked-HTML validation as before. **Tasks (`io.modelcontextprotocol/tasks`) is intentionally not advertised and not supported in this release**: neither the bridge client nor `createPrismMcpServer` declares it, draft-era `task` members on tool results fail closed (`McpBridgeError` surfaced as a `ToolResult.error`, never read as tool output; modern `resultType: "task"` fails SDK decode), and task handles are not accepted without a supported extension codec plus a durable ownership model. Re-evaluate Tasks when the official TypeScript client/server extension codec supports task result dispatch, polling/update/cancel, and subscription notifications with green conformance.
38
+
35
39
  ```ts
36
40
  const bridge = await connectMcpCapabilities({
37
41
  serverId: "research",
38
42
  transport: { type: "streamable-http", url, allowedOrigins: [origin] },
39
- roots: () => [{ uri: "file:///workspace", name: "workspace" }],
40
- sampling: hostSampling, // host selects model/provider/credentials
41
- elicitation: hostElicitation, // URL mode returns approval; Prism never opens/fetches URL
43
+ roots: () => [{ uri: "file:///workspace", name: "workspace" }], // deprecated: kept for legacy callers only
44
+ sampling: hostSampling, // deprecated: kept for legacy callers only
45
+ elicitation: hostElicitation, // active; URL mode returns approval; Prism never opens/fetches URL
42
46
  });
43
47
  await bridge.listResources();
44
48
  await bridge.getPrompt("review", { topic: "security" });
45
49
  ```
46
50
 
47
- Server capability matrix for SDK 1.30.0: tools/resources/prompts and their list-change notifications are supported through official registrations; roots/sampling/form+URL elicitation are supported as explicit client callbacks. Missing server resources/prompts throw `McpUnsupportedCapabilityError` with `ERR_PRISM_MCP_UNSUPPORTED_CAPABILITY`. Resource/prompt results and sampling/elicitation inputs/results are bounded JSON. Accepted form/URL elicitation requires host-only `humanInteraction: true`; bridge strips marker before protocol output and fails closed when absent. Automatic root discovery/consent, model selection, credential resolution, URL navigation, generic command proxying, and custom JSON-RPC are unsupported.
51
+ Roots and sampling callbacks are **deprecated with MCP 2026-07-28 (SEP-2577)** and kept only for existing legacy callers; Synapta adds nothing on those surfaces and they may be removed when the protocol revision does (earliest per spec: a revision released on or after 2027-07-28). Elicitation is the active capability: on the modern era a server answering a tool call with `input_required` is fulfilled by the SDK's MRTR driver against the same `elicitation` callback, capped by `maxMrtrRounds` (default 10, the SDK's own bound — the option can only tighten it) with the call timeout as the outer ceiling; on the legacy era the same handler serves direct `elicitation/create` requests. `humanInteraction: true` remains mandatory for accepted elicitation, and no hand-written retry/state machinery exists on either path.
52
+
53
+ Server capability matrix for the modular TypeScript SDK v2 (`@modelcontextprotocol/client` + `@modelcontextprotocol/server` 2.0.0): tools/resources/prompts and their list-change notifications are supported through official registrations; roots/sampling/form+URL elicitation are supported as explicit client callbacks. Missing server resources/prompts throw `McpUnsupportedCapabilityError` with `ERR_PRISM_MCP_UNSUPPORTED_CAPABILITY`. Resource/prompt results and sampling/elicitation inputs/results are bounded JSON. Accepted form/URL elicitation requires host-only `humanInteraction: true`; bridge strips marker before protocol output and fails closed when absent. Automatic root discovery/consent, model selection, credential resolution, URL navigation, generic command proxying, and custom JSON-RPC are unsupported.
48
54
 
49
55
  Server direction:
50
56
 
@@ -66,13 +72,17 @@ const handleMcp = await createPrismMcpWebHandler(server, {
66
72
  resolveAuthInfo: authenticateRequest,
67
73
  allowedHosts: ["api.example.test"],
68
74
  allowedOrigins: ["https://app.example.test"],
69
- // Omit these two for bounded stateless JSON mode.
75
+ // Omit these two for dual-era serving without legacy sessions.
70
76
  sessionIdGenerator: crypto.randomUUID,
71
77
  resolveIdentity: (_request, auth) => auth ? { id: validatedPrincipalId(auth) } : false,
72
78
  });
73
79
  ```
74
80
 
75
- `McpServer.connect(transport)` remains available for SDK stdio or in-memory transports. The helper uses SDK `WebStandardStreamableHTTPServerTransport`; it does not start a listener. Default remains bounded stateless JSON-response mode. Supplying `sessionIdGenerator` enables SDK `MCP-Session-Id` POST/GET/DELETE/SSE lifecycle and requires exact `allowedOrigins` plus host `resolveIdentity`. Every request re-authenticates, and a different principal receives non-disclosing 404. SDK owns protocol-version/session headers and SSE semantics. SDK 1.30.0's in-memory event store is not enabled, so `Last-Event-ID` replay is explicitly unsupported; reconnect starts only through SDK-supported active session GET.
81
+ `createPrismMcpWebHandler()` is dual-era on the modular SDK v2 serving entries. Default (factory, no `sessionIdGenerator`): modern 2026-07-28 serving through SDK `createMcpHandler` — one fresh `McpServer` per request, SDK-generated `server/discover`, result metadata, cancellation and modern headers — with the SDK stateless fallback answering 2025-era traffic; modern responses carry no `Mcp-Session-Id`. Supplying `sessionIdGenerator` keeps documented legacy `MCP-Session-Id` POST/GET/DELETE/SSE lifecycle on a sessionful `WebStandardStreamableHTTPServerTransport` beside a strict modern handler (classified legacy traffic routes separately), and still requires exact `allowedOrigins` plus host `resolveIdentity`; every request re-authenticates, and a different principal receives non-disclosing 404. `Last-Event-ID` replay remains explicitly unsupported.
82
+
83
+ Security gates run in front of the SDK entries because `createMcpHandler` intentionally provides none: exact Host/Origin allowlist checks execute before body parsing, auth, and dispatch; bounded body parsing feeds `parsedBody`; verified `AuthInfo` is passed explicitly; authorization and identity checks run on every request; modern serving state never trusts a transport session id. A bare `McpServer` instance with sessions keeps legacy-only behavior; stateless mode requires a factory (one fresh server per request). The returned value stays callable and carries SDK lifecycle properties: `fetch` (same function), `close()` (tears down both eras), `notify.toolsChanged()/promptsChanged()/resourcesChanged()/resourceUpdated(uri)`, and `bus` for `subscriptions/listen`. `maxSubscriptions` (SDK default 1024) and `keepAliveMs` (SDK default 15000, 0 disables) bound modern subscription streams.
84
+
85
+ `servePrismMcpStdio(factory, options)` serves dual-era stdio: the opening exchange pins the era (one factory instance per connection; `legacy: "serve"` default keeps 2025 openings working, `"reject"` answers them with the unsupported-protocol-version error), and stdout stays protocol-only. `McpServer.connect(transport)` remains available for in-memory transports; direct `server.connect()` over HTTP is legacy-only. `createPrismMcpServer({ cacheHints })` emits SEP-2549 cache hints on cacheable 2026-07-28 results (default `ttlMs: 0`, `private`). Neither HTTP nor stdio helper starts a listener or process lifecycle for you.
76
86
 
77
87
  ## When to use it
78
88
 
@@ -92,7 +102,7 @@ Do **not** use this package as a sandbox, permission engine, or auto-discovery l
92
102
 
93
103
  `McpToolBridge` exposes `tools`, optional `apps`, `refresh()`, and `close()`. Normal tools are Prism `ToolDefinition`s. Apps requires server acknowledgement; nested resource metadata wins over flat/deprecated and app-only tools stay outside `tools`. Resource reads require linked bounded `ui://` HTML5 with exact MIME; content metadata wins over list defaults.
94
104
 
95
- `createPrismMcpServer()` returns the SDK `McpServer`. It lists only passed tools/commands and explicitly selected `agentRuns` lifecycle tools; JSON Schema parameters are converted through installed Zod v4 for SDK validation, then Prism tool calls still pass through `dispatchToolCall` permission/validator/redactor gates. Command definitions support explicitly selected direct/background/replay workflow operations and optional ownership-scoped schedule operations from `createWorkflowCommands()`; none are registered unless the host passes those command definitions. Calls return bounded MCP text content and `isError` on denial/failure. `createPrismMcpWebHandler()` returns `(Request) => Promise<Response>`.
105
+ `createPrismMcpServer()` returns the SDK `McpServer`. It lists only passed tools/commands and explicitly selected `agentRuns` lifecycle tools; JSON Schema parameters are converted through installed Zod v4 for SDK validation, then Prism tool calls still pass through `dispatchToolCall` permission/validator/redactor gates. Command definitions support explicitly selected direct/background/replay workflow operations and optional ownership-scoped schedule operations from `createWorkflowCommands()`; none are registered unless the host passes those command definitions. Calls return bounded MCP text content and `isError` on denial/failure. `createPrismMcpWebHandler()` remains callable for source compatibility and additionally carries `fetch`, `close`, `notify`, and `bus` from the SDK serving entry.
96
106
 
97
107
  ## Request/response example
98
108
 
@@ -241,15 +251,27 @@ import { createMcpClientAuth } from "@arnilo/prism-mcp";
241
251
  const auth = createMcpClientAuth(
242
252
  {
243
253
  state, // required persistence seam: load/save tokens, discovery, client info, code verifier
244
- strategy: { kind: "static", clientId: "prism", clientSecret: "..." }, // or { kind: "dcr", clientMetadata }
254
+ strategy: { kind: "cimd", clientMetadataUrl: "https://client.example/oauth/metadata.json" }, // preferred
255
+ // or { kind: "static", clientId: "prism", clientSecret: "..." }
256
+ // or { kind: "dcr", clientMetadata } — deprecated (RFC 7591), application_type defaults to "native"
245
257
  redirectUri: "http://localhost:33418/callback",
246
258
  onRedirectRequired: (url) => openBrowser(url), // interactive flows
259
+ onInsufficientScope: "reauthorize", // or "throw" to surface SDK InsufficientScopeError instead
247
260
  },
248
261
  { serverUrl: "https://mcp.example.com/api", fetch },
249
262
  );
250
263
  ```
251
264
 
252
- The flow reuses the MCP SDK's `auth()` helper (401 → protected-resource metadata → RFC 8414 discovery → PKCE S256 → token exchange/refresh) wrapped in Prism policy: discovery URLs are SSRF-checked, https-only (loopback http opt-in), DNS-pinned, zero-redirect, and byte-bounded; RFC 8707 resource binding is enforced on every token request (`ERR_PRISM_MCP_OAUTH_AUDIENCE` on origin drift); issuer origin must match the discovered authorization server (`ERR_PRISM_MCP_OAUTH_ORIGIN`); bearer tokens are only ever attached to the allow-listed server origin. `McpClientAuthState` has no default implementation — production hosts back it with an encrypted/keychain store (refresh tokens must not live in plaintext persistence).
265
+ The flow reuses the MCP SDK's `auth()` helper (401 → protected-resource metadata → RFC 8414 discovery → PKCE S256 → token exchange/refresh) wrapped in Prism policy: discovery URLs are SSRF-checked, https-only (loopback http opt-in), DNS-pinned, zero-redirect, and byte-bounded; RFC 8707 resource binding is enforced on every token request (`ERR_PRISM_MCP_OAUTH_AUDIENCE` on origin drift); issuer origin must match the discovered authorization server (`ERR_PRISM_MCP_OAUTH_ORIGIN`); bearer tokens are only ever attached to the allow-listed server origin.
266
+
267
+ 2026-07-28 authorization conformance (plan 063 task 5):
268
+
269
+ - **Callback completion takes the full query**: `await auth.finishAuth(new URL(callbackUrl).searchParams)`. The persisted OAuth `state` is validated first (fail-closed `ERR_PRISM_MCP_OAUTH_STATE` when absent or mismatched), then the RFC 9207 `iss` parameter is checked against the persisted authorization server; the SDK independently validates `iss` against the recorded issuer (RFC 9207 §2.4) and the callback-leg AS binding (SEP-2352) before redeeming the code. Callback `error`/`error_description` fields are never surfaced after an issuer mismatch. A bare code string is accepted as the legacy form (no state/iss validation).
270
+ - **Issuer-keyed credential storage**: persisted token/client records are SDK issuer-stamped (`StoredOAuthTokens`/`StoredOAuthClientInformation`); `McpClientAuthState` credential methods take the validated `issuer` so partitioned stores can key on it. Records stamped for a different issuer are never served, and ambiguous un-stamped pre-upgrade records are refused on issuer-keyed reads rather than guessed — re-authorization runs once and every persisted record is stamped thereafter.
271
+ - **Registration strategy**: CIMD (SEP-991 URL-based client ids) is preferred, `static` remains supported, and `dcr` is deprecated; the DCR metadata defaults `application_type` to `"native"` when the host omits it.
272
+ - **Insufficient-scope policy**: `onInsufficientScope: "reauthorize"` (default) runs the SDK's bounded step-up flow; `"throw"` surfaces the typed SDK `InsufficientScopeError` (with the challenge scope) instead of redirecting. Hosts gate interactive consent either way.
273
+
274
+ `McpClientAuthState` has no default implementation — production hosts back it with an encrypted/keychain store (refresh tokens must not live in plaintext persistence).
253
275
 
254
276
  **Server** — advertise protected-resource metadata and challenge unauthenticated requests:
255
277
 
@@ -264,12 +286,22 @@ const handler = await createPrismMcpWebHandler(factory, {
264
286
  });
265
287
  ```
266
288
 
267
- The handler serves `GET /.well-known/oauth-protected-resource` and returns `401 WWW-Authenticate: Bearer resource_metadata="<origin>/.well-known/oauth-protected-resource"` on rejected requests. Token verification remains entirely host-owned via `resolveIdentity`; Prism only advertises and challenges.
289
+ The handler serves `GET /.well-known/oauth-protected-resource` and returns `401 WWW-Authenticate: Bearer resource_metadata="<origin>/.well-known/oauth-protected-resource", scope="<configured scopes>"` on rejected requests. Token verification remains entirely host-owned via `resolveIdentity` — the host token verifier must validate the token audience (`aud`) against the protected resource and enforce the challenged scopes; Prism only advertises and challenges.
268
290
 
269
291
  ## Vendor web MCP prototype boundary
270
292
 
271
293
  Official Exa/Firecrawl MCP servers may be tested only as explicit hardened prototypes: pin endpoint/origin/auth, inspect declared capabilities, allow-list individual tools/resources, retain all MCP bounds, and never expose generic remote passthrough. Production web research uses direct host-selected `@arnilo/prism-web-tools` adapters so provider choice, credentials, schema, and costs remain outside model control.
272
294
 
295
+ ## Real-client smoke (plans/064 Task 8)
296
+
297
+ An operator-gated smoke drives `createPrismMcpServer` through the real `@modelcontextprotocol/client` SDK over a real stdio subprocess — the same shape a production MCP host uses:
298
+
299
+ ```bash
300
+ PRISM_TEST_MCP_CLIENT=1 node scripts/mcp-client-smoke.mjs
301
+ ```
302
+
303
+ The scenario stays sandboxed (read-only echo tool, authorize-gated denial, no policy bypass) and covers: modern auto + legacy handshakes, `tools/list` of registered capabilities, `tools/call` round-trip, the authorize gate enforced over the wire, and malformed-frame fail-closed (the transport tears the connection down instead of answering garbage). Server-initiated `elicitation/create` is a documented Prism boundary (`scripts/mcp-conformance-2026-baseline.yaml`); the bridge-side MRTR elicitation round-trip is covered over real HTTP by the package's modern-bridge tests. Registered in `scripts/live-matrix.json` as `mcp/client-smoke`.
304
+
273
305
  ## Related APIs
274
306
 
275
307
  - [Agent identity](agent-identity.md): optional verified identity on MCP authorize results
@@ -278,7 +310,6 @@ Official Exa/Firecrawl MCP servers may be tested only as explicit hardened proto
278
310
  - [Tool execution primitives](tool-execution-primitives.md): Plan 055 design and conformance matrix
279
311
  - [Host security guide](host-security.md): permission, trust, validation checklist
280
312
  - [Web-standard server handler](server.md): agent/workflow HTTP routes and shared remote-boundary rules
281
- - [Antigravity delegated agent](antigravity-agent.md): per-run loopback HTTP MCP server exposure for the official Antigravity CLI.
282
313
  - [ACP coding-host interop](acp.md): ACP clients may attach MCP servers to sessions — bounded configs (8/32 servers, 16 KiB/256 KiB config, 4 KiB/64 KiB header values), http/sse only when advertised, stdio accepted behind the gate, UNSTABLE `acp` always rejected, and every server approved by host `mcp.select` before the bridge connects.
283
314
 
284
315
  ## Testing
@@ -101,7 +101,7 @@ export const extension: Extension = {
101
101
  - `retry` middleware may stop retrying or adjust delay, but runtime still owns retry event emission, abort-aware waiting, and provider-turn boundaries.
102
102
  - The registry does not discover packages, read manifests, load config, call providers, execute tools, read resources, or start sessions.
103
103
  - Hosts may pass a middleware registry into `createExtensionKernel({ middleware })` to share it with direct host code.
104
- - For OpenTelemetry export, prefer `session.subscribe()` + `@arnilo/prism-observability-opentelemetry` (see [Observability](observability.md)) rather than adding a parallel event bus. Middleware hooks remain for transforming payloads at named boundaries.
104
+ - For OpenTelemetry export, prefer `session.subscribe()` + `@arnilo/prism-core/governance/observability` (see [Observability](observability.md)) rather than adding a parallel event bus. Middleware hooks remain for transforming payloads at named boundaries.
105
105
 
106
106
  ## Security and performance notes
107
107
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## What changes
8
8
 
9
- Prism 0.4 replaces 62 separate 0.3 package manifests with 11 active packages and explicit
9
+ Prism 0.4 replaces 62 separate 0.3 package manifests with 10 active packages and explicit
10
10
  subpaths. The code and behavior move; this is not a database/data migration. It is a **package
11
11
  name and import-specifier migration**.
12
12
 
@@ -50,7 +50,8 @@ name and import-specifier migration**.
50
50
  | `@arnilo/prism-mcp` | MCP interop. |
51
51
  | `@arnilo/prism-acp-agent` | ACP interop. |
52
52
  | `@arnilo/prism-ag-ui` | AG-UI/A2A/A2UI interop. |
53
- | `@arnilo/prism-antigravity-agent` | Antigravity interop. |
53
+
54
+ `@arnilo/prism-antigravity-agent` has no 0.4 successor. Remove it and keep delegated CLI execution in the host, composed through generic process, tool, MCP, or A2A contracts as needed.
54
55
 
55
56
  ## Package and import mapping
56
57
 
@@ -204,7 +205,6 @@ subpaths, but do not rename the dependency merely because of 0.4.
204
205
  | `@arnilo/prism-mcp` | Unchanged interop package. |
205
206
  | `@arnilo/prism-acp-agent` | Unchanged interop package. |
206
207
  | `@arnilo/prism-ag-ui` | Unchanged interop package. |
207
- | `@arnilo/prism-antigravity-agent` | Unchanged interop package. |
208
208
 
209
209
  ## Optional peers, host binaries, and trust boundaries
210
210
 
@@ -0,0 +1,122 @@
1
+ # Migrate Prism 0.4 to 0.5
2
+
3
+ > **Status: released 2026-09-06** (`v0.5.0` tag). Covers every host-visible change from plan 055 onward: new provider adapters (055), security hardening (056), the dead-export cut (058), dependency majors (062), the MCP 2026-07-28 adoption (063), the CLI real-provider contract (064), and model-aware thinking effort (065).
4
+
5
+ ## What changes
6
+
7
+ Prism 0.5 is a **lockstep cut**: all 10 publishable manifests move `0.4.x` → `0.5.0` and internal first-party ranges move `^0.4.0` → `^0.5.0`. Package names and import subpaths from 0.4 stay valid; the breaking surface is (1) 27 removed unused exports, (2) the MCP SDK module move, (3) two thinking-effort wire moves, and (4) two hardening behavior changes. Everything else is additive.
8
+
9
+ ## 1. New provider adapters — additive (plan 055, shipped as 0.4.1)
10
+
11
+ `@arnilo/prism-providers/hyper` and `@arnilo/prism-providers/commandcode` join the first-party catalog (Hyper with chat + `/v1/responses` passthrough + intelligent-routing metadata; Command Code with dual-route chat/Anthropic-Messages and GPT-5.6 explicit caching). No action needed for existing hosts; both adapters follow the standard provider factory contract (`factory({ apiKey })`).
12
+
13
+ ## 2. Security hardening — behavior changes (plan 056)
14
+
15
+ - **Tenant-aware store factories:** session/memory/enterprise store factory signatures now require an explicit tenant scope — constructing a durable store without a tenant fails closed. Pass the tenant in the factory options (see the store pages under `docs/`).
16
+ - **Child process env allow-list:** spawned processes (shell default, LSP client, process sessions, computer-use-linux MCP transport) no longer inherit the ambient `process.env`. A deterministic allow-list (`buildChildEnv` / `DEFAULT_CHILD_ENV_INHERIT`, `src/agent/env.ts`) governs inheritance. If you relied on custom env vars reaching child tools, pass them explicitly through the session/spawn options.
17
+
18
+ ## 3. Dead-export removal — 27 symbols (plan 058, breaking)
19
+
20
+ Every removed symbol was verified unused (zero in-repo references, no third-party import evidence) and carried `@deprecated` "no replacement" earlier in the same cycle. If your code compiled against 0.4 without importing any symbol below, this section does not affect you.
21
+
22
+ | package | removed export | kind |
23
+ |---|---|---|
24
+ | `@arnilo/prism` | `statusFromState` | function |
25
+ | `@arnilo/prism` | `isInitProvider` | type-guard function |
26
+ | `@arnilo/prism` | `isInitTemplate` | function |
27
+ | `@arnilo/prism` | `parseContextFile` | function |
28
+ | `@arnilo/prism` | `parseToolFile` | function |
29
+ | `@arnilo/prism` | `defaultUserSettingsPath` | function |
30
+ | `@arnilo/prism` | `resolveProviderMediaBlock` | async function |
31
+ | `@arnilo/prism` | `ProviderSecretLeakConformanceOptions` | interface |
32
+ | `@arnilo/prism` | `runToolEffectStoreConformance` | async function |
33
+ | `@arnilo/prism-core` | `OidcIdentityVerifierResult` | type alias |
34
+ | `@arnilo/prism-core` | `assertDiffLines` | function |
35
+ | `@arnilo/prism-core` | `ResolvedPromptLimits` | type alias |
36
+ | `@arnilo/prism-core` | `encodeMetadata` | function |
37
+ | `@arnilo/prism-core` | `parseListOffsetCursor` | function |
38
+ | `@arnilo/prism-core` | `nodeKindOf` | function |
39
+ | `@arnilo/prism-memory` | `DEFAULT_MAX_PROMPT_CHARS` | const |
40
+ | `@arnilo/prism-memory` | `GRAFT_RESOLVE_ERROR_CODE` | const |
41
+ | `@arnilo/prism-memory` | `LinterOptions` | interface |
42
+ | `@arnilo/prism-memory` | `ResolvedGraftExtension` | interface |
43
+ | `@arnilo/prism-memory` | `WikiCategory` | type alias |
44
+ | `@arnilo/prism-coding-tools` | `codingSha256Hex` | function |
45
+ | `@arnilo/prism-coding-tools` | `DEFAULT_MAX_REVIEW_DELTA_ENTRIES` | const |
46
+ | `@arnilo/prism-coding-tools` | `HARD_MAX_REVIEW_DELTA_ENTRIES` | const |
47
+ | `@arnilo/prism-coding-tools` | `indexErrorCode` | function |
48
+ | `@arnilo/prism-coding-tools` | `PONYTAIL_PEER_RANGE` | const |
49
+ | `@arnilo/prism-coding-tools` | `CavemanSkillName` | type alias |
50
+ | `@arnilo/prism-providers` | `withOpenRouterCacheMarker` | function |
51
+
52
+ Per-symbol local replacements for the two cases where 0.4 docs showed a usage pattern:
53
+
54
+ - `defaultUserSettingsPath(appName)` → build the path with stdlib:
55
+ `join(homedir(), ".config", appName, "settings.json")` (see
56
+ [`docs/settings-auth-trust-security.md`](settings-auth-trust-security.md)).
57
+ - `parseContextFile` / `parseToolFile` → colocated `CONTEXT.md` / tool-descriptor parsing is
58
+ host-owned; parse frontmatter with `parseAgentFile` (kept) plus your own file reading
59
+ (see [`docs/agent-definitions.md`](agent-definitions.md)).
60
+
61
+ ## 4. Dependency majors (plan 062)
62
+
63
+ - **`pdf-parse` 1.1 → 2.4** (`@arnilo/prism-coding-tools` optional peer): v2 parses in a worker thread and transfers the `data` TypedArray — do not reuse the buffer after `extract`; requires Node ≥ 20.16 (repo engines already `>=20`). Embedded-script execution is hard-disabled (`isEvalSupported: false`). Extracted text is byte-identical to v1 modulo trailing newline.
64
+ - **`better-sqlite3` 12 → 13** (`@arnilo/prism-core` optional peer): N-API rewrite with bundled prebuilt binaries (no per-Node rebuild); unsupported platforms compile from source during install. No API removals.
65
+ - **`@napi-rs/keyring` 1.x → 2.0** (`@arnilo/prism-core`): locked/inaccessible keychain reads and deletes now **reject** with typed errors (`CredentialStoreLockedError`, `CredentialStoreUnavailableError`) instead of silently resolving `undefined`/`false`. A missing credential still resolves `undefined`. If you treated a locked keychain as an empty vault, handle the typed errors.
66
+
67
+ ## 5. MCP: TypeScript SDK v2 modular adoption (plan 063, breaking for MCP hosts)
68
+
69
+ `@arnilo/prism-mcp` replaces the monolithic `@modelcontextprotocol/sdk` 1.30.0 with the modular v2 packages (`@modelcontextprotocol/client` + `@modelcontextprotocol/server` 2.0.0 exact pins). Prism's public surface (`createPrismMcpServer`, `createPrismMcpWebHandler`) keeps its shape; hosts that imported SDK types directly must move to the v2 module imports. Highlights: modern client negotiation, SDK-managed routing headers (SEP-2243), MRTR-compatible elicitation, dual-era HTTP/stdio serving, 2026-07-28-conformant OAuth (issuer-keyed storage, RFC 9207 `iss` validation). Draft-era task vocabulary fails closed. Full migration table + legacy-session timeline: [`docs/migration.md`](migration.md), canonical API in [`docs/mcp-tools.md`](mcp-tools.md).
70
+
71
+ ## 6. CLI real-provider contract (plan 064)
72
+
73
+ `prism --provider <id>` runs real providers: ids resolve through the init provider catalog (`templates/init/providers.json`), the factory is imported from the installed `@arnilo/prism-providers/*` package, and the credential comes from the catalog's env var. `--mode print|json|rpc` all honor it. **Contract change:** omitting `--provider` fails with a usage error (exit 2) — the mock provider runs only on explicit `--provider mock`. The RPC session factory is async-capable (`AgentSession | Promise<AgentSession>`).
74
+
75
+ ## 7. Model-aware thinking effort (plan 065, two wire moves)
76
+
77
+ Thinking/reasoning effort is now model-aware, declared, and snapped: every reasoning-capable model in a first-party catalog declares `capabilities.thinkingLevels` and a `compat.thinkingFamily` stamp, and one adapter resolves, snaps, and merges the level.
78
+
79
+ Breaking / behavior changes:
80
+
81
+ 1. **Anthropic Messages: `effort` → `output_config.effort`.** Prism emits `output_config: { effort }` (the `output_config_effort` family). Hosts hand-building `compat.effort` / `compat.reasoning_effort` keep working (resolver reads the aliases); hosts *reading* emitted bodies must look at `output_config.effort`. Thinking is generation-aware: 4.6+ models map bare `enabled` to `adaptive`; legacy 4.5 models get `enabled` + `budget_tokens` default 10000 (bare `enabled` without a budget is rejected upstream).
82
+ 2. **xAI now sends `reasoning_effort`** (previously dropped): grok-4.6 `low/medium/high/xhigh`, grok-4.5 `low/medium/high`, grok-4.3 `none/low/medium/high`; out-of-set values snap. `grok-build` and unknown models pass through verbatim. `reasoning_content` replay is unchanged.
83
+ 3. **Snapping replaces silent drop** on declared models: out-of-set portable levels snap to the nearest declared level (ladder distance, ties up; below-minimum snaps up). Provider-documented tables (DeepSeek, Z.AI GLM-5.2/5.3, Kimi K3, ClinePass slot maps) remain wire authority. Opaque strings on reasoning-capable models still pass through.
84
+ 4. **Azure / Vertex / Bedrock forward thinking compat** through a sanitized forwarder (`reasoning_effort` + aliases, or `reasoning` object with `summary` preserved). Unrecognized compat keys are dropped, not leaked. Hand-built `options.extra` workarounds can become `compat`.
85
+
86
+ New surface on `@arnilo/prism`:
87
+
88
+ | Export | Purpose |
89
+ |---|---|
90
+ | `applyThinkingLevelForModel(base, level, model)` | One-call adapter: family resolution + snap + merge — prefer over `applyThinkingLevel` |
91
+ | `parseThinkingLevel(value)` | Known level → canonical; other string → opaque passthrough; empty/non-string → `undefined` (fail closed) |
92
+ | `isSupportedThinkingLevel(model, level)` | Is the level in the model's declared set |
93
+ | `thinkingLevelsForModel(model)` | Declared set or `undefined` |
94
+ | `snapThinkingLevel(model, level)` | Snap to the declared set (nearest, ties up) |
95
+
96
+ New compat families: `google` (`{ thinkingLevel }`) and `output_config_effort` (`{ output_config: { effort } }`). Family inference is stamp-first via `compat.thinkingFamily`.
97
+
98
+ What to do:
99
+
100
+ 1. Replace `applyThinkingLevel(base, level, family)` call sites with `applyThinkingLevelForModel(base, level, model)`.
101
+ 2. Gate level pickers on `model.capabilities?.thinkingLevels` when present.
102
+ 3. Update any body assertion reading Anthropic top-level `effort` to `output_config.effort`.
103
+ 4. Remove xAI "never send reasoning_effort" workarounds.
104
+ 5. Nothing else: `mergeProviderRequestOptions`, `RunOptions`, and persisted shapes are unchanged.
105
+
106
+ Contract reference: [`docs/thinking-and-reasoning.md`](thinking-and-reasoning.md); per-provider declared levels + wire fields on each `docs/providers/*.md` page.
107
+
108
+ ## Upgrade steps
109
+
110
+ 1. Bump every `@arnilo/*` dependency/peer to `^0.5.0`.
111
+ 2. Build; if the compiler flags a removed symbol above, apply the replacement from the table.
112
+ 3. If you host MCP: move SDK imports to the v2 modular packages (section 5).
113
+ 4. If you spawn child processes with ambient env: pass env explicitly (section 2).
114
+ 5. If you construct durable stores: pass the tenant scope (section 2).
115
+ 6. If you set thinking levels: move to `applyThinkingLevelForModel` (section 7).
116
+ 7. Run your suite. No persisted-data migration exists or is needed.
117
+
118
+ ## Rollback
119
+
120
+ Pin the previous version: `@arnilo/prism@0.4.x` (exact pins per package). Nothing persisted
121
+ changes under 0.5, so a pin rollback is safe. The MCP module move (section 5) is the only
122
+ migration that touches host import code — keep a 0.4 pin if you need the monolithic SDK.
package/docs/migration.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Migration guide
2
2
 
3
+ ## 0.4.x → 0.5.0 lockstep cut (breaking)
4
+
5
+ Prism 0.5 (plans 055–065) ships four breaking surfaces: the 27 removed unused exports from the plan 058 sweep (symbol-surface only), the MCP TypeScript SDK v2 module move (plan 063 — hosts importing `@modelcontextprotocol/sdk` directly must move to the modular `client`/`server` 2.0.0 packages), the thinking-effort wire moves (plan 065 — Anthropic `effort` → `output_config.effort`, xAI `reasoning_effort` now sent, snap-on-declared semantics), and the plan 056 hardening behavior changes (tenant-scoped store factories, child env allow-list). Dependency majors (plan 062: pdf-parse 2.4 with Node ≥ 20.16, better-sqlite3 13, keyring 2 with typed locked-store errors) and the CLI real-provider contract (plan 064: explicit `--provider` required, exit 2 when omitted) are behavior changes without import impact. See the complete guide with per-symbol replacements and upgrade steps in [migrate-to-0.5.md](migrate-to-0.5.md). All 10 publishable manifests bump to `0.5.0` lockstep; internal first-party ranges move `^0.4.0` → `^0.5.0`. Security keeper surface (ownership/checkpoint guards, `secureCompare`, `zeroBuffer`, sandbox path-escape guard, RAG scope guard, MCP content-bounds guard, secret-leak conformance assert) is unchanged.
6
+
3
7
  ## 0.3.3 → 0.4.0 package reorganization (breaking)
4
8
 
5
9
  Prism 0.4 consolidates package names into explicit family subpaths. It is a dependency and import-specifier migration, not a persisted-data migration. See the complete [legacy 0.3 → 0.4 guide](migrate-to-0.4.md) for all 54 retired package mappings, profile replacements, optional peers/host binaries, security checks, rollback, and npm legacy-warning behavior.
@@ -818,7 +822,31 @@ Existing text `createA2AHandler({ exposure })`, `client.send()`, and `client.str
818
822
 
819
823
  ## 0.0.7 → 0.0.8 MCP capabilities and sessions
820
824
 
821
- `@arnilo/prism-mcp` now pins official SDK 1.29.0. Existing `connectMcpTools()` and stateless web handlers remain compatible. Use `connectMcpCapabilities()` for bounded resources/prompts and explicit roots/sampling/elicitation callbacks. Server resources/prompts must be selected explicitly and authorize every operation. Stateful Streamable HTTP additionally requires `sessionIdGenerator`, exact `allowedOrigins`, and host `resolveIdentity`; omission preserves stateless mode. `Last-Event-ID` replay is not enabled. Missing capability calls fail with `ERR_PRISM_MCP_UNSUPPORTED_CAPABILITY`.
825
+ `@arnilo/prism-mcp` now pins the modular TypeScript SDK v2 packages (`@modelcontextprotocol/client` + `@modelcontextprotocol/server` 2.0.0). Existing `connectMcpTools()` and stateless web handlers remain compatible. Use `connectMcpCapabilities()` for bounded resources/prompts and explicit callbacks. **Roots (`roots`) and sampling (`sampling`) callbacks are deprecated with protocol revision 2026-07-28 (SEP-2577)** and kept for existing legacy callers only — migrate server-hosted state to explicit tool arguments and host-side model calls; elicitation is the active capability and works across eras (legacy direct `elicitation/create` dispatch, or SDK MRTR `input_required` auto-fulfilment on the modern era, capped by `maxMrtrRounds` with the call timeout as the outer ceiling). Server resources/prompts must be selected explicitly and authorize every operation.
826
+
827
+ HTTP/stdio serving is dual-era: `createPrismMcpWebHandler(factory)` now serves modern 2026-07-28 traffic through SDK `createMcpHandler` (one fresh `McpServer` per request, no `Mcp-Session-Id`, no sticky routing) with the SDK stateless fallback for 2025 traffic, and stays callable while gaining `fetch`/`close`/`notify`/`bus`. Host/origin allowlists are enforced by Prism before body parsing and auth (the SDK entry provides no validation); `maxRequestBytes`, response bounding, concurrency, and request timeouts are unchanged. Configuring `sessionIdGenerator` keeps legacy sessionful serving (identity-bound POST/GET/DELETE/SSE) beside a strict modern handler; a bare `McpServer` instance with sessions is legacy-only — pass a factory for dual-era serving. `servePrismMcpStdio(factory, options)` replaces hand-wired stdio serving with SDK dual-era `serveStdio` (era pinned by the opening exchange, stdout protocol-only). Stateful Streamable HTTP still requires `sessionIdGenerator`, exact `allowedOrigins`, and host `resolveIdentity`. `Last-Event-ID` replay is not enabled. Missing capability calls fail with `ERR_PRISM_MCP_UNSUPPORTED_CAPABILITY`.
828
+
829
+ OAuth client behavior is 2026-07-28 conformant: `finishAuth` now takes the full callback `URLSearchParams` (persisted `state` validated fail-closed, RFC 9207 `iss` checked before the code is redeemed; the bare-code string form remains as the unvalidated legacy path — switch to the params form). Persisted token/client records are SDK issuer-stamped; `McpClientAuthState` credential methods take the validated `issuer` (existing implementations ignoring the parameter keep working for single-server hosts, but **un-stamped pre-upgrade records are refused on issuer-keyed reads** rather than guessed — hosts see one interactive re-authorization, after which every record is stamped). CIMD (SEP-991) is the preferred registration strategy, `dcr` is `@deprecated` with `application_type` defaulting to `"native"`, and `onInsufficientScope: "reauthorize" | "throw"` makes the 403 step-up policy explicit. Server 401 challenges now include the configured `scope`; host token verifiers must validate the token audience.
830
+
831
+ ### Monolithic SDK 1.x → modular SDK v2 migration table
832
+
833
+ | v1 (`@modelcontextprotocol/sdk` 1.30.0) | v2 / current Synapta API | Notes |
834
+ | --- | --- | --- |
835
+ | `new Client(...)` from `sdk/client/index.js` | same name from `@modelcontextprotocol/client` | `ClientOptions` gains `versionNegotiation`, `listChanged`, `inputRequired`, `cachePartition`, `listMaxPages` |
836
+ | `InMemoryTransport` / `StdioClientTransport` / `StreamableHTTPClientTransport` | `InMemoryTransport`/`StreamableHTTPClientTransport` from `@modelcontextprotocol/client`; `StdioClientTransport` from `@modelcontextprotocol/client/stdio` | subpath moves only |
837
+ | `McpServer`, `WebStandardStreamableHTTPServerTransport` from `sdk/server/*` | `@modelcontextprotocol/server` root | `createMcpHandler` (dual-era HTTP) + `serveStdio` (dual-era stdio) replace hand-wired serving |
838
+ | `client.request({method}, Schema, options)` | method-keyed `client.request(...)` or high-level `listTools`/`listResources`/`readResource`/`callTool` | per-page list walks must send an explicit cursor (`{cursor: ""}` on page 0) — the no-cursor form is the SDK's uncapped aggregate |
839
+ | `CompatibilityCallToolResultSchema` (`toolResult` member) | gone from the client codec; `callTool` decodes `CallToolResult` only | draft-era `task` members fail closed at the bridge |
840
+ | `server.setRequestHandler(Schema, handler)` | `setRequestHandler("method/string", handler)` with `ctx.mcpReq.{id,signal}` | `ctx.authInfo` moved to `ctx.http?.authInfo`; unknown tools now error (`-32602`) instead of `isError` results |
841
+ | `sdk/server/auth/types.js` `AuthInfo` | `AuthInfo` from `@modelcontextprotocol/server` | type-only move |
842
+ | `OAuthClientProvider` (string-keyed persistence) | issuer-keyed `StoredOAuthTokens`/`StoredOAuthClientInformation` + optional `ctx: {issuer}` params, `clientMetadataUrl` (CIMD) | see the OAuth paragraph above |
843
+ | `LATEST_PROTOCOL_VERSION` mock fixtures (initialize results) | legacy-era negotiation unchanged; modern (2026-07-28) results need top-level `resultType: "complete"`, `ttlMs`, `cacheScope` | raw mock fixtures only |
844
+
845
+ ### Legacy-session timeline
846
+
847
+ - **Now (default):** `createPrismMcpWebHandler(factory)` without `sessionIdGenerator` is stateless dual-era — modern 2026-07-28 serving plus the SDK stateless fallback for 2025 clients. No `Mcp-Session-Id`, no sticky routing, `Last-Event-ID` replay not enabled.
848
+ - **Now (opt-in):** configuring `sessionIdGenerator` keeps identity-bound legacy sessions (POST/GET/DELETE/SSE beside the strict modern handler) for hosts that still need them; the pairing requires exact `allowedOrigins` and host `resolveIdentity`.
849
+ - **Planned removal:** the legacy session leg is deprecated once Synapta's clients and documented hosts are modern-era; removal lands as a breaking 0.x cut with a migration note here (the `sessionIdGenerator` option disappears and legacy traffic gets the SDK stateless fallback, which 2025 clients already work against). No date is committed in this release.
822
850
 
823
851
  ## 0.0.7 → 0.0.8 OpenTelemetry adapter
824
852
 
@@ -107,14 +107,51 @@ Provider packages register models through `ProviderPackageAPI.registerModel(mode
107
107
 
108
108
  `ModelConfig.compat` remains for provider-owned inert JSON. Prefer typed fields (`capabilities`, `limits`, `cost`, `cache`) for generic behavior shared across providers.
109
109
 
110
+ ### Model-list/capability discovery with provenance
111
+
112
+ `ModelDiscovery` (plan 062) is the normalized `listModels()` seam: it returns the existing `ModelConfig` contract verbatim (id = `model`, context window = `limits`, pricing hint = `cost`) plus `provenance` (`provider`, `fetchedAt` ISO timestamp, `source: "api" | "catalog"`, and `ttlMs` cache guidance). Discovery execution stays provider/host code — Prism core ships only the result types; adapters live in `@arnilo/prism-providers/model-discovery`:
113
+
114
+ ```ts
115
+ import { createOpenAiCompatibleModelDiscovery, createGoogleModelDiscovery, createFakeModelDiscovery, runModelDiscoveryConformance } from "@arnilo/prism-providers/model-discovery";
116
+
117
+ const discovery = createOpenAiCompatibleModelDiscovery({
118
+ baseUrl: "https://gw.internal/v1", // GET <baseUrl>/models
119
+ apiKey, // CredentialValueSource — sent as Bearer, resolved via the existing credential seam
120
+ catalog: registry.list(), // host overrides merged by model id; catalog fields win
121
+ });
122
+ const { models, provenance } = await discovery.listModels({ ttlMs: 3_600_000 });
123
+
124
+ // Independent provider: Google Gemini `GET <baseUrl>/v1beta/models` (x-goog-api-key), paginated:
125
+ const google = createGoogleModelDiscovery({ apiKey: gcpKey });
126
+
127
+ // Network-free fake + conformance for any ModelDiscovery implementation:
128
+ await runModelDiscoveryConformance(() => createFakeModelDiscovery());
129
+ ```
130
+
131
+ - Passthrough normalization: entries the provider does not describe stay bare (`{provider, model}`); there is no hard-coded catalog in core or adapters. Hosts merge catalog overrides (`capabilities`/`limits`/`cost`/`displayName`) over normalized entries by model id via the `catalog` option.
132
+ - Results cache per discovery instance within the configured TTL (default 3,600,000 ms). `listModels()` in loops performs no network until the TTL expires; `ttlMs: 0` forces a refresh. The provenance `fetchedAt`/`ttlMs` fields let hosts layer their own caching on top.
133
+ - `ModelDiscoveryError` is the typed failure (provider label + HTTP status); credentials are resolved through the existing `CredentialValueSource` seam, redacted from every error message, and responses are byte-bounded through the shared provider transport.
134
+
110
135
  ## Security and performance notes
111
136
 
112
137
  - Model metadata must not contain credentials or secrets.
113
138
  - Declare truthful `capabilities.input` tags. Prism core rejects undeclared modalities in `assembleProviderInput()` when the list is present.
114
139
  - Registration is in-memory and O(1) by provider/model key.
115
140
  - `ModelConfig.cache` is declarative capability info only; it does not grant permissions, select tools, or bypass auth.
141
+ - Model-discovery listings are untrusted metadata: normalized entries carry no tool authority, cached results stay per discovery instance (no cross-provider cache bleed), and discovery requests reuse the bounded transport with credential redaction. Catalog overrides come from host-owned registries only — provider responses never write into the host's `ModelRegistry` without host code in between.
116
142
  - Provider-specific behavior belongs in provider packages, not Prism core.
117
143
 
144
+ ## Live probe (discovery adapters)
145
+
146
+ The `@arnilo/prism-providers/model-discovery` adapters have an opt-in live leg that runs `runModelDiscoveryConformance` against a real listing endpoint:
147
+
148
+ ```bash
149
+ PRISM_LIVE_PROVIDER_TESTS=1 OPENAI_API_KEY=... # or GEMINI_API_KEY for the Google route
150
+ node --test packages/prism-providers/dist/model-discovery/__tests__/live.test.js
151
+ ```
152
+
153
+ Chooses the OpenAI-compatible or Google route based on which key is present; neither → skip.
154
+
118
155
  ## Related APIs
119
156
 
120
157
  - [Multimodal content](multimodal-content.md): `audio`/`file`/`document` blocks and `MODEL_INPUT_CAPABILITIES`.
@@ -122,3 +159,4 @@ Provider packages register models through `ProviderPackageAPI.registerModel(mode
122
159
  - [Provider caching](provider-caching.md): `ModelCacheCapabilities` and cache helpers.
123
160
  - [Provider packages](provider-packages.md): package registration of model metadata.
124
161
  - [Public contracts](public-contracts.md): `ModelConfig`, `ModelCost`, and cache type contracts.
162
+ - [Provider layer](provider-layer.md): `ModelDiscovery` adapters, provenance, and TTL semantics.