@arnilo/prism 0.0.22 → 0.0.23

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.23] - 2026-08-03
4
+
5
+ ### Added
6
+ - `@arnilo/prism-enterprise-postgres`: optional PostgreSQL composition for policy decisions, evaluation records, work-mutation idempotency, and model-router state.
7
+ - Checked enterprise PostgreSQL conformance/restart/contention, cleanup/index/storage performance evidence, and protected `PRISM_TEST_POSTGRES_URL` gate.
8
+
9
+ ### Changed
10
+ - `@arnilo/prism-work-tools` idempotency uses claim/CAS lifecycle states; ambiguous connector outcomes are `unknown` and require reconciliation.
11
+ - `@arnilo/prism-model-router` accepts durable async state; `recordUsage`/`recordOutcome` are awaited and `providerSource` cannot bypass a supplied state store.
12
+ - Publishable graph: **47** manifests (was 46); `@arnilo/prism-all` includes enterprise PostgreSQL state.
13
+
14
+ ### Breaking (minor, pre-1.0)
15
+ - Hosts implementing `IdempotencyStore` must migrate from `get`/`put` to `begin`/transition methods.
16
+ - Hosts using durable router state must await router methods with verified identity; synchronous `providerSource` is memory-state only.
17
+
18
+ See [docs/migration.md](docs/migration.md) for the 0.0.22 → 0.0.23 guide.
19
+
20
+
3
21
  ## [0.0.22] - 2026-07-31
4
22
 
5
23
  ### Added
package/dist/index.d.ts CHANGED
@@ -100,5 +100,5 @@ export { createToolParameterValidator, createToolRegistry, dispatchToolCall, fil
100
100
  export type { ResolvedUseCaseModel, ResolveUseCaseModelInput, UseCaseModelBinding, } from "./use-case-model.js";
101
101
  export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
102
102
  export declare const name = "prism";
103
- export declare const version = "0.0.22";
103
+ export declare const version = "0.0.23";
104
104
  export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
package/dist/index.js CHANGED
@@ -54,6 +54,6 @@ export { applyThinkingLevel, isThinkingLevel, normalizeThinkingLevel, THINKING_L
54
54
  export { createToolParameterValidator, createToolRegistry, dispatchToolCall, filterTools } from "./tools.js";
55
55
  export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
56
56
  export const name = "prism";
57
- export const version = "0.0.22";
57
+ export const version = "0.0.23";
58
58
  export const description = "Agent harness for AI providers, agents, sessions, and tools.";
59
59
  //# sourceMappingURL=index.js.map
@@ -1,12 +1,12 @@
1
1
  # 0.1.0 / 1.0 Readiness Gates
2
2
 
3
- Status: **0.0.22** is the current release line (Phase 5 third-party behavior integrations); **1.0** readiness remains operator-gated, not automatic.
3
+ Status: **0.0.23** is the current release line (Phase 6 production enterprise PostgreSQL state adapters); **1.0** readiness remains operator-gated, not automatic.
4
4
 
5
5
  This page distills runnable readiness gates into one command-per-gate table. The
6
6
  **Last evidence** column records the 2026-07-26 **0.0.16** baseline snapshot
7
7
  (Phase 11, Node v24.18.0, Linux x86_64). Treat it as historical floor evidence,
8
8
  not the current release tag. Re-run each gate on the target release tree before
9
- cutting 0.0.19 / 1.0. The decision to cut 1.0 stays with the operator after
9
+ cutting 0.0.23 / 1.0. The decision to cut 1.0 stays with the operator after
10
10
  operator-gated legs run in a protected environment and Phase 12 demand evidence
11
11
  exists.
12
12
 
@@ -14,14 +14,15 @@ Evidence trail: [`docs/review-coverage-2026-07-26-phase-11.md`](./review-coverag
14
14
  (addenda 0–9), [`docs/release-and-install.md`](./release-and-install.md),
15
15
  [`docs/migration.md`](./migration.md), [`docs/performance.md`](./performance.md).
16
16
 
17
- ## Current line (0.0.21)
17
+ ## Current line (0.0.23)
18
18
 
19
19
  | Item | Status |
20
20
  |---|---|
21
- | Published graph | **44** publishable manifests at **0.0.21** (`docs/release-and-install.md`) |
22
- | Phase 4 coding-tool gaps | `outputMode`, bounded `glob`, optional read-before-write, bounded `delete`/`move`, approval/sandbox wiring |
23
- | Docs tripwires | `node --test dist/__tests__/docs.test.js` — migration section `0.0.20 → 0.0.21 coding-tool capability gaps` documents intentional breaks |
24
- | Readiness table below | **0.0.16 measured values** — refresh evidence columns when 1.0 RC gates are recorded |
21
+ | Published graph | **47** publishable manifests at **0.0.23** (`docs/release-and-install.md`) |
22
+ | Phase 6 enterprise state | `@arnilo/prism-enterprise-postgres`: policy/evaluation/work-idempotency/router state, checksummed migration, explicit cleanup |
23
+ | Docs tripwires | `node --test dist/__tests__/docs.test.js` — migration section `0.0.22 → 0.0.23 production enterprise state adapters` documents idempotency and async-router changes |
24
+ | Protected database evidence | `PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres`; benchmark evidence fixes 10 scenarios, 14 index plans, and 50/100 ms p95 ceilings |
25
+ | Readiness table below | **0.0.16 measured values** remain historical network-free baseline; 0.0.23 database evidence is recorded separately |
25
26
 
26
27
  ## Gate table
27
28
 
@@ -39,7 +40,7 @@ Evidence trail: [`docs/review-coverage-2026-07-26-phase-11.md`](./review-coverag
39
40
  | Whitespace hygiene | `git diff --check` | clean | CI |
40
41
  | Publish order + tarball validation | `node scripts/release.mjs publish --version <v> --dry-run --allow-dirty --allow-untagged` | 44/44 packages `dry-run`, deterministic dependency order, no failures | Operator (dry-run), CI |
41
42
  | Node 20 compatibility | CI `node20-compat` (build + public-import smoke) | all 21 root exports import cleanly on Node 20.20.2 | CI |
42
- | PostgreSQL suite | `npm run test:postgres` | **operator-gated** (requires live PostgreSQL) | Operator |
43
+ | PostgreSQL suite | `PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres` | 57 checks including enterprise migration/restart/contention/cleanup; operator-gated | Operator |
43
44
  | Keychain / live-provider suites | `npm run test:live` (protected) | **operator-gated** (requires credentials) | Operator |
44
45
  | SAST | GitHub CodeQL | **operator-gated** (runs in CI workflow) | CI |
45
46
  | Signed, provenance publication | `npm run release:publish` (clean tagged tree, OIDC) | **operator-gated** (see "Remaining for 1.0") | Operator |
@@ -79,7 +80,7 @@ Deterministic budgets (CI gate, `scripts/budget-gate.test.mjs`):
79
80
  | Root unpacked bytes | 2,043,402 | +5% | 2.1 MB (within) |
80
81
  | Root file count | 270 | +5% | 270 |
81
82
  | Cold-startup import | 38 ms | ceiling 250 ms | ~38 ms |
82
- | Aggregate packed (44 manifests, reference only) | 1,217,694 | +10% | not gated in fast test |
83
+ | Aggregate packed (47 manifests, reference only) | 1,217,694 | +10% | remeasure for the 0.0.23 graph before release |
83
84
 
84
85
  Benchmark medians (on-demand evidence, `scripts/benchmark-0.0.16.mjs`, ±25%):
85
86
 
@@ -8,6 +8,8 @@ Prism itself does not ship a production database adapter. The built-in `SessionS
8
8
 
9
9
  Plan 056 Task 1 adds dialect-neutral shared primitives under `@arnilo/prism/testing/persistence-schema`, `@arnilo/prism/testing/session-store-conformance`, and `@arnilo/prism/testing/run-ledger-conformance`. Task 2 ships `@arnilo/prism-session-store-sqlite` (see [SQLite persistence](sqlite-persistence.md)); Task 3 ships `@arnilo/prism-session-store-postgres` (see [PostgreSQL persistence](postgres-persistence.md)). Both implement dialect-local SQL against the shared model; Prism core still ships no ORM, driver, or migration runner.
10
10
 
11
+ Release 0.0.23 additionally ships [`@arnilo/prism-enterprise-postgres`](enterprise-postgres-state.md), a separate PostgreSQL composition for policy decisions, evaluations, work-mutation claims, and model-router state. It is not a `ProductionPersistenceStore` replacement and does not store sessions/runs. Its fixed `prism_enterprise_migrations` history is independent of `prism_migrations`; hosts may use both compositions against the same validated schema.
12
+
11
13
  ## When to use it
12
14
 
13
15
  Use these contracts when you write a database-backed `SessionStore` or a separate persistence adapter that needs:
@@ -461,6 +463,7 @@ const dbStore: ProductionPersistenceStore = {
461
463
  ## Related APIs
462
464
 
463
465
  - [Session store conformance](session-store-conformance.md): executable adapter baseline for append/idempotency/conflict/branch invariants.
466
+ - [Enterprise PostgreSQL state](enterprise-postgres-state.md): durable governance and connector/router state outside the session/run contract.
464
467
  - [Migration guide](migration.md): before/after shapes for moving from in-memory/JSONL to this contract.
465
468
  - [Performance limits](performance.md): production sizing, subscriber queues, branch-read limits, and database adapter guidance.
466
469
  - [Session stores and branching](session-stores-and-branching.md): `SessionStore`, `SessionEntry`, branch helpers, and runtime branch semantics.
@@ -0,0 +1,173 @@
1
+ # Enterprise PostgreSQL state
2
+
3
+ ## What it does
4
+
5
+ `@arnilo/prism-enterprise-postgres` is one optional PostgreSQL composition for the four existing enterprise state seams:
6
+
7
+ | State | Composition property | Durable behavior |
8
+ | --- | --- | --- |
9
+ | Policy decisions | `policy` | Append-only `PolicyDecisionStore` with owner-bound cursor pages. |
10
+ | Evaluations | `evaluations` | `EvaluationStore` append/query records with exact owner pages. |
11
+ | Work mutations | `workIdempotency` | Atomic claim/CAS lifecycle for connector effects. |
12
+ | Model routing | `modelRouter` | Shared rate, budget, and circuit state for router replicas. |
13
+
14
+ `createPostgresEnterpriseState()` opens a host-supplied or adapter-owned `pg` pool, verifies/applies the fixed checksum-protected `001_enterprise_state` migration, and returns those stores plus explicit cleanup and close operations. Importing it performs no I/O. It is separate from session/run persistence in [`@arnilo/prism-session-store-postgres`](postgres-persistence.md).
15
+
16
+ ## When to use it
17
+
18
+ Use it for a multi-process production host that needs policy/evaluation audit state, connector mutation reconciliation, or model-router limits to survive restart and coordinate across replicas.
19
+
20
+ Use memory/file stores only for tests, demos, or a deliberately single-process host. They do not provide PostgreSQL cross-replica coordination. This package is optional; it adds no database driver to `@arnilo/prism` core.
21
+
22
+ ## Inputs / request
23
+
24
+ ```ts
25
+ import { createPostgresEnterpriseState } from "@arnilo/prism-enterprise-postgres";
26
+ import { Pool } from "pg";
27
+
28
+ const pool = new Pool({
29
+ connectionString: process.env.DATABASE_URL,
30
+ max: 10,
31
+ ssl: { rejectUnauthorized: true },
32
+ });
33
+
34
+ const state = await createPostgresEnterpriseState({ pool, schema: "prism" });
35
+ ```
36
+
37
+ | Input | Meaning |
38
+ | --- | --- |
39
+ | `pool` | Existing `pg` pool. Exactly one of `pool` and `connectionString` is required; caller retains pool lifecycle. |
40
+ | `connectionString` | Creates an adapter-owned pool when `pool` is omitted. |
41
+ | `schema` | Validated identifier; defaults to `"prism"`. It is never interpolated as an unchecked SQL identifier. |
42
+ | `poolMax` / `poolConfig` | Adapter-owned pool settings; maximum defaults to 10 and is capped at 100. Put TLS in `poolConfig.ssl`. |
43
+ | `skipMigrations` | Isolated-test escape hatch only. Production opens verify/apply the fixed migration before request traffic. |
44
+ | `cleanup({ tenantId, accountId?, userId?, principalId, limit?, signal? })` | Explicit exact-owner cleanup; default 100 and hard maximum 500 rows. No worker starts automatically. |
45
+
46
+ Every durable policy/work/router action starts from an active host-verified `AgentIdentity`. Evaluation records/queries must be projected by the host from that verified ownership. PostgreSQL rejects missing tenant scope; optional account/user values are normalized and matched exactly, including absence.
47
+
48
+ ## Outputs / response / events
49
+
50
+ `PostgresEnterpriseState` has this public shape:
51
+
52
+ ```ts
53
+ interface PostgresEnterpriseState {
54
+ readonly policy: PolicyDecisionStore;
55
+ readonly evaluations: EvaluationStore;
56
+ readonly workIdempotency: IdempotencyStore;
57
+ readonly modelRouter: ModelRouterStateStore;
58
+ cleanup(input: EnterpriseStateCleanupInput): Promise<EnterpriseStateCleanupResult>;
59
+ close(): Promise<void>;
60
+ }
61
+ ```
62
+
63
+ `close()` ends only a pool created from `connectionString`; it leaves a caller-owned pool open. `cleanup()` returns `{ removed, transitioned }`. It transitions expired work claims to `unknown` and abandoned circuit probes back to cooldown before deleting expired/idle retained state.
64
+
65
+ Work mutations expose six observable states: **absent**, `in_progress`, `completed`, `failed_retryable`, `failed_terminal`, and `unknown`. `begin()` atomically returns `acquired` or the existing record; `complete`/`fail`/`markUnknown` use claim-token plus version compare-and-swap. `unknown` requires an operator/connector-specific `resolveUnknown` decision. It is not automatically replayed and it does **not** claim exactly-once external effects.
66
+
67
+ Model-router state is asynchronous and owner/principal/provider/model scoped. Supplying it to `createModelRouter({ stateStore })` requires awaited `resolve`, `recordUsage`, and `recordOutcome` calls with verified identity. The legacy synchronous `providerSource` facade throws `ERR_PRISM_MODEL_ROUTER_ASYNC_STATE` when durable state is configured.
68
+
69
+ ## Request/response example
70
+
71
+ ```json
72
+ {
73
+ "schema": "prism",
74
+ "cleanup": {
75
+ "tenantId": "tenant-1",
76
+ "userId": "user-7",
77
+ "principalId": "agent-9",
78
+ "limit": 100
79
+ },
80
+ "result": { "removed": 12, "transitioned": 1 }
81
+ }
82
+ ```
83
+
84
+ A migration creates `prism_policy_decisions`, `prism_evaluations`, `prism_work_idempotency`, three `prism_model_router_*` tables, and its separate `prism_enterprise_migrations` history. Startup serializes per-schema setup with an advisory transaction lock and rejects checksum or catalog drift rather than silently repairing it.
85
+
86
+ ## Implementation example
87
+
88
+ ```ts
89
+ import type { AgentIdentity } from "@arnilo/prism";
90
+ import { createPostgresEnterpriseState, type PostgresEnterpriseState } from "@arnilo/prism-enterprise-postgres";
91
+
92
+ const identity: AgentIdentity = {
93
+ tenantId: "tenant-1",
94
+ userId: "user-7",
95
+ principal: { kind: "agent", id: "agent-9" },
96
+ scopes: ["enterprise:write"],
97
+ verified: true,
98
+ issuedAt: "2026-08-03T00:00:00.000Z",
99
+ };
100
+
101
+ export async function recordEnterpriseState(state: PostgresEnterpriseState) {
102
+ const now = new Date().toISOString();
103
+ await state.policy.append({
104
+ id: "policy-1",
105
+ policyId: "mail",
106
+ policyVersion: "2026-08-03",
107
+ outcome: "approval",
108
+ identity,
109
+ target: { kind: "draft", id: "draft-1" },
110
+ evidenceRefs: ["rule:external-recipient"],
111
+ createdAt: now,
112
+ });
113
+ await state.evaluations.append({
114
+ id: "eval-1",
115
+ scorerId: "quality",
116
+ status: "scored",
117
+ score: 1,
118
+ sampled: true,
119
+ tenantId: identity.tenantId,
120
+ userId: identity.userId,
121
+ createdAt: now,
122
+ });
123
+
124
+ const claim = await state.workIdempotency.begin({ identity, key: "mail-send-1", op: "mail.send" });
125
+ if (claim.outcome === "acquired") {
126
+ // Run approved connector effect outside PostgreSQL transaction.
127
+ await state.workIdempotency.complete({
128
+ identity,
129
+ key: "mail-send-1",
130
+ op: "mail.send",
131
+ claimToken: claim.record.claimToken!,
132
+ expectedVersion: claim.record.version,
133
+ result: { draftId: "draft-1", resourceId: "message-1" },
134
+ });
135
+ }
136
+
137
+ await state.modelRouter.addUsage({
138
+ key: { tenantId: "tenant-1", userId: "user-7", principalId: "agent-9", provider: "openai", model: "gpt-4.1-mini" },
139
+ tokens: 100,
140
+ windowMs: 86_400_000,
141
+ now: Date.now(),
142
+ });
143
+ return state.cleanup({ tenantId: "tenant-1", userId: "user-7", principalId: "agent-9" });
144
+ }
145
+
146
+ // `state` comes from `await createPostgresEnterpriseState({ pool, schema: "prism" })`.
147
+ ```
148
+
149
+ ## Extension and configuration notes
150
+
151
+ - `createModelRouter({ resolver, stateStore: state.modelRouter })` keeps allow-list, residency, fallback, and diagnostics behavior in `@arnilo/prism-model-router`; this package only supplies durable state.
152
+ - Policy/evaluation/query public contracts stay in their owning packages. This package exports only `createPostgresEnterpriseState`, its options/result types, and `EnterprisePostgresError`; it has no SQL, DDL, codec, queryable, or migration subpath.
153
+ - The fixed schema has no generic key/value table and no background cleanup scheduler. Schedule `state.cleanup()` from an authorized host job, size its bounded batch for the deployment, and monitor unknown work rows for reconciliation. Run protected integration checks with `PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres`; the command rejects an absent URL instead of silently skipping database coverage.
154
+ - Request-path state SQL uses `SELECT`, `INSERT`, `UPDATE`, and `DELETE` on the six state tables. The open/migration lifecycle additionally needs schema/catalog/advisory-lock and DDL permissions. Use a deployment migration principal for that lifecycle and a least-privilege request role for request traffic; this release intentionally does not ship a migration CLI or worker.
155
+
156
+ ## Security and performance notes
157
+
158
+ - Configure TLS, database credentials, pool timeouts, backups, restore drills, retention schedule, and database role grants in the host. Never put connection strings, tokens, prompts, raw connector responses, unrestricted payloads, or provider credentials in records.
159
+ - Every value is a bound parameter. Schema identifiers are validated; table/index names are fixed. Cursors embed and recheck ownership, so a foreign tenant cannot reuse a page cursor.
160
+ - Policy records cap at 64 KiB; evaluations at 64 KiB; work rows at 8 KiB; router material at 512 bytes. JSON rejects prototype-pollution keys, non-finite values, excess depth/properties, and over-size material.
161
+ - PostgreSQL transaction SQLSTATE `40001`/`40P01` retries whole safe transactions up to three times. Connector effects remain outside those transactions and ambiguous errors become `unknown` rather than being retried.
162
+ - Recorded `postgres:16-alpine` evidence (Node 24.18.0/Linux x64, 10 tenants × 10 principals × 1,000 policy/evaluation rows, 10,000 router keys, 16 clients) stayed below 50 ms p95 for point operations and 100 ms for cursor/cleanup pages. The highest recorded p95 was router-circuit contention at 28.410 ms. Fourteen representative `EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)` shapes used named indexes with no sequential scans. These are recorded comparison evidence, not hardware-independent guarantees.
163
+
164
+ ## Related APIs
165
+
166
+ - [Policy and audit](policy-and-audit.md): policy record and WORM export semantics.
167
+ - [Evaluations](evaluations.md): scorer/evaluation record lifecycle.
168
+ - [Work tools](work-tools.md): draft approval, unknown outcomes, and connector boundaries.
169
+ - [Model routing](model-routing.md): durable asynchronous router migration.
170
+ - [PostgreSQL persistence](postgres-persistence.md): sessions/runs/checkpoints/leases adapter.
171
+ - [Database persistence](database-persistence.md): host retention and persistence guidance.
172
+ - [Host security](host-security.md): database ownership, TLS, secrets, and role boundaries.
173
+ - [Migration guide](migration.md): 0.0.22 → 0.0.23 upgrade steps.
@@ -146,6 +146,18 @@ Release 0.0.9 ships curated network-free adversarial fixtures in package tests:
146
146
 
147
147
  Fixtures reuse `@arnilo/prism-evals` (`defineDataset` / `defineScorer` / `scoreRun` / `assertEvaluationThreshold` / `serializeEvaluationReport`). Optional SWE-bench-compatible or live-browser harnesses remain host adapters — they are not default dependencies or quality claims. Protected real Docker/Playwright gates stay env-gated (`PRISM_TEST_DOCKER_SANDBOX`, `PRISM_LIVE_PLAYWRIGHT`) and never enter `sdk:ready`.
148
148
 
149
+ ## PostgreSQL enterprise state (0.0.23)
150
+
151
+ `createPostgresEnterpriseState({ pool, schema }).evaluations` implements this package's existing `EvaluationStore`. The host creates an `EvaluationRecord` from verified ownership before append; every PostgreSQL query requires tenant scope, uses exact normalized account/user matching, and returns owner-bound opaque cursor pages. It is durable across reopen and supports the existing id/scorer/session/run/trace/dataset/item/experiment/status filters.
152
+
153
+ ```ts
154
+ const state = await createPostgresEnterpriseState({ pool });
155
+ await state.evaluations.append(record); // record is already host-owned and redacted
156
+ const page = await state.evaluations.query({ tenantId: "t1", userId: "u1", status: "scored", limit: 100 });
157
+ ```
158
+
159
+ Memory evaluation storage remains suitable for development and deterministic tests; it is not cross-replica production storage. PostgreSQL bounds each evaluation row to 64 KiB (reason/error 8 KiB each and metadata 32 KiB).
160
+
149
161
  ## Related APIs
150
162
 
151
163
  - [Agent/session runtime](agent-session-runtime.md): `AgentRunResult` and `session.run()`
@@ -153,4 +165,5 @@ Fixtures reuse `@arnilo/prism-evals` (`defineDataset` / `defineScorer` / `scoreR
153
165
  - [Observability](observability.md): use `onTraceReference` or bounded `traceId(runId)` to supply `ScoreRunOptions.traceId`; evaluation telemetry emits no reason/explanation content
154
166
  - [Coding agent tools](coding-agent-tools.md) / [Browser automation](browser-automation.md) / [Workflows](workflows.md): network-free coding-task composition at `examples/durable-coding-workflow.ts`; adversarial coding/browser eval example at `examples/coding-browser-evaluation.ts`
155
167
  - [Performance limits](performance.md): `scripts/benchmark-0.0.11.mjs` search/budget evidence, `scripts/benchmark-0.0.10.mjs` workspace-mode evidence, and `scripts/benchmark-0.0.9.mjs` coding/browser evidence fields
168
+ - [Enterprise PostgreSQL state](enterprise-postgres-state.md): durable owner-scoped evaluation storage.
156
169
  - [Release and install](release-and-install.md): optional package install and protected sandbox-browser workflow
@@ -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
@@ -152,6 +153,7 @@ Wire those values where they matter: provider adapters receive the resolved cred
152
153
  - 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
154
  - Permission checks happen before tool validation and before `tool.execute()`. Middleware cannot grant permission by renaming a tool.
154
155
  - 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.
156
+ - `@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
157
  - Prefer `createExtensionKernel({ loadPolicy })` allow-list/signature checks before loading third-party extension packages.
156
158
  - Provider-owned auth/content/session/cache/security headers win over caller headers in adapters that merge headers.
157
159
  - Security checks are bounded explicit calls on the active path. Prism adds no hidden global middleware, background workers, watchers, network calls, or filesystem scans.
@@ -213,4 +215,5 @@ PostgreSQL TLS/network policy, MCP endpoint trust/credentials and egress policy
213
215
  - [Session stores](session-stores.md): durable session store contract and secret/persistence boundaries.
214
216
  - [Runs and usage ledger](runs-and-usage.md): redacted run/event/tool/usage ledger records.
215
217
  - [Database persistence](database-persistence.md): production schema, ownership, indexes, retention, and adapter readiness checklist.
218
+ - [Enterprise PostgreSQL state](enterprise-postgres-state.md): durable policy/evaluation/work/router state and database-role boundary.
216
219
  - [Provider caching](provider-caching.md): cache keys and provider-owned header safety rules.
package/docs/index.md CHANGED
@@ -7,8 +7,8 @@ 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.
@@ -17,7 +17,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
17
17
  - [Guardrails](guardrails.md): typed fail-closed input/output/tool checks with buffered provider output and redacted decision records.
18
18
  - [Agent events](agent-events.md): redacted lifecycle stream used by UIs, ledgers, and metadata-only parented telemetry; 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
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.
23
23
  - [Structured output](structured-output.md): the `Artifact*` seam plus provider-native `StructuredOutputOptions` / `structuredOutputMode` for capable models.
@@ -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 state, exact ownership, checksummed migration, and explicit cleanup.
38
+ - [Migration guide](migration.md): **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
 
@@ -67,7 +68,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
67
68
  - [Tool validator JSON Schema package](../packages/tool-validator-json-schema/README.md): optional `@arnilo/prism-tool-validator-json-schema` adapter for `tool.parameters`.
68
69
  - [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
70
  - [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).
71
+ - [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
72
  - [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
73
  - [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
74
  - [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.
@@ -101,7 +102,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
101
102
  - [Workflow/TUI scope](workflow-tui-primitives.md): records why 0.0.5 ships workflow APIs/RPC control but no interactive terminal UI.
102
103
 
103
104
  ## 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.
105
+ - [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
106
  - [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
107
  - [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
108
  - [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 +115,15 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
114
115
  - [Compaction conformance](compaction-conformance.md): assert any `CompactionStrategy` returns a non-empty redacted summary and observes abort from `@arnilo/prism/testing/compaction-conformance`.
115
116
  - [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
117
  - [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).
118
+ - `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/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
119
 
119
120
  ## Third-party integrations
120
121
  - [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
122
  - [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
123
 
123
124
  ## 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.
125
+ - [Release and install](release-and-install.md): current **0.0.23** 47-package graph (Phase 6 enterprise PostgreSQL state adapters; plan 006), 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.
126
+ - [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
127
  - [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
128
  - [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
129
  - [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/migration.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Migration guide
2
2
 
3
+ ## 0.0.22 → 0.0.23 production enterprise state adapters (intentional pre-1.0 contract changes)
4
+
5
+ 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.
6
+
7
+ 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.
8
+ 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.
9
+ 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.
10
+ 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.
11
+ 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.
12
+ 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.
13
+
14
+ ```ts
15
+ const state = await createPostgresEnterpriseState({ pool, schema: "prism" });
16
+ const router = createModelRouter({ resolver, stateStore: state.modelRouter });
17
+ const selected = await router.resolve({ model, identity });
18
+ await router.recordOutcome({ identity, provider: selected.provider.id, model: selected.model.model, success: true, circuitProbeToken: selected.circuitProbeToken });
19
+ await state.close(); // caller-owned pool stays open
20
+ ```
21
+
3
22
  ## 0.0.21 → 0.0.22 third-party behavior integrations (additive)
4
23
 
5
24
  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,20 @@ 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.23 enterprise PostgreSQL evidence
10
+
11
+ `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.
12
+
13
+ | Scenario | Recorded p95 ms | Ceiling |
14
+ | --- | ---: | ---: |
15
+ | Policy append / query | 0.747 / 1.479 | 50 / 100 |
16
+ | Evaluation append / query | 0.698 / 0.963 | 50 / 100 |
17
+ | Work claim+complete / contention | 1.892 / 4.162 | 50 / 50 |
18
+ | Router rate / budget / circuit contention | 12.011 / 6.715 / 28.410 | 50 / 50 / 50 |
19
+ | Explicit cleanup batch | 2.981 | 100 |
20
+
21
+ 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.
22
+
9
23
  ## Release 0.0.16 performance budgets and artifact diet
10
24
 
11
25
  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
 
@@ -141,4 +141,5 @@ PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres --workspace @arnil
141
141
  - [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): package matrix and threat model.
142
142
  - [Workflows](workflows.md): adapt `persistence.checkpoints` and pass `persistence.leases` to `createWorkflowCoordinator()` and `createWorkflowSchedules()` for durable background execution and schedules.
143
143
  - [Working and semantic memory](working-and-semantic-memory.md): optional `@arnilo/prism-memory` PostgreSQL/pgvector working + semantic stores (separate from session/run persistence).
144
+ - [Enterprise PostgreSQL state](enterprise-postgres-state.md): separate durable policy/evaluation/work/router stores and cleanup.
144
145
  - [Migration guide](migration.md): moving from JSONL/in-memory to database-backed persistence.
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- Prism is published as one core package, thirty-nine first-party capability packages, and six pure-manifest family/profile packages (**46** publishable manifests total). This page describes how they are packed, what each tarball contains, how to install them, the required `@arnilo/prism` peer dependency, the release workflow, and the offline test budget. The measurable 1.0 readiness gates (command-per-gate) live in [`0.1.0-readiness.md`](./0.1.0-readiness.md).
5
+ Prism is published as one core package, forty first-party capability packages, and six pure-manifest family/profile packages (**47** publishable manifests total). This page describes how they are packed, what each tarball contains, how to install them, the required `@arnilo/prism` peer dependency, the release workflow, and the offline test budget. The measurable 1.0 readiness gates (command-per-gate) live in [`0.1.0-readiness.md`](./0.1.0-readiness.md).
6
6
 
7
7
  Core package:
8
8
 
9
9
  - `@arnilo/prism` — the runtime, contracts, registries, streaming events, CLI (including `prism init`), and the `/docs` hub. `files`: `dist` (with `!dist/__tests__` and `!dist/**/*.map` negations), `docs`, `templates`, `CHANGELOG.md`. `bin`: `prism` -> `dist/cli.js`. `sideEffects`: `["dist/cli.js"]`.
10
10
 
11
- First-party workspace packages (each has non-optional `@arnilo/prism@0.0.22` peer and `sideEffects: false`; RAG also peers on memory, and server also peers on workflows):
11
+ First-party workspace packages (each has non-optional `@arnilo/prism@0.0.23` peer and `sideEffects: false`; RAG also peers on memory, and server also peers on workflows):
12
12
 
13
13
  - `@arnilo/prism-provider-anthropic`, `@arnilo/prism-provider-google`, `@arnilo/prism-provider-openai`, `@arnilo/prism-provider-openrouter`, `@arnilo/prism-provider-kimi`, `@arnilo/prism-provider-zai`, `@arnilo/prism-provider-opencode-go`, `@arnilo/prism-provider-neuralwatt` — provider adapters.
14
14
  - `@arnilo/prism-provider-azure`, `@arnilo/prism-provider-bedrock`, `@arnilo/prism-provider-vertex` — optional enterprise-cloud adapters (Entra/IAM/ADC; separate from consumer Anthropic/Google).
@@ -22,7 +22,8 @@ First-party workspace packages (each has non-optional `@arnilo/prism@0.0.22` pee
22
22
  - `@arnilo/prism-tool-validator-json-schema` — bounded JSON Schema tool argument validation.
23
23
  - `@arnilo/prism-mcp` — MCP transport/client bridge plus explicit authorized Prism tool/command server exposure.
24
24
  - `@arnilo/prism-coding-agent` / `@arnilo/prism-coding-security` — optional host shell/filesystem tools plus approval, containment, and sandbox policy.
25
- - `@arnilo/prism-session-store-sqlite` / `@arnilo/prism-session-store-postgres` — production persistence, checkpoints, and leases.
25
+ - `@arnilo/prism-session-store-sqlite` / `@arnilo/prism-session-store-postgres` — production session/run persistence, checkpoints, and leases.
26
+ - `@arnilo/prism-enterprise-postgres` — optional PostgreSQL policy/evaluation/work-idempotency/model-router composition; separate checksum migration and explicit cleanup.
26
27
  - `@arnilo/prism-credentials-node` — encrypted-file and keychain credential storage.
27
28
  - `@arnilo/prism-workflows` — typed bounded DAG orchestration with durable approval, schedules/background runs, composition/state/replay, and multi-process coordination.
28
29
  - `@arnilo/prism-evals` — optional deterministic scorers, immutable datasets, and bounded batch experiments over `AgentRunResult`.
@@ -38,7 +39,7 @@ First-party workspace packages (each has non-optional `@arnilo/prism@0.0.22` pee
38
39
 
39
40
  ### 0.0.12 AG-UI package boundary
40
41
 
41
- `@arnilo/prism-ag-ui` is a publishable optional code package with root AG-UI exports and stable `./acp` sibling, peer `@arnilo/prism@0.0.22`, pinned `@ag-ui/core@0.0.57` / `@agentclientprotocol/sdk@1.3.0`, and no import-time network/listener/run. It is included by `@arnilo/prism-all` only—not `@arnilo/prism-code` or `@arnilo/prism-sdk`—so coding and SDK profiles stay free of UI protocol dependencies.
42
+ `@arnilo/prism-ag-ui` is a publishable optional code package with root AG-UI exports and stable `./acp` sibling, peer `@arnilo/prism@0.0.23`, pinned `@ag-ui/core@0.0.57` / `@agentclientprotocol/sdk@1.3.0`, and no import-time network/listener/run. It is included by `@arnilo/prism-all` only—not `@arnilo/prism-code` or `@arnilo/prism-sdk`—so coding and SDK profiles stay free of UI protocol dependencies.
42
43
 
43
44
  Family/profile packages (pure manifests, no code or `dist`; ship `README.md` and `CHANGELOG.md`; use exact hard `dependencies`):
44
45
 
@@ -48,7 +49,7 @@ Family/profile packages (pure manifests, no code or `dist`; ship `README.md` and
48
49
  - `@arnilo/prism-code` — base + coding-agent + coding-security + MCP; providers and persistence remain explicit choices.
49
50
  - `@arnilo/prism-sdk` — base + workflows + MCP + Node credentials + OpenTelemetry; providers and persistence remain explicit choices.
50
51
  - `@arnilo/prism-evals` remains optional and network-free by default; model judges are host callbacks and live credentialed gates run separately. `examples/evaluation-gate.ts` demonstrates non-zero threshold gating.
51
- - `@arnilo/prism-all` — every first-party package: code + SDK + providers + persistence + evals + memory/RAG + server + supervisor + web tools + browser + Phase 8 optional policy/router/enterprise providers/work-tools. **0.0.13** enrolls `@arnilo/prism-policy`, `@arnilo/prism-model-router`, `@arnilo/prism-provider-azure`, `@arnilo/prism-provider-bedrock`, `@arnilo/prism-provider-vertex`, and `@arnilo/prism-work-tools` in the umbrella only. Installation alone activates no network/listener, telemetry, database, memory, evaluation, delegation, MCP, shell, filesystem, or browser capability.
52
+ - `@arnilo/prism-all` — every first-party package: code + SDK + providers + session and enterprise PostgreSQL persistence + evals + memory/RAG + server + supervisor + web tools + browser + optional policy/router/enterprise providers/work-tools. Installation activates nothing. **0.0.13** enrolls `@arnilo/prism-policy`, `@arnilo/prism-model-router`, `@arnilo/prism-provider-azure`, `@arnilo/prism-provider-bedrock`, `@arnilo/prism-provider-vertex`, and `@arnilo/prism-work-tools` in the umbrella only. Installation alone activates no network/listener, telemetry, database, memory, evaluation, delegation, MCP, shell, filesystem, or browser capability.
52
53
 
53
54
  Profile footprint snapshot (Node 24/npm 11, lockfile graph, 2026-07-19): `base` reaches 6 first-party packages and one external dependency root (Ajv); `code` reaches 10 and three (Ajv, MCP SDK, diff); `sdk` reaches 11 and three (Ajv, MCP SDK, keyring); `all` reaches **41** first-party manifests after Phase 8 optional packages ship (graph bump Task 10); AG-UI adds only its protocol SDK dependencies while native database drivers remain all-profile-only. Native database drivers stay out of base/code/sdk; both appear only in all.
54
55
 
@@ -80,9 +81,10 @@ Consumers install the core package for the runtime and add first-party packages
80
81
  | Run the default (network-free) test suite | `npm test` |
81
82
  | Dry-run pack core + every package | `npm run pack:dry-run` |
82
83
  | Local mirror of the release verify gate | `npm run release:dry-run` |
83
- | Validate clean tag/version/ranges and reject registry collisions | `npm run release:check -- --version 0.0.22` |
84
- | Preview deterministic publish order | `npm run release:publish -- --version 0.0.22 --dry-run --allow-dirty --allow-untagged` |
85
- | Resume interrupted tagged publication | `npm run release:publish -- --version 0.0.22 --resume --report release-artifacts/publish-report.json` |
84
+ | Validate clean tag/version/ranges and reject registry collisions | `npm run release:check -- --version 0.0.23` |
85
+ | Preview deterministic publish order | `npm run release:publish -- --version 0.0.23 --dry-run --allow-dirty --allow-untagged` |
86
+ | Resume interrupted tagged publication | `npm run release:publish -- --version 0.0.23 --resume --report release-artifacts/publish-report.json` |
87
+ | Protected PostgreSQL enterprise suite | `PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres` |
86
88
  | Full SDK readiness gate (typecheck + offline tests + pack) | `npm run sdk:ready` |
87
89
 
88
90
  Public core import specifiers (from the root `exports` map):
@@ -119,7 +121,7 @@ A packed tarball contains only public compiled output and release files:
119
121
  - Code packages ship `README.md`, `LICENSE`, and `CHANGELOG.md`; family/profile packages ship `README.md` and `CHANGELOG.md`.
120
122
  - The core tarball additionally ships the full `docs/` directory (the docs hub) and `templates/init/` used by `prism init`.
121
123
  - `dist/cli.js` and the `bin` link in core.
122
- - **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.0.22.tgz`; first-party packages produce `arnilo-prism-provider-<name>-0.0.22.tgz` / `arnilo-prism-compaction-<name>-0.0.22.tgz` / `arnilo-prism-coding-agent-0.0.22.tgz`; family/profile packages produce `arnilo-prism-{providers,compaction,base,code,sdk,all}-0.0.22.tgz`. The CLI bin name `prism` is unaffected by the package name (`npx prism` still works; npm allows the bin field to differ from the package name).
124
+ - **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.0.23.tgz`; first-party packages produce `arnilo-prism-provider-<name>-0.0.23.tgz` / `arnilo-prism-compaction-<name>-0.0.23.tgz` / `arnilo-prism-coding-agent-0.0.23.tgz`; family/profile packages produce `arnilo-prism-{providers,compaction,base,code,sdk,all}-0.0.23.tgz`. The CLI bin name `prism` is unaffected by the package name (`npx prism` still works; npm allows the bin field to differ from the package name).
123
125
 
124
126
  Excluded from every tarball by `files` negation:
125
127
 
@@ -138,9 +140,9 @@ Excluded from every tarball by `files` negation:
138
140
  "name": "host-app",
139
141
  "type": "module",
140
142
  "dependencies": {
141
- "@arnilo/prism": "0.0.22",
142
- "@arnilo/prism-provider-openai": "0.0.22",
143
- "@arnilo/prism-compaction-observational-memory": "0.0.22"
143
+ "@arnilo/prism": "0.0.23",
144
+ "@arnilo/prism-enterprise-postgres": "0.0.23",
145
+ "@arnilo/prism-provider-openai": "0.0.23"
144
146
  }
145
147
  }
146
148
  ```
@@ -183,11 +185,11 @@ For SDK readiness, run the same one-command gate directly. It composes existing
183
185
  npm run sdk:ready
184
186
  ```
185
187
 
186
- Release publication derives all **46** manifests from the workspace once, validates exact `0.0.22` manifest/lockfile/internal ranges, then uses deterministic dependency order. `release:check` requires a clean commit tagged `v0.0.22` and rejects any existing registry version. `release:publish --resume` skips only registry versions whose internal dependency fingerprint matches the local manifest; conflicting versions fail closed. Each attempted package is written immediately to the JSON report, so a failed job can rerun safely. `--dry-run` performs registry availability checks and invokes `npm publish --dry-run` with explicit public access, provenance, and `latest` tag, but does not publish.
188
+ Release publication derives all **47** manifests from the workspace once, validates exact `0.0.23` manifest/lockfile/internal ranges, then uses deterministic dependency order. `release:check` requires a clean commit tagged `v0.0.23` and rejects any existing registry version. `release:publish --resume` skips only registry versions whose internal dependency fingerprint matches the local manifest; conflicting versions fail closed. Each attempted package is written immediately to the JSON report, so a failed job can rerun safely. `--dry-run` performs registry availability checks and invokes `npm publish --dry-run` with explicit public access, provenance, and `latest` tag, but does not publish.
187
189
 
188
190
  ```bash
189
- npm run release:check -- --version 0.0.22
190
- npm run release:publish -- --version 0.0.22 --dry-run --allow-dirty --allow-untagged
191
+ npm run release:check -- --version 0.0.23
192
+ npm run release:publish -- --version 0.0.23 --dry-run --allow-dirty --allow-untagged
191
193
  ```
192
194
 
193
195
  `--allow-dirty` and `--allow-untagged` exist only for local preview; real publication and CI never pass them. npm registry calls occur only in these release preflight/publication commands, never build/test/package discovery.
@@ -198,6 +200,31 @@ Optional live smoke tests stay separate from SDK readiness because they require
198
200
  PRISM_LIVE_PROVIDER_TESTS=1 npm run test --workspaces --if-present
199
201
  ```
200
202
 
203
+ ### 0.0.23 publish handoff
204
+
205
+ **Decision: GO after protected operator prerequisites below.** Release **0.0.23** (Phase 6, plan 006) adds `@arnilo/prism-enterprise-postgres`, the optional PostgreSQL composition for policy decisions, evaluation records, work-mutation claim/CAS state, and cross-replica model-router rate/budget/circuit state. The publish graph is **47 manifests** (41 code + 6 family/profile; +1 package). `@arnilo/prism-all` includes it; core remains dependency-free. See [migration](migration.md#0022--0023-production-enterprise-state-adapters-intentional-pre-10-contract-changes) and [enterprise PostgreSQL state](enterprise-postgres-state.md).
206
+
207
+ Intentional pre-1.0 migration points: work idempotency now uses `begin`/CAS transitions and never automatically replays `unknown`; durable router state requires awaited methods plus verified identity and disables `providerSource`. Policy/evaluation/work/router data remain opt-in. No Redis, queue, event delivery, exactly-once effect claim, worker, migration CLI, ORM, or new core API ships.
208
+
209
+ ```bash
210
+ git diff --check
211
+ npm ci
212
+ npm run sdk:ready
213
+ PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres
214
+ PRISM_TEST_POSTGRES_URL="$DATABASE_URL" node scripts/benchmark-0.0.23.mjs
215
+ node --test scripts/budget-gate.test.mjs scripts/tooling-gate.test.mjs
216
+ node scripts/scan-secrets.mjs && node scripts/verify-sbom.mjs
217
+ npm audit --audit-level=moderate
218
+ npm run release:gate
219
+ npm run release:check -- --version 0.0.23 --allow-dirty --allow-untagged --report /tmp/prism-0.0.23-preflight.json
220
+ npm run release:publish -- --version 0.0.23 --dry-run --allow-dirty --allow-untagged --report /tmp/prism-0.0.23-dry-run.json
221
+ git tag -s v0.0.23 -m "Prism 0.0.23"
222
+ git verify-tag v0.0.23
223
+ git push origin v0.0.23
224
+ ```
225
+
226
+ `npm publish --dry-run` is non-publishing and applies `publishConfig.access`; the real protected tag workflow is the only publication path, with provenance and resume report. It must run the PostgreSQL suite using a protected, disposable database URL. The recorded benchmark is local/CI comparison evidence, not a portable SLO. npm publication is immutable: a partial publish resumes from the same tag; a confirmed defect requires deprecation plus a fixed version.
227
+
201
228
  ### 0.0.22 publish handoff
202
229
 
203
230
  **Decision: GO after protected operator prerequisites below.** Release **0.0.22** (Phase 5 third-party behavior integrations, plan 005) ships `@arnilo/prism-caveman` and `@arnilo/prism-ponytail` as opt-in behavior packages (upstream Caveman/Ponytail wiring, session mode persistence, progressive disclosure + injector split). Core `@arnilo/prism` runtime is unchanged. The publish graph is **46 manifests** (+2). No intentional pre-1.0 breaks for hosts that do not install the new packages — see [migration](migration.md) under `0.0.21 → 0.0.22 third-party behavior integrations`.
@@ -877,8 +904,8 @@ npm publication is not transactional and published versions are immutable. Parti
877
904
 
878
905
  ## Extension and configuration notes
879
906
 
880
- - **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional `@arnilo/prism@0.0.22` peer (`peerDependenciesMeta` must not mark `@arnilo/prism` optional; other peers such as `playwright-core` may be optional). The range stays pinned to `0.0.22` for the current 0.x release and will widen to `^1.0.0` at the 1.x stable release. Inside the workspace each package also declares `"@arnilo/prism": "file:../.."` in `devDependencies` so `npm install` resolves the peer locally; that devDependency is stripped from consumer installs and is not a runtime dependency.
881
- - **Public access.** All 46 manifests (40 code packages + 6 family/profile packages) declare `"publishConfig": { "access": "public" }`; the publisher also passes `--access public` explicitly because scoped packages otherwise default to restricted on first publish.
907
+ - **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional `@arnilo/prism@0.0.23` peer (`peerDependenciesMeta` must not mark `@arnilo/prism` optional; other peers such as `playwright-core` may be optional). The range stays pinned to `0.0.23` for the current 0.x release and will widen to `^1.0.0` at the 1.x stable release. Inside the workspace each package also declares `"@arnilo/prism": "file:../.."` in `devDependencies` so `npm install` resolves the peer locally; that devDependency is stripped from consumer installs and is not a runtime dependency.
908
+ - **Public access.** All 47 manifests (41 code packages + 6 family/profile packages) declare `"publishConfig": { "access": "public" }`; the publisher also passes `--access public` explicitly because scoped packages otherwise default to restricted on first publish.
882
909
  - **Map retention knob.** Source maps are emitted locally but stripped from tarballs by `!dist/**/*.map`. Removing that `files` negation ships maps in releases (larger tarballs, better consumer stack traces).
883
910
  - **Release workflow.** `.github/workflows/release.yml` has six jobs. `verify` runs network-free SDK readiness on Node 24; `node20-compat` builds/imports every public root `exports` default target on Node 20 for declared `engines.node >=20` (docs examples need Node >=22.6 native TypeScript stripping); `postgres-integration` uses `pgvector/pgvector:pg16`; `supply-chain` runs high-severity audit, SPDX/license policy, and tracked-source secret scanning; and tag-only `codeql-release` runs SAST. Tag-only `publish` needs all five gates, preserves clean exact-tag/version/topological publication, and alone receives `NPM_TOKEN`, `id-token: write`, and `attestations: write`. Before npm publish it packs all current tarballs, generates checksums plus SPDX, scans unpacked public artifacts, creates GitHub attestations for tarballs and SBOM, then retains artifacts for 30 days. Registry state remains the resumable journal. Local `npm run release:dry-run` remains network-free SDK readiness; local PostgreSQL coverage is `PRISM_TEST_POSTGRES_URL=... npm run test:postgres`.
884
911
  - **Adding a package.** New workspace packages are picked up automatically by `npm run build --workspaces`, `npm test --workspaces`, `npm run pack:dry-run`, the packaging guard (`src/__tests__/packaging.test.ts`), and the install-smoke test (`src/__tests__/install-smoke.test.ts`) via the workspace glob; add the package to both tests' config arrays for explicit per-package assertions.
@@ -992,13 +1019,14 @@ Every release gate maps to an exact enforcement test or command, so the checklis
992
1019
  | Gate | Enforcement |
993
1020
  | --- | --- |
994
1021
  | Docs coverage for persistence/runtime/migration surfaces | `docs.test.ts` enrolls every API page in `apiPages` (heading + index-link + bare-specifier + secret-scan checks); dedicated section assertions pin `database-persistence.md`, `runs-and-usage.md`, `session-stores-and-branching.md`, `migration.md`, `agent-definitions.md`, `performance.md`, and the Phase 41 `external_app_example_*` / `phase41_external_app_surfaces_*` gates. |
995
- | Package exports/subpaths resolve to built output | `public-export-contract.test.ts` asserts every `exports`/`main`/`types`/`bin` target resolves to a built file under `dist/` with a sibling `.d.ts`, and no target escapes `dist/` (no `src/` or `examples/` leak). CI `node20-compat` also imports every public root `exports` default target on Node 20. |
1022
+ | Package exports/subpaths resolve to built output | `public-export-contract.test.ts` asserts every `exports`/`main`/`types`/`bin` target resolves to a built file under `dist/` with a sibling `.d.ts` (`dist/index.js` + `dist/index.d.ts` for a root package), and no target escapes `dist/` (no `src/` or `examples/` leak). CI `node20-compat` also imports every public root `exports` default target on Node 20. |
996
1023
  | Public-API drift | `public-export-contract.test.ts` `phase39_public_protocol_exports_and_types_do_not_drift` pins the runtime protocol (`providerToolCallDelta`, `ToolCallDeltaContent`), the `/testing/provider-conformance` subpath shape, and the observational-memory runtime `.d.ts` surface. |
997
1024
  | Root SDK export surface freeze | `public-export-contract.test.ts` `root export surface is frozen` snapshots every value and type export of `src/index.ts` (107 value + 69 type) so any add/remove is a deliberate test update; `every frozen value export resolves at runtime` rebuilds `dist/index.js` and asserts each value export is present (catches build drift), and `every frozen type export appears in the built type declarations` asserts each type export is in `dist/index.d.ts`. |
998
1025
  | Examples compile and are listed; runnable demos execute | `npm run typecheck` runs `tsc -p examples --noEmit`; `docs.test.ts` checks every `examples/*.ts` file is listed in `examples/README.md`, then runs demos offline and scans output for secrets. |
999
1026
  | Examples run to completion with no secret leakage | `docs.test.ts` `examples_demos_run_to_completion_and_emit_no_secret` runs each demo (Node strips TypeScript types natively) with exit-0 and real-secret scans; `external_app_example_*` pins the DB-backed adapter reference exercising the `RunLedger`, branch-handle checkout, fork, and prior-run resume. |
1000
- | Tarball excludes built tests, source maps, and source | `packaging.test.ts` rejects `dist/__tests__/`, `*.map`, `src/`, `plans/`, and internal files; confirms every package ships README/changelog (and code packages ship LICENSE), core ships docs + CLI, exported targets exist (`dist/index.js` + `dist/index.d.ts` for NeuralWatt), and `prism-all` transitively reaches all 32 published first-party manifests. |
1027
+ | Tarball excludes built tests, source maps, and source | `packaging.test.ts` rejects `dist/__tests__/`, `*.map`, `src/`, `plans/`, and internal files; confirms every package ships README/changelog (and code packages ship LICENSE), core ships docs + CLI, and every export target exists. `prism-all` reaches every current publishable package except the deliberate Caveman/Ponytail opt-outs. |
1001
1028
  | NeuralWatt package/docs/examples release gate | `packaging.test.ts` pins `@arnilo/prism-provider-neuralwatt` package exports/type declarations and `@arnilo/prism-providers`/`@arnilo/prism-all` membership; `docs.test.ts` asserts `docs/index.md` links `providers/neuralwatt.md` and `provider-caching.md`, and that `examples/cache-aware-prompt-assembly.ts` plus `examples/neuralwatt-agent-run.ts` exist and are listed. |
1029
+ | Enterprise PostgreSQL package/docs/example gate | Packaging/install/public-contract tests include `@arnilo/prism-enterprise-postgres`; `docs.test.ts` pins its API page, four-store migration/ownership/unknown-outcome/async-router guidance, and `examples/enterprise-postgres-state.ts`; `npm run test:postgres` exercises migration, restart, contention, and cleanup with an explicit database URL. |
1002
1030
  | Version graph and resumable publication | `release.test.ts` covers exact package/lock/range validation, topological order, registry collisions, dry-run, interrupted reports/resume, clean tagged git state, provenance/public/tag arguments, and token-safe errors. `release:check` and `release:publish` derive the workspace graph without a manual package list. |
1003
1031
  | Pre-publish compatibility gates | `release:gate` (in `sdk:ready`) fails on removed/changed `.d.ts` exports vs `scripts/compat-baseline/` (unless `--allow-break` + migration note), version-range/lockfile drift, and tarball deny-list violations (`plans/`, `code-reviews/`, `docs/review-coverage-*`, `*.map`, `__tests__/`); unit-tested in `scripts/release-gate.test.mjs`. |
1004
1032
  | Formatting, linting, and coverage thresholds | `npm run lint` and `npm run format:check` run Biome (single root `biome.json`, workspaces inherit) and fail on any lint error or unformatted file; `npm run test:coverage` uses Node's built-in `--experimental-test-coverage` with enforced minimums (lines 60 / functions 70 / branches 75) and no third-party service. All three run inside `sdk:ready`. |
@@ -89,7 +89,22 @@ Startup: M365 `version --output json`; GWS `--version`. Forbidden: `login`, `set
89
89
 
90
90
  ### Draft → approve → execute
91
91
 
92
- Mutation tools (`*_mail_draft_send`, `*_draft_*`) create an in-adapter draft and return `{ status: "pending_approval", draftId }` until `approval.isApproved` is true. Retries with the same `idempotencyKey` return `{ status: "duplicate" }` after first successful execute.
92
+ Mutation tools (`*_mail_draft_send`, `*_draft_*`) create an in-adapter draft and return `{ status: "pending_approval", draftId }` until `approval.isApproved` is true.
93
+
94
+ ### Durable idempotency (0.0.23)
95
+
96
+ `createMemoryIdempotencyStore()` remains for tests and a single process. For production replicas, use `createPostgresEnterpriseState({ pool }).workIdempotency`. It changes the old `get`/`put` replay abstraction to explicit async state transitions:
97
+
98
+ | Observable state | Meaning / host action |
99
+ | --- | --- |
100
+ | absent | `begin()` atomically acquires the first claim. |
101
+ | `in_progress` | Another worker owns the claim; do not dispatch a second connector effect. |
102
+ | `completed` | Return the bounded stored `{ draftId, resourceId? }` duplicate summary. |
103
+ | `failed_retryable` | A later `begin()` may reclaim it within the capped attempt policy. |
104
+ | `failed_terminal` | Do not retry; surface the bounded failure. |
105
+ | `unknown` | External result is ambiguous; reconcile with the connector/operator through `resolveUnknown()`. Never auto-replay. |
106
+
107
+ Call `begin({ identity, key, op })` **before** the external effect. After it succeeds, call `complete`, `fail`, or `markUnknown` with the returned claim token and version. The connector effect stays outside the database transaction, so this is claim-before-effect/deduplication—not exactly-once delivery. Claims default to 15 minutes (hard 60 minutes); expired claims transition to `unknown`; attempts default to 3 (hard 5). Stored rows contain no request body, token, raw provider response, or unrestricted payload.
93
108
 
94
109
  ## Limits
95
110
 
@@ -111,6 +126,7 @@ Mutation tools (`*_mail_draft_send`, `*_draft_*`) create an in-adapter draft and
111
126
 
112
127
  ## Related
113
128
 
129
+ - [Enterprise PostgreSQL state](enterprise-postgres-state.md): durable claim/CAS store, cleanup, and operator reconciliation.
114
130
  - [Work connectors](work-connectors.md)
115
131
  - [Agent identity](agent-identity.md)
116
132
  - [Host security](host-security.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arnilo/prism",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "Agent harness for AI providers, agents, sessions, and tools.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -123,6 +123,7 @@
123
123
  "packages/work-tools",
124
124
  "packages/policy",
125
125
  "packages/model-router",
126
+ "packages/enterprise-postgres",
126
127
  "packages/browser",
127
128
  "packages/ag-ui",
128
129
  "packages/prism-*"
@@ -138,7 +139,7 @@
138
139
  "format": "biome format --write .",
139
140
  "format:check": "biome format .",
140
141
  "pack:dry-run": "npm pack --dry-run && npm run pack:dry-run --workspaces --if-present",
141
- "test:postgres": "npm run test:postgres --workspace @arnilo/prism-session-store-postgres && npm run test:postgres --workspace @arnilo/prism-memory",
142
+ "test:postgres": "node scripts/require-postgres-url.mjs && npm run test:postgres --workspace @arnilo/prism-session-store-postgres && npm run test:postgres --workspace @arnilo/prism-memory && npm run test:postgres --workspace @arnilo/prism-enterprise-postgres",
142
143
  "release:dry-run": "npm run sdk:ready",
143
144
  "release:check": "node scripts/release.mjs check",
144
145
  "release:publish": "node scripts/release.mjs publish",