@arnilo/prism 0.0.22 → 0.0.24

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 (45) hide show
  1. package/CHANGELOG.md +36 -2
  2. package/dist/agent-event-source.d.ts +11 -0
  3. package/dist/agent-event-source.js +512 -0
  4. package/dist/agent-run-state.js +6 -1
  5. package/dist/agents.js +7 -2
  6. package/dist/contracts.d.ts +157 -0
  7. package/dist/index.d.ts +7 -2
  8. package/dist/index.js +4 -1
  9. package/dist/testing/agent-event-source-conformance.d.ts +4 -0
  10. package/dist/testing/agent-event-source-conformance.js +54 -0
  11. package/dist/testing/persistence-schema.d.ts +2 -2
  12. package/dist/testing/persistence-schema.js +58 -21
  13. package/dist/testing/tool-effect-store-conformance.d.ts +9 -0
  14. package/dist/testing/tool-effect-store-conformance.js +85 -0
  15. package/dist/tool-effects.d.ts +15 -0
  16. package/dist/tool-effects.js +338 -0
  17. package/dist/tools.d.ts +3 -1
  18. package/dist/tools.js +204 -9
  19. package/docs/0.1.0-readiness.md +10 -9
  20. package/docs/a2a.md +6 -2
  21. package/docs/ag-ui-adoption.md +77 -0
  22. package/docs/ag-ui.md +39 -42
  23. package/docs/agent-events.md +5 -1
  24. package/docs/browser-automation.md +2 -0
  25. package/docs/coding-agent-tools.md +2 -0
  26. package/docs/database-persistence.md +5 -0
  27. package/docs/enterprise-postgres-state.md +177 -0
  28. package/docs/evaluations.md +13 -0
  29. package/docs/host-security.md +11 -1
  30. package/docs/index.md +17 -14
  31. package/docs/mcp-tools.md +17 -2
  32. package/docs/migration.md +41 -0
  33. package/docs/model-routing.md +17 -8
  34. package/docs/performance.md +38 -0
  35. package/docs/policy-and-audit.md +12 -0
  36. package/docs/postgres-persistence.md +7 -3
  37. package/docs/public-contracts.md +2 -0
  38. package/docs/release-and-install.md +67 -680
  39. package/docs/server.md +9 -6
  40. package/docs/sqlite-persistence.md +10 -2
  41. package/docs/supervisors.md +2 -0
  42. package/docs/tool-effects.md +95 -0
  43. package/docs/tools.md +4 -0
  44. package/docs/work-tools.md +21 -1
  45. package/package.json +11 -2
@@ -36,6 +36,7 @@ Start from explicit host inputs. Do not let runtime code discover security state
36
36
  | Remote agent/workflow API | host authentication + ownership mapping | `@arnilo/prism-server`, `createPrismHandler()` |
37
37
  | Authenticated identity | host `IdentityVerifier` → verified `AgentIdentity` | [Agent identity](agent-identity.md), `assertIdentityActive`, `narrowIdentity` |
38
38
  | Policy decision audit | optional redacted ledger + host WORM sink | [Policy and audit](policy-and-audit.md), `@arnilo/prism-policy` |
39
+ | Durable enterprise state | host PostgreSQL pool, TLS, exact owner/principal projection, migration/runtime database roles, backup and explicit cleanup schedule | [Enterprise PostgreSQL state](enterprise-postgres-state.md), `@arnilo/prism-enterprise-postgres` |
39
40
  | MCP server exposure | host MCP auth + selected capability list | `createPrismMcpServer()`, `createPrismMcpWebHandler()` |
40
41
 
41
42
  ## Outputs / response / events
@@ -142,6 +143,9 @@ Wire those values where they matter: provider adapters receive the resolved cred
142
143
  - Evaluation trace readers require exact supplied ownership plus session/run identity, reject cursor/identity drift, and redact before bounded scorer/judge input. Model-judge callbacks receive no credential resolver, tools, or workspace; keep live judges outside default CI and redact report artifacts.
143
144
  - Prism-generated session/run/tool/workflow/evaluation IDs use Node cryptographic UUIDs. Keep host-provided IDs authorization-scoped and validate them as untrusted identifiers; do not substitute timestamps or `Math.random()` for durable/security-relevant IDs.
144
145
  - MCP client tools from `@arnilo/prism-mcp` are untrusted remote servers. Stdio remains an explicit host executable. Streamable HTTP requires exact HTTPS origins, rejects credentials/fragments/redirects/private or mixed DNS, pins a validated address on every SDK request/reconnect, and bounds each response; plaintext is explicit loopback-only development mode. Discovery has finite page/tool/cursor/metadata/schema totals and commits atomically. Every result branch shares byte/depth/property bounds before core dispatch; supply a known-secret `SecretRedactor`, `PermissionPolicy`, and `ToolValidator` there. MCP server direction exposes only passed tools/commands/resources/prompts, requires per-operation `authorize`, and retains core gates. Sampling, roots, model/credential selection, and elicitation consent stay host-owned; URL elicitation is never opened automatically. Stateful web mode requires host `resolveAuthInfo` plus `resolveIdentity`, exact origin policy, and binds every POST/GET/DELETE/SSE request to one non-secret principal; mismatches return 404. Handler still needs TLS and edge rate limiting. See [MCP client/server exposure](mcp-tools.md).
146
+ - AG-UI fields stay untrusted after schema validation. `input.project` returns host-selected messages/handoffs only; never merge state/tools/context/props into ownership, identity, permissions, provider options, or media policy. Apply Prism media SSRF/MIME bounds before resolution; output projectors are bounded allow-lists; interrupt edits deny rather than mutate persisted calls.
147
+ - AG-UI MCP Apps requires negotiated `mcpApps`, exact proxy origin/auth, owned-run context, approval, one bridge, separate-origin sandbox (`allow-scripts allow-same-origin`), and no-wider CSP. Never execute HTML in host origin or retry a UI mutation; Task 4 adds recovery.
148
+ - AG-UI A2A requires exact-origin verified client, host-owned task selection/correlation, explicit data/tool/A2UI projection, and reauthorized follow/cancel.
145
149
  - `@arnilo/prism-server` exposes no agent/workflow by default and requires `authorize()` for every matched operation. Derive complete tenant/account/user ownership from validated host identity, never request JSON. Workflow active identity and cancellation compare exact ownership; a tenant-only scope intentionally cannot cancel a checkpoint/run carrying account or user identity. The artifact review service (`createArtifactService`) requires authenticated identity + thread ownership on every attach/revise/compare/approve/reject/download, resolves concurrent reviewers via checkpoint CAS (no lost approvals), rejects local filesystem paths in `uri`/citations, redacts records before persist and on response, and serves downloads only through signed expiring links that are reauthorized against the token's ownership per request. Pass the current explicitly revised workflow definition so recursive hash mismatch fails before abort or durable mutation. Configure exact host/origin allow-lists where needed, wire redaction before execution, retain tool/workflow policy checks, and adapt the Web handler behind host TLS/rate limits. Disconnect abort is default; persistent reconnect/status belongs to durable workflow checkpoints, not an invented in-memory agent result cache.
146
150
  - Coding tools from `@arnilo/prism-coding-agent` accept an optional `ExecutionPolicy` checked inside each tool before side effects; shared policy propagation includes `createReadOnlyTools()`. They enforce finite text-scan/image/edit/write/shell limits, repository list/search depth/entry/match/scan/time caps, structured Git path/ref/message/output/patch/worktree caps, named-check concurrency/output caps, a 600-second default shell wall time, and a 64 MiB default total-output ceiling. Opt-in `createGitTools()` uses argument arrays with hooks/credential prompts/external diff disabled, requires host `commitIdentity` for commits, and never pushes or opens PRs. Successful truncated shell output leaves a host-owned exclusive `0600` temp file; delete `metadata.fullOutputPath` after use. Error/abort/timeout/overflow removes unpublished spills. Custom read/edit/shell/repository backends must honor supplied caps/signals. Use `@arnilo/prism-coding-security` for path roots, command rules, identity-scoped approval caching, required `workspaceMode` on `createSandboxCodingComposition()` / `createSandboxCodingTools()`, and the optional `createDockerSandbox()` reference adapter. **Host mode is never contained execution** (`containmentClaim: false`). Sandbox mode claims containment only when FS backends target the disposable tree; mixed wiring requires `allowMixedWorkspaceWiring` and still does not claim containment. Limits alone are not containment: construct the Docker adapter (absolute CLI, digest-pinned image, network none by default) or an equivalent host sandbox before treating coding execution as production-safe. Docker daemon/image trust, egress firewall/proxy, and artifact retention remain host-owned.
147
151
  - Optional `@arnilo/prism-browser` requires a host-supplied Playwright Browser (`playwright-core@1.61.0` peer). Import is inert. One non-persistent context belongs to one run; actions serialize; refs are snapshot-scoped; CSS/evaluate/CDP/persistent profiles are denied. Context routing + `serviceWorkers: "block"` deny file/data/blob/devtools/private/loopback by default and require contained-proxy attestation for external egress (Playwright routing is defense in depth, not DNS containment). Uploads are realpath-rooted; downloads quarantine with hash/MIME until host `approveRelease`; screenshots return bounded `ImageContent`. Observation vs mutation/high-impact actions map to `ExecutionPolicy`. Treat snapshot/page text as untrusted external content. Close contexts with `browser_close` or `manager.closeRun(runId)` on abort/terminal. Browser control endpoint, binary/image pin, and real egress firewall/proxy remain host-owned. Shared sandbox: `createSharedSandboxBrowserOptions()` + `assertBrowserSandboxNetwork()`.
@@ -152,6 +156,7 @@ Wire those values where they matter: provider adapters receive the resolved cred
152
156
  - Default remote-media loading resolves every DNS answer, rejects the hostname if any address is non-public, and pins one validated address through the request. Explicit `allowedHostnames` can trust private destinations. A host-supplied `fetch` owns DNS/rebinding/proxy/redirect safety; a custom `requestUrl` must connect to its supplied validated address.
153
157
  - Permission checks happen before tool validation and before `tool.execute()`. Middleware cannot grant permission by renaming a tool.
154
158
  - Session stores and ledgers receive redacted values when a redactor is active, but durable storage remains host-owned. Enforce tenant/account/user ownership, retention, legal hold, and quotas via `ProductionPersistenceStore.lifecycle` (or host-equivalent DB controls). Hold always blocks delete.
159
+ - `@arnilo/prism-enterprise-postgres` request paths require exact tenant scope plus principal for work/router state, use bound SQL values, and retain no prompts, connector request bodies, raw provider results, tokens, or credentials. Configure TLS/credential rotation/connection limits with the host `pg` pool. Run checksum/catalog migration setup with a controlled migration principal; keep request-path SQL least-privilege (`USAGE`, `SELECT`, `INSERT`, `UPDATE`, `DELETE` on six state tables) and do not grant request workers `CREATE`, `ALTER`, `DROP`, `TRUNCATE`, `GRANT`, or `COPY PROGRAM`. Back up and restore-test the schema; run bounded owner-scoped `state.cleanup()` from an authorized host job. `unknown` connector outcomes require reconciliation and must never auto-replay.
155
160
  - Prefer `createExtensionKernel({ loadPolicy })` allow-list/signature checks before loading third-party extension packages.
156
161
  - Provider-owned auth/content/session/cache/security headers win over caller headers in adapters that merge headers.
157
162
  - Security checks are bounded explicit calls on the active path. Prism adds no hidden global middleware, background workers, watchers, network calls, or filesystem scans.
@@ -197,10 +202,14 @@ PostgreSQL TLS/network policy, MCP endpoint trust/credentials and egress policy
197
202
  - Scheduled/manual coding/browser containment checks run in protected `sandbox-browser` environment (`.github/workflows/sandbox-browser.yml`). They receive no provider/npm/OIDC secrets; Docker/Playwright enablement is variable-gated with host-preloaded digest-pinned images/binaries; uploads are redacted aggregate status only.
198
203
  - Live endpoint operators own TLS, egress allow-lists, account-dollar budget, cleanup beyond MCP session DELETE, and revocation. Failed canaries log only operation kind plus status/timeout; inspect provider-side audit logs for details.
199
204
 
205
+ ## Distributed events and tool effects
206
+
207
+ Every durable `AgentEventSource` page/subscribe and tool-effect claim rechecks exact ownership. Opaque cursors never select tenants. Oversized events/effects fail closed. Ambiguous tool outcomes stay `unknown` until operator reconciliation — never silent replay. Request-path SQL stays DML-only; migration/listener principals stay separate. See [tool effects](tool-effects.md) and [agent events](agent-events.md).
208
+
200
209
  ## Related APIs
201
210
 
202
211
  - [Web-standard server handler](server.md): remote agent/workflow route, ownership, limits, abort, and deployment boundary.
203
- - [Frontend interoperability (AG-UI and ACP)](ag-ui.md): authorize every protocol selector/operation; default-deny tool/state/path projection; exact interrupt/version resume; redacted, ownership-scoped replay.
212
+ - [Frontend interoperability (AG-UI and ACP)](ag-ui.md): authorize every protocol selector/operation; full AG-UI input/output only through bounded host allow-lists; exact interrupt/version resume; redacted, ownership-scoped replay.
204
213
  - [Supervisor delegation](supervisors.md): local child permission/memory/budget boundary.
205
214
  - [A2A interoperability](a2a.md): remote card/auth/origin/signature boundary.
206
215
  - [Settings, auth, trust, and security controls](settings-auth-trust-security.md): low-level helpers and boundary hardening table.
@@ -213,4 +222,5 @@ PostgreSQL TLS/network policy, MCP endpoint trust/credentials and egress policy
213
222
  - [Session stores](session-stores.md): durable session store contract and secret/persistence boundaries.
214
223
  - [Runs and usage ledger](runs-and-usage.md): redacted run/event/tool/usage ledger records.
215
224
  - [Database persistence](database-persistence.md): production schema, ownership, indexes, retention, and adapter readiness checklist.
225
+ - [Enterprise PostgreSQL state](enterprise-postgres-state.md): durable policy/evaluation/work/router state and database-role boundary.
216
226
  - [Provider caching](provider-caching.md): cache keys and provider-owned header safety rules.
package/docs/index.md CHANGED
@@ -7,19 +7,19 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
7
7
 
8
8
  ## Identity and governance
9
9
  - [Agent identity](agent-identity.md): host-verified `Principal` / `AgentIdentity`, delegation narrowing, ownership projection, and redacted telemetry refs for enterprise runs/tools/MCP/A2A/workflows.
10
- - [Policy and audit](policy-and-audit.md): optional `@arnilo/prism-policy` decision ledger (allow/deny/modify/approval), evidence refs only, and cursor-paginated WORM export.
11
- - [Model routing](model-routing.md): optional `@arnilo/prism-model-router` allow-list/residency/budget/rate/circuit/fallback governance over `ProviderResolver` with redacted diagnostics.
10
+ - [Policy and audit](policy-and-audit.md): optional `@arnilo/prism-policy` decision ledger (allow/deny/modify/approval), evidence refs only, cursor-paginated WORM export, and durable PostgreSQL composition.
11
+ - [Model routing](model-routing.md): optional `@arnilo/prism-model-router` allow-list/residency/budget/rate/circuit/fallback governance with redacted diagnostics; durable state requires awaited identity-scoped calls.
12
12
 
13
13
  ## Agent/session runtime
14
14
  - [Agent/session runtime](agent-session-runtime.md): create explicit or opt-in secure agents/sessions, get direct `AgentRunResult` values from `run`/`prompt`, mid-run `steer` (turn-boundary or softInterrupt), use integrated `stream()`/`resumeAgentRunStream()`, subscribe to normalized events, and expose opted-in durable lifecycle capabilities.
15
15
  - [Agent definitions](agent-definitions.md): resolve declarative `AgentDefinition` values via `resolveAgentDefinition`, and turn app-config `<configRoot>/agents/<name>/AGENT.md` bundles into runnable agents via `discoverAgentBundles` / `resolveAgentBundle` (explicit tool/skill activation by name, fail-closed omitted capabilities, migration-only `activateAllCapabilities`, strict duplicate scope checks, configurable prompt layers, no auto-discovery).
16
16
  - [Agent loops](agent-loops.md): replaceable per-run control loops — `singleShotLoop` default and opt-in bounded artifact-loop tool rounds with host-supplied `validator`/`parser`/`repairer` callbacks.
17
17
  - [Guardrails](guardrails.md): typed fail-closed input/output/tool checks with buffered provider output and redacted decision records.
18
- - [Agent events](agent-events.md): redacted lifecycle stream used by UIs, ledgers, and metadata-only parented telemetry; message/progress deltas never create spans.
18
+ - [Agent events](agent-events.md): live `session.subscribe` plus durable `AgentEventSource` page/subscribe/resume for cross-replica reconnect; message/progress deltas never create spans.
19
19
  - [Observability](observability.md): OTel GenAI agent/provider/tool hierarchy, host context parenting, bounded trace linkage, safe evaluation events, controlled metrics, and exporter isolation.
20
- - [Evaluations](evaluations.md): deterministic and bounded trace/model-judge/pairwise scoring, CI thresholds, OTel trace-reference linkage, coding/browser adversarial fixtures, and ID-only linkage to immutable owned run feedback.
20
+ - [Evaluations](evaluations.md): deterministic and bounded trace/model-judge/pairwise scoring, CI thresholds, OTel trace-reference linkage, coding/browser adversarial fixtures, ID-only linkage to immutable owned run feedback, and optional durable PostgreSQL records.
21
21
  - [Runs and usage ledger](runs-and-usage.md): durable run/event/tool/usage persistence, optional bounded FIFO durability policies, session snapshot caching, and immutable run/trace feedback.
22
- - [Performance limits](performance.md): 0.0.15 network-free provider/RAG/memory benchmark evidence and frozen caps, bounded evaluation traces/judges/reports, 0.0.12 frontend interoperability, 0.0.11 search/budget, 0.0.10 workspace-mode, 0.0.9 coding/browser, security scan/live-canary backstops, live subscriber queues, branch-read pagination expectations, JSONL/dev-store limits, and production sizing assumptions.
22
+ - [Performance limits](performance.md): 0.0.24 distributed event/effect PostgreSQL evidence, 0.0.23 enterprise state evidence, 0.0.15 network-free provider/RAG/memory benchmark evidence and frozen caps, bounded evaluation traces/judges/reports, and production sizing assumptions.
23
23
  - [Structured output](structured-output.md): the `Artifact*` seam plus provider-native `StructuredOutputOptions` / `structuredOutputMode` for capable models.
24
24
 
25
25
  ## Compaction/session memory
@@ -34,7 +34,8 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
34
34
  - [Database persistence](database-persistence.md): production persistence contracts, shared checksummed migration/full-shape catalog primitives (`@arnilo/prism/testing/persistence-schema`), conditional append, indexes, `readBranchPath`, reference relational schema, retention/legal-hold/quota lifecycle (`lifecycle`), and NoSQL mapping.
35
35
  - [SQLite persistence](sqlite-persistence.md): optional `better-sqlite3` adapter with session/run storage, checkpoints/leases, feedback, FTS `searchSessions` (migration-v4), and transactionally verified/backfilled migration metadata.
36
36
  - [PostgreSQL persistence](postgres-persistence.md): optional pooled `pg` adapter with session/run/checkpoint/lease/feedback storage, FTS `searchSessions` (migration-v4), advisory-locked checksummed/full-shape migrations, and opt-in live conformance.
37
- - [Migration guide](migration.md): **0.0.22** third-party behavior integrations (Caveman, Ponytail); **0.0.21** coding-tool capability gaps (`outputMode`, glob, read-before-write, delete/move, aggregator 9/4); **0.0.20** progressive skill disclosure, empty registry default, `load_skill`, priority budget demotion, optional tool-result fold; **0.0.19** observational memory lifecycle; **0.0.15** OpenAI hosted tools/continuation/Realtime, exact AI SDK v4 matrix, RAG lifecycle/reranking/trust/status, and memory export/rebuild; **0.0.14** conversations, memory consent/lifecycle, artifact co-work review, AG-UI co-work events, scoped M365/GWS OAuth connectors, browser checkpoints, device contracts, and Alibaba/Ollama providers; plus prior release migrations.
37
+ - [Enterprise PostgreSQL state](enterprise-postgres-state.md): optional `@arnilo/prism-enterprise-postgres` composition for durable policy/evaluation/work-idempotency/model-router/`toolEffects` state, exact ownership, checksummed migrations, and explicit cleanup.
38
+ - [Migration guide](migration.md): **0.0.24** distributed events and recoverable tool effects; **0.0.23** enterprise PostgreSQL state adapters (async router and work reconciliation); **0.0.22** third-party behavior integrations (Caveman, Ponytail); **0.0.21** coding-tool capability gaps (`outputMode`, glob, read-before-write, delete/move, aggregator 9/4); **0.0.20** progressive skill disclosure, empty registry default, `load_skill`, priority budget demotion, optional tool-result fold; **0.0.19** observational memory lifecycle; **0.0.15** OpenAI hosted tools/continuation/Realtime, exact AI SDK v4 matrix, RAG lifecycle/reranking/trust/status, and memory export/rebuild; **0.0.14** conversations, memory consent/lifecycle, artifact co-work review, AG-UI co-work events, scoped M365/GWS OAuth connectors, browser checkpoints, device contracts, and Alibaba/Ollama providers; plus prior release migrations.
38
39
  - [Node JSONL session store](node-jsonl-session-store.md): development-only JSONL file adapter for single-process Node hosts; no cross-process safety; `searchSessions` throws `SessionSearchUnsupportedError`.
39
40
  - [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): Plan 056 inventory — session/run-ledger/persistence contracts, credential/OAuth seams, content/resource/model capabilities, package dependency matrix, conformance matrix, and threat model for production adapters.
40
41
 
@@ -62,12 +63,13 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
62
63
  - [Retrieval-augmented generation](rag.md): optional bounded source lifecycle, document adapters, host reranking, ingestion status, attributable citations, and inert context injection.
63
64
 
64
65
  ## Tools
66
+ - [Recoverable tool effects](tool-effects.md): optional effect declarations, `ToolEffectStore` claim/CAS, unknown reconciliation (not exactly-once), and adapter classifications.
65
67
  - [Tools](tools.md): register host-owned active tools with replace-or-error duplicate policy, apply exact allow/deny filtering, dispatch normal or opt-in bounded artifact-loop calls, and optionally bound untrusted JSON Schema compilation.
66
68
  - [Tool execution primitives](tool-execution-primitives.md): finite JSON Schema LRU validation, exclusive-aware bounded parallel dispatch, MCP bridge mapping, coding execution policy, and image-read bounds.
67
69
  - [Tool validator JSON Schema package](../packages/tool-validator-json-schema/README.md): optional `@arnilo/prism-tool-validator-json-schema` adapter for `tool.parameters`.
68
70
  - [MCP client bridge and server exposure](mcp-tools.md): SDK-1.29.0 bounded tools/resources/prompts, host-owned roots/sampling/elicitation, exact-origin DNS-pinned client transport, and principal-bound opt-in Streamable HTTP sessions.
69
71
  - [Web search, fetch, and extraction](web-tools.md): optional host-selected Brave/Exa discovery and Firecrawl Markdown/schema tools with native fetch, stable citations, late credentials, finite limits, and explicit untrusted-content boundaries.
70
- - [Work tools](work-tools.md): optional `@arnilo/prism-work-tools` identity-scoped M365 + GWS connectors (hard-coded CLI argv, draft-then-approve, idempotency, shared result shapes); 0.0.14 adds a late-bound per-identity `tokenProvider` (env-only, fail-closed).
72
+ - [Work tools](work-tools.md): optional `@arnilo/prism-work-tools` identity-scoped M365 + GWS connectors (hard-coded CLI argv, draft-then-approve, state-machine idempotency, shared result shapes); 0.0.14 adds a late-bound per-identity `tokenProvider` (env-only, fail-closed).
71
73
  - [Work connectors](work-connectors.md): connector principles, capability gates, scoped OAuth establishment (0.0.14), and out-of-scope boundaries (Slack/Teams channels not shipped) for Microsoft 365 / Google Workspace.
72
74
  - [Browser automation](browser-automation.md): optional `@arnilo/prism-browser` with host-supplied Playwright contexts, AI-mode snapshots/refs, ordered `browser_open`/`browser_snapshot`/`browser_act`/`browser_close`, egress/side-effect/upload/download/screenshot policy, finite page/action/snapshot/network/artifact caps, and 0.0.14 verified-state checkpoints with reload/verify-before-side-effect.
73
75
  - [Device adapters](device-adapters.md): deny-by-default realtime voice / desktop-control contract + conformance (0.0.14); no vendor package — admission fails closed without explicit consent+sandbox+approval, stream bounds, shared `RunLimits`, redacted telemetry.
@@ -87,12 +89,13 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
87
89
  - [Resource loading](resource-loading.md): decode text, JSON, binary, and manifests through caller-provided loaders; bridge host-authorized artifacts to bounded RAG document loading.
88
90
 
89
91
  ## Server/API
90
- - [Web-standard server handler](server.md): optional framework-free authorized direct/SSE agent, durable agent lifecycle, durable workflow routes, plus optional health/drain/rate-limit/replay/deployment-lease seams; explicit bounds and zero default exposure.
92
+ - [Web-standard server handler](server.md): optional framework-free authorized direct/SSE agent, cross-replica durable event reconnect via `Last-Event-ID`, durable agent lifecycle/workflow routes, plus health/drain/rate-limit/replay/deployment-lease seams; explicit bounds and zero default exposure.
91
93
 
92
94
  ## Multi-agent and interoperability
93
95
  - [Supervisor delegation](supervisors.md): optional explicit child allow-list, derived memory scopes, narrowing-only permissions, lifecycle hooks, nested delegation, cancellation, finite budgets, host-projected delegation telemetry, and separate A2A durable adapter boundary.
94
- - [A2A interoperability](a2a.md): A2A 1.0 JSON-RPC/HTTPS cards plus host-owned durable task get/list/cancel/subscribe, bounded rich parts/replay, principal-scoped push configs, and exact-origin verified client.
95
- - [Frontend interoperability (AG-UI and ACP)](ag-ui.md): optional `@arnilo/prism-ag-ui` AG-UI mapper/authorized Web handler/replay and stable ACP sibling over shared redacted event and durable-approval seams; 0.0.14 adds reconnectable co-work events (artifact progress/approval/download-link, connector drafts, redacted browser snapshots); no TUI, editor, filesystem, or A2A runtime.
96
+ - [A2A interoperability](a2a.md): A2A 1.0 JSON-RPC/HTTPS cards plus host-owned durable task get/list/cancel/subscribe, shared `AgentEventSource` task adapter, bounded rich parts/replay, principal-scoped push configs, exact-origin verified client, and rich stream seam for explicit AG-UI fronting.
97
+ - [Frontend interoperability (AG-UI and ACP)](ag-ui.md): optional `@arnilo/prism-ag-ui` full AG-UI 0.0.57 input/event/capability mapper, authorized Web handler/distributed source follow, explicit hardened MCP/MCP Apps/remote A2A adapters, and stable ACP sibling over shared redacted event and durable-approval seams; 0.0.14 adds reconnectable co-work events.
98
+ - [AG-UI adoption evaluation](ag-ui-adoption.md): official 0.0.57 input/event/capability matrix and shipped hardened MCP/MCP Apps/A2A handshake boundaries.
96
99
 
97
100
  ## CLI/RPC
98
101
  - [CLI/RPC](cli-rpc.md): Run print/json modes and LF-delimited RPC over the public AgentSession runtime, including mid-run `steer`, branch-handle results, fixed `forkSession`, and `checkout`. `prism init` scaffolds a tiny TypeScript project with one selected provider and an offline mock test.
@@ -101,7 +104,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
101
104
  - [Workflow/TUI scope](workflow-tui-primitives.md): records why 0.0.5 ships workflow APIs/RPC control but no interactive terminal UI.
102
105
 
103
106
  ## Security and credentials
104
- - [Host security guide](host-security.md): fail-closed checklist for supply-chain/attestation/canary isolation, bounded credentials, AG-UI/ACP/A2A/web remote boundaries, untrusted external content, settings, redaction, trust roots, workflow ownership, coding I/O, permissions, persistence, extensions, and tool validation.
107
+ - [Host security guide](host-security.md): fail-closed checklist for supply-chain/attestation/canary isolation, bounded credentials, AG-UI/ACP/A2A/web remote boundaries, untrusted external content, settings, redaction, trust roots, workflow ownership, coding I/O, permissions, PostgreSQL TLS/roles/cleanup, persistence, extensions, and tool validation.
105
108
  - [Security/auth/trust](settings-auth-trust-security.md): settings providers, credential helpers, trust/permission policies, redaction controls, host-owned settings/credentials wiring outside `AgentConfig`, and security-boundary hardening summary.
106
109
  - [Credentials and redaction](credentials-and-redaction.md): compose explicit credential resolver order, use caller-supplied env objects/OAuth refresh + revoke helpers, resolve credentials only at the provider edge, redact known secret values, and follow the provider-authorized subscription OAuth matrix.
107
110
  - [Credential storage](credential-storage.md): optional `@arnilo/prism-credentials-node` adapter with strict bounded AES-GCM envelopes, async finite scrypt, restrictive Unix files, abort-aware bounded system-keychain calls, optional host-KMS wrap (`encryptWithHostKms`), and 0.0.14 Microsoft 365 / Google Workspace OAuth providers (PKCE/device-code, least-privilege scope bundles, per-identity work-token bridge).
@@ -114,15 +117,15 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
114
117
  - [Compaction conformance](compaction-conformance.md): assert any `CompactionStrategy` returns a non-empty redacted summary and observes abort from `@arnilo/prism/testing/compaction-conformance`.
115
118
  - [Tool conformance](tool-conformance.md): assert the tool-dispatch blocked-reason matrix (unknown/denied/invalid/permission/validator) and success path from `@arnilo/prism/testing/tool-conformance`.
116
119
  - [Extension conformance](extension-conformance.md): assert an `Extension` setup runs, contributions stay inert, and setup errors are redacted or rethrown from `@arnilo/prism/testing/extension-conformance`.
117
- - `examples/`: compile-checked typed examples and runnable mock demos (SDK basics, provider registration, auth, tools, [`examples/ag-ui-server.ts`](../examples/ag-ui-server.ts), [`examples/enterprise-identity.ts`](../examples/enterprise-identity.ts), [`examples/enterprise-policy-audit.ts`](../examples/enterprise-policy-audit.ts), [`examples/enterprise-work-connectors.ts`](../examples/enterprise-work-connectors.ts), [`examples/conversation-durable-replay.ts`](../examples/conversation-durable-replay.ts), [`examples/artifact-review-delivery.ts`](../examples/artifact-review-delivery.ts), [`examples/server-deployment-seams.ts`](../examples/server-deployment-seams.ts), cache-aware prompt assembly, NeuralWatt agent run ([`examples/neuralwatt-agent-run.ts`](../examples/neuralwatt-agent-run.ts)), [`examples/coding-compaction.ts`](../examples/coding-compaction.ts), [`examples/caveman-ponytail.ts`](../examples/caveman-ponytail.ts), stores/branching, structured-output/artifact-loop, CLI, RPC, workflow orchestration).
120
+ - `examples/`: compile-checked typed examples and runnable mock demos (SDK basics, provider registration, auth, tools, [`examples/ag-ui-server.ts`](../examples/ag-ui-server.ts), [`examples/ag-ui-mcp-apps.ts`](../examples/ag-ui-mcp-apps.ts), [`examples/enterprise-identity.ts`](../examples/enterprise-identity.ts), [`examples/enterprise-policy-audit.ts`](../examples/enterprise-policy-audit.ts), [`examples/enterprise-work-connectors.ts`](../examples/enterprise-work-connectors.ts), [`examples/enterprise-postgres-state.ts`](../examples/enterprise-postgres-state.ts), [`examples/conversation-durable-replay.ts`](../examples/conversation-durable-replay.ts), [`examples/artifact-review-delivery.ts`](../examples/artifact-review-delivery.ts), [`examples/server-deployment-seams.ts`](../examples/server-deployment-seams.ts), cache-aware prompt assembly, NeuralWatt agent run ([`examples/neuralwatt-agent-run.ts`](../examples/neuralwatt-agent-run.ts)), [`examples/coding-compaction.ts`](../examples/coding-compaction.ts), [`examples/caveman-ponytail.ts`](../examples/caveman-ponytail.ts), stores/branching, structured-output/artifact-loop, CLI, RPC, workflow orchestration).
118
121
 
119
122
  ## Third-party integrations
120
123
  - [Caveman behavior integration](caveman.md): optional `@arnilo/prism-caveman` — upstream Caveman skills/commands, `caveman-mode` injector, session `caveman-level` persistence, progressive catalog + `load_skill`; requires host `upstreamPath` and session attach callbacks; inert until `kernel.load`.
121
124
  - [Ponytail behavior integration](ponytail.md): optional `@arnilo/prism-ponytail` — upstream Ponytail skills/commands, `ponytail-mode` injector, session `ponytail-mode` persistence; resolves peer `@dietrichgebert/ponytail` or `upstreamPath`; opt-in (not in code/sdk profiles).
122
125
 
123
126
  ## Release and install
124
- - [Release and install](release-and-install.md): current **0.0.22** 46-package graph (Phase 5 Caveman/Ponytail behavior integrations; plan 005), exact-peer/install/tarball rules, deterministic resumable publication and publish dry-run, pinned supply-chain gates, offline tests, the 0.0.15 provider/AI-SDK/RAG/memory protected live-canary matrix, and sandbox-browser Docker/Playwright gates.
125
- - [0.1.0 / 1.0 readiness gates](0.1.0-readiness.md): command-per-gate 1.0 readiness table — frozen API surface + compat gate, migration/docs tripwires, budget table, live-suite matrix, security matrix, current-line status (**0.0.22** published target), signed-publication/live-canary prerequisites for 1.0, and Phase 12 demand-evidence entry criteria.
127
+ - [Release and install](release-and-install.md): current **0.0.24** 47-package graph (Phase 7 distributed events and tool effects; plan 007), exact-peer/install/tarball rules, deterministic resumable publication and publish dry-run, protected PostgreSQL gate, pinned supply-chain gates, offline tests, the 0.0.15 provider/AI-SDK/RAG/memory protected live-canary matrix, and sandbox-browser Docker/Playwright gates.
128
+ - [0.1.0 / 1.0 readiness gates](0.1.0-readiness.md): command-per-gate 1.0 readiness table — frozen API surface + compat gate, migration/docs tripwires, budget table, live-suite matrix, security matrix, current-line status (**0.0.23** published target), signed-publication/live-canary prerequisites for 1.0, and Phase 12 demand-evidence entry criteria.
126
129
  - [Review coverage (2026-07-26 Phase 11)](review-coverage-2026-07-26-phase-11.md): Plan 079 evidence freeze — baseline size/startup/benchmark budgets, hotspot domain extraction table, confirmed duplication survivors (redactor/cleanJson/row-codecs/checkpoints/exec-runner/approval/ownership), profile adoption recommendations, and tarball artifact-diet findings for 0.0.16.
127
130
  - [Review coverage (2026-07-26 Phase 10)](review-coverage-2026-07-26-phase-10.md): Plan 078 evidence freeze — OpenAI hosted tools/continuation/realtime, AI SDK version matrix, remaining provider metadata parity, RAG replaceSource/loaders/parsers/reranker/provenance/ingestion-status, memory export/rebuild/conformance, and 0.0.15 (43 → 43 manifests; no new package) release gates.
128
131
  - [Review coverage (2026-07-25 Phase 9)](review-coverage-2026-07-25-phase-9.md): Plan 077 evidence freeze — conversation service, memory consent/lifecycle, artifact co-work review, AG-UI co-work events, scoped M365/GWS OAuth, browser checkpoint composition, and deny-by-default device contracts for 0.0.14 (41 → 43 manifests; only the two provider packages are new).
package/docs/mcp-tools.md CHANGED
@@ -21,6 +21,17 @@ await bridge.close(); // close client + transport
21
21
 
22
22
  Advanced hosts that manage their own `Client` + `Transport` can call `attachMcpToolBridge()` or `attachMcpCapabilities()` after connect. `connectMcpCapabilities()` keeps resources/prompts as host-facing facades rather than converting them into model tools, and declares roots/sampling/elicitation only when callbacks are supplied.
23
23
 
24
+ MCP Apps is an explicit opt-in on the normal bridge:
25
+
26
+ ```ts
27
+ const bridge = await connectMcpTools({ serverId: "weather", transport, mcpApps: true });
28
+ // Fails unless the server acknowledges io.modelcontextprotocol/ui.
29
+ const app = await bridge.apps!.readResource("ui://weather/card");
30
+ // bridge.tools excludes _meta.ui.visibility: ["app"] tools.
31
+ ```
32
+
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
+
24
35
  ```ts
25
36
  const bridge = await connectMcpCapabilities({
26
37
  serverId: "research",
@@ -78,7 +89,7 @@ Do **not** use this package as a sandbox, permission engine, or auto-discovery l
78
89
 
79
90
  ## Outputs / response / events
80
91
 
81
- The resolved `McpToolBridge` exposes `tools`, `refresh()`, and `close()`. Each discovered MCP tool becomes a normal Prism `ToolDefinition`; calls return `ToolResult`, with remote `isError` mapped to `ToolResult.error`. List-change notifications invalidate the cache but register nothing automatically.
92
+ `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.
82
93
 
83
94
  `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>`.
84
95
 
@@ -126,6 +137,7 @@ Duplicate prefixed names throw `McpToolNameCollisionError` at refresh time.
126
137
  | `serverId` | required | Stable identifier used in default name prefix |
127
138
  | `transport` | required | `stdio` or `streamable-http` config |
128
139
  | `namePrefix` | `mcp:<serverId>:` | Registry namespace for remote tools |
140
+ | `mcpApps` | `false` | Explicitly negotiate `io.modelcontextprotocol/ui`; exposes `bridge.apps` only after server acknowledgement |
129
141
  | `listCacheTtlMs` | 30 s (24 h hard) | Skip re-listing until TTL expires (invalidated on list-changed) |
130
142
  | `callTimeoutMs` | 60 s (30 min hard) | Connect, list-page, and tool-call SDK request timeout/abort |
131
143
  | `maxListPages` / `maxTools` | 20 / 500 (hard 100 / 5,000) | Stop pagination before another request/append |
@@ -187,6 +199,8 @@ Plaintext is accepted only when `allowLoopbackHttp: true`, the URL hostname is l
187
199
 
188
200
  Web handler defaults: 1 MiB request (8 MiB hard), 2 MiB response (16 MiB hard), 32 concurrent requests (512 hard), 60 s timeout (30 min hard), and 32 sessions (512 hard). Stateful mode is intentionally one official SDK transport/session lineage per handler; use one handler/server instance per independently hosted endpoint when multi-tenant transport isolation is required. It parses bounded JSON before passing `parsedBody` to the SDK transport. `allowedHosts`/`allowedOrigins` activate SDK DNS-rebinding checks only when explicitly configured. Authentication data comes only from host `resolveAuthInfo()`.
189
201
 
202
+ Remote MCP tools default to `external_mutation`/`unsupported` unless the host `effect` policy classifies them. MCP Apps (`io.modelcontextprotocol/ui`) stay behind host CSP/origin/visibility gates. See [tool effects](tool-effects.md) and [AG-UI adoption](ag-ui-adoption.md).
203
+
190
204
  ## Security and performance notes
191
205
 
192
206
  | Risk | Mitigation |
@@ -195,6 +209,7 @@ Web handler defaults: 1 MiB request (8 MiB hard), 2 MiB response (16 MiB hard),
195
209
  | SSRF / DNS rebinding / redirects (HTTP) | Exact HTTPS origins; credentials/fragments/redirects denied; every DNS answer public; one address pinned per request; explicit loopback-only HTTP escape hatch |
196
210
  | Hostile discovery / schema compilation | Raw SDK `tools/list` requests avoid SDK Ajv output-schema compilation; finite pages/tools/cursors/metadata/schema totals; failed refresh leaves previous tools unchanged |
197
211
  | Tool-name shadowing | Prefixed names + `createToolRegistry({ duplicate: "error" })` |
212
+ | MCP Apps metadata/HTML | Explicit extension acknowledgement; bounded nested metadata; app-only tools absent from model list; only linked `ui://` HTML/MIME resource body reaches the host renderer |
198
213
  | Oversized/deep/wide server output | One aggregate byte/depth/property walk covers content, structured content, compatibility `toolResult`, and bounded remote errors before `ToolResult` |
199
214
  | Unvalidated arguments | Register tools with `createJsonSchemaToolArgumentValidator()` at dispatch |
200
215
  | Missing permission gate | Client direction: `PermissionPolicy` on `tool:mcp:<serverId>:<name>:execute`; server direction: required MCP `authorize` plus optional core `PermissionPolicy` |
@@ -207,7 +222,7 @@ Web handler defaults: 1 MiB request (8 MiB hard), 2 MiB response (16 MiB hard),
207
222
 
208
223
  For durable lifecycle exposure, construct `createAgentRunLifecycle({ checkpoints, resolveAgent })` in core, then pass selected entries as `agentRuns: { support: { lifecycle } }`. MCP registers two tools: `agent.support.status` accepts `{ runId, sessionId? }`; `agent.support.resume` accepts `{ runId, sessionId?, decision, expectedVersion }`. Do not expose an agent without durable checkpoints and a restart-safe `SessionStore`; no lifecycle tool appears by default.
209
224
 
210
- MCP output is untrusted. Register bridge tools through core dispatch with a `SecretRedactor` so bounded remote content/errors are redacted before persistence or display. `CreatePrismMcpServerOptions.guardrails` applies shared tool-input/output stages to registered Prism tools; commands remain host callbacks. See [Guardrails](guardrails.md). Prism does not infer unknown secrets. MCP server authorization does not replace tool `PermissionPolicy`, argument validation, coding `ExecutionPolicy`, workflow ownership checks, TLS, rate limiting, or sandboxing. A timed-out tool must cooperate with `AbortSignal` to stop side effects; protocol retention and HTTP responses remain bounded when remote work ignores abort.
225
+ MCP output is untrusted. Register bridge tools through core dispatch with a `SecretRedactor`. Apps renderer needs separate origin, `allow-scripts allow-same-origin`, no-wider CSP, and authenticated proxy approval; no app mutation retry before Task 4 recovery. `CreatePrismMcpServerOptions.guardrails` applies shared tool-input/output stages to registered Prism tools; commands remain host callbacks. See [Guardrails](guardrails.md). Prism does not infer unknown secrets. MCP server authorization does not replace tool `PermissionPolicy`, argument validation, coding `ExecutionPolicy`, workflow ownership checks, TLS, rate limiting, or sandboxing. A timed-out tool must cooperate with `AbortSignal` to stop side effects; protocol retention and HTTP responses remain bounded when remote work ignores abort.
211
226
 
212
227
  Discovery validation is atomic: cursor/page/tool/name/description/schema failures reject `refresh()` and preserve the previous immutable tool-array reference. The bridge intentionally uses raw SDK `request()` for `tools/list` and `tools/call`; this avoids eager Ajv compilation/validation of untrusted remote output schemas. Host `ToolValidator` remains the argument-validation owner.
213
228
 
package/docs/migration.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Migration guide
2
2
 
3
+ ## 0.0.23 → 0.0.24 distributed events and recoverable tool effects (intentional pre-1.0 contract changes)
4
+
5
+ Release **0.0.24** adds a replaceable durable `AgentEventSource`, recoverable `ToolEffectStore`, full AG-UI 0.0.57 compatibility, and AG-UI fronting for MCP / MCP Apps / remote A2A. Core remains dependency-free; PostgreSQL adapters and effect stores stay opt-in. Delivery is at-least-once with consumer deduplication — not exactly-once.
6
+
7
+ 1. **Open persistence for durable events.** `createPostgresPersistence({ pool, eventCursorSecret })` exposes `persistence.events` (`AgentEventSource`). Migration **006** adds `prism_agent_event_streams`; migration **007** adds the exact-owner retention index. Share one HMAC `eventCursorSecret` across replicas. SQLite gains sequence compatibility only (no distributed subscribe). Backup before upgrade; rollback restores both session-store and enterprise migration histories.
8
+ 2. **Reconnect through the shared source.** Prefer `events.subscribe({ ownership, sessionId, runId, after })` or transport cursors (`Last-Event-ID` / `?cursor=` / A2A `afterEventId` Prism extension). Live `session.subscribe()` remains process-local. Consumers must dedupe `record.id`; sticky sessions are optional.
9
+ 3. **Opt into tool effects.** Pass `effectStore` on the agent/run. Declare `tool.effect` (`kind` + `idempotency`). Core derives `idempotencyKey` — model keys are ignored. Required effects without a store fail closed. Ambiguous post-dispatch outcomes become `unknown` and need `resolveUnknown`; they never auto-replay.
10
+ 4. **Enterprise tool effects.** `createPostgresEnterpriseState` applies enterprise migration **002** (`prism_tool_effects`) and exposes `state.toolEffects`. Cleanup remains host-scheduled via `state.cleanup`.
11
+ 5. **Package adapters.** Coding/browser/work/MCP/supervisor tools ship effect declarations or host policies. Work mutations require the core key + store. MCP defaults remote tools to unsupported unless the host policy classifies them.
12
+ 6. **AG-UI.** Handler accepts full RunAgentInput with host `input.project` / `frontendTools` / interrupt resume; optional `mcp` / `a2a` adapters. Direct Prism MCP/A2A APIs remain independent.
13
+
14
+ ```ts
15
+ const persistence = await createPostgresPersistence({ pool, eventCursorSecret: secret });
16
+ const enterprise = await createPostgresEnterpriseState({ pool });
17
+ const agent = createAgent({ model, provider, tools, runLedger: persistence, effectStore: enterprise.toolEffects });
18
+ for await (const { record, cursor } of persistence.events.subscribe({ ownership, sessionId, runId, after })) {
19
+ save(cursor); // dedupe record.id; reconnect never reruns completed effects
20
+ }
21
+ ```
22
+
23
+ Example: `node examples/distributed-events-and-tool-effects.ts` (network-free memory reference). Hosts that never open an event source or effect store keep prior behavior.
24
+
25
+ ## 0.0.22 → 0.0.23 production enterprise state adapters (intentional pre-1.0 contract changes)
26
+
27
+ Release **0.0.23** adds `@arnilo/prism-enterprise-postgres` and makes work-mutation idempotency plus durable model-router state explicit. Core agent/session behavior stays unchanged; install/configure this package only when a host needs PostgreSQL coordination.
28
+
29
+ 1. **Install and open deliberately.** Add `@arnilo/prism-enterprise-postgres` with `@arnilo/prism`, the four domain packages, and `pg`. Call `await createPostgresEnterpriseState({ pool, schema })`; import is inert. Open applies/verifies the checksum-protected enterprise migration under a per-schema advisory lock. Keep its migration history separate from session-store PostgreSQL history; backup/restore-test both. Configure TLS, credentials, pool limits, roles, and a deployment migration principal in the host.
30
+ 2. **Use one composition, not ad-hoc SQL.** Wire `state.policy`, `state.evaluations`, and `state.workIdempotency` into existing package APIs. Policy/work/router operations require active verified identity; hosts must project evaluation records and queries from verified ownership. Every query needs tenant scope; owner-bound cursors cannot cross tenants. Memory/JSONL stores remain test/single-process adapters, not production substitutes.
31
+ 3. **Replace work `get`/`put` with claim transitions.** `IdempotencyStore` now uses async `begin`, `complete`, `fail`, `markUnknown`, and `resolveUnknown` (plus reconciliation `get`). Call `begin` before an approved external connector effect and use returned claim token/version for CAS transitions. Treat **absent**, `in_progress`, `completed`, `failed_retryable`, `failed_terminal`, and `unknown` differently. Only completed summaries replay; ambiguous `unknown` requires connector/operator reconciliation and is never auto-replayed. This is not exactly-once delivery.
32
+ 4. **Make router paths asynchronous when state is durable.** Pass `stateStore: state.modelRouter` to `createModelRouter`. Await `resolve`, `recordUsage`, and `recordOutcome`, pass a verified identity to each, and retain any `circuitProbeToken` from `resolve` for outcome recording. `providerSource` is memory-only; with a durable state store it throws `ERR_PRISM_MODEL_ROUTER_ASYNC_STATE` rather than bypassing rate/budget/circuit state.
33
+ 5. **Own cleanup.** No background worker starts. Schedule bounded `await state.cleanup({ tenantId, accountId?, userId?, principalId, limit })` from an authorized host job; expired work claims become `unknown` and expired circuit probes reopen safely. Do not make a global sweep or auto-resolve unknown outcomes.
34
+ 6. **Keep request roles narrow.** Request state SQL is limited to `SELECT`/`INSERT`/`UPDATE`/`DELETE` on six state tables plus schema `USAGE`; DDL/catalog/advisory-lock work belongs to controlled migration setup. Never persist prompt/body/tool-argument material, raw connector/provider results, JWTs, or credentials. See [Enterprise PostgreSQL state](enterprise-postgres-state.md) for bounds, cleanup, SQL inventory, and recorded performance evidence.
35
+
36
+ ```ts
37
+ const state = await createPostgresEnterpriseState({ pool, schema: "prism" });
38
+ const router = createModelRouter({ resolver, stateStore: state.modelRouter });
39
+ const selected = await router.resolve({ model, identity });
40
+ await router.recordOutcome({ identity, provider: selected.provider.id, model: selected.model.model, success: true, circuitProbeToken: selected.circuitProbeToken });
41
+ await state.close(); // caller-owned pool stays open
42
+ ```
43
+
3
44
  ## 0.0.21 → 0.0.22 third-party behavior integrations (additive)
4
45
 
5
46
  Release **0.0.22** adds two optional behavior packages; core `@arnilo/prism` runtime behavior is unchanged.
@@ -14,7 +14,7 @@ Do not put secrets, prompts, or raw OpenRouter keys into diagnostics. Do not hon
14
14
 
15
15
  | API / field | Meaning |
16
16
  | --- | --- |
17
- | `createModelRouter({ resolver, ... })` | Wraps host `ProviderResolver` |
17
+ | `createModelRouter({ resolver, stateStore?, ... })` | Wraps host `ProviderResolver`; omit `stateStore` for in-process memory state or pass durable async state. |
18
18
  | `allowList.providers` / `allowList.models` | Exact provider id / model id or `provider/model` |
19
19
  | `allowedResidencies` | Request residency must match when configured |
20
20
  | `budgets` / per-call `maxTokens` / `maxCostUsd` | Finite non-negative ceilings; `recordUsage` charges |
@@ -24,7 +24,7 @@ Do not put secrets, prompts, or raw OpenRouter keys into diagnostics. Do not hon
24
24
  | `allowOpenRouterRouting` | Default `false`; when false, routing metadata is stripped |
25
25
  | `onDiagnostics` | Optional redacted hook (e.g. policy ledger evidence ref) |
26
26
  | `router.resolve({ model, identity?, residency?, ... })` | Rich async selection |
27
- | `router.providerSource` | Sync `ProviderResolver` facade for `AgentConfig` |
27
+ | `router.providerSource` | Sync facade only for memory state; with `stateStore` it throws `ERR_PRISM_MODEL_ROUTER_ASYNC_STATE` rather than bypass durable checks. |
28
28
 
29
29
  Frozen caps (default / hard): attempts `3 / 8`, circuit keys `1,024 / 16,384`, diagnostics `8 KiB / 64 KiB`.
30
30
 
@@ -32,7 +32,7 @@ Frozen caps (default / hard): attempts `3 / 8`, circuit keys `1,024 / 16,384`, d
32
32
 
33
33
  - `ModelRouterResolveResult` — selected `provider` + possibly stripped `model`, `diagnostics`, and `providerRequestPolicy`.
34
34
  - Deny throws `ModelRouterError` with code + redacted `diagnostics` (allow-list/residency/budget fail closed without calling resolver).
35
- - `recordOutcome({ success })` opens/closes circuits; `recordUsage` advances budgets.
35
+ - `await recordOutcome({ identity, success, circuitProbeToken? })` opens/closes circuits; `await recordUsage({ identity, ... })` advances budgets. Pass the probe token returned by `resolve` for a half-open outcome.
36
36
 
37
37
  ## Request/response example
38
38
 
@@ -56,9 +56,12 @@ Frozen caps (default / hard): attempts `3 / 8`, circuit keys `1,024 / 16,384`, d
56
56
  ```ts
57
57
  import { createAgent, createProviderResolver } from "@arnilo/prism";
58
58
  import { createModelRouter } from "@arnilo/prism-model-router";
59
+ import { createPostgresEnterpriseState } from "@arnilo/prism-enterprise-postgres";
59
60
 
61
+ const enterprise = await createPostgresEnterpriseState({ pool, schema: "prism" });
60
62
  const router = createModelRouter({
61
63
  resolver: createProviderResolver(providers),
64
+ stateStore: enterprise.modelRouter,
62
65
  allowList: { providers: ["openai", "openrouter"] },
63
66
  allowedResidencies: ["eu"],
64
67
  fallbacks: [{ provider: "openrouter", model: "auto" }],
@@ -77,7 +80,11 @@ const agent = createAgent({
77
80
  provider,
78
81
  providerRequestPolicies: [providerRequestPolicy],
79
82
  });
80
- // or: providerSource: router.providerSource
83
+ await router.recordUsage({ identity, provider: provider.id, model: model.model, tokens: 500 });
84
+ await router.recordOutcome({ identity, provider: provider.id, model: model.model, success: true });
85
+ await enterprise.close();
86
+
87
+ // `router.providerSource` is unavailable with durable state; resolve before provider I/O.
81
88
  ```
82
89
 
83
90
  ## Extension and configuration notes
@@ -87,10 +94,11 @@ Router is optional. Chain returned `providerRequestPolicy` with other `ProviderR
87
94
  ## Security and performance notes
88
95
 
89
96
  - Allow-list and residency denies never call the underlying resolver.
90
- - Budget/rate/circuit state is memory-capped; oldest keys evict.
91
- - Diagnostics carry identity refs and attempt outcomes only — no prompts/secrets.
92
- - Selection is O(attempts × map ops); no network I/O inside the router.
93
- - Raising hard caps requires Phase 8 freeze + tests + docs updates.
97
+ - Without `stateStore`, budget/rate/circuit state is process-local, memory-capped, and oldest keys evict. It is not a cross-replica production path.
98
+ - With `stateStore: createPostgresEnterpriseState(...).modelRouter`, rate/budget updates and circuit probes are atomic across replicas, use database time, and are owner/principal/provider/model scoped. Router calls become asynchronous and require verified identity.
99
+ - Diagnostics carry identity refs and attempt outcomes only — no prompts/secrets. Durable state stores at most bounded numeric/timestamp/token material, never prompts or credentials.
100
+ - Selection is O(attempts × state operations); no provider network I/O happens inside state updates. Recorded 0.0.23 PostgreSQL p95 point operations stayed under 50 ms and cursor/cleanup pages under 100 ms on the documented fixture.
101
+ - Raising hard caps requires a reviewed release update with tests and docs.
94
102
 
95
103
  ## Related APIs
96
104
 
@@ -99,4 +107,5 @@ Router is optional. Chain returned `providerRequestPolicy` with other `ProviderR
99
107
  - [OpenRouter](providers/openrouter.md)
100
108
  - [Policy and audit](policy-and-audit.md)
101
109
  - [Agent identity](agent-identity.md)
110
+ - [Enterprise PostgreSQL state](enterprise-postgres-state.md): durable router state, migration, cleanup, and ownership requirements.
102
111
  - Package README: [`@arnilo/prism-model-router`](../packages/model-router/README.md)
@@ -6,6 +6,44 @@ Evaluation defaults are finite: 100 trace rows × 20 pages and 4 MiB aggregate t
6
6
 
7
7
  This page states Prism runtime limits that keep slow consumers and long sessions from becoming unbounded memory or latency problems.
8
8
 
9
+ ## Release 0.0.24 distributed events and tool effects
10
+
11
+ `node scripts/benchmark-0.0.24.mjs` is an explicit protected PostgreSQL benchmark behind `PRISM_TEST_POSTGRES_URL`. Checked `scripts/benchmark-0.0.24.json` (Node v24.18.0/Linux x64, PostgreSQL 16.14): 10 tenants × 10 principals × 1,000 events/owner, 16 producers/subscribers, 100 warmups, 1,000 measured ops, 10,000-event sustained replay, 100-row cleanup.
12
+
13
+ | Scenario | Recorded p95 ms | Ceiling |
14
+ | --- | ---: | ---: |
15
+ | Event append / page | 1.502 / 3.103 | 50 / 100 |
16
+ | Effect claim+transition / cleanup | 3.084 / 3.242 | 50 / 100 |
17
+ | Event cleanup / reconnect catch-up | 1.370 / 7.883 | 100 / 2000 |
18
+
19
+ Sustained replay delivered 160,000 subscriber-events at 101.34 events/s. Five `EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)` plans used named indexes with no sequential scans. Process conformance (`scripts/phase7-conformance.test.mjs`) covers 16-process producers, `LISTEN` backend kill + poll catch-up, and pending/dispatched effect crash windows. Values are environment evidence, not universal SLOs.
20
+
21
+ ## Release 0.0.24 distributed events and tool effects
22
+
23
+ `node scripts/benchmark-0.0.24.mjs` is an explicit protected PostgreSQL benchmark behind `PRISM_TEST_POSTGRES_URL`. Checked `scripts/benchmark-0.0.24.json` (Node v24.18.0/Linux x64, PostgreSQL 16.14): 10 tenants × 10 principals × 1,000 events/owner, 16 producers/subscribers, 100 warmups, 1,000 measured ops, 10,000-event sustained replay, 100-row cleanup.
24
+
25
+ | Scenario | Recorded p95 ms | Ceiling |
26
+ | --- | ---: | ---: |
27
+ | Event append / page | 1.502 / 3.103 | 50 / 100 |
28
+ | Effect claim+transition / cleanup | 3.084 / 3.242 | 50 / 100 |
29
+ | Event cleanup / reconnect catch-up | 1.370 / 7.883 | 100 / 2000 |
30
+
31
+ Sustained replay delivered 160,000 subscriber-events at 101.34 events/s. Five `EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)` plans used named indexes with no sequential scans. Process conformance (`scripts/phase7-conformance.test.mjs`) covers 16-process producers, `LISTEN` backend kill + poll catch-up, and pending/dispatched effect crash windows. Values are environment evidence, not universal SLOs.
32
+
33
+ ## Release 0.0.23 enterprise PostgreSQL evidence
34
+
35
+ `node scripts/benchmark-0.0.23.mjs` is an explicit protected PostgreSQL benchmark, not part of `npm test` or `sdk:ready`. It requires `PRISM_TEST_POSTGRES_URL`, creates/drops an isolated schema, and checks frozen p95 ceilings from `scripts/budgets.json`. The checked `scripts/benchmark-0.0.23.json` evidence was recorded on Node v24.18.0/Linux x64 with `postgres:16-alpine`: 10 tenants × 10 principals × 1,000 policy/evaluation rows, 10,000 router keys, 16 pool clients, 100 warmups, 1,000 measured operations, and 100-row cleanup batches.
36
+
37
+ | Scenario | Recorded p95 ms | Ceiling |
38
+ | --- | ---: | ---: |
39
+ | Policy append / query | 0.747 / 1.479 | 50 / 100 |
40
+ | Evaluation append / query | 0.698 / 0.963 | 50 / 100 |
41
+ | Work claim+complete / contention | 1.892 / 4.162 | 50 / 50 |
42
+ | Router rate / budget / circuit contention | 12.011 / 6.715 / 28.410 | 50 / 50 / 50 |
43
+ | Explicit cleanup batch | 2.981 | 100 |
44
+
45
+ The same run accepted 1,000 rate claims, accumulated 16,000 budget tokens, granted 1,000 circuit probes, and verified 14 named-index `EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)` plans with no sequential scans. Before cleanup it recorded 101,100 policy rows (68,517,888 bytes), 101,100 evaluation rows (97,296,384 bytes), and 121,100 router-rate rows; cleanup removed exactly 100,000 expired rate rows. PostgreSQL relation allocation does not necessarily shrink after `DELETE` under MVCC, so row removal—not immediate file shrink—is the cleanup assertion. Values are dated environment evidence, not universal production SLOs; size pools, partitions, retention, and cleanup frequency from host measurements.
46
+
9
47
  ## Release 0.0.16 performance budgets and artifact diet
10
48
 
11
49
  Release 0.0.16 is a simplification/readiness release: it added no performance-affecting code, so the six network-free scenario medians are held at the 0.0.15 baseline and the win is a smaller published artifact. Budgets live in `scripts/budgets.json` (measured baselines + tolerance) and are enforced two ways:
@@ -117,6 +117,17 @@ Policy is optional. Hosts wire `record*` helpers or `evaluateAndAppend` at permi
117
117
  - Evaluate/append are O(fields) and network-free in-package; remote WORM I/O stays in the host sink/adapter.
118
118
  - Export never full-scans: page size is capped; raise hard caps only with Phase 8 freeze + tests + docs updates.
119
119
 
120
+ ## PostgreSQL enterprise state (0.0.23)
121
+
122
+ For durable multi-replica policy decisions, construct [`createPostgresEnterpriseState`](enterprise-postgres-state.md) and pass its `policy` store to the existing helpers. PostgreSQL keeps the same append/query contract, requires tenant scope and verified identity at append, binds owner data into opaque cursors, validates record bounds on read, and rejects duplicate ids. Memory and JSONL remain development/reference adapters, not production WORM or cross-replica stores.
123
+
124
+ ```ts
125
+ const state = await createPostgresEnterpriseState({ pool, schema: "prism" });
126
+ await evaluateAndAppend(request, { store: state.policy, evaluator, id: crypto.randomUUID() });
127
+ ```
128
+
129
+ `state.close()` leaves a caller-owned pool open. Run `state.cleanup(...)` from an authorized host schedule only when expiration cleanup is needed; it does not run in the background.
130
+
120
131
  ## Related APIs
121
132
 
122
133
  - [Model routing](model-routing.md)
@@ -125,4 +136,5 @@ Policy is optional. Hosts wire `record*` helpers or `evaluateAndAppend` at permi
125
136
  - [Runs and usage ledger](runs-and-usage.md)
126
137
  - [Workflows](workflows.md): proactive schedule capability enable/revoke events bridge here via `onCapability`.
127
138
  - [Host security](host-security.md)
139
+ - [Enterprise PostgreSQL state](enterprise-postgres-state.md): durable policy/evaluation/work/router composition.
128
140
  - Package README: [`@arnilo/prism-policy`](../packages/policy/README.md)
@@ -24,7 +24,7 @@ Use this package when you need server-backed persistence with pooled connections
24
24
  - managed cloud databases (RDS, Cloud SQL, Neon, Supabase, etc.)
25
25
  - CI integration tests against a real PostgreSQL service
26
26
 
27
- Prefer [`@arnilo/prism-session-store-sqlite`](sqlite-persistence.md) for local CLI tools, single-writer workloads, and network-free default tests. This adapter stores sessions/runs, not semantic vectors; use the separate [`@arnilo/prism-memory` pgvector path](working-and-semantic-memory.md), which rejects non-finite vectors before SQL, when vector recall is needed.
27
+ Prefer [`@arnilo/prism-session-store-sqlite`](sqlite-persistence.md) for local CLI tools, single-writer workloads, and network-free default tests. This adapter stores sessions/runs, not semantic vectors; use the separate [`@arnilo/prism-memory` pgvector path](working-and-semantic-memory.md), which rejects non-finite vectors before SQL, when vector recall is needed. For durable policy decisions, evaluations, work mutation idempotency, and model-router state, use the separate [`@arnilo/prism-enterprise-postgres`](enterprise-postgres-state.md) composition; it has its own migration history and does not replace session/run persistence.
28
28
 
29
29
  ## Inputs / request
30
30
 
@@ -40,6 +40,8 @@ import { createPostgresPersistence } from "@arnilo/prism-session-store-postgres"
40
40
  | `schema` | `string` | PostgreSQL schema for Prism tables. Defaults to `"prism"`. Validated and double-quoted. |
41
41
  | `poolMax` | `number` | Maximum pool size for adapter-owned pools. Defaults to `10`. |
42
42
  | `feedbackRedactor` | `SecretRedactor` | Optional redaction for feedback comment/tags/metadata before insert. |
43
+ | `eventSource` | `AgentEventSourceOptions` | Bounds durable event pages, subscribers, polling, reconnects, and cleanup. |
44
+ | `eventCursorSecret` | `string \| Uint8Array` | Stable HMAC secret shared by replicas that resume durable event cursors. |
43
45
  | `poolConfig` | `PoolConfig` | Additional `pg` options (TLS, idle timeout, application name, etc.). |
44
46
 
45
47
  Hosts own TLS (`ssl` in `poolConfig`), credentials, connection limits, and backup/retention enforcement.
@@ -54,12 +56,13 @@ Hosts own TLS (`ssl` in `poolConfig`), credentials, connection limits, and backu
54
56
  | `SessionStore.list` / `get` | Indexed reads by `session_id` and primary key. |
55
57
  | `SessionStore.readBranchPath` | Recursive ancestor query from `leafId` (or latest leaf) in root→leaf order. |
56
58
  | `RunLedger.append*` | Inserts run/event/tool/usage rows; events receive monotonic per-run `sequence` values. |
59
+ | `events` | Durable `AgentEventSource`; `LISTEN`/`NOTIFY` only wakes exact owned indexed reads, while polling remains recovery fallback. |
57
60
  | `ProductionPersistenceStore.query*` | Parameterized cursor pagination on indexed columns with tenant/account/user filters. |
58
61
  | `checkpoints` | Generic versioned `CheckpointStore` backed by `prism_checkpoints`; ownership, CAS/fencing checks, bounded pagination, and workflow suspended/denied/schedule/state/replay values without a schema migration. |
59
62
  | `leases` | Atomic `LeaseStore` backed by `prism_leases`; database-clock expiry, opaque renew/release token, monotonic takeover fence. |
60
63
  | `close()` | Ends the pool when the adapter created it from `connectionString`. |
61
64
 
62
- Migrations run automatically on open and are idempotent across reopen. Concurrent setup uses per-schema advisory transaction locks. While holding that lock, startup verifies ordered contract name/version/SHA-256 rows and full schema-v4 `information_schema`/catalog shape (all required tables, columns/types/nullability/defaults, PK/unique/FK keys, and named indexes) before any runtime write. A complete legacy 0.0.5 history with all `checksum` values `NULL` is shape-verified then backfilled transactionally once. Unknown, duplicate, out-of-order, partial-legacy, checksum, or shape drift rejects open; restore or apply reviewed DDL rather than editing migration rows.
65
+ Migrations run automatically on open and are idempotent across reopen. Concurrent setup uses per-schema advisory transaction locks. While holding that lock, startup verifies ordered contract name/version/SHA-256 rows and full schema-v7 `information_schema`/catalog shape (all required tables, columns/types/nullability/defaults, PK/unique/FK keys, and named indexes) before any runtime write. A complete legacy 0.0.5 history with all `checksum` values `NULL` is shape-verified then backfilled transactionally once. Unknown, duplicate, out-of-order, partial-legacy, checksum, or shape drift rejects open; restore or apply reviewed DDL rather than editing migration rows.
63
66
 
64
67
  ## Request/response example
65
68
 
@@ -116,7 +119,7 @@ PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres --workspace @arnil
116
119
  - The package is optional and workspace-local; `@arnilo/prism` core has no PostgreSQL dependency.
117
120
  - Schema names must match `^[a-zA-Z_][a-zA-Z0-9_]*$`; the adapter quotes them and never interpolates user values into identifier positions.
118
121
  - `SessionAppendOptions` idempotency rows are durable in `prism_session_append_idempotency` and survive reopen.
119
- - Schema version **5** applies `001_init`, `002_usage_scope`, `003_run_feedback`, `004_session_search`, and `005_lifecycle_hold_quota`. Migration 003 adds immutable `prism_run_feedback` rows with run FK/cascade deletion and owner/run/trace cursor indexes. Migration 004 adds session search FTS (Postgres `tsvector` FTS table dual-written on append) plus `prism_sessions(updated_at, id)` cursor index; existing entries are backfilled once. `persistence.feedback` validates exact run ownership, bounds/redacts through optional `feedbackRedactor`, queries bounded pages, and deletes only exact-owned IDs. Search hits never include credentials; ownership filters apply when present. SQLite shares the same model with dialect-local DDL.
122
+ - Schema version **6** applies `001_init`, `002_usage_scope`, `003_run_feedback`, `004_session_search`, `005_lifecycle_hold_quota`, and `006_agent_event_source`. Migration 006 backfills `prism_agent_event_streams`, then replaces the former non-unique run sequence index with unique `(run_id, sequence)` allocation enforced in the event append transaction. `LISTEN` registration commits before initial catch-up; notifications carry only a constant wake token, so dropped/coalesced notifications affect latency rather than delivery. Migration 003 adds immutable `prism_run_feedback` rows with run FK/cascade deletion and owner/run/trace cursor indexes. Migration 004 adds session search FTS (Postgres `tsvector` FTS table dual-written on append) plus `prism_sessions(updated_at, id)` cursor index; existing entries are backfilled once. `persistence.feedback` validates exact run ownership, bounds/redacts through optional `feedbackRedactor`, queries bounded pages, and deletes only exact-owned IDs. Search hits never include credentials; ownership filters apply when present. SQLite shares sequence compatibility only; it does not provide distributed subscriptions.
120
123
  - Pass an existing `pg` `Pool` when your host already manages pooling, TLS, and credential rotation.
121
124
 
122
125
  ## Security and performance notes
@@ -141,4 +144,5 @@ PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres --workspace @arnil
141
144
  - [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): package matrix and threat model.
142
145
  - [Workflows](workflows.md): adapt `persistence.checkpoints` and pass `persistence.leases` to `createWorkflowCoordinator()` and `createWorkflowSchedules()` for durable background execution and schedules.
143
146
  - [Working and semantic memory](working-and-semantic-memory.md): optional `@arnilo/prism-memory` PostgreSQL/pgvector working + semantic stores (separate from session/run persistence).
147
+ - [Enterprise PostgreSQL state](enterprise-postgres-state.md): separate durable policy/evaluation/work/router stores and cleanup.
144
148
  - [Migration guide](migration.md): moving from JSONL/in-memory to database-backed persistence.
@@ -429,6 +429,8 @@ void credentials;
429
429
  - `createAgent()` and `createAgentSession()` implement the session runtime. They use explicit providers only; no hidden provider registry is created. Store-backed sessions use explicit `SessionStore` values and branch methods on `AgentSession`. `AgentSession.compact()` and `AgentConfig`/`RunOptions.compaction` provide manual and opt-in auto-compaction. `AgentConfig`/`RunOptions.retry` provide bounded provider-turn retry before observable output.
430
430
  - `createMemorySessionStore()` is the built-in in-memory `SessionStore`. Node hosts can opt into file durability with `@arnilo/prism/node/session-store-jsonl`. `createSessionEntry()`, `getSessionBranchEntries()`, `listSessionBranches()`, and `rebuildSessionContext()` are pure helpers for branch-aware session entries. `rebuildSessionContext()` understands compaction entries produced by `createDefaultCompactionStrategy()`, reducing provider-context messages while keeping raw entries. They do not read files or call providers.
431
431
 
432
+ Phase 7 contracts: `AgentEventSource`, `ToolEffectDeclaration`/`ToolEffectStore`, and related error codes. Opt-in only; hosts without stores keep prior dispatch behavior.
433
+
432
434
  ## Security and performance notes
433
435
 
434
436
  - Type-only imports have no runtime side effects.