@arnilo/prism 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -5
- package/README.md +12 -11
- package/dist/agent-approval.d.ts +4 -0
- package/dist/agent-approval.js +5 -1
- package/dist/agent-definitions.js +1 -0
- package/dist/agent-run-lifecycle.js +39 -4
- package/dist/agent-run-state.d.ts +18 -0
- package/dist/agent-run-state.js +39 -9
- package/dist/agent-session/helpers.js +6 -1
- package/dist/agent-session/session/assemble.js +159 -7
- package/dist/agent-session/session/persist.d.ts +16 -0
- package/dist/agent-session/session/persist.js +64 -4
- package/dist/agent-session/session/provider-round.d.ts +3 -3
- package/dist/agent-session/session/provider-round.js +12 -6
- package/dist/agent-session/session/tool-round.js +5 -1
- package/dist/agent-session/session/types.d.ts +22 -1
- package/dist/agent-session/session.d.ts +16 -0
- package/dist/agent-session/session.js +42 -3
- package/dist/artifacts.d.ts +39 -1
- package/dist/artifacts.js +73 -0
- package/dist/attention-compiler.d.ts +121 -0
- package/dist/attention-compiler.js +479 -0
- package/dist/checkpoints.js +7 -11
- package/dist/cli-init.js +20 -6
- package/dist/context-budget.d.ts +20 -1
- package/dist/context-budget.js +10 -1
- package/dist/contracts-core/agent.d.ts +7 -0
- package/dist/contracts-core/attention.d.ts +66 -0
- package/dist/contracts-core/attention.js +2 -0
- package/dist/contracts-core/compaction.d.ts +59 -0
- package/dist/contracts-core/compaction.js +77 -1
- package/dist/contracts-core/content.d.ts +5 -0
- package/dist/contracts-core/loop.d.ts +42 -0
- package/dist/contracts-core/provider.d.ts +4 -0
- package/dist/contracts-core/run-limits.d.ts +2 -0
- package/dist/contracts-core.d.ts +1 -0
- package/dist/contracts-core.js +1 -0
- package/dist/contracts-protocol.d.ts +44 -3
- package/dist/contracts-run-state.d.ts +32 -5
- package/dist/evidence-grounding.d.ts +29 -0
- package/dist/evidence-grounding.js +162 -0
- package/dist/host-composition.d.ts +91 -0
- package/dist/host-composition.js +279 -0
- package/dist/index.d.ts +13 -6
- package/dist/index.js +7 -4
- package/dist/input.d.ts +13 -1
- package/dist/input.js +40 -1
- package/dist/provider-events.d.ts +3 -1
- package/dist/provider-events.js +2 -2
- package/dist/providers/transport.d.ts +3 -1
- package/dist/providers/transport.js +36 -0
- package/dist/redaction.js +18 -2
- package/dist/run-bundle.d.ts +89 -0
- package/dist/run-bundle.js +149 -0
- package/dist/secure-agent.d.ts +2 -0
- package/dist/secure-agent.js +6 -1
- package/dist/testing/state-concurrency-conformance.js +5 -12
- package/dist/tool-result-fold.d.ts +12 -0
- package/dist/tool-result-fold.js +13 -6
- package/dist/tools.d.ts +10 -0
- package/dist/tools.js +41 -0
- package/docs/acp-agent.md +42 -11
- package/docs/acp.md +2 -1
- package/docs/ag-ui.md +10 -3
- package/docs/agent-definitions.md +9 -1
- package/docs/agent-events.md +4 -1
- package/docs/agent-loops.md +33 -0
- package/docs/agent-session-runtime.md +8 -7
- package/docs/attention-compiler.md +272 -0
- package/docs/cli-rpc.md +4 -2
- package/docs/coding-agent-tools.md +1 -1
- package/docs/coding-security.md +6 -3
- package/docs/coding-tools.md +0 -1
- package/docs/coding-workspaces.md +22 -0
- package/docs/compaction-and-retry.md +36 -4
- package/docs/compaction-observational-memory.md +63 -10
- package/docs/connected-apps.md +116 -0
- package/docs/context-and-skills.md +17 -2
- package/docs/conversations.md +1 -1
- package/docs/core.md +1 -1
- package/docs/dev-inspector.md +4 -0
- package/docs/device-adapters.md +1 -0
- package/docs/diagrams.md +6 -6
- package/docs/document-reader.md +18 -10
- package/docs/documents.md +40 -11
- package/docs/durable-runs.md +87 -0
- package/docs/enterprise-postgres-state.md +6 -2
- package/docs/evaluations.md +168 -4
- package/docs/execution-timeline.md +186 -0
- package/docs/guardrails.md +33 -0
- package/docs/history/0.7.0-primitive-review.md +254 -0
- package/docs/history/079-messaging-primitive-review.md +391 -0
- package/docs/history/080-messaging-followon-primitive-review.md +234 -0
- package/docs/history/081-connected-apps-primitive-review.md +74 -0
- package/docs/history/083-prism-work-primitive-review.md +84 -0
- package/docs/history/084-primitive-review.md +96 -0
- package/docs/history/085-honesty-and-cut-primitive-review.md +91 -0
- package/docs/history/README.md +5 -0
- package/docs/history/migration-0.0.md +2 -2
- package/docs/history/release-handoffs.md +75 -1
- package/docs/host-compositions.md +149 -0
- package/docs/host-security.md +2 -2
- package/docs/hosted-sandboxes.md +94 -0
- package/docs/index.md +82 -45
- package/docs/input-and-prompt-assembly.md +1 -0
- package/docs/knowledge-sync.md +84 -0
- package/docs/language-intelligence.md +1 -1
- package/docs/live-testing.md +8 -3
- package/docs/mcp-tools.md +3 -1
- package/docs/memory-fabric.md +416 -0
- package/docs/messaging-channel-operations.md +166 -0
- package/docs/messaging-channels.md +150 -0
- package/docs/migrate-to-0.5.md +1 -1
- package/docs/migrate-to-0.6.md +1 -0
- package/docs/migrate-to-0.7.md +345 -0
- package/docs/migrate-to-0.8.md +124 -0
- package/docs/migration.md +43 -1
- package/docs/model-registry.md +12 -2
- package/docs/model-routing.md +79 -4
- package/docs/multi-agent-patterns.md +20 -6
- package/docs/observability.md +52 -1
- package/docs/openapi-tools.md +1 -1
- package/docs/operations.md +14 -4
- package/docs/options-index.md +47 -3
- package/docs/peer-dependencies.md +12 -10
- package/docs/postgres-persistence.md +1 -1
- package/docs/process-sessions.md +3 -1
- package/docs/prompt-registry.md +1 -1
- package/docs/provider-caching.md +4 -2
- package/docs/provider-conformance.md +1 -1
- package/docs/provider-layer.md +2 -2
- package/docs/provider-packages.md +22 -22
- package/docs/providers/bedrock.md +71 -7
- package/docs/providers/neuralwatt.md +5 -1
- package/docs/providers/openai.md +1 -1
- package/docs/rag.md +24 -8
- package/docs/realtime-voice.md +87 -0
- package/docs/release-and-install.md +53 -45
- package/docs/run-bundle.md +92 -0
- package/docs/runs-and-usage.md +17 -2
- package/docs/server.md +7 -3
- package/docs/sheets.md +9 -9
- package/docs/signal-channel.md +112 -0
- package/docs/speech.md +7 -1
- package/docs/sqlite-persistence.md +1 -1
- package/docs/supervisors.md +33 -5
- package/docs/telegram-channel.md +157 -0
- package/docs/testing.md +2 -2
- package/docs/thinking-and-reasoning.md +3 -1
- package/docs/tools.md +6 -5
- package/docs/web-tools.md +2 -1
- package/docs/wiki.md +1 -1
- package/docs/work-artifacts-and-review.md +14 -4
- package/docs/work-connectors.md +12 -10
- package/docs/work-sandbox.md +115 -0
- package/docs/work-tools.md +50 -18
- package/docs/workflows.md +69 -1
- package/docs/working-and-semantic-memory.md +25 -14
- package/package.json +5 -3
- package/templates/README.md +2 -0
- package/templates/business-worker/README.md.tmpl +19 -0
- package/templates/business-worker/env.example.tmpl +1 -0
- package/templates/business-worker/gitignore.tmpl +11 -0
- package/templates/business-worker/manifest.json +12 -0
- package/templates/business-worker/package.json.tmpl +23 -0
- package/templates/business-worker/src/agent.ts.tmpl +92 -0
- package/templates/business-worker/src/index.ts.tmpl +13 -0
- package/templates/business-worker/src/tests/agent.test.ts.tmpl +77 -0
- package/templates/business-worker/tsconfig.json.tmpl +15 -0
- package/templates/personal-assistant/README.md.tmpl +18 -0
- package/templates/personal-assistant/env.example.tmpl +1 -0
- package/templates/personal-assistant/gitignore.tmpl +11 -0
- package/templates/personal-assistant/manifest.json +11 -0
- package/templates/personal-assistant/package.json.tmpl +23 -0
- package/templates/personal-assistant/src/agent.ts.tmpl +65 -0
- package/templates/personal-assistant/src/index.ts.tmpl +13 -0
- package/templates/personal-assistant/src/tests/agent.test.ts.tmpl +28 -0
- package/templates/personal-assistant/tsconfig.json.tmpl +15 -0
package/docs/model-routing.md
CHANGED
|
@@ -19,13 +19,19 @@ Do not put secrets, prompts, or raw OpenRouter keys into diagnostics. Do not hon
|
|
|
19
19
|
| `allowedResidencies` | Request residency must match when configured |
|
|
20
20
|
| `budgets` / per-call `maxTokens` / `maxCostUsd` | Finite non-negative ceilings; requests with a per-call cap reserve it atomically at admission; `recordUsage` commits actuals against the reservation |
|
|
21
21
|
| `budgets.reservationTtlMs` | How long an admission reservation pins capacity (default 60s); a run that outlives it reconciles as unknown usage |
|
|
22
|
+
| `budgets.strict` | When `true`, enables strict hard-budget mode: calls lacking enforceable bounds or candidates lacking cost pricing are denied fail-closed with `ERR_PRISM_MODEL_ROUTER_BUDGET` |
|
|
22
23
|
| `rateLimit` | Per identity+model key window |
|
|
23
24
|
| `circuit` | Failure threshold + cooldown; keys capped |
|
|
24
25
|
| `fallbacks` | Ordered candidates after primary; total attempts capped |
|
|
25
26
|
| `allowOpenRouterRouting` | Default `false`; when false, routing metadata is stripped |
|
|
26
27
|
| `onDiagnostics` | Optional redacted hook (e.g. policy ledger evidence ref) |
|
|
27
|
-
| `router.resolve({ model, identity?, residency?, maxTokens?, ... })` | Rich async selection; returns `budgetReservation` when a per-request cap was reserved |
|
|
28
|
-
| `router.
|
|
28
|
+
| `router.resolve({ model, identity?, residency?, maxTokens?, taskId?, kind?, attemptId?, ... })` | Rich async selection; accepts optional `taskId` and `kind` (`"generation" \| "embedding" \| "compaction" \| "tool"`); returns `budgetReservation` when a per-request cap was reserved |
|
|
29
|
+
| `router.releaseBudget({ identity, provider, model, budgetReservation, taskId?, kind? })` | Explicitly releases a held budget reservation back to the pool |
|
|
30
|
+
| `router.renewBudget({ identity, provider, model, budgetReservation, extendTtlMs?, taskId?, kind? })` | Renews an active reservation hold before expiry, returning an updated reservation with an advanced fencing token |
|
|
31
|
+
| `router.readBudget({ identity, provider?, model?, taskId? })` | Reads budget utilization; when `taskId` is provided, returns granular `byModel`, `byKind`, and `attributions` breakdowns |
|
|
32
|
+
| `createGovernedProvider(options)` / `router.createGovernedProvider(options)` | Wraps an `AIProvider` in an opt-in adapter that automates the full admission, atomic reservation, request-policy application, streaming, and explicit settlement lifecycle (supports `taskId`, `kind`, and `renewBudget()`) |
|
|
33
|
+
| `isGovernedProvider(provider)` | Type guard and runtime predicate indicating that a provider has opt-in governance lifecycle wrapping |
|
|
34
|
+
| `router.providerSource` | Sync facade for allow-list and residency-only routers without async state, budgets, rate limits, circuits, fallbacks, or selection policies. Throws `ERR_PRISM_MODEL_ROUTER_ASYNC_STATE` or `ERR_PRISM_MODEL_ROUTER_ASYNC_REQUIRED` when unenforced governance options are configured; use `router.resolve()` instead. |
|
|
29
35
|
|
|
30
36
|
Frozen caps (default / hard): attempts `3 / 8`, circuit keys `1,024 / 16,384`, diagnostics `8 KiB / 64 KiB`, rate keys `4,096 / 65,536`, budget keys `4,096 / 65,536`.
|
|
31
37
|
|
|
@@ -33,7 +39,9 @@ Frozen caps (default / hard): attempts `3 / 8`, circuit keys `1,024 / 16,384`, d
|
|
|
33
39
|
|
|
34
40
|
- `ModelRouterResolveResult` — selected `provider` + possibly stripped `model`, `diagnostics`, and `providerRequestPolicy`; `budgetReservation` carries the admission reservation handle when the request had a per-call budget cap.
|
|
35
41
|
- Deny throws `ModelRouterError` with code + redacted `diagnostics` (allow-list/residency/budget fail closed without calling resolver); budget denies carry `details.retryAfterMs`.
|
|
36
|
-
- `await recordOutcome({ identity, success, circuitProbeToken? })` opens/closes circuits; `await recordUsage({ identity, budgetReservation?, ... })` commits the reservation against actual usage (pass the handle returned by `resolve`) or advances budgets directly. Pass the probe token returned by `resolve` for a half-open outcome.
|
|
42
|
+
- `await recordOutcome({ identity, success, circuitProbeToken? })` opens/closes circuits; `await recordUsage({ identity, budgetReservation?, taskId?, kind?, attemptId?, ... })` commits the reservation against actual usage (pass the handle returned by `resolve`) or advances budgets directly. Pass the probe token returned by `resolve` for a half-open outcome.
|
|
43
|
+
- `await renewBudget({ identity, provider, model, budgetReservation, extendTtlMs?, taskId?, kind? })` extends hold TTL and advances fencing tokens for long-running workflows. Expired holds fail closed with `ERR_PRISM_MODEL_ROUTER_STATE`.
|
|
44
|
+
- `await readBudget({ identity, taskId? })` returns `{ tokens, costUsd, byModel?, byKind?, attributions? }`. When queried with `taskId`, attributions decompose usage across models and paid work categories (`generation`, `embedding`, `compaction`, `tool`).
|
|
37
45
|
- A reservation whose TTL elapses before `recordUsage` charges the **reserved** amount and emits one redacted `unknown_usage` diagnostic (deterministic reconciliation, never a silent drop).
|
|
38
46
|
|
|
39
47
|
## Request/response example
|
|
@@ -86,7 +94,7 @@ await router.recordUsage({ identity, provider: provider.id, model: model.model,
|
|
|
86
94
|
await router.recordOutcome({ identity, provider: provider.id, model: model.model, success: true });
|
|
87
95
|
await enterprise.close();
|
|
88
96
|
|
|
89
|
-
// `router.providerSource` is unavailable with durable state; resolve before provider I/O.
|
|
97
|
+
// `router.providerSource` is unavailable with durable state or unenforced governance (budgets, rate limits, circuit breaker, fallbacks, selection); resolve before provider I/O.
|
|
90
98
|
```
|
|
91
99
|
|
|
92
100
|
## Extension and configuration notes
|
|
@@ -138,14 +146,81 @@ await router.recordOutcome({ identity, provider, model, success: true, latencyMs
|
|
|
138
146
|
process-local; durable latency statistics would require a
|
|
139
147
|
`ModelRouterStateStore` contract change and are demand-gated.
|
|
140
148
|
|
|
149
|
+
### Governed provider adapter (`createGovernedProvider`)
|
|
150
|
+
|
|
151
|
+
Hosts that want complete, automated governance enforcement across every provider call can wrap their `ModelRouter` in an opt-in `GovernedProvider` adapter instead of manually coordinating `resolve`, `recordUsage`, and `recordOutcome`:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { createModelRouter, isGovernedProvider } from "@arnilo/prism-core/governance/model-router";
|
|
155
|
+
|
|
156
|
+
const router = createModelRouter({
|
|
157
|
+
resolver,
|
|
158
|
+
allowList: { providers: ["anthropic", "openai"] },
|
|
159
|
+
budgets: { maxTokens: 10_000 },
|
|
160
|
+
fallbacks: [{ provider: "openai", model: "gpt-4o" }],
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const governed = router.createGovernedProvider({
|
|
164
|
+
identity,
|
|
165
|
+
model: { provider: "anthropic", model: "claude-3-5-sonnet" },
|
|
166
|
+
maxTokens: 500,
|
|
167
|
+
onSettlement: (settlement) => {
|
|
168
|
+
console.log(settlement.outcome, settlement.durationMs, settlement.usage);
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
assert.ok(isGovernedProvider(governed));
|
|
173
|
+
for await (const event of governed.generate({ model, messages })) {
|
|
174
|
+
// stream events incrementally; bounded chunks, no full-stream buffering
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The adapter executes an explicit six-stage lifecycle for every call:
|
|
179
|
+
|
|
180
|
+
1. **Admission & Selection**: Runs `router.resolve(...)` across configured candidates. If the primary model fails admission (e.g. not allow-listed, residency mismatch, budget exhausted, or circuit open) and safe fallbacks are configured, the router or adapter evaluates the next candidate.
|
|
181
|
+
2. **Atomic Budget Reservation**: When `maxTokens` or `maxCostUsd` is specified, capacity is reserved atomically prior to invoking the provider.
|
|
182
|
+
3. **Request Policy Application**: Applies model and provider request policies (e.g., stripping unauthorized OpenRouter metadata) before calling the provider.
|
|
183
|
+
4. **Bounded Streaming**: Consumes provider events incrementally without full-response buffering.
|
|
184
|
+
5. **Safe Fallback vs Forbidden Replay**:
|
|
185
|
+
- **Safe Fallback**: If an error occurs *before* any output or tool call is emitted, the adapter catches the failure, records outcome feedback, releases any reservation, and seamlessly falls back to the next configured candidate.
|
|
186
|
+
- **Forbidden Fallback**: If a failure happens *after* text deltas or tool calls have already been delivered to the consumer, failover is strictly forbidden to prevent replay of side effects or inconsistent state.
|
|
187
|
+
6. **Explicit Settlement**: Idempotently settles the call exactly once across all outcomes:
|
|
188
|
+
- `success`: Commits actual tokens and cost; releases unspent reservations; records positive outcome.
|
|
189
|
+
- `abort` / `error` / `early_close` before output: Releases the reservation cleanly; records outcome.
|
|
190
|
+
- `abort` / `early_close` after output: Commits the held reservation as `unknownUsage: true` (missing actual usage is charged as reserved liability, never assumed to be zero).
|
|
191
|
+
- Upstream EOF without usage: Commits the reservation as `unknownUsage: true`.
|
|
192
|
+
- Usage recording errors: Propagate out on success so persistence failures are not concealed.
|
|
193
|
+
- Invokes `onSettlement` callback with `GovernedInvocationSettlement` telemetry.
|
|
194
|
+
|
|
195
|
+
### Synchronous facade (`providerSource`) governance matrix
|
|
196
|
+
|
|
197
|
+
The synchronous `router.providerSource(model)` facade is strictly intended for simple synchronous resolution where allow-lists and residency checks are sufficient. Any configuration requiring asynchronous state or multi-candidate evaluations fails closed at call time:
|
|
198
|
+
|
|
199
|
+
| Feature / Configuration | Supported in `resolve()` | Supported in `providerSource(model)` | Refusal Code |
|
|
200
|
+
| --- | --- | --- | --- |
|
|
201
|
+
| Allow-list (`allowList`) | Yes | Yes | `ERR_PRISM_MODEL_ROUTER_ALLOW_LIST` |
|
|
202
|
+
| Allowed residencies (`allowedResidencies`) | Yes | Yes (from `model.compat.residency`) | `ERR_PRISM_MODEL_ROUTER_RESIDENCY` |
|
|
203
|
+
| Token / cost budgets (`budgets`) | Yes | Refused | `ERR_PRISM_MODEL_ROUTER_ASYNC_REQUIRED` |
|
|
204
|
+
| Rate limits (`rateLimit`) | Yes | Refused | `ERR_PRISM_MODEL_ROUTER_ASYNC_REQUIRED` |
|
|
205
|
+
| Circuit breaker (`circuit`) | Yes | Refused | `ERR_PRISM_MODEL_ROUTER_ASYNC_REQUIRED` |
|
|
206
|
+
| Fallbacks (`fallbacks`) | Yes | Refused | `ERR_PRISM_MODEL_ROUTER_ASYNC_REQUIRED` |
|
|
207
|
+
| Selection policy (`selection`) | Yes | Refused | `ERR_PRISM_MODEL_ROUTER_ASYNC_REQUIRED` |
|
|
208
|
+
| Durable state (`stateStore`) | Yes | Refused | `ERR_PRISM_MODEL_ROUTER_ASYNC_STATE` |
|
|
209
|
+
|
|
210
|
+
All refusals and denials occur before any resolver invocation. In untyped JavaScript contexts, malformed model arguments fail closed with `ERR_PRISM_MODEL_ROUTER_VALIDATION`.
|
|
211
|
+
|
|
141
212
|
## Security and performance notes
|
|
142
213
|
|
|
143
214
|
- Allow-list and residency denies never call the underlying resolver.
|
|
215
|
+
- Zero-budget facade probes or unmetered executions are strictly prevented: `providerSource` never executes wrapped resolver logic when any budget, rate limit, circuit, fallback, selection, or durable state is configured.
|
|
144
216
|
- Budget admission is **reservation-based** when the request carries a per-request cap: `resolve` atomically reserves the full cap against remaining capacity (`max − used − reserved`) and returns a `budgetReservation` handle; parallel admissions can never collectively exceed the reserved budget. Commit the handle in `recordUsage` with the actual tokens/cost (a negative remainder is released back); release happens automatically on internal denial (rate limit, circuit open, provider miss), on TTL expiry, or on an explicit late commit (which charges the reserved amount as unknown usage). Requests without a per-request cap keep read-then-compare admission (`used >= cap` denies) and are outside the reservation guarantee.
|
|
145
217
|
- Without `stateStore`, budget/rate/circuit state is process-local, memory-capped (rate/budget/circuit keys), and LRU-evicts on insert; a held reservation's budget row is never evicted. It is not a cross-replica production path.
|
|
146
218
|
- With `stateStore: createPostgresEnterpriseState(...).modelRouter`, rate/budget updates, reservations, 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.
|
|
147
219
|
- Diagnostics carry identity refs and attempt outcomes only — no prompts/secrets, tokens, or reservation material. Durable state stores at most bounded numeric/timestamp/token material, never prompts or credentials.
|
|
148
220
|
- Selection is O(attempts × state operations); no provider network I/O happens inside state updates. Reservation is one atomic UPSERT (denial adds one retry-after query); commit/release are O(1) row updates. Recorded 0.0.23 PostgreSQL p95 point operations stayed under 50 ms and cursor/cleanup pages under 100 ms on the documented fixture.
|
|
221
|
+
- **Aggregate task/tenant accounting**: When `taskId` is supplied, admission reservations and usage records are aggregated at the task level rather than per provider/model alone. Parallel requests (tested up to 32 concurrent attempts across models and child workers) share the atomic budget ceiling without oversubscription. Model switches, tool calls, and background jobs attribute separately to `byModel` and `byKind` while sharing a single liability pool.
|
|
222
|
+
- **Strict hard-budget mode**: Setting `budgets.strict = true` prevents unmetered or unbounded model execution: requests lacking output/token bounds or models lacking cost pricing are rejected fail-closed with `ERR_PRISM_MODEL_ROUTER_BUDGET`.
|
|
223
|
+
- **Hold renewal fencing**: `router.renewBudget()` and `governedProvider.renewBudget()` advance the reservation's fencing token upon extending TTL; previous fencing tokens are immediately invalidated to prevent stale or split-brain commits. Expired holds cannot be renewed and fail closed with `ERR_PRISM_MODEL_ROUTER_STATE`.
|
|
149
224
|
- Raising hard caps requires a reviewed release update with tests and docs.
|
|
150
225
|
|
|
151
226
|
## Related APIs
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
# Multi-agent patterns: handoff,
|
|
1
|
+
# Multi-agent patterns: handoff, crew, supervisor, spawn tool, A2A
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
Maps
|
|
5
|
+
Maps five Prism answers for "more than one agent" onto one decision table. All five compose existing seams — none introduces a new runtime:
|
|
6
6
|
|
|
7
7
|
- **In-session handoff (swarm)** — agent A transfers control of the ongoing conversation to agent B by calling a host-built `handoff` tool; the host resolves the target `AgentDefinition` with `resolveAgentDefinition` and opens the specialist against the same session (same store + session id, previous run's `leafId`). One transcript, no new session. No helper primitive ships; the tool factory lives in [`examples/handoff-swarm.ts`](../examples/handoff-swarm.ts).
|
|
8
8
|
- **Hierarchical crew** — a manager agent decomposes a goal into typed tasks (`{ tasks: [{ role, instruction }] }`) via structured output ([`Artifact*`](structured-output.md)), fans out to parallel role specialists with bounded `maxFanOut` ([`fanOutNode`](workflows.md)), aggregates deliverables with host reduce ([`joinNode`](workflows.md)), and validates outputs with conditional routing to completion or revision ([`conditionalNode`](workflows.md)). The entire process is a deterministic DAG workflow with zero new runtime primitives. Live demo in [`examples/crew-hierarchy.ts`](../examples/crew-hierarchy.ts).
|
|
9
9
|
- **Supervisor delegation** — `@arnilo/prism-core/runtime/supervisor` `delegate()` invokes allow-listed child agents as bounded runs and returns their result to the parent. Separate child transcripts, hooks, budgets, narrowing.
|
|
10
|
+
- **In-process spawn tool** — `createSpawnAgentTool({ supervisor })` gives a parent model non-exclusive sync or bounded async `spawn_agent` calls over that same host-owned supervisor catalog; `wait_agent` / `cancel_agent` join or abort local handles. It is an API adapter, not a runtime.
|
|
10
11
|
- **A2A 1.0** — cross-service interop over the JSON-RPC/HTTPS binding; the remote peer's lifecycle is host-owned behind `A2ATaskLifecycle`.
|
|
11
12
|
|
|
12
13
|
## When to use it
|
|
@@ -15,10 +16,11 @@ Maps the four Prism answers for "more than one agent" onto one decision table. A
|
|
|
15
16
|
| --- | --- | --- | --- | --- |
|
|
16
17
|
| In-session handoff | One host, one ongoing conversation; the model decides **when** to transfer; specialists are alternate definitions of the same app | One continuous transcript chain (same store, session id, `leafId`) | Same session scope; give the specialist its own identity via its definition (`AgentConfig.identity` / `RunOptions.identity`) | Attribution is per-run: each `session.run()`'s events/result belong to the active definition — record the swap in host bookkeeping; no `delegated_agent_step` event exists for in-process swaps |
|
|
17
18
|
| Hierarchical crew | A goal requires dynamic decomposition by a manager LLM, parallel execution by role specialists, host aggregation, and conditional validation/revision loop | Workflow DAG execution — each specialist executes a bounded child task session; final deliverable returns to host | Workflow tenant/ownership scopes propagate; specialists activate only their own narrowed `tools` | Workflow node events (`node_started`/`node_finished`/`agent_event`); task attribution per role in the aggregated deliverable |
|
|
18
|
-
| Supervisor delegation |
|
|
19
|
+
| Supervisor delegation | Host code dynamically selects a bounded child run | Separate runs; child result returns to the host | Parent identity/effectStore propagate; child factories receive derived resource/thread ids and AND-composed permission | Dedicated `delegation_started/finished/rejected/error` events, projectable through observability `handleDelegation()`; opt-in `delegation_child_event` passthrough |
|
|
20
|
+
| In-process spawn tool | Parent model needs an allow-listed child as a non-exclusive tool call | Separate runs; sync result returns through `spawn_agent`, async handle joins through `wait_agent` | Host owns catalog, tools, scopes, limits, and local handles; schema accepts only child ID/input/thread ID/mode | Same supervisor `delegation_*` events |
|
|
19
21
|
| A2A 1.0 | The other agent is owned by a **different service/deployment**; cross-org or cross-cluster; needs durable task lifecycle, push configs, streaming | Protocol boundary (JSON-RPC/HTTPS agent card); replay/reconnect via host-owned task adapter | Exact-origin verified client, `A2AAuthorization` per operation, principal-scoped push configs | Host-owned task adapter records the remote lifecycle; Prism creates no worker/store |
|
|
20
22
|
|
|
21
|
-
Rule of thumb: same conversation → handoff; dynamic task decomposition + parallel execution → hierarchical crew; same
|
|
23
|
+
Rule of thumb: same conversation → handoff; dynamic task decomposition + parallel execution → hierarchical crew; host-selected same-process subtask → supervisor delegation; model-requested allow-listed subtask → in-process spawn tool; different deployment/trust boundary → A2A.
|
|
22
24
|
|
|
23
25
|
## How in-session handoff works
|
|
24
26
|
|
|
@@ -138,6 +140,17 @@ Live demo: [`examples/crew-hierarchy.ts`](../examples/crew-hierarchy.ts) — man
|
|
|
138
140
|
| **Validation & Quality Review** | Conditional Node ([`conditionalNode`](workflows.md)) | Deterministic branch routing to `complete` or `revise` based on validation criteria. |
|
|
139
141
|
| **Process Revision Loop** | Node Retries / DAG Branching / Loop Node ([`loopNode`](workflows.md)) | Bounded retry/revision path or bounded in-graph loop iteration. |
|
|
140
142
|
|
|
143
|
+
## How the in-process spawn tool works
|
|
144
|
+
|
|
145
|
+
`createSpawnAgentTool({ supervisor })` exposes the host's child catalog as one non-exclusive `spawn_agent` tool, so a parent model can request children the host already allows. It is a thin adapter over [`supervisor.delegate()`](supervisors.md) — no new runtime, no discovery:
|
|
146
|
+
|
|
147
|
+
- The schema carries only `childId` (an enum of `Supervisor.childIds`), `input`, optional `threadId`, and `mode`. Unknown child IDs fail closed as standard tool errors before any delegation, and model arguments can never supply child tools, identity, scopes, or limits.
|
|
148
|
+
- Named tool calls in one tool turn run in parallel under the parent session's `toolConcurrency`, while the supervisor's reservation keeps concurrent children within `maxActiveChildren` — over-cap sync calls fail closed, async calls return handles.
|
|
149
|
+
- `mode: "async"` returns a local `{ delegationId, status: "running" }` handle; `createWaitAgentTool` joins it, `createCancelAgentTool` aborts it, and the parent run's abort propagates to running children. Handles are in-process, ownership-scoped, and do not survive host restart.
|
|
150
|
+
- Children needing repository isolation wrap their factory with `createWorktreeChildFactory` ([Coding workspaces](coding-workspaces.md#spawn-isolation-supervisor-children)); timeline visibility comes from the supervisor `delegation_*` events, optionally bridged to coding `subagent_started` / `subagent_stopped` ([Coding agent tools](coding-agent-tools.md)).
|
|
151
|
+
|
|
152
|
+
Live demo: [`examples/spawn-agent-tool.ts`](../examples/spawn-agent-tool.ts) — a narrowed read-only explore child spawned twice in parallel, an uncatalogued child refused, and both handles joined.
|
|
153
|
+
|
|
141
154
|
## Where Prism is stronger
|
|
142
155
|
|
|
143
156
|
- **Durable Human-in-the-Loop (HITL)**: Prism workflows support durable pause and resume via [`suspend()`](workflows.md#durable-suspension-and-resumption) and [`resumeWorkflow()`](workflows.md) across worker restarts or approval gates ([Agent durable approval](agent-session-runtime.md)).
|
|
@@ -149,7 +162,7 @@ Live demo: [`examples/crew-hierarchy.ts`](../examples/crew-hierarchy.ts) — man
|
|
|
149
162
|
## Security and performance notes
|
|
150
163
|
|
|
151
164
|
- **Transfers are explicit model-initiated, host-authorized.** The `handoff` tool exists on the triage agent's allow-list only; the target name is validated against the host-authored targets map before any definition resolves. Unknown names fail closed as a standard tool error (`Unknown handoff target: <name>`).
|
|
152
|
-
- **No permission escalation through handoff or
|
|
165
|
+
- **No permission escalation through handoff, delegation, or spawn.** The specialist's capabilities come solely from its own `AgentDefinition` or host-owned supervisor child factory (fail-closed for omitted capabilities). Handoff, fan-out, and spawn grant nothing: tools/identity are what the host put on that definition. The specialist cannot invoke manager tools unless its definition explicitly includes them — the standard `unknown_tool` block applies otherwise.
|
|
153
166
|
- **Narrowing on transfer, never widening.** If the specialist needs the caller's verified identity, project it through `narrowIdentity` / `assertIdentityPropagation` ([Agent identity](agent-identity.md)) so scopes and tenant cannot widen across the swap. For delegation the same discipline is built in (`narrowIdentity`, AND-composed policies); for A2A the exact-origin client plus per-operation authorization is the boundary.
|
|
154
167
|
- **Manager-generated task plans are untrusted model output.** Manager plan outputs are validated against the typed schema via `ArtifactValidator` before being persisted to workflow state or dispatched to `fan_out`. Malformed or invalid plans trigger the artifact repair loop or fail closed before any specialist is invoked.
|
|
155
168
|
- **Redaction of carried context.** Handoff carries the raw transcript by design — same rows a human replay would read. Apply the session egress seams on the way out: `redactSessionEntry` / `redactMessage` with a host field policy (see [Data classification](data-classification.md)) and `AgentConfig.redactor`; for durable replay across tenants reuse the redacted transcript seam discipline used by ACP `sessions.transcript` ([ACP interop](acp.md)).
|
|
@@ -168,7 +181,8 @@ Live demo: [`examples/crew-hierarchy.ts`](../examples/crew-hierarchy.ts) — man
|
|
|
168
181
|
- [Workflows](workflows.md): `defineWorkflow`, `fanOutNode`, `joinNode`, `conditionalNode`, `runWorkflow`.
|
|
169
182
|
- [Structured output](structured-output.md): `ArtifactParser`, `ArtifactValidator`, `generateValidateReviseLoop`.
|
|
170
183
|
- [Agent definitions](agent-definitions.md): `resolveAgentDefinition` and fail-closed capability activation — the swap seam itself.
|
|
171
|
-
- [Supervisor delegation](supervisors.md): same-process subtasks
|
|
184
|
+
- [Supervisor delegation](supervisors.md): same-process subtasks, model-facing spawn tool, budgets, hooks, and durable nested approvals.
|
|
185
|
+
- [Coding workspaces](coding-workspaces.md): per-child worktree isolation for parallel spawns.
|
|
172
186
|
- [A2A interoperability](a2a.md): the cross-service protocol boundary.
|
|
173
187
|
- [Agent identity](agent-identity.md): verified identity propagation and narrowing (`narrowIdentity`, `assertIdentityPropagation`).
|
|
174
188
|
- [Agent events](agent-events.md): `delegated_agent_step` and delegation event surfaces for timelines.
|
package/docs/observability.md
CHANGED
|
@@ -13,6 +13,9 @@ APIs:
|
|
|
13
13
|
- `createOpenTelemetryInstrumentation()`, `wrapOpenTelemetryApi()`, `createInMemoryTelemetry()` in `@arnilo/prism-core/governance/observability`
|
|
14
14
|
- `createRagTelemetry()` in `@arnilo/prism-core/governance/observability` (RAG spans/events; see span tree below)
|
|
15
15
|
- `handleRunFeedback()` / `handleEvaluation()` for explicit safe post-run projection
|
|
16
|
+
- `projectAgentTimeline()`, `projectTraceTimeline()`, `projectWorkflowTimeline()`, `createTimelineFolder()` — [execution timeline projection](execution-timeline.md) for host cockpits and trajectory evals
|
|
17
|
+
- `summarizeTimeline()`, `summarizeSession()` — [cockpit aggregations](#cockpit-aggregations-and-session-summaries) (bounded tool counts, token and cost rollups without double counting)
|
|
18
|
+
- `attachWorkflow()`, `handleWorkflowEvent()` on `OpenTelemetryInstrumentation` for workflow DAG spans and metrics
|
|
16
19
|
|
|
17
20
|
## When to use it
|
|
18
21
|
|
|
@@ -67,6 +70,10 @@ const telemetry = createOpenTelemetryInstrumentation({
|
|
|
67
70
|
|
|
68
71
|
const detach = telemetry.attachSession(session);
|
|
69
72
|
// or: for await (const event of session.subscribe()) telemetry.handleAgentEvent(event);
|
|
73
|
+
|
|
74
|
+
// Workflows:
|
|
75
|
+
const detachWorkflow = telemetry.attachWorkflow(eventBus);
|
|
76
|
+
// or: eventBus.subscribe((event) => telemetry.handleWorkflowEvent(event));
|
|
70
77
|
```
|
|
71
78
|
|
|
72
79
|
Set `enabled: false` or omit `tracer`/`meter` for a no-op adapter. Feedback handlers accept only `runId`, rating/score, booleans, bounded counts, and fixed status — never comment, tag values, scorer/evaluation IDs, or arbitrary metadata.
|
|
@@ -97,6 +104,14 @@ OpenTelemetry mapping (when enabled):
|
|
|
97
104
|
| `handleRunFeedback` | active-run `prism.run.feedback` event or ended-run span | `prism.run.feedback` |
|
|
98
105
|
| `handleEvaluation` | active-run `gen_ai.evaluation.result` event or ended-run span | `prism.run.evaluation` (`status`) |
|
|
99
106
|
|
|
107
|
+
Workflow mapping (when enabled via `attachWorkflow` or `handleWorkflowEvent`):
|
|
108
|
+
|
|
109
|
+
| Workflow event | Span | Metric labels |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| `workflow_started` / `workflow_finished` | `invoke_workflow {workflowId}` (`INTERNAL`) | `prism.workflow.duration` (`prism.workflow.id`, `prism.workflow.status`) |
|
|
112
|
+
| `node_started` / `node_finished` / `node_failed` / `node_skipped` | `prism.workflow.node {nodeId}` child (`INTERNAL`) | none (span attributes: `prism.node.kind`, `prism.node.status`) |
|
|
113
|
+
| `node_iteration_started` / `finished` | `prism.workflow.iteration` events on node span | none |
|
|
114
|
+
|
|
100
115
|
RAG span tree (`@arnilo/prism-memory/rag` + `createRagTelemetry()`):
|
|
101
116
|
|
|
102
117
|
| Span | Parent | Notes |
|
|
@@ -171,6 +186,41 @@ const ragTelemetry = createRagTelemetry({ tracer: memory.tracer, meter: memory.m
|
|
|
171
186
|
const found = await retrieveContext("policy", { embedder, store, scope, telemetry: ragTelemetry }); // rag_request tree
|
|
172
187
|
```
|
|
173
188
|
|
|
189
|
+
## Cockpit aggregations and session summaries
|
|
190
|
+
|
|
191
|
+
Host cockpits and dashboard cards need fast aggregate summaries of an execution without re-walking every raw event or risking prompt/secret leaks:
|
|
192
|
+
|
|
193
|
+
- `summarizeTimeline(timeline)`: rolls up an `ExecutionTimeline` into a `TimelineSummary` containing duration, turn count, tool call counts, provider attempts, total tokens, cost, error counts, and suspension state.
|
|
194
|
+
- `summarizeSession(timelines)`: rolls up an array of `ExecutionTimeline`s for a session/conversation into a `SessionSummary` with aggregated tokens, costs, run counts, and duration.
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { summarizeTimeline, summarizeSession } from "@arnilo/prism-core/governance/observability";
|
|
198
|
+
|
|
199
|
+
const summary = summarizeTimeline(timeline);
|
|
200
|
+
// summary: TimelineSummary
|
|
201
|
+
// {
|
|
202
|
+
// durationMs: 1250,
|
|
203
|
+
// turnCount: 2,
|
|
204
|
+
// toolCallCount: 3,
|
|
205
|
+
// toolCounts: { search: 2, lookup: 1 },
|
|
206
|
+
// providerAttempts: 2,
|
|
207
|
+
// usage: { totalTokens: 450, promptTokens: 300, completionTokens: 150 },
|
|
208
|
+
// cost: { amount: 0.0012, currency: "USD" },
|
|
209
|
+
// errorCount: 0,
|
|
210
|
+
// blockedToolCount: 0,
|
|
211
|
+
// suspended: false,
|
|
212
|
+
// status: "succeeded",
|
|
213
|
+
// }
|
|
214
|
+
|
|
215
|
+
const sessionSummary = summarizeSession([run1Timeline, run2Timeline]);
|
|
216
|
+
// sessionSummary: SessionSummary
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Cardinality and correctness guarantees:
|
|
220
|
+
- **Bounded cardinality**: `toolCounts` is capped to `MAX_SUMMARY_DISTINCT_TOOLS = 64` distinct tool names. If more tools are invoked, lowest-frequency tool names overflow into an `"other"` bucket.
|
|
221
|
+
- **No double counting**: Token usage is derived from the root run's `run_total` (or aggregated across `turn` / `provider` steps if no run-level total exists), avoiding double counting between provider turn steps and run totals. Costs are rounded to 6 decimal places to prevent floating-point drift.
|
|
222
|
+
- **Payload-free**: Summaries contain counts, durations, status codes, and usage metrics only — zero prompt text, tool arguments, or credentials.
|
|
223
|
+
|
|
174
224
|
## Extension and configuration notes
|
|
175
225
|
|
|
176
226
|
- Events flow through `redactAgentEvent` before subscribers and ledger writes — configure `createSecretRedactor` on the agent/run.
|
|
@@ -214,11 +264,12 @@ const entries = capture.events(); // oldest-first snapshot; capture.clear() rese
|
|
|
214
264
|
- Capture middleware follows the same default: `redact: "secrets"` drops message content; buffers are capped and secrets are redacted unconditionally, so a captured buffer can be persisted or replayed without leaking credentials.
|
|
215
265
|
- Use `identityTelemetryAttributes(identity)` when attaching enterprise identity to run metadata or OTel attributes; it emits `prism.identity.*` refs only (tenant/principal/scope counts), never credential secrets or raw tokens.
|
|
216
266
|
- Opt-in content in other event types (`message_delta`, tool `result`) is still subject to `redactAgentEvent`.
|
|
217
|
-
- Metric labels stay low-cardinality (`gen_ai.operation.name`, `gen_ai.provider.name`, token type, controlled outcome/status, feedback rating bucket/link presence); never use session/run/request/call IDs, model output, comments, tag values, scorer/evaluation IDs, or arbitrary metadata as labels. Token usage is recorded once at provider operation scope.
|
|
267
|
+
- Metric labels stay low-cardinality (`gen_ai.operation.name`, `gen_ai.provider.name`, token type, controlled outcome/status, feedback rating bucket/link presence, controlled `prism.workflow.id` names); never use session/run/request/call IDs, dynamic run IDs, model output, comments, tag values, scorer/evaluation IDs, or arbitrary metadata as labels. Token usage is recorded once at provider operation scope.
|
|
218
268
|
- Target overhead when enabled is under 5% excluding exporter I/O; disabled hooks allocate no spans.
|
|
219
269
|
- Provider transport limits and redaction order are documented in [Provider primitives](provider-primitives.md).
|
|
220
270
|
|
|
221
271
|
## Related APIs
|
|
272
|
+
- [Execution timeline](execution-timeline.md): cockpit projection — fold `AgentEvent` or `WorkflowEvent` into `ExecutionTimeline`.
|
|
222
273
|
- [Agent identity](agent-identity.md): redacted identity attribute helper for telemetry.
|
|
223
274
|
- [Evaluations](evaluations.md): optional scorers can link scores to run/session/trace IDs from agent events.
|
|
224
275
|
|
package/docs/openapi-tools.md
CHANGED
|
@@ -4,7 +4,7 @@ Optional `createOpenApiTools` compiles host-selected OpenAPI 3.1 operations into
|
|
|
4
4
|
|
|
5
5
|
## When to use it
|
|
6
6
|
|
|
7
|
-
Hosts that already expose a JSON API with an OpenAPI 3.1 document and want the agent to call a **fixed, host-chosen subset** of it — never model-driven discovery, never a raw method/path passthrough. For vendor web search/extraction use `@arnilo/prism-web-tools`; for M365/GWS use `@arnilo/prism-
|
|
7
|
+
Hosts that already expose a JSON API with an OpenAPI 3.1 document and want the agent to call a **fixed, host-chosen subset** of it — never model-driven discovery, never a raw method/path passthrough. For vendor web search/extraction use `@arnilo/prism-web-tools`; for M365/GWS use `@arnilo/prism-work/connectors`; this adapter is for arbitrary host APIs.
|
|
8
8
|
|
|
9
9
|
## Usage
|
|
10
10
|
|
package/docs/operations.md
CHANGED
|
@@ -8,6 +8,7 @@ This page is the operator runbook for the high-availability story proven by plan
|
|
|
8
8
|
|
|
9
9
|
- Before running any multi-replica deployment of the server, workflow coordinator, saga runner, ACP host, or enterprise dispatcher — read the local-registry limitations and the lease/fence model.
|
|
10
10
|
- When an operator or on-call engineer sees a hung lease, an uncertain commit, or a split-brain suspicion: follow "Failover procedure" and "Uncertain commits" below before touching anything.
|
|
11
|
+
- When background workers share a checkpoint queue across tenants: use `admission` on `createWorkflowCoordinator` and `createPrismOperatorHandler` for inspect/cancel/reconcile. Do not add a second job broker.
|
|
11
12
|
- When sizing leases: the failover ceiling is lease TTL plus the peer's acquisition poll interval; the drill asserts `failoverMs <= ttlMs + 5000`.
|
|
12
13
|
|
|
13
14
|
## Inputs / request
|
|
@@ -81,9 +82,7 @@ tenant's reads, writes, and lease takeover all fail closed.
|
|
|
81
82
|
fencing token), and a stale token's renewal returns `null`. There is
|
|
82
83
|
intentionally no "force unlock" operation — deleting a lease row manually
|
|
83
84
|
bypasses fencing and can cause split-brain writes; never do it.
|
|
84
|
-
- Tenant ownership is checked on every durable read/write
|
|
85
|
-
reads, saves, and lease acquisitions fail closed with ownership-mismatch
|
|
86
|
-
errors.
|
|
85
|
+
- Tenant ownership is checked on every durable read/write: a foreign checkpoint read or delete is a miss (never an existence oracle), a foreign checkpoint write fails as `ERR_PRISM_CHECKPOINT_CONFLICT`, and lease acquisition still fails closed with an ownership-mismatch error.
|
|
87
86
|
- An uncertain commit (side effect landed, cursor not advanced) is resolved
|
|
88
87
|
by replaying the effect with its stable id — never by guessing. Reload
|
|
89
88
|
durable state before any retry; retries that skip the reload risk
|
|
@@ -94,6 +93,16 @@ tenant's reads, writes, and lease takeover all fail closed.
|
|
|
94
93
|
with bounded, jittered acquisition polls (no hot loops) and reports the
|
|
95
94
|
measured numbers in the evidence JSON.
|
|
96
95
|
|
|
96
|
+
## Fair worker admission and operator intervention
|
|
97
|
+
|
|
98
|
+
`createWorkflowCoordinator({ admission })` is still checkpoint+lease polling — not a job DSL. Each `pollOnce` walks at most `maxPagesPerPoll` list pages from a wrap cursor so a saturated first page cannot hide later tenants. `perTenant` / `perClass` cap **this worker's** concurrent claims. `deadlineMs` skips (does not auto-fail) stale `createdAt`. While `drain.isDraining`, no new claims; after `snapshot().expired`, in-flight work is aborted if the node honors the run signal. Leases are released only via the holder token — never deleted by hand.
|
|
99
|
+
|
|
100
|
+
`createPrismOperatorHandler({ authorize, checkpoints, workflows, unknownEffects? })` mounts `/ops/queue|suspended|failed|unknown` and `POST /ops/cancel|reconcile`. `authorize` is required and fail-closed; lists are ownership-scoped; bodies omit workflow input/state. Reconcile accepts only `completed` or `failed_terminal` plus optional evidence. Unknown effects are never retried from this route.
|
|
101
|
+
|
|
102
|
+
Metric hook labels: `outcome` and allowlisted `class` only.
|
|
103
|
+
|
|
104
|
+
Network-free composition: [`examples/server-deployment-seams.ts`](../examples/server-deployment-seams.ts).
|
|
105
|
+
|
|
97
106
|
## Live probe (plans/064 Task 9)
|
|
98
107
|
|
|
99
108
|
The outbound webhook notifier has an operator-gated live probe against a receiver you own:
|
|
@@ -110,6 +119,7 @@ Probes: one signed delivery to your receiver (verify `x-prism-signature: sha256=
|
|
|
110
119
|
- `LeaseStore` / `CheckpointStore` — the durable contracts this runbook relies on.
|
|
111
120
|
- `createPostgresPersistence` — the PostgreSQL adapter used by the drill.
|
|
112
121
|
- `ErpOutboxStore` — the idempotent side-effect carrier used to make replay safe.
|
|
113
|
-
- `createWorkflowCoordinator` and `
|
|
122
|
+
- `createWorkflowCoordinator` (`admission`) and `createPrismOperatorHandler` — fair claim policy and ownership-scoped cancel/reconcile.
|
|
123
|
+
- `defineSaga`/`runSaga`/`resumeSaga` — higher-level consumers with the same fencing/cursor semantics.
|
|
114
124
|
- `scripts/phase27-ha-worker.mjs` / `scripts/phase27-ha.test.mjs` — the reproducible drill; `docs/_evidence/phase27-ha-evidence.json` — the recorded run.
|
|
115
125
|
- [Signed, hash-chained audit export](audit-export.md) — the cursor/CAS pattern applied to audit exports.
|
package/docs/options-index.md
CHANGED
|
@@ -29,6 +29,24 @@ Field-level detail (defaults, bounds, failure modes) lives on the owning page
|
|
|
29
29
|
- **Request-time overrides narrow, never widen.** `RunOptions.limits` can only tighten `AgentConfig.limits`; a configured finite ceiling wins over `null`.
|
|
30
30
|
- **Byte caps are not estimator-dependent.** Token-budget options that accept a host estimator (`tokenEstimator`) affect eviction accounting only; byte caps and redaction stay authoritative.
|
|
31
31
|
|
|
32
|
+
## Durable runs
|
|
33
|
+
|
|
34
|
+
**Agent run state** — [`durable-runs.md`](durable-runs.md)
|
|
35
|
+
`AgentRunStateOptions`
|
|
36
|
+
|
|
37
|
+
`AgentRunStateOptions.checkpointPolicy` (`"decision"` | `"every-turn"`) writes turn-boundary checkpoints so a crashed worker resumes with `decision: "continue"`; that action stays host-only and is never reachable from AG-UI or the server boundary.
|
|
38
|
+
|
|
39
|
+
## Host stop, snapshot, and honesty surfaces
|
|
40
|
+
|
|
41
|
+
| Surface | What it controls | Owning page |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| `RunOptions.turnPolicy` (`TurnPolicyOptions`) | Synchronous host stop at a turn boundary; a stop lands as `stopReason: "host_policy"` and stays resumable | [Agent loops](agent-loops.md) |
|
|
44
|
+
| `CreateAgUiHandlerOptions.inputPolicy` (`AgUiInputPolicyOptions`) | `clientState: "honor"` \| `"ignore"` — whether the server honors client-supplied AG-UI state and tools | [Frontend interoperability](ag-ui.md) |
|
|
45
|
+
| `snapshotRunBundle(...)` → `RunBundleSnapshot` | Inspectable digest projection of the effective run bundle (prompt/skill/tool/guardrail digests, limits, storage kinds) | [Run bundle](run-bundle.md) |
|
|
46
|
+
| `createClaimGroundingGuardrail` (`ClaimGroundingGuardrailOptions`) | `"output"`-stage guardrail that blocks or flags numeric claims no tool result or host evidence supports | [Guardrails](guardrails.md) |
|
|
47
|
+
| `ErrorInfo.failureClass` (`ProviderFailureClass`) | Typed provider failure on run outcomes, ledger rows, and tool results (`quota`, `rate_limited`, `auth`, `transient`, `permanent`) | [Runs and usage](runs-and-usage.md) |
|
|
48
|
+
| `ModelConfig.capabilities.toolCallStrictness` | Advisory tool-call reliability (`"strict"` \| `"lenient"` \| `"legacy"`); catalog conformance, not a promise | [Model registry](model-registry.md) |
|
|
49
|
+
|
|
32
50
|
## Agent/session runtime
|
|
33
51
|
|
|
34
52
|
**Agent definitions** — [`agent-definitions.md`](agent-definitions.md)
|
|
@@ -38,10 +56,13 @@ Field-level detail (defaults, bounds, failure modes) lives on the owning page
|
|
|
38
56
|
`SubscribeOptions`
|
|
39
57
|
|
|
40
58
|
**Agent loops** — [`agent-loops.md`](agent-loops.md)
|
|
41
|
-
`AgentLoopOptions`
|
|
59
|
+
`AgentLoopOptions`, `TurnPolicyOptions`
|
|
60
|
+
|
|
61
|
+
**Guardrails** — [`guardrails.md`](guardrails.md)
|
|
62
|
+
`ClaimGroundingGuardrailOptions`
|
|
42
63
|
|
|
43
64
|
**Agent/session runtime** — [`agent-session-runtime.md`](agent-session-runtime.md)
|
|
44
|
-
`AgentConfig`, `AgentRunResumeStreamOptions`, `AgentSessionCloneOptions`, `AgentSessionConfig`, `AgentSessionForkOptions`, `RunOptions`, `SteerOptions`
|
|
65
|
+
`AgentConfig`, `AgentRunResumeOptions`, `AgentRunResumeStreamOptions`, `AgentSessionCloneOptions`, `AgentSessionConfig`, `AgentSessionForkOptions`, `RunOptions`, `SteerOptions`
|
|
45
66
|
|
|
46
67
|
**Evaluations** — [`evaluations.md`](evaluations.md)
|
|
47
68
|
`ScoreRunOptions`
|
|
@@ -78,6 +99,12 @@ Field-level detail (defaults, bounds, failure modes) lives on the owning page
|
|
|
78
99
|
**Working and semantic memory** — [`working-and-semantic-memory.md`](working-and-semantic-memory.md)
|
|
79
100
|
`PostgresVectorStoreOptions`, `RecallScoringOptions`
|
|
80
101
|
|
|
102
|
+
**Memory fabric** — [`memory-fabric.md`](memory-fabric.md)
|
|
103
|
+
`CreateMemoryFabricOptions`, `MemoryFabricAttachOptions`, `MemoryFabricToolsOptions`, `MemoryFabricWriteOptions`, `MemoryFabricRecallOptions`, `MemoryFabricConsolidationOptions`, `MemoryFabricLinkerOptions`, `MemoryFabricEvolutionOptions`, `MemoryFabricConversationSearchOptions`, `CreateFabricFileJailOptions`
|
|
104
|
+
|
|
105
|
+
**Observational memory** — [`compaction-observational-memory.md`](compaction-observational-memory.md)
|
|
106
|
+
`ObservationalMemoryAppendOptions`, `WorkScopeControllerOptions`
|
|
107
|
+
|
|
81
108
|
## Provider and model connection
|
|
82
109
|
|
|
83
110
|
**Model registry** — [`model-registry.md`](model-registry.md)
|
|
@@ -142,6 +169,9 @@ Field-level detail (defaults, bounds, failure modes) lives on the owning page
|
|
|
142
169
|
**Context and skills** — [`context-and-skills.md`](context-and-skills.md)
|
|
143
170
|
`ResolveActiveSkillsOptions`, `ResolveContextOptions`
|
|
144
171
|
|
|
172
|
+
**Attention compiler** — [`attention-compiler.md`](attention-compiler.md)
|
|
173
|
+
`AttentionCompilerOptions`, `AttentionInputCapOptions`, `AttentionCompileOptions`, `AttentionTruncationTriggerOptions`
|
|
174
|
+
|
|
145
175
|
**Input and prompt assembly** — [`input-and-prompt-assembly.md`](input-and-prompt-assembly.md)
|
|
146
176
|
`AssembleProviderInputOptions`, `PromptTemplateOptions`
|
|
147
177
|
|
|
@@ -221,13 +251,27 @@ Field-level detail (defaults, bounds, failure modes) lives on the owning page
|
|
|
221
251
|
`AcpCapabilitiesOptions`, `CreatePrismAcpAgentOptions`
|
|
222
252
|
|
|
223
253
|
**Frontend interoperability (AG-UI and ACP)** — [`ag-ui.md`](ag-ui.md)
|
|
224
|
-
`AgUiLimitOptions`
|
|
254
|
+
`AgUiInputPolicyOptions`, `AgUiLimitOptions`
|
|
255
|
+
|
|
256
|
+
**Supervisors and subagents** — [`supervisors.md`](supervisors.md)
|
|
257
|
+
`CreateSupervisorOptions`, `SupervisorLimits`, `ResolvedSupervisorLimits`, `DelegationWaitOptions`, `CreateSpawnAgentToolOptions`, `CreateDelegationControlToolOptions`, `WorktreeChildFactoryOptions`, `ObserveSupervisorLifecycleOptions`
|
|
225
258
|
|
|
226
259
|
## CLI/RPC
|
|
227
260
|
|
|
228
261
|
**Workflows** — [`workflows.md`](workflows.md)
|
|
229
262
|
`RunWorkflowOptions`
|
|
230
263
|
|
|
264
|
+
## Third-party integrations
|
|
265
|
+
|
|
266
|
+
**Messaging channels** — [`messaging-channels.md`](messaging-channels.md)
|
|
267
|
+
`MessagingRuntimeOptions`, `ChannelLimits`, `ChannelDeliveryJournalOptions`, `ChannelPairingStoreOptions`, `ChannelStateStoreOptions`
|
|
268
|
+
|
|
269
|
+
**Telegram channel** — [`telegram-channel.md`](telegram-channel.md)
|
|
270
|
+
`TelegramAdapterOptions`, `TelegramWebhookHandlerOptions`
|
|
271
|
+
|
|
272
|
+
**Signal channel (experimental)** — [`signal-channel.md`](signal-channel.md)
|
|
273
|
+
`SignalAdapterOptions`
|
|
274
|
+
|
|
231
275
|
## Conformance harnesses
|
|
232
276
|
|
|
233
277
|
These option objects configure the shipped test doubles a host runs against its own adapters (provider, store, run ledger, compaction, tool, extension). They are host-facing, but only in test code.
|
|
@@ -11,7 +11,7 @@ Lists every third-party peer a Prism package declares, what importing that peer
|
|
|
11
11
|
- Debugging a "peer not installed" error from a gated subpath.
|
|
12
12
|
- Auditing which of your already-installed packages a Prism surface will reuse.
|
|
13
13
|
|
|
14
|
-
Internal `@arnilo/*` peers are not listed here: every first-party package declares a required `@arnilo/prism` peer, and the release gate keeps all internal ranges locked to the cut version. This page covers the **
|
|
14
|
+
Internal `@arnilo/*` peers are not listed here: every first-party package declares a required `@arnilo/prism` peer, and the release gate keeps all internal ranges locked to the cut version. This page covers the **12 third-party declarations across 6 packages**.
|
|
15
15
|
|
|
16
16
|
## Matrix
|
|
17
17
|
|
|
@@ -22,8 +22,9 @@ One row per declaration. `Unlocks` names the subpath whose import reaches the pe
|
|
|
22
22
|
| `zod` | `^3.25.0 \|\| ^4.0.0` | no | `@arnilo/prism-ag-ui` | `./acp` | `npm i zod` | no |
|
|
23
23
|
| `@nanonets/graft` | `^0.16.0 \|\| ^0.18.0` | yes | `@arnilo/prism-memory` | `./graft` | `npm i @nanonets/graft` | no |
|
|
24
24
|
| `@dietrichgebert/ponytail` | `^4.9.0` | yes | `@arnilo/prism-coding-tools` | `./ponytail` | `npm i @dietrichgebert/ponytail` | no |
|
|
25
|
-
| `mammoth` | `^1.8.0` | yes | `@arnilo/prism-
|
|
26
|
-
| `pdf-parse` | `^2.4.5` | yes | `@arnilo/prism-
|
|
25
|
+
| `mammoth` | `^1.8.0` | yes | `@arnilo/prism-work` | `./document-reader` | `npm i mammoth` | no |
|
|
26
|
+
| `pdf-parse` | `^2.4.5` | yes | `@arnilo/prism-work` | `./document-reader` | `npm i pdf-parse` | no |
|
|
27
|
+
| `e2b` | `2.49.1` | yes | `@arnilo/prism-coding-tools` | `./security` | `npm i e2b@2.49.1` | yes |
|
|
27
28
|
| `better-sqlite3` | `^13.0.3` | yes | `@arnilo/prism-core` | `./sessions/sqlite`, `./governance/prompts` | `npm i better-sqlite3` | no |
|
|
28
29
|
| `pg` | `^8.23.0` | yes | `@arnilo/prism-core` | `./sessions/postgres`, `./enterprise/postgres`, `./governance/prompts` | `npm i pg` | yes |
|
|
29
30
|
| `@nats-io/jetstream` | `^3.4.0` | yes | `@arnilo/prism-core` | `./sessions/nats` | `npm i @nats-io/jetstream @nats-io/transport-node` | yes |
|
|
@@ -37,19 +38,20 @@ Two peers are pinned to an exact version instead of a range, because the pin is
|
|
|
37
38
|
|
|
38
39
|
- **`playwright-core@1.63.0`** (`@arnilo/prism-web-tools/browser`, `/obscura`). Browser automation rides Playwright's CDP transport and accessibility snapshot shapes, which move between minors. Prism never launches, downloads, or bundles a browser: the host supplies the binary, the image, and the cache, and must match the pinned client. See [Browser automation](browser-automation.md).
|
|
39
40
|
- **`@ai-sdk/provider@4.0.13`** (`@arnilo/prism-providers/ai-sdk`). The adapter consumes deterministic specification-versioned types (`LanguageModelV4`) and gates on an exact supported-version matrix at construction, so an unlisted version fails closed instead of silently mis-mapping. See [AI SDK provider](providers/ai-sdk.md).
|
|
41
|
+
- **`e2b@2.49.1`** (`@arnilo/prism-coding-tools/security`). Pause `keepMemory`, `Sandbox.connect` auto-resume, and `ServiceBusyError` 503 semantics are version-specific. Hosts may inject `{ client }` instead of installing the peer. See [Hosted sandboxes](hosted-sandboxes.md).
|
|
40
42
|
|
|
41
43
|
`zod` is the only **required** third-party peer. `@agentclientprotocol/sdk` — a hard dependency of `@arnilo/prism-ag-ui` — declares `zod: ^3.25.0 || ^4.0.0` as its own peer, so `@arnilo/prism-ag-ui` re-declares the same range to keep the install tree satisfiable; the range is deliberately identical to the SDK's. Nothing in Prism imports zod directly.
|
|
42
44
|
|
|
43
45
|
## Peers that touch the network
|
|
44
46
|
|
|
45
|
-
`pg`, `@nats-io/jetstream`, `@nats-io/transport-node`,
|
|
47
|
+
`pg`, `@nats-io/jetstream`, `@nats-io/transport-node`, `playwright-core`, and `e2b` open sockets. For a supply-chain review of those five:
|
|
46
48
|
|
|
47
|
-
- **Connection targets are host-owned.** Every one of them is passed a host-supplied connection string, endpoint list, browser instance, or service URL. Prism holds no default endpoint, and no peer is reachable from the root import.
|
|
49
|
+
- **Connection targets are host-owned.** Every one of them is passed a host-supplied connection string, endpoint list, browser instance, API key, or service URL. Prism holds no default endpoint, and no peer is reachable from the root import.
|
|
48
50
|
- **Bytes stay local otherwise.** `better-sqlite3`, `mammoth`, `pdf-parse`, `@nanonets/graft`, and `@dietrichgebert/ponytail` are filesystem/process peers; the remaining two (`zod`, `@ai-sdk/provider`) are pure types/schemas.
|
|
49
51
|
- **No secrets are read by the peers.** Prism resolves credentials through host providers and redacts them at the boundary; peers only ever receive a resolved connection string or model object. See [Credentials and redaction](credentials-and-redaction.md) and [Host security guide](host-security.md).
|
|
50
52
|
- **Nothing is installed implicitly.** Optional peers are never auto-installed by npm; a missing one fails closed at the call site with a typed error naming the peer and the subpath. Required peers (today only `zod`) are installed by npm with the package.
|
|
51
53
|
|
|
52
|
-
Test-only dependencies are *not* peers. `playwright-core` appears in `@arnilo/prism-
|
|
54
|
+
Test-only dependencies are *not* peers. `playwright-core` appears in `@arnilo/prism-work` as a devDependency only, because the office diagrams embed takes a host-supplied iframe and the sole consumer is the gated live draw.io conformance test.
|
|
53
55
|
|
|
54
56
|
## Implementation example
|
|
55
57
|
|
|
@@ -64,7 +66,7 @@ npm i @arnilo/prism-core pg
|
|
|
64
66
|
npm i @arnilo/prism-core @nats-io/transport-node @nats-io/jetstream
|
|
65
67
|
|
|
66
68
|
# Document reader: pick the parser you need (both are independent)
|
|
67
|
-
npm i @arnilo/prism-
|
|
69
|
+
npm i @arnilo/prism-work pdf-parse mammoth
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
```ts
|
|
@@ -78,17 +80,17 @@ const tools = await createBrowserTools({ browser });
|
|
|
78
80
|
## Extension and configuration notes
|
|
79
81
|
|
|
80
82
|
- A peer is an *implementation the host owns*. When a peer's default wiring is not what you want, pass your own implementation instead of installing theirs: the document reader accepts host parsers (`createReadTool({ documentReader })`), the memory `/graft` resolver accepts an explicit package root, and the browser surfaces accept a host `Browser`.
|
|
81
|
-
- Subpaths that need a peer isolate that import, so importing another subpath of the same package never evaluates it. The office family is the extreme case: zero peers, because it takes structural inputs.
|
|
83
|
+
- Subpaths that need a peer isolate that import, so importing another subpath of the same package never evaluates it. The office family is the extreme case: zero peers, because it takes structural inputs. `@arnilo/prism-channels` also has no third-party peers: Telegram uses native `fetch`, and signal-cli is a host-operated binary rather than an npm peer.
|
|
82
84
|
- Adding a peer to a Prism package is a release-gated change: the declaration must be optional unless a hard dependency's own peer forces it (the `zod` case), and exact pins must come with a version-gate or compatibility rationale.
|
|
83
85
|
|
|
84
86
|
## Security and performance notes
|
|
85
87
|
|
|
86
88
|
- Pinned peers must be updated through the release process, not by a host override: an unpinned browser client or AI SDK type surface is a silent behavior change.
|
|
87
|
-
- Peer installs are host-visible supply-chain additions. Prefer one peer per capability, keep them out of the root import, and audit transitive dependencies of the
|
|
89
|
+
- Peer installs are host-visible supply-chain additions. Prefer one peer per capability, keep them out of the root import, and audit transitive dependencies of the five network-touching peers in your own policy.
|
|
88
90
|
- Prism adds no runtime cost for an uninstalled peer; the failure is a typed error at first use.
|
|
89
91
|
|
|
90
92
|
## Related APIs
|
|
91
93
|
|
|
92
94
|
- [Release and install](release-and-install.md): install profiles that pair with each peer.
|
|
93
95
|
- [Configuration options index](options-index.md): the option surfaces each peer unlocks.
|
|
94
|
-
- Package-level detail: [Coding tools](coding-tools.md), [Core runtime](core.md), [Session stores](session-stores.md), [Browser automation](browser-automation.md), [Document reader](document-reader.md), [Graft](graft.md), [Ponytail](ponytail.md), [Provider packages](provider-packages.md).
|
|
96
|
+
- Package-level detail: [Coding tools](coding-tools.md), [Core runtime](core.md), [Session stores](session-stores.md), [Browser automation](browser-automation.md), [Document reader](document-reader.md), [Graft](graft.md), [Ponytail](ponytail.md), [Provider packages](provider-packages.md), [Messaging channels](messaging-channels.md).
|
|
@@ -60,7 +60,7 @@ Hosts own TLS (`ssl` in `poolConfig`), credentials, connection limits, and backu
|
|
|
60
60
|
| `RunLedger.append*` | Inserts run/event/tool/usage rows; events receive monotonic per-run `sequence` values. |
|
|
61
61
|
| `events` | Durable `AgentEventSource`; `LISTEN`/`NOTIFY` only wakes exact owned indexed reads, while polling remains recovery fallback. |
|
|
62
62
|
| `ProductionPersistenceStore.query*` | Parameterized cursor pagination on indexed columns with tenant/account/user filters. |
|
|
63
|
-
| `checkpoints` | Generic versioned `CheckpointStore` backed by `prism_checkpoints`; ownership, CAS/fencing checks, bounded pagination, and workflow suspended/denied/schedule/state/replay values without a schema migration. |
|
|
63
|
+
| `checkpoints` | Generic versioned `CheckpointStore` backed by `prism_checkpoints`; ownership, CAS/fencing checks, bounded pagination, and workflow suspended/denied/schedule/state/replay values without a schema migration. A load or delete under a non-matching ownership scope reads as absent and a cross-scope write fails as a generic `ERR_PRISM_CHECKPOINT_CONFLICT` (plan 080 Task 3) — no ownership-shaped existence oracle. |
|
|
64
64
|
| `leases` | Atomic `LeaseStore` backed by `prism_leases`; database-clock expiry, opaque renew/release token, monotonic takeover fence. |
|
|
65
65
|
| `close()` | Ends the pool when the adapter created it from `connectionString`. |
|
|
66
66
|
|
package/docs/process-sessions.md
CHANGED
|
@@ -136,7 +136,8 @@ await sessions.dispose();
|
|
|
136
136
|
- Host restart: call `reconcile()` on a new registry for in-memory orphans, or listen for `process_unknown` and wire Phase 7 `ToolEffectStore.markUnknown` in the host.
|
|
137
137
|
- Expiry sweep runs on registry/handle access — no timers at import.
|
|
138
138
|
- Command fingerprint is SHA-256 of `[command, ...args]` only (no env).
|
|
139
|
-
- Docker
|
|
139
|
+
- Docker sandbox (`createDockerSandbox`) implements `startProcess` returning a compliant `SandboxProcessHandle` with durable `ref` for attested reconnect. Process operations (`write`, bounded `onData` output, `wait`, `signal`, `kill`, `release`) compose with `ProcessSessions`. The sandbox `attachProcess(ref)` resolves an opaque `prism-docker-proc:` ref to a live in-memory handle with fail-closed validation (container ID, workspace, command fingerprint). `createDockerProcessRecoveryBackend(sandbox, options?)` provides a ready-made `ProcessRecoveryBackend` with optional `expectedContainerId`, `expectedWorkspace`, and `expectedLabels` assertions. When a sandbox is passed to `createProcessSessions` and has `attachProcess`, the recovery backend is wired automatically.
|
|
140
|
+
- Hosted E2B sandbox (`createE2BSandbox`) also implements `startProcess` / `attachProcess` with opaque `prism-e2b-proc:` refs (sandbox id, pid, command fingerprint, workspace). `createE2BProcessRecoveryBackend` mirrors the Docker backend (`expectedSandboxId` / `expectedWorkspace` / `expectedLabels`). Filesystem-only `pause({ keepMemory: false })` reports process loss: `attachProcess` returns null after that snapshot. `connectE2BSandbox` reconnects by sandbox id without auto-resume; call `resume()` explicitly. See [Hosted sandboxes](hosted-sandboxes.md).
|
|
140
141
|
|
|
141
142
|
## Durable process recovery (plan 026 Task 5)
|
|
142
143
|
|
|
@@ -199,4 +200,5 @@ optionally `recoveryBackend` + `recoveryLimits`. With durability configured:
|
|
|
199
200
|
- [Coding agent tools](coding-agent-tools.md): one-shot `shell` vs long-running sessions.
|
|
200
201
|
- [Language intelligence](language-intelligence.md): LSP servers may later register as managed sessions.
|
|
201
202
|
- [Coding security](coding-security.md): `SandboxProcessHandle` / optional `DisposableSandbox.startProcess`.
|
|
203
|
+
- [Hosted sandboxes](hosted-sandboxes.md): E2B `pause`/`resume` and `prism-e2b-proc:` reconnect refs.
|
|
202
204
|
- [Tool effects](tool-effects.md): unknown-outcome vocabulary mirrored by `markUnknown` / `process_unknown` / `reconcile`.
|
package/docs/prompt-registry.md
CHANGED
|
@@ -90,7 +90,7 @@ const v = await assertPromptPromotion({
|
|
|
90
90
|
if (v.verdict === "promote") await store.put({ ...hostInput, body: v.candidate.body, labels: ["production"] });
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
The verdict carries `promote`/`hold`, per-scorer `wins/losses/ties/failures`, `winRate`, the raw `ComparisonReport`, a redacted bounded `reportJson` (`serializeEvaluationReport`), and `reasons` on hold. The default gate holds unless the candidate wins strictly more scored comparisons than the baseline; `minimumWinRate` and `thresholds` add stricter gates, and threshold equality passes. Requires the optional peer `@arnilo/prism-core/governance/evals` (install it or the helper fails closed with `ERR_PRISM_PROMPT_EVALS_PEER`). Promotion itself stays a host decision: applying the verdict means `put`-ing a new version with labels — the helper never does.
|
|
93
|
+
The verdict carries `promote`/`hold`, per-scorer `wins/losses/ties/failures`, `winRate`, the raw `ComparisonReport`, a redacted bounded `reportJson` (`serializeEvaluationReport`), and `reasons` on hold. The default gate holds unless the candidate wins strictly more scored comparisons than the baseline; `minimumWinRate` and `thresholds` add stricter gates, and threshold equality passes. Requires the optional peer `@arnilo/prism-core/governance/evals` (install it or the helper fails closed with `ERR_PRISM_PROMPT_EVALS_PEER`). Promotion itself stays a host decision: applying the verdict means `put`-ing a new version with labels — the helper never does. When provenance tracking is required, evaluation experiments bind candidate prompt versions via `EvalManifest.promptId` and `promptVersion`; release evidence also calls `validateReleaseEvalManifest` (see [Evaluations](evaluations.md#repeated-trials-and-manifests)).
|
|
94
94
|
|
|
95
95
|
## Limits and security
|
|
96
96
|
|
package/docs/provider-caching.md
CHANGED
|
@@ -166,7 +166,8 @@ Provider request policies can set `ProviderRequestOptions.cache` or the legacy `
|
|
|
166
166
|
| `@arnilo/prism-providers/xai` | `implicit` | No `prompt_cache_key`. Package-local `x-grok-conv-id` is `sanitizeCacheKey(cache.key ?? cacheKey ?? sessionId, 128)`. | Same server + unchanged message prefix. Replay `reasoning_content` on reasoning models or the prefix breaks. | Conv-id is never a credential or SuperGrok token. Omitted when `cache.mode` is `off` or `cacheRetention` is `none`. `cached_tokens` → `cacheReadTokens` (inclusive or exclusive reports kept as-is). |
|
|
167
167
|
| `@arnilo/prism-providers/clinepass` | `implicit` | No `cache_control` / `prompt_cache_key`. Gateway-owned prefix cache. | Resend unchanged prior history. Stream only. | Best-effort and backend-dependent (`cline-pass/*` slugs). `cached_tokens` / `prompt_cache_hit_tokens` map when present. |
|
|
168
168
|
| `@arnilo/prism-providers/azure` | none | No Prism cache mapping. | Endpoint/model-specific. | Host owns Azure cache policy. |
|
|
169
|
-
| `@arnilo/prism-providers/bedrock` | none | No Prism cache mapping. | Endpoint/model-specific. | Host owns Bedrock cache policy. |
|
|
169
|
+
| `@arnilo/prism-providers/bedrock` (`compatible`) | none | No Prism cache mapping. | Endpoint/model-specific. | Host owns Bedrock cache policy. |
|
|
170
|
+
| `@arnilo/prism-providers/bedrock` (`converse`) | `cache_control` | Prism breakpoints become standalone `cachePoint` blocks in `system`/message content; long retention adds `ttl: "1h"` when the model allows it. `tools` caching stays host-owned. | Stable prefix in the documented order `tools → system → messages`; changing an earlier section invalidates later ones. | `cacheReadInputTokens`/`cacheWriteInputTokens` map to `Usage.cacheReadTokens`/`Usage.cacheWriteTokens`; `inputTokens` is the non-cached remainder and is never folded. |
|
|
170
171
|
| `@arnilo/prism-providers/vertex` | none | No Prism cache mapping. | Endpoint/model-specific. | Host owns Vertex cache policy. |
|
|
171
172
|
|
|
172
173
|
Detailed first-party provider notes:
|
|
@@ -188,7 +189,7 @@ Detailed first-party provider notes:
|
|
|
188
189
|
- DeepSeek (`@arnilo/prism-providers/deepseek`): `kind: "implicit"`. Official disk prefix cache is automatic (byte-identical prefix from token 0). Adapter sends no cache payload; tool `parameters` use shared `canonicalizeJsonSchema` (object keys + unordered `required` only; `enum`/`prefixItems`/`examples` keep caller order). `prompt_cache_hit_tokens` maps to `Usage.cacheReadTokens`. Caller-gated `listDeepSeekModels`.
|
|
189
190
|
- xAI (`@arnilo/prism-providers/xai`): `kind: "implicit"`. Automatic prefix cache. Sticky `x-grok-conv-id` is a sanitized session/cache key (128 chars), never an OAuth access token. Reasoning models must replay `reasoning_content`. `prompt_tokens_details.cached_tokens` maps to `Usage.cacheReadTokens`. Caller-gated `listXaiModels`.
|
|
190
191
|
- ClinePass (`@arnilo/prism-providers/clinepass`): `kind: "implicit"`. No explicit cache payload; multi-backend gateway may report `cached_tokens` or `prompt_cache_hit_tokens`. Static `cline-pass/*` catalog only — no `listClinePassModels`.
|
|
191
|
-
- Azure, Bedrock, and Vertex: their OpenAI-compatible packages intentionally emit no Prism cache fields. Endpoint/model-specific cache controls remain host-owned rather than guessed from another provider family.
|
|
192
|
+
- Azure, Bedrock, and Vertex: their OpenAI-compatible packages intentionally emit no Prism cache fields. Endpoint/model-specific cache controls remain host-owned rather than guessed from another provider family. Bedrock's native `converse` route is the exception: it is a documented cache-control surface (`cachePoint`, shared `applyCacheControl` markers) and maps cache usage fields instead of leaving them host-owned.
|
|
192
193
|
|
|
193
194
|
### NeuralWatt cache-aware limiter
|
|
194
195
|
|
|
@@ -303,6 +304,7 @@ for (const sample of report.samples) {
|
|
|
303
304
|
## Related APIs
|
|
304
305
|
|
|
305
306
|
- [Input and prompt assembly](input-and-prompt-assembly.md): opt-in cache-aware ordering for stable provider payload prefixes.
|
|
307
|
+
- [Attention compiler](attention-compiler.md): opt-in per-turn shrink that only rewrites rows *behind* the stable prefix, so cache hits survive.
|
|
306
308
|
- [Provider request policies](provider-request-policies.md): set cache hints before provider calls.
|
|
307
309
|
- [Model registry](model-registry.md): register `ModelConfig.cache` capability metadata.
|
|
308
310
|
- [Provider layer](provider-layer.md): provider/model registries and provider events.
|