@arnilo/prism 0.6.0 → 0.7.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 +44 -5
- package/README.md +10 -10
- package/dist/agent-definitions.js +1 -0
- package/dist/agent-run-lifecycle.js +11 -0
- package/dist/agent-run-state.d.ts +6 -0
- package/dist/agent-run-state.js +29 -9
- package/dist/agent-session/session/assemble.js +33 -2
- package/dist/agent-session/session/persist.js +6 -2
- package/dist/agent-session/session/tool-round.js +1 -0
- package/dist/agent-session/session/types.d.ts +10 -0
- package/dist/agent-session/session.d.ts +14 -0
- package/dist/agent-session/session.js +40 -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/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/provider.d.ts +4 -0
- package/dist/contracts-core.d.ts +1 -0
- package/dist/contracts-core.js +1 -0
- package/dist/contracts-protocol.d.ts +29 -0
- package/dist/contracts-run-state.d.ts +6 -0
- package/dist/host-composition.d.ts +78 -0
- package/dist/host-composition.js +248 -0
- package/dist/index.d.ts +9 -6
- package/dist/index.js +5 -4
- package/dist/input.d.ts +13 -1
- package/dist/input.js +40 -1
- package/dist/secure-agent.d.ts +2 -0
- package/dist/secure-agent.js +6 -1
- 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 +5 -3
- package/docs/agent-definitions.md +9 -1
- package/docs/agent-events.md +4 -1
- package/docs/agent-session-runtime.md +6 -6
- 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 +5 -3
- package/docs/coding-tools.md +1 -1
- package/docs/coding-workspaces.md +22 -0
- package/docs/compaction-and-retry.md +36 -4
- package/docs/compaction-observational-memory.md +62 -9
- package/docs/context-and-skills.md +4 -2
- package/docs/conversations.md +1 -1
- package/docs/dev-inspector.md +4 -0
- package/docs/device-adapters.md +1 -0
- package/docs/document-reader.md +11 -3
- package/docs/documents.md +10 -2
- package/docs/enterprise-postgres-state.md +2 -2
- package/docs/evaluations.md +168 -4
- package/docs/execution-timeline.md +180 -0
- package/docs/history/0.7.0-primitive-review.md +254 -0
- package/docs/history/migration-0.0.md +2 -2
- package/docs/history/release-handoffs.md +37 -1
- package/docs/host-compositions.md +147 -0
- package/docs/hosted-sandboxes.md +94 -0
- package/docs/index.md +58 -39
- 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 +4 -1
- package/docs/mcp-tools.md +2 -1
- package/docs/memory-fabric.md +416 -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/migration.md +13 -1
- package/docs/model-routing.md +79 -4
- package/docs/multi-agent-patterns.md +20 -6
- package/docs/observability.md +52 -1
- package/docs/operations.md +13 -1
- package/docs/options-index.md +13 -1
- package/docs/peer-dependencies.md +6 -4
- 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-packages.md +22 -22
- package/docs/providers/bedrock.md +71 -7
- 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 +36 -34
- package/docs/runs-and-usage.md +3 -2
- package/docs/server.md +5 -3
- package/docs/speech.md +2 -0
- package/docs/supervisors.md +33 -5
- package/docs/testing.md +1 -1
- package/docs/thinking-and-reasoning.md +3 -1
- package/docs/tools.md +6 -5
- package/docs/web-tools.md +2 -1
- package/docs/work-artifacts-and-review.md +14 -4
- package/docs/work-connectors.md +3 -1
- package/docs/work-tools.md +14 -4
- package/docs/workflows.md +69 -1
- package/docs/working-and-semantic-memory.md +25 -14
- package/package.json +1 -1
- 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 +11 -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
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# Migrate Prism 0.6 to 0.7
|
|
2
|
+
|
|
3
|
+
> **Status: 0.7.0** (Host Completeness, Evidence, and Capability Boundaries).
|
|
4
|
+
|
|
5
|
+
This document details migration steps, security tightenings, and compatibility notes for upgrading from Prism 0.6.0 to 0.7.0.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Security Tightenings and Breaking Behavioral Changes
|
|
10
|
+
|
|
11
|
+
### 1. ACP MCP Destination Matching (Trap A)
|
|
12
|
+
|
|
13
|
+
In Prism 0.6.0 and earlier, `@arnilo/prism-acp-agent` matched candidate MCP server URLs against `mcp.allow` using string prefix matching (`server.url.startsWith(entry)`). This permitted:
|
|
14
|
+
- **Origin lookalikes**: An allow entry for `https://mcp.example.com` inadvertently matched `https://mcp.example.com.attacker.invalid/mcp`.
|
|
15
|
+
- **Path prefix bleeding**: An allow entry for `https://mcp.example.com/mcp` matched sibling paths like `https://mcp.example.com/mcp-other`.
|
|
16
|
+
|
|
17
|
+
In Prism 0.7.0, destination matching strictly adheres to WHATWG URL origin and path-segment subtree standards:
|
|
18
|
+
|
|
19
|
+
- **Origin matching**: Normalizes scheme (`http:` / `https:`), hostname (case-insensitive, punycode IDN, IPv6 brackets), and effective port (default ports 80/443 normalized). Lookalike hosts are rejected.
|
|
20
|
+
- **Path-segment subtree matching**:
|
|
21
|
+
- Origin-level entries (e.g. `https://mcp.example.com` or `https://mcp.example.com/`) match any path under that exact origin.
|
|
22
|
+
- Path-scoped entries (e.g. `https://mcp.example.com/mcp` or `https://mcp.example.com/mcp/`) match `/mcp`, `/mcp/`, and `/mcp/sub`, but reject sibling prefixes such as `/mcp-other` or `/mcpextra`.
|
|
23
|
+
- **Configuration validation**:
|
|
24
|
+
- Every `mcp.allow` entry in `prism-acp-agent.json` must be `"stdio"` or a valid absolute `http:` or `https:` URL.
|
|
25
|
+
- Entries containing userinfo (`user:pass@`), query parameters (`?query`), fragment identifiers (`#hash`), or ambiguous encoded path characters (`%2e%2e`, `%2f`, `%5c`, `..`, backslashes) throw `ConfigError` during config parsing.
|
|
26
|
+
- **Candidate URL validation**: Candidate server URLs with embedded credentials or ambiguous encoded path forms fail closed at selection time.
|
|
27
|
+
- **Transports**: Unstable `acp` transport remains rejected. `stdio` servers require the explicit `"stdio"` marker in `mcp.allow`.
|
|
28
|
+
|
|
29
|
+
#### Migration Actions
|
|
30
|
+
- Review `mcp.allow` in `prism-acp-agent.json`: remove any query strings, hashes, or credentials from allow entries.
|
|
31
|
+
- If previously relying on prefix matching across sibling paths (e.g. relying on `/mcp` to match `/mcp-internal`), explicitly list both entries or use an origin-level entry.
|
|
32
|
+
|
|
33
|
+
### 2. Model Router Facade Fail-Closed Governance (Trap B)
|
|
34
|
+
|
|
35
|
+
In Prism 0.6.0 and earlier, the synchronous `router.providerSource(model)` facade only checked allow-lists, residencies, and durable state presence. When configured with in-memory budgets (such as `maxCostUsd: 0` or positive limits), rate limits, circuit breakers, fallbacks, or selection policies, `router.providerSource(model)` returned an unwrapped provider and bypassed these governance controls without enforcement.
|
|
36
|
+
|
|
37
|
+
In Prism 0.7.0, `router.providerSource(model)` enforces fail-closed boundary safety:
|
|
38
|
+
- **Refuses unenforced governance**: If the router is configured with `budgets`, `rateLimit`, `circuit`, `fallbacks`, or `selection`, calling `providerSource(model)` throws `ModelRouterError` with stable code `ERR_PRISM_MODEL_ROUTER_ASYNC_REQUIRED`.
|
|
39
|
+
- **Refuses durable state**: If configured with `stateStore`, calling `providerSource(model)` throws `ERR_PRISM_MODEL_ROUTER_ASYNC_STATE`.
|
|
40
|
+
- **Runtime validation**: Untyped JavaScript callers passing malformed model arguments fail closed before resolver invocation with `ERR_PRISM_MODEL_ROUTER_VALIDATION`.
|
|
41
|
+
- **Resolver isolation**: The underlying `resolver` function is never called when `providerSource` refuses or denies a request.
|
|
42
|
+
- **Synchronous allow-list / residency support**: Simple allow-list-only and residency-only routers without async state or governance caps remain fully supported via `router.providerSource(model)`.
|
|
43
|
+
|
|
44
|
+
#### Migration Actions
|
|
45
|
+
|
|
46
|
+
Do not use `router.providerSource(model)` when governance (budgets, rate limits, circuit breaker, fallbacks, selection policies, or durable state) is configured. Replace calls with the asynchronous admission method `await router.resolve(...)`:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
// ❌ Bypasses governance in 0.6.0 / Refused in 0.7.0 (throws ERR_PRISM_MODEL_ROUTER_ASYNC_REQUIRED)
|
|
50
|
+
const provider = router.providerSource(model);
|
|
51
|
+
|
|
52
|
+
// ✅ Governed admission in 0.7.0
|
|
53
|
+
const { provider, model: selectedModel, providerRequestPolicy, budgetReservation } = await router.resolve({
|
|
54
|
+
model,
|
|
55
|
+
identity,
|
|
56
|
+
maxCostUsd: 0.25,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Pass to agent
|
|
60
|
+
const agent = createAgent({
|
|
61
|
+
model: selectedModel,
|
|
62
|
+
provider,
|
|
63
|
+
providerRequestPolicies: [providerRequestPolicy],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Commit actual usage after run
|
|
67
|
+
await router.recordUsage({
|
|
68
|
+
identity,
|
|
69
|
+
provider: provider.id,
|
|
70
|
+
model: selectedModel.model,
|
|
71
|
+
tokens: runTokens,
|
|
72
|
+
budgetReservation,
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
To test whether a router configuration supports `providerSource` before calling it, use `isProviderSourceEligible(options)` or `assertProviderSourceEligible(options)` exported from `@arnilo/prism-core/governance/model-router`.
|
|
77
|
+
|
|
78
|
+
### 3. ACP Real-Provider Launcher and Explicit Mock Mode (Trap C / R05)
|
|
79
|
+
|
|
80
|
+
In Prism 0.6.0 and earlier, `@arnilo/prism-acp-agent` silently defaulted to `createMockProvider()` when no provider was passed and no model was configured. Hosts deploying the ACP binary without explicit provider wiring ran mock sessions without warning or token generation (Trap C).
|
|
81
|
+
|
|
82
|
+
In Prism 0.7.0, `createSpawnableAgent` enforces fail-closed startup validation:
|
|
83
|
+
|
|
84
|
+
- **Explicit model required**: Config must specify a valid `model` (`{ "provider": "<name>", "model": "<id>" }`) unless a provider instance is passed via API options.
|
|
85
|
+
- **Credential reference required**: Real providers require `credentialRef` in the configuration. The secret value is resolved from `process.env[ref]` or an optional host `credentialResolver`. If missing or unresolvable, startup fails immediately with `ConfigError` (code `PRISM_ACP_AGENT_CONFIG`).
|
|
86
|
+
- **Provider matching & allow-list**: Only allow-listed first-party providers are supported (`openai`, `anthropic`, `google`, `deepseek`, `openrouter`, `ollama`, `xai`, `zai`, `alibaba`, `kimi`, `clinepass`, `commandcode`, `neuralwatt`, `opencode-go`, `hyper`, and `mock`). Unknown providers or mismatches between configured models and injected providers fail before startup.
|
|
87
|
+
- **Explicit mock mode**: Mock mode requires explicit configuration: `{ "model": { "provider": "mock", "model": "mock" } }` (or injected `provider.id === "mock"`). It stays 100% offline and requires no credentials.
|
|
88
|
+
- **Lazy provider loading**: First-party provider adapters are dynamically imported on demand on the first generation call; `@arnilo/prism-providers` is never loaded at root import or during mock runs.
|
|
89
|
+
- **Credential safety**: Secret values never enter config persistence, argv flags, stdout protocol streams, events, or model context. Identity records only the non-secret `credentialRef`.
|
|
90
|
+
- **Durable session reconstruction**: With SQLite persistence, per-session agents (including editor-backed filesystem sessions) are reconstructed across process restarts with their selected model and provider intact.
|
|
91
|
+
|
|
92
|
+
#### Migration Actions
|
|
93
|
+
|
|
94
|
+
- **For real-provider deployments**: Add `model` and `credentialRef` to `prism-acp-agent.json`:
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"userId": "local",
|
|
98
|
+
"cwd": ".",
|
|
99
|
+
"model": { "provider": "openai", "model": "gpt-4o" },
|
|
100
|
+
"credentialRef": "OPENAI_API_KEY",
|
|
101
|
+
"sessionStore": { "type": "sqlite", "path": ".prism/sessions.db" }
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
- **For offline testing / mock journeys**: Explicitly declare mock mode:
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"userId": "local",
|
|
108
|
+
"cwd": ".",
|
|
109
|
+
"model": { "provider": "mock", "model": "mock" }
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 4. Aggregate Task/Tenant Accounting Across All Paid Work (R01)
|
|
114
|
+
|
|
115
|
+
In Prism 0.6.0, model-router budgets were strictly per-model and per-provider (`(provider, model)`). When complex tasks involved retries, model switches, delegated child tasks, embeddings, compactions, or paid tools, each model bucket maintained separate counters, allowing budget resets through model hopping or child delegation.
|
|
116
|
+
|
|
117
|
+
In Prism 0.7.0, router state introduces unified task-level aggregate accounting:
|
|
118
|
+
|
|
119
|
+
- **Task-scoped reservations & accounting**: Supplying `taskId` and `kind` (`"generation" | "embedding" | "compaction" | "tool"`) groups all paid work for a given task under a single shared atomic budget ceiling.
|
|
120
|
+
- **Attribution breakdown**: Calling `router.readBudget({ identity, taskId })` decomposes total utilization into typed attributions (`byModel`, `byKind`, `attributions`).
|
|
121
|
+
- **Strict hard-budget mode (`budgets.strict = true`)**: In strict hard-budget mode, requests lacking enforceable bounds or candidates lacking cost pricing are denied fail-closed with `ERR_PRISM_MODEL_ROUTER_BUDGET`.
|
|
122
|
+
- **Hold renewal (`renewBudget`)**: Long-running holds can be renewed via `router.renewBudget()` or `governedProvider.renewBudget()` before expiry; the hold's fencing token advances and previous fencing tokens are invalidated. Expired holds fail closed with `ERR_PRISM_MODEL_ROUTER_STATE`.
|
|
123
|
+
- **PostgreSQL schema migration `006_aggregate_budgets`**: Adds `task_id` and `attributions` columns to `prism_model_router_budgets` with partial index `prism_model_router_budgets_task_idx`.
|
|
124
|
+
|
|
125
|
+
#### Migration Actions
|
|
126
|
+
|
|
127
|
+
- **Durable PostgreSQL deployments**: Apply migration `006_aggregate_budgets` on startup via `createPostgresEnterpriseState()`. Existing rows automatically default `task_id` to empty string and `attributions` to `{}`.
|
|
128
|
+
- **Task/Job workflows**: Pass `taskId` to `router.resolve()`, `recordUsage()`, and `createGovernedProvider()` to prevent budget oversubscription across retries and child agents.
|
|
129
|
+
- **Budget enforcement**: Set `budgets.strict: true` when hard cost/token ceilings must be guaranteed against unmetered or unpriced providers.
|
|
130
|
+
|
|
131
|
+
### 5. Durable Business-Action Draft Persistence and Resumption (R02)
|
|
132
|
+
|
|
133
|
+
In Prism 0.6.0, Microsoft 365 and Google Workspace connector mutation drafts were kept ephemerally in adapter memory. If an agent process restarted or if an asynchronous human approval took minutes or hours, drafts were lost, forcing re-creation of drafts from scratch without revision guarantees. Furthermore, modifying a draft did not systematically invalidate existing approvals.
|
|
134
|
+
|
|
135
|
+
In Prism 0.7.0, business-action drafts feature durable lifecycle tracking:
|
|
136
|
+
|
|
137
|
+
- **Checkpoint-backed draft store**: Adapters accept `checkpoints: CheckpointStore` (e.g. `createPostgresEnterpriseState({ pool }).checkpoints` or `createMemoryCheckpointStore()`) or a dedicated `draftStore: WorkDraftStore`. Drafts persist under namespace `prism.work.draft` and survive process restart.
|
|
138
|
+
- **Exact revision binding & payload digest**: Drafts start at `revision: 1` with a canonical SHA-256 `payloadDigest`. Approvals bind strictly to `{ draftId, revision, payloadDigest, identityKey, approvedAt, expiresAt, policyRevision }`.
|
|
139
|
+
- **Edits invalidate approval**: Updating draft payload or recipients increments `revision`, clears `approval`, and resets `status` to `pending_approval`.
|
|
140
|
+
- **Resuming approved revisions**: Tools (`m365_mail_draft_send`, `gws_mail_draft_send`, `m365_calendar_draft_add`, `gws_calendar_draft_add`) accept `{ draftId, revision }` without repeating the payload; the tool resumes the stored payload and executes after approval validation.
|
|
141
|
+
- **Fail-closed ambiguous failure handling**: Connector failures after dispatch mark both the idempotency record and the draft `unknown`. Automatic replays are rejected with `ERR_PRISM_WORK_IDEMPOTENCY_UNKNOWN` until operator reconciliation.
|
|
142
|
+
|
|
143
|
+
#### Migration Actions
|
|
144
|
+
|
|
145
|
+
- **For persistent workloads**: Pass `checkpoints` (and optionally `bodies: ArtifactBodyStore`) to `createMicrosoft365CliAdapter` and `createGoogleWorkspaceCliAdapter` so drafts survive process restart.
|
|
146
|
+
- **Approval systems**: Ensure approval decisions provide or verify `{ draftId, revision, payloadDigest }`.
|
|
147
|
+
|
|
148
|
+
### 6. RAG document authorization (R04)
|
|
149
|
+
|
|
150
|
+
`retrieveContext` without `authorization` is unchanged. Passing `authorization` injects host-verified principal/group constraints into both vector and lexical legs **before** ranking, then rechecks before rerank and injection.
|
|
151
|
+
|
|
152
|
+
- `filter` is metadata equality only. It is not document ACL.
|
|
153
|
+
- Stores that do not declare `authorization: "acl"` throw rather than claim protection. In-memory and PostgreSQL adapters declare it (`setSourceAccess` / `checkSourceAccess`).
|
|
154
|
+
- Missing grants and unresolved `accessVersion` deny. Revoking a grant (empty principal+group lists, bumped version) hides the source on the next query without re-embedding.
|
|
155
|
+
- `authorization.tenantId` must match every retrieve scope.
|
|
156
|
+
|
|
157
|
+
#### Migration Actions
|
|
158
|
+
|
|
159
|
+
- Hosts that need per-document ACL pass `authorization` and call `setSourceAccess` for each source. Hosts that do not pass `authorization` keep today's retrieve behavior.
|
|
160
|
+
- Custom `VectorStore` implementations must implement query-time ACL and `checkSourceAccess` before advertising `authorization: "acl"`.
|
|
161
|
+
|
|
162
|
+
### 7. Drive knowledge synchronization (R04)
|
|
163
|
+
|
|
164
|
+
Opt-in. `syncKnowledge` + `createGoogleDriveConnector` page Drive `files.list` / `changes.list` into existing `replaceSource` / `setSourceAccess`. Cursors live on the host `CheckpointStore` and advance only after a page commits. `changes.watch` payloads are not authorization.
|
|
165
|
+
|
|
166
|
+
#### Migration Actions
|
|
167
|
+
|
|
168
|
+
- Hosts that want incremental Drive RAG import wire a token provider (`drive.readonly`), `resolveAccess` for permissions, and a durable checkpoint key. Hosts that do not call `syncKnowledge` are unchanged.
|
|
169
|
+
|
|
170
|
+
### 8. Hosted E2B sandbox (R03)
|
|
171
|
+
|
|
172
|
+
Opt-in. `createE2BSandbox` / `connectE2BSandbox` implement `DisposableSandbox` plus `pause`/`resume`. Docker and native adapters are unchanged. E2B `Sandbox.connect` auto-resumes paused VMs; Prism reconnects paused sandboxes **without** calling it unless the host calls `resume()` or passes `resume: true`. `lifecycle.autoResume` is always false. `pause({ keepMemory: false })` is filesystem-only: processes do not survive resume. A 503 pause refusal leaves the sandbox running. `close({ export })` is unsupported (pause is the snapshot).
|
|
173
|
+
|
|
174
|
+
Default capabilities do **not** claim Docker `network: none` / egress parity.
|
|
175
|
+
|
|
176
|
+
#### Migration Actions
|
|
177
|
+
|
|
178
|
+
- Hosts that want E2B install optional peer `e2b@2.49.1` (or inject `client`) and pass `apiKey` at the edge. Hosts that keep Docker/native are unchanged.
|
|
179
|
+
|
|
180
|
+
### 9. Fair worker admission and operator routes (R06)
|
|
181
|
+
|
|
182
|
+
Opt-in. Existing `createWorkflowCoordinator` and `createPrismDrainController` stay the composition entry points. Optional `admission` adds cursor wrap, per-tenant/per-class claim caps, queue deadlines, and drain-aware claiming. `createPrismOperatorHandler` is a separate authorized handler for queue/suspended/failed/unknown inspect plus cancel/reconcile. Reconcile cannot mark unknown effects retryable. No new job broker.
|
|
183
|
+
|
|
184
|
+
#### Migration Actions
|
|
185
|
+
|
|
186
|
+
- Hosts that want fair multi-tenant workers set `admission` and mount `/ops`. Hosts that keep a single-tenant coordinator with default FIFO first-page polling are unchanged.
|
|
187
|
+
|
|
188
|
+
### 10. Behavioral eval inspector wiring (R07)
|
|
189
|
+
|
|
190
|
+
Opt-in. Trajectory scorers, scenario runner, manifests, and `summarizeTimeline` stay in `@arnilo/prism-core/governance/evals` and `@arnilo/prism-core/governance/observability`. The dev inspector adds `GET /runs/:id/summary` and `POST /compare` that render those artifacts. Host-journey fixtures live in `examples/behavior-evaluation.ts`.
|
|
191
|
+
|
|
192
|
+
#### Migration Actions
|
|
193
|
+
|
|
194
|
+
- Hosts that want inspector quality/cost/latency compare wire `eventSource` + `resolveRun` and call `compareInspectorRuns` / `POST /compare`. Hosts that keep the previous inspector timeline-only UI are unchanged.
|
|
195
|
+
|
|
196
|
+
### 11. Cross-layer memory lineage (R08)
|
|
197
|
+
|
|
198
|
+
Opt-in. `remember({ lineage: { sourceIds } })` stamps `metadata._lineage` (schema v1). `forget` / `setConsent(visible: false)` / `correct` write invalidation rows **before** body delete. Query/lexical legs exclude forgotten/revoked/held ids and any record listing them in `_lineage.sourceIds`. `correct` keeps the source (`reason: corrected`). `forget({ hold: true })` skips body delete. Missing `_lineage` is self-only. Parent-child `shareWith` is an explicit allow-list; siblings and expired grants fail closed. Observational projection/recall take `invalidatedIds`. `revokedIdsAbsent` is the 072 invariant body.
|
|
199
|
+
|
|
200
|
+
Custom `VectorStore` implementations that omit `lineage: "invalidation"` keep pre-0.7 forget-as-delete (dependents may leak). In-memory and PostgreSQL adapters declare it.
|
|
201
|
+
|
|
202
|
+
#### Migration Actions
|
|
203
|
+
|
|
204
|
+
- Hosts that want derived-context exclusion stamp `lineage` on writes and pass `authorization`/invalidation through recall. Hosts that do not stamp lineage keep self-only delete. Do not edit frozen `docs/migrate-to-0.6.md`.
|
|
205
|
+
- `setConsent(entryId, { visible: false })` marks `revoked` (not `legal_hold`), so recall excludes the record but a later `forget` still purges it; re-granting clears only a revoked mark and never resurrects a `corrected`/`forgotten` one. Use `forget({ ids, hold: true })` when you actually need retention.
|
|
206
|
+
- Observational recall keeps a reflection whose supporting observation was dropped by compaction and annotates it `(dropped)`; invalidation still wins, so a revoked observation id (or a revoked source of one) withholds the reflection with `reason: "revoked"`.
|
|
207
|
+
|
|
208
|
+
### 12. Evidence-backed citations and document diffs (R10)
|
|
209
|
+
|
|
210
|
+
Additive. `ArtifactCitation` may include `sourceId` / `revision` / `contentHash` / `retrievedAt` / `excerpt` / `span` / `tenantId` / `support`. Approvals stamp `evidenceDigest`. `checkCitationIntegrity` fails closed on missing source, hash/span mismatch, revoked ACL, or tenant mismatch; `support` is ignored. `diffDocument` walks Document Model nodes with op/node caps (`truncated` instead of unbounded diff). `createCitationIntegrityScorer` is a 072 invariant: score 0 cannot be averaged away by a semantic judge.
|
|
211
|
+
|
|
212
|
+
#### Migration Actions
|
|
213
|
+
|
|
214
|
+
- Legacy uri/title/kind citations still attach. Evidence fields are optional until the host starts integrity checks.
|
|
215
|
+
- Structural Office review uses `diffDocument`; artifact `compare` remains hash+metadata.
|
|
216
|
+
|
|
217
|
+
### 13. Import fidelity and optional OCR (R10)
|
|
218
|
+
|
|
219
|
+
Additive. `importDocument` returns `{ model, fidelity }` — ZIP-name report of dropped OOXML structures (macros, comments, media, …). `parseDocument` still returns the model only.
|
|
220
|
+
|
|
221
|
+
`createMistralOcrParser({ apiKey })` is a host-selected `DocumentParser`. Default `createDocumentReader()` still uses `pdf-parse` / `mammoth` only and does not call OCR. Inline data URLs; no Files API. `documentUrl` is SSRF-checked. `recordUsage` admits pages/bytes into Task 7 accounting.
|
|
222
|
+
|
|
223
|
+
#### Migration Actions
|
|
224
|
+
|
|
225
|
+
- Existing `parseDocument` callers are unchanged. Switch to `importDocument` when review UI must show lost structures.
|
|
226
|
+
- OCR is opt-in. Do not add the OCR parser to default `parsers`.
|
|
227
|
+
|
|
228
|
+
### 14. Per-run tool narrowing (R11)
|
|
229
|
+
|
|
230
|
+
Additive. `RunOptions.toolNames` allow-lists registered tool names for one run. Omitted = full registry (0.6 behavior). Empty = no tools. Unknown names throw. Resume persists the grant and will not widen it. MCP `execute` fails closed when a refresh changes schema/effect or revokes the tool.
|
|
231
|
+
|
|
232
|
+
Still no `RunOptions.tools` or `RunOptions.toolFilter`.
|
|
233
|
+
|
|
234
|
+
#### Migration Actions
|
|
235
|
+
|
|
236
|
+
- Existing `session.run(input)` callers are unchanged.
|
|
237
|
+
- Pass `{ toolNames: ["web_search"] }` when a continuing session should see a subset.
|
|
238
|
+
|
|
239
|
+
### 15. Governed realtime voice (R14)
|
|
240
|
+
|
|
241
|
+
Additive. `createRealtimeVoiceBridge` (`@arnilo/prism-core/runtime/realtime`) admits a voice device, dispatches host `function_call` items through the ordinary execute path, cancels queued calls on barge-in, and skips `seenCallIds` on reconnect. Transcripts are off by default; audio is never retained. `RealtimeEvent` may include `usage`. `RealtimeSession.completeTool` is optional. OpenAI Realtime advertises host tools via `session.update` and completes them with `function_call_output`.
|
|
242
|
+
|
|
243
|
+
#### Migration Actions
|
|
244
|
+
|
|
245
|
+
- Existing `createOpenAIRealtimeSession` callers that only send audio are unchanged.
|
|
246
|
+
- Wire `execute` through `dispatchToolCall` / durable approval. Session microphone consent is not tool approval.
|
|
247
|
+
|
|
248
|
+
### 16. Eval primitives match their contracts (R07)
|
|
249
|
+
|
|
250
|
+
Additive. `runExperiment({ trials: N, seed })` re-runs each item N times (cap 16) and reports `sampleCount` / `standard_error`. `wrapAgentWithFailureInjection` honors `failStore` (fails on `run`), `denyTools`, and `unknownEffect`. Timeline collection no longer uses a 10ms drain. `validateEvalManifest` stays two-field; `validateReleaseEvalManifest` adds prompt/tools/model/policy binding.
|
|
251
|
+
|
|
252
|
+
#### Migration Actions
|
|
253
|
+
|
|
254
|
+
- Existing `validateEvalManifest({ runtimeRevision, datasetVersion })` callers are unchanged.
|
|
255
|
+
- Call `validateReleaseEvalManifest` when packing release evidence. Do not pass `expectedBehavior`, `failStoreAfterTurns`, `denyToolCalls`, or `uncertaintyMethod: "bootstrap"` — those were docs drift, not APIs.
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
### 17. Native Bedrock Converse route (R12)
|
|
261
|
+
|
|
262
|
+
Additive. `@arnilo/prism-providers/bedrock` now ships a native route next to the OpenAI-compatible one: `createBedrockConverseProvider({ region, credential, stream })` runs `ConverseStream` (default) or one `Converse` call, and `createBedrockProviderPackage({ region, credential, api: "converse" })` registers it under the same `id`/model bindings. Prism messages map to `messages`/`system`/`inferenceConfig`/`toolConfig`, Prism media blocks map to `image`/`document` blocks, cache breakpoints map to `cachePoint` blocks, `options.structuredOutput` maps to `outputConfig.textFormat`, and Anthropic/OpenAI-family reasoning maps to `additionalModelRequestFields.thinking` / `.reasoning_effort`. Responses are decoded from the `application/vnd.amazon.eventstream` framing with bounded frame sizes and CRC validation, still with no AWS SDK dependency.
|
|
263
|
+
|
|
264
|
+
#### Migration Actions
|
|
265
|
+
|
|
266
|
+
- Nothing changes for existing `createBedrockProvider` / `createBedrockProviderPackage` hosts: the default route stays `compatible`.
|
|
267
|
+
- To move a provider id to Converse, set `api: "converse"` (and keep `stream: false` only if the deployment must avoid `InvokeModelWithResponseStream`). One provider id serves one route; register a second `id` if you need both.
|
|
268
|
+
- Model bindings that relied on OpenAI image parts must declare `capabilities.input` with `image`/`document` and use Prism media blocks; unsupported media types now refuse before the request instead of being dropped.
|
|
269
|
+
- Do not pass `cachePoint`, `toolConfig`, `outputConfig`, or `additionalModelRequestFields` through `compat`; use Prism cache breakpoints, `tools`, `structuredOutput`, and `compat.thinking`/`compat.reasoning_effort` (or `options.extra.additionalModelRequestFields` for model-specific fields).
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Opt-in additions (no behavior change until enabled)
|
|
274
|
+
|
|
275
|
+
### 18. Attention compiler (R17, opt-in)
|
|
276
|
+
|
|
277
|
+
`createAttentionCompiler(options?, context?)` is a per-turn gate that measures the assembled input against a host ratio of the model input cap and rewrites nothing until that ratio is reached; over the ratio it mutates a **history clone** monotonically (oldest `thinking` blocks first, then oldest fold-eligible tool results), so prompt-cache prefixes survive and the session store, observational-memory ledger, and input history array are untouched. Still over after every eligible row → `AttentionBudgetError` instead of silent eviction.
|
|
278
|
+
|
|
279
|
+
#### Migration Actions
|
|
280
|
+
|
|
281
|
+
- Nothing changes by default: with `attentionCompiler` omitted, request bytes and the store are byte-identical to 0.6.0.
|
|
282
|
+
- Turn it on per agent (`AgentConfig.attentionCompiler`) or per run (`RunOptions.attentionCompiler`, which narrows, never widens: `false` disables for that run).
|
|
283
|
+
- Hosts that compact on overflow can drive compaction from the gate instead of guessing a token threshold with the host-programmable compaction trigger.
|
|
284
|
+
- Do not use it as a replacement for compaction or `applyContextBudget` eviction: compaction is the boundary operation that writes a summary.
|
|
285
|
+
|
|
286
|
+
`AgentDefinition.attentionCompiler` and the direct `assembleProviderInput` seam carry the same option shape; a definition sets it for every resolved config.
|
|
287
|
+
|
|
288
|
+
### 19. Memory Fabric subpath (opt-in)
|
|
289
|
+
|
|
290
|
+
`@arnilo/prism-memory/fabric` adds typed notes over the stores a host already configured: `createMemoryFabric({ memory, observational?, linker?, ... })` writes `fact`, `procedure`, `file`, `working`, and `episode` notes through the existing working/vector stores, with links, validity windows, and time/tool recall.
|
|
291
|
+
|
|
292
|
+
#### Migration Actions
|
|
293
|
+
|
|
294
|
+
- Add the subpath import; no new package, provider, database, or mandatory dependency is introduced.
|
|
295
|
+
- The fabric is inert until `fabric.attach(session)`: importing the subpath starts no worker, no timer, and no process, and an unattached fabric enriches nothing.
|
|
296
|
+
- Episode notes (`kind: "episode"`) and `searchConversation` require an attached observational-memory session; without one they fail closed.
|
|
297
|
+
- Reserved keys: `metadata.fabric` on vector records and `_fabric.blocks` inside the working value. A host schema that validates working values must allow them; the host schema remains the last word on block content.
|
|
298
|
+
- Existing `createMemory` hosts keep working unchanged — the fabric writes through the same store, consent, redaction, and lineage paths.
|
|
299
|
+
|
|
300
|
+
### 20. Work-scope memory index (R16, opt-in)
|
|
301
|
+
|
|
302
|
+
`createWorkScopeController({ session, appendEntry, secrets? })` appends `om.scope.*` entries to one observational-memory ledger and folds them with `foldWorkScopeMap()`; `projectWorkMemory(ledger, map, query)` filters observations/reflections to a host-selected working set and `withWorkScope(controller, spec, fn)` opens/enters/leaves around a callback.
|
|
303
|
+
|
|
304
|
+
#### Migration Actions
|
|
305
|
+
|
|
306
|
+
- Nothing changes without `om.scope.*` entries: the map has only its implicit `session` root, context renders the existing active pool, and the dropper keeps its 0.6.0 behavior.
|
|
307
|
+
- While any host scope exists, the runtime skips the observation dropper — the working set is now the projection, and the folded-payload byte cap stays a storage safety cap, not garbage collection.
|
|
308
|
+
- Bindings are exact ids (`om:<12-hex>` / `reflection:<12-hex>`); a reflection bound on a **closed** scope can graduate into durable semantic memory through the fabric's `remember({ kind: "fact" | "procedure", reflectionId })`.
|
|
309
|
+
- Caps fail closed: 256 scopes, depth/stack 8, 4,096 binds per scope, 512-character labels/kinds, ids `[A-Za-z0-9._:/-]{1,128}` without `..`.
|
|
310
|
+
- `recallObservationalMemory()` still reads the complete current branch by exact id — the projection never hides memory from exact-id recall.
|
|
311
|
+
|
|
312
|
+
### 21. Host-owned subagent spawn (opt-in)
|
|
313
|
+
|
|
314
|
+
`createSupervisor({ ownership, children })` gains model-facing tools: `createSpawnAgentTool({ supervisor, name?, mode? })` returns a non-exclusive `spawn_agent` whose closed schema exposes only the host's allow-listed child IDs, `input`, an optional `threadId`, and `mode: "sync" | "async"`; `createWaitAgentTool` / `createCancelAgentTool` return `wait_agent` / `cancel_agent` for host-owned async handles. `delegateAsync()` returns `{ delegationId, status: "running" }` without waiting.
|
|
315
|
+
|
|
316
|
+
#### Migration Actions
|
|
317
|
+
|
|
318
|
+
- Nothing changes for existing `delegate()` callers; the tools are additive and opt-in.
|
|
319
|
+
- Advertised children come from the supervisor's own list — a model cannot name a child the host did not construct, cannot supply child tools, identity, scopes, or higher limits, and sees redacted results and error text only.
|
|
320
|
+
- Child identity narrows from the parent (`narrowIdentity` + `assertIdentityPropagation`): a delegated scope set must be non-empty, must not widen the parent's scopes, and must not extend the parent expiry.
|
|
321
|
+
- Async handles are **in-process**: ownership-scoped, bounded, cached terminal records retained only up to `limits.maxQueuedEvents`, and they do not survive a host restart. Parent-run abort propagates to running children.
|
|
322
|
+
- Install `createWorktreeChildFactory` (`@arnilo/prism-coding-tools/agent`) and pass its `after` as the supervisor terminal hook when parallel children would otherwise collide in one working tree; the child context then carries `cwd` pointing at its own linked worktree, and cleanup runs on every terminal outcome — including a child that suspended for approval and later resumes.
|
|
323
|
+
- `observeSupervisorLifecycle` (coding tools) bridges supervisor `delegation_*` events to redacted coding `subagent_started` / `subagent_stopped` lifecycle events (and AG-UI/ACP projections).
|
|
324
|
+
|
|
325
|
+
## Upgrade steps
|
|
326
|
+
|
|
327
|
+
1. Bump every `@arnilo/*` dependency and peer to `^0.7.0` (all ten manifests cut together; a range that only *satisfies* 0.7.0 is refused by the release gate). The published predecessor is 0.6.0.
|
|
328
|
+
2. Read §1–§3 if you run the ACP agent (`mcp.allow` entries may need tightening) or call `router.providerSource()` with governance configured — those are the only hard refusals for an existing host.
|
|
329
|
+
3. Re-check each §4–§17 item your host touches: they are behavioral tightenings inside existing surfaces (accounting, drafts, authorization, narrowing, fidelity), not new opt-ins.
|
|
330
|
+
4. Adopt the opt-in additions only where they matter: §18 attention compiler, §19 memory fabric, §20 work scopes, §21 spawn tools.
|
|
331
|
+
5. Build and run your suite. No persisted-data migration exists or is needed: the 0.7.0 additions write through existing stores (working/vector records, checkpoints, observational-memory entries) under the same schema, and stores still fail closed on unknown or newer schema versions rather than rewriting data.
|
|
332
|
+
6. Optional: re-run `npm run release:gate` locally to reproduce the release evidence matrix.
|
|
333
|
+
|
|
334
|
+
## Rollback
|
|
335
|
+
|
|
336
|
+
Pin the previous published line: `@arnilo/prism@0.6.0` (exact pins per package, plus each package's `0.6.0`). Nothing persisted under 0.7 is rewritten in place: the new subpaths and options are additive, and the fabric/scopes/spawn features write only new records through existing stores — a rolled-back 0.6.0 host keeps reading its data and simply stops seeing entries it never wrote. The refusal changes in §1–§3 are the only deltas a 0.6.0 host regains by rolling back (and it regains the unsafe prefix matching and facade bypass with them, so re-check anything relying on those).
|
|
337
|
+
|
|
338
|
+
Back up the session/checkpoint store before a rollback if the 0.7 host wrote fabric notes or scope entries you intend to keep: 0.6.0 code paths ignore them rather than deleting them, but only the 0.7 docs describe them.
|
|
339
|
+
|
|
340
|
+
## Related APIs
|
|
341
|
+
|
|
342
|
+
- [Migration guide](migration.md): the era index of migration cuts with replacement tables and rollback notes.
|
|
343
|
+
- [Migrate Prism 0.5 to 0.6](migrate-to-0.6.md): Node 22 floor, folded 0.5.7 delta, third-party floors.
|
|
344
|
+
- [Release and install](release-and-install.md): packed surfaces, install rules, support matrix, and the offline test budget.
|
|
345
|
+
- [Memory fabric](memory-fabric.md), [Observational memory compaction](compaction-observational-memory.md), [Attention compiler](attention-compiler.md), [Supervisors](supervisors.md): owning pages for the opt-in additions above.
|
package/docs/migration.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Migration guide
|
|
2
2
|
|
|
3
|
+
## 0.6.0 → 0.7.0 (host completeness, evidence, and capability boundaries)
|
|
4
|
+
|
|
5
|
+
**Prism 0.7.0 is a lockstep minor for all ten publishable packages.** Node `>=22` stays the floor; no import path was removed and no store schema changed, so a host that does not touch the ACP agent or the model-router facade upgrades by moving every `@arnilo/*` dependency and peer to `^0.7.0`. The full guide — per-item migration actions, opt-in activation, and rollback — is [migrate-to-0.7.md](migrate-to-0.7.md).
|
|
6
|
+
|
|
7
|
+
What a 0.6.0 host must check before upgrading:
|
|
8
|
+
|
|
9
|
+
- **Two hard refusals inside existing surfaces (the only host-breaking changes).** `@arnilo/prism-acp-agent` now matches `mcp.allow` destinations by WHATWG origin plus path-segment subtree (prefix lookalikes such as `https://mcp.example.com.attacker.invalid` no longer match, and allow entries with userinfo/query/fragment/ambiguous encodings fail `ConfigError` at parse time); `router.providerSource(model)` throws `ERR_PRISM_MODEL_ROUTER_ASYNC_REQUIRED` / `ERR_PRISM_MODEL_ROUTER_ASYNC_STATE` instead of silently bypassing budgets, rate limits, circuits, fallbacks, selection policies, or durable state — move those call sites to `await router.resolve(...)`. An ACP deployment without an explicit provider no longer silently runs `createMockProvider()`; mock mode is explicit.
|
|
10
|
+
- **Behavioral tightenings inside existing surfaces** (agent tool narrowing is monotonic per run, RAG queries are authorized in both legs, memory corrections/revocations propagate, import-fidelity reports replace silent drops, model-router/worker accounting is aggregated): these need a host read-through, not a code change.
|
|
11
|
+
- **Additive declarations only.** No export was removed in 0.7.0; the compat baselines were regenerated because existing declaration groups gained members (new fields on options/results and new subpath exports for the attention compiler, memory fabric, work scopes, and supervisor spawn tools).
|
|
12
|
+
- **Opt-in additions are inert by default**: attention compiler (`AgentConfig`/`RunOptions`), `@arnilo/prism-memory/fabric`, the observational-memory work-scope index, and the supervisor `spawn_agent`/`wait_agent`/`cancel_agent` tools. Omitted, request bytes, stores, and tool lists are unchanged.
|
|
13
|
+
- **Channel adapters (Telegram/Signal) are not in 0.7.0.**
|
|
14
|
+
|
|
3
15
|
## 0.5.6 → 0.6.0 (Node 22 floor; folds the never-published 0.5.7)
|
|
4
16
|
|
|
5
17
|
**Prism 0.6.0 requires Node `>=22`.** Every publishable package declares `"engines": { "node": ">=22" }`; a Node 20 host gets an `EBADENGINE` warning from npm (a hard failure under `engine-strict`) and an unsupported runtime. Node 20 reached upstream end-of-life on 2026-04-30, so the 0.6.0 line moves to Node 22 (maintenance LTS to 2027-04-30) while Node 24 stays the CI default (active LTS to 2028-04-30). The full 0.5.6 → 0.6.0 guide — third-party floors, the removed office peer, the additive host knobs, and upgrade/rollback steps — lives in [migrate-to-0.6.md](migrate-to-0.6.md).
|
|
@@ -48,7 +60,7 @@ Stream tokens coalesce on persist (adjacent `text`/`thinking` deltas merge). Rep
|
|
|
48
60
|
## 0.5.0 → 0.5.1 (additive)
|
|
49
61
|
|
|
50
62
|
|
|
51
|
-
Kernel constructs valid provider requests: session correlation, default cache breakpoints, and `thinkingLevel` on `AgentConfig` / `RunOptions`.
|
|
63
|
+
Kernel constructs valid provider requests: session correlation, default cache breakpoints, and `thinkingLevel` on `AgentConfig` / `RunOptions`. Hosts may drop a host-side `createSessionCachePolicy` overlay. OpenCode Go raw `generate` without `sessionId` throws `ProviderRequirementError` (`ERR_PRISM_PROVIDER_REQUIREMENT`) before fetch instead of an upstream 400. Observational memory uses derived `om:{session.id}`; LLM compaction uses the agent session id. See [migrate-to-0.5.md](migrate-to-0.5.md#8-provider-request-construction--additive-plan-066--051).
|
|
52
64
|
|
|
53
65
|
## What it does
|
|
54
66
|
|
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.
|