@arnilo/prism 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +62 -26
  3. package/dist/agent-loops.d.ts +8 -1
  4. package/dist/agent-loops.js +57 -11
  5. package/dist/agents.js +212 -32
  6. package/dist/checkpoints.d.ts +11 -0
  7. package/dist/checkpoints.js +144 -0
  8. package/dist/cli-init.d.ts +41 -0
  9. package/dist/cli-init.js +390 -0
  10. package/dist/cli-runner.d.ts +7 -1
  11. package/dist/cli-runner.js +13 -1
  12. package/dist/compaction.js +9 -1
  13. package/dist/content.d.ts +121 -0
  14. package/dist/content.js +538 -0
  15. package/dist/contracts.d.ts +236 -11
  16. package/dist/contracts.js +8 -0
  17. package/dist/event-multiplexer.d.ts +23 -0
  18. package/dist/event-multiplexer.js +136 -0
  19. package/dist/execution-policy.d.ts +28 -0
  20. package/dist/execution-policy.js +24 -0
  21. package/dist/feedback.d.ts +48 -0
  22. package/dist/feedback.js +230 -0
  23. package/dist/index.d.ts +20 -6
  24. package/dist/index.js +13 -5
  25. package/dist/input.js +11 -1
  26. package/dist/leases.d.ts +8 -0
  27. package/dist/leases.js +111 -0
  28. package/dist/node/agent-definitions.js +3 -5
  29. package/dist/node/config.d.ts +1 -0
  30. package/dist/node/config.js +5 -3
  31. package/dist/node/contribution-discovery.js +5 -8
  32. package/dist/node/session-store-jsonl.js +8 -5
  33. package/dist/node/settings.js +2 -2
  34. package/dist/node/trust.js +2 -4
  35. package/dist/observability.d.ts +3 -0
  36. package/dist/observability.js +18 -0
  37. package/dist/providers/media.d.ts +44 -0
  38. package/dist/providers/media.js +126 -0
  39. package/dist/providers/openai-compatible.js +18 -119
  40. package/dist/providers/openai-primitives.d.ts +9 -0
  41. package/dist/providers/openai-primitives.js +129 -0
  42. package/dist/providers/transport.d.ts +40 -0
  43. package/dist/providers/transport.js +221 -0
  44. package/dist/redaction.js +40 -13
  45. package/dist/resources.d.ts +5 -0
  46. package/dist/resources.js +4 -0
  47. package/dist/structured-output.d.ts +11 -0
  48. package/dist/structured-output.js +59 -0
  49. package/dist/testing/feedback.d.ts +6 -0
  50. package/dist/testing/feedback.js +37 -0
  51. package/dist/testing/persistence-schema.d.ts +102 -0
  52. package/dist/testing/persistence-schema.js +487 -0
  53. package/dist/testing/provider-conformance.js +10 -1
  54. package/dist/testing/run-ledger-conformance.d.ts +33 -0
  55. package/dist/testing/run-ledger-conformance.js +178 -0
  56. package/dist/testing/session-store-conformance.d.ts +16 -0
  57. package/dist/testing/session-store-conformance.js +73 -0
  58. package/dist/tools.d.ts +17 -0
  59. package/dist/tools.js +29 -2
  60. package/docs/a2a.md +73 -0
  61. package/docs/agent-events.md +17 -10
  62. package/docs/agent-loops.md +11 -5
  63. package/docs/agent-session-runtime.md +15 -16
  64. package/docs/cli-rpc.md +36 -5
  65. package/docs/coding-agent-tools.md +43 -9
  66. package/docs/coding-security.md +88 -0
  67. package/docs/compaction-observational-memory.md +2 -0
  68. package/docs/context-and-skills.md +1 -0
  69. package/docs/credential-storage.md +177 -0
  70. package/docs/credentials-and-redaction.md +4 -3
  71. package/docs/database-persistence.md +52 -7
  72. package/docs/evaluations.md +122 -0
  73. package/docs/extensions.md +2 -2
  74. package/docs/host-security.md +33 -2
  75. package/docs/index.md +46 -18
  76. package/docs/input-and-prompt-assembly.md +6 -5
  77. package/docs/mcp-tools.md +184 -0
  78. package/docs/middleware-hooks.md +2 -0
  79. package/docs/migration.md +51 -28
  80. package/docs/model-registry.md +5 -3
  81. package/docs/multimodal-content.md +156 -0
  82. package/docs/observability.md +171 -0
  83. package/docs/performance.md +249 -1
  84. package/docs/persistence-credentials-multimodality-primitives.md +303 -0
  85. package/docs/postgres-persistence.md +143 -0
  86. package/docs/provider-conformance.md +18 -0
  87. package/docs/provider-layer.md +1 -1
  88. package/docs/provider-packages.md +2 -0
  89. package/docs/provider-primitives.md +281 -0
  90. package/docs/providers/ai-sdk.md +113 -0
  91. package/docs/providers/kimi.md +1 -0
  92. package/docs/providers/neuralwatt.md +1 -0
  93. package/docs/providers/openai-compatible.md +2 -1
  94. package/docs/providers/openai.md +8 -1
  95. package/docs/providers/opencode-go.md +1 -0
  96. package/docs/providers/openrouter.md +1 -0
  97. package/docs/providers/zai.md +1 -0
  98. package/docs/public-contracts.md +13 -5
  99. package/docs/rag.md +113 -0
  100. package/docs/release-and-install.md +237 -30
  101. package/docs/resource-loading.md +14 -4
  102. package/docs/review-coverage-2026-07-14.md +260 -0
  103. package/docs/review-coverage-2026-07-15.md +193 -0
  104. package/docs/run-ledger-conformance.md +96 -0
  105. package/docs/runs-and-usage.md +43 -4
  106. package/docs/server.md +139 -0
  107. package/docs/session-store-conformance.md +16 -0
  108. package/docs/session-stores-and-branching.md +1 -0
  109. package/docs/settings-auth-trust-security.md +6 -5
  110. package/docs/sqlite-persistence.md +123 -0
  111. package/docs/structured-output.md +9 -0
  112. package/docs/supervisors.md +71 -0
  113. package/docs/tool-conformance.md +1 -0
  114. package/docs/tool-execution-primitives.md +374 -0
  115. package/docs/tools.md +39 -1
  116. package/docs/workflow-orchestration-primitives.md +581 -0
  117. package/docs/workflow-tui-primitives.md +5 -0
  118. package/docs/workflows.md +293 -0
  119. package/docs/working-and-semantic-memory.md +169 -0
  120. package/package.json +43 -5
  121. package/templates/init/README.md.tmpl +28 -0
  122. package/templates/init/env.example.tmpl +1 -0
  123. package/templates/init/gitignore.tmpl +11 -0
  124. package/templates/init/optional/evals-example.ts.tmpl +17 -0
  125. package/templates/init/optional/workflows-example.ts.tmpl +27 -0
  126. package/templates/init/package.json.tmpl +22 -0
  127. package/templates/init/providers.json +76 -0
  128. package/templates/init/src/agent.ts.tmpl +10 -0
  129. package/templates/init/src/index.ts.tmpl +12 -0
  130. package/templates/init/src/tests/agent.test.ts.tmpl +24 -0
  131. package/templates/init/tsconfig.json.tmpl +15 -0
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- `RunLedger` is the host-implemented, write-only seam Prism uses to durably persist run metadata, agent events, tool calls, and usage during a `session.run()`. The runtime calls the adapter as each record becomes available; the adapter decides how to write it (SQL insert, NoSQL put, JSONL append, time-series batch, etc.).
5
+ `RunLedger` is the host-implemented, write-only seam Prism uses to durably persist run metadata, agent events, tool calls, and usage during a `session.run()`. `RunFeedbackStore` is the separate post-run seam for immutable ratings, comments, tags, and evaluation links. The runtime calls the adapter as each record becomes available; the adapter decides how to write it (SQL insert, NoSQL put, JSONL append, time-series batch, etc.).
6
6
 
7
7
  APIs:
8
8
 
@@ -13,6 +13,7 @@ APIs:
13
13
  - `ToolCallRecord` / `ToolCallStatus`
14
14
  - `UsageRecord`
15
15
  - `redactRunLedgerRecord()`
16
+ - `RunFeedbackRecord` / `RunFeedbackStore` / `createMemoryRunFeedbackStore()`
16
17
 
17
18
  ## When to use it
18
19
 
@@ -37,7 +38,7 @@ Set the ledger and optional ownership scope/idempotency key on the agent or the
37
38
  | `appendRun` | `RunRecord` | After run starts (`running`) and again at finish (`succeeded`/`failed`/`aborted`). |
38
39
  | `appendEvent` | `AgentEventRecord` | After every emitted `AgentEvent`, after redaction. |
39
40
  | `appendToolCall` | `ToolCallRecord` | For each tool-call `started`, `progress`, `finished`, `error`, and `blocked` transition. |
40
- | `appendUsage` | `UsageRecord` | For each provider `usage` event and for the final loop usage. |
41
+ | `appendUsage` | `UsageRecord` | Once per terminal provider turn (`scope: "provider_turn"`) and once for the O(turns) aggregate (`scope: "run_total"`). |
41
42
 
42
43
  All methods may be sync or async (`void | Promise<void>`). The runtime awaits them at safe boundaries, so a slow adapter blocks the run.
43
44
 
@@ -92,9 +93,40 @@ The adapter receives these record shapes:
92
93
  | --- | --- |
93
94
  | `id` | Unique ledger row id. |
94
95
  | `runId` / `sessionId` / `entryId` | Correlation ids. |
96
+ | `scope` | `provider_turn` for billable source rows; `run_total` for the aggregate. Never sum both scopes. |
97
+ | `turn` / `attempt` | Provider-turn attribution; absent on `run_total`. |
95
98
  | `usage` | `Usage` shape: input/output/total/cache tokens, cost, currency. |
96
99
  | `recordedAt` | ISO timestamp. |
97
100
 
101
+ ## Run/trace feedback
102
+
103
+ `RunFeedbackStore.append()` accepts an immutable record only when `resolveRun` finds the same `runId` under the exact `{ tenantId, accountId?, userId? }` scope. A tenant plus account or user is mandatory. Records contain `sessionId`, optional `traceId`, finite `rating` in `[-1, 1]`, comment, tags, scorer IDs, evaluation IDs, timestamp, creator, and metadata. Correction appends a new ID; records are never updated in place. `delete()` is the explicit privacy/retention operation.
104
+
105
+ ```ts
106
+ import { createMemoryRunFeedbackStore } from "@arnilo/prism";
107
+
108
+ const feedback = createMemoryRunFeedbackStore({
109
+ resolveRun: ({ runId }) => runId === result.runId
110
+ ? { runId, sessionId: result.sessionId, tenantId: "t1", userId: "u1" }
111
+ : false,
112
+ redactor,
113
+ });
114
+ await feedback.append({
115
+ id: "fb_1",
116
+ runId: result.runId,
117
+ rating: 1,
118
+ comment: "Useful and cited",
119
+ tags: ["reviewed"],
120
+ evaluationIds: ["eval_1"],
121
+ tenantId: "t1",
122
+ userId: "u1",
123
+ });
124
+ const page = await feedback.query({ runId: result.runId, tenantId: "t1", userId: "u1", limit: 50 });
125
+ await feedback.delete({ id: "fb_1", tenantId: "t1", userId: "u1" });
126
+ ```
127
+
128
+ Default/hard bounds: comment 4/16 KiB, tags 16/64, scorer/evaluation IDs 16/64 each, metadata 16/64 KiB, query page 100/500; tags are 64 characters and identifiers 128. The store redacts comment/tags/metadata after run ownership validation and before persistence. IDs are linked, not scorer payloads. `ProductionPersistenceStore.feedback?` exposes this capability; first-party SQLite/PostgreSQL adapters implement it in schema migration `003_run_feedback` and reject missing/cross-owned runs.
129
+
98
130
  ## Status transitions
99
131
 
100
132
  ```
@@ -189,7 +221,9 @@ await session.run("Hello", {
189
221
  });
190
222
 
191
223
  console.log(runs.at(-1)?.status); // succeeded
192
- console.log(cacheUsageReport(usageRows.at(-1)?.usage));
224
+ const billable = usageRows.filter((row) => row.scope === "provider_turn");
225
+ const aggregate = usageRows.find((row) => row.scope === "run_total");
226
+ console.log(cacheUsageReport(aggregate?.usage));
193
227
  // { cacheReadTokens: 0, cacheWriteTokens: 0, ... } when provider usage is present
194
228
  ```
195
229
 
@@ -208,9 +242,11 @@ console.log(cacheUsageReport(usageRows.at(-1)?.usage));
208
242
  - `AgentConfig.idempotencyKey` is the default idempotency key; `RunOptions.idempotencyKey` overrides it per run.
209
243
  - The runtime resolves `model` and `provider` from `AgentConfig`/`RunOptions`/`AgentDefinition` before writing the start `RunRecord`.
210
244
  - Adapters should treat appends as ordered within a `runId`: event and tool-call rows preserve emission order because the runtime drains pending appends before writing the final `RunRecord`.
245
+ - Billing queries must filter `scope = "provider_turn"`; presentation queries normally read the single `run_total`. `UsageQuery.scope`, `turn`, and `attempt` are explicit filters.
211
246
  - Adapters that need upsert semantics can use `RunRecord.id` (== `runId`) as the stable key.
212
247
  - Use `cacheUsageReport(record.usage, model)` for cache diagnostics from normalized usage. It works when a provider reports `cacheReadTokens` without `cacheWriteTokens`; missing write tokens are reported as `0`, and unavailable hit rate/savings stay `undefined`.
213
248
  - **Provider-specific telemetry is package-owned.** Core `Usage` carries token counts and `cost`/`currency`; it has no energy or detailed cost-breakdown fields. Providers that surface extra telemetry (e.g. `@arnilo/prism-provider-neuralwatt` exposes `neuralWattEventsWithTelemetry()`, `parseNeuralWattComment()`, and `mapNeuralWattTelemetry()` for `: energy`/`: cost` SSE comments and non-streaming top-level fields) keep that data in package-specific helpers/types. Telemetry never enters `RunLedger` usage rows unless the host explicitly copies it in; it carries usage/cost numbers only — never prompts, API keys, or headers. Account-level quota is likewise package-owned: `@arnilo/prism-provider-neuralwatt` exports an explicit `getNeuralWattQuota()` helper that the host calls on demand (never during generation); NeuralWatt rate-limits that endpoint to 1 request per second per customer, so the caller owns throttling.
249
+ - **Live timing metadata.** `provider_turn_*` events and `ToolExecutionMetadata` on terminal `tool_execution_*` events expose latency, retry `attempt`, and tool `durationMs` for subscribers and ledger replay — see [Observability](observability.md).
214
250
 
215
251
  ## Security and performance notes
216
252
 
@@ -218,9 +254,11 @@ console.log(cacheUsageReport(usageRows.at(-1)?.usage));
218
254
  - **Redaction.** The runtime calls `redactRunLedgerRecord()` and `redactAgentEvent()` with the active `SecretRedactor` before handing records to the adapter. `AgentEventRecord.redacted` and `ToolCallRecord.redacted` are set to `true` when a redactor is configured. Hosts should still redact before writing to durable storage if they perform additional transformations.
219
255
  - **Message content stays in `SessionStore`.** `AgentEventRecord.event` may contain `message_delta` / `message_finished` payloads; these are redacted but still belong conceptually to the session store. Do not use the ledger as the source of truth for messages.
220
256
  - **Cache diagnostics stay numeric.** `cacheUsageReport()` derives reports from `Usage` numbers and optional `ModelConfig.cost`; do not add prompt text, cache keys, headers, credentials, or provider payloads to usage rows.
257
+ - **No double billing.** Sum `provider_turn` rows or read `run_total`; never sum both. Run totals add every turn/attempt in O(turns), derive missing per-turn totals from input/output tokens, and omit aggregate cost when reported currencies conflict.
221
258
  - **Synchronous adapters block the run.** An adapter that performs network or heavy DB writes inline will slow down the agent loop. For high-throughput hosts, buffer or batch inside the adapter and return quickly; the runtime awaits the returned promise. If batching, preserve per-run order before acknowledging a batch: `appendEvent` rows should be pageable by `(runId, sequence)`, run rows by `(sessionId, startedAt, id)`, and usage rows by `(runId, recordedAt, id)`.
222
259
  - **Idempotency is host-owned.** The runtime writes the key into `RunRecord.idempotencyKey`; enforcing unique keys and deduplicating retries is the host adapter's responsibility.
223
- - **Tenant isolation.** `OwnershipScope` fields are copied from the active ownership scope, but the runtime does not enforce tenant isolation. Host adapters must apply their own access controls when querying persisted ledger rows.
260
+ - **Tenant isolation.** `OwnershipScope` fields are copied from the active ownership scope, but the runtime does not enforce tenant isolation for ledger rows. Feedback is stricter: append/query/delete require tenant plus account/user, and first-party stores compare the exact scope to the linked run.
261
+ - **Feedback privacy.** Comments/tags/metadata can contain PII. Configure a feedback redactor, apply retention, and call owned `delete()` for erasure. Never copy comments or tag values into metric labels.
224
262
 
225
263
  ## Related APIs
226
264
 
@@ -233,4 +271,5 @@ console.log(cacheUsageReport(usageRows.at(-1)?.usage));
233
271
  - [Session stores](session-stores.md): `SessionStore` contract for session entries and branches.
234
272
  - [Credentials and redaction](credentials-and-redaction.md): `createSecretRedactor()` and redaction helpers.
235
273
  - [Provider caching](provider-caching.md): cache hints and `cacheUsageReport()` diagnostics.
274
+ - [Observability](observability.md): `provider_turn_*` events, tool duration metadata, OpenTelemetry adapter.
236
275
  - [Public contracts](public-contracts.md): full contract inventory.
package/docs/server.md ADDED
@@ -0,0 +1,139 @@
1
+ # Web-standard server handler
2
+
3
+ ## What it does
4
+
5
+ `@arnilo/prism-server` exposes explicitly selected agents and workflows through one framework-free `(Request) => Promise<Response>` handler. It supports direct agent results, bounded agent/workflow SSE, durable workflow start/enqueue/status/cancel/resume/replay, ownership-scoped schedules, host authorization, ownership propagation, redaction, and resource ceilings.
6
+
7
+ No listener starts on import. Empty `agents`/`workflows` maps expose nothing. Authentication, authorization, route selection, durable stores, TLS, rate limiting, and framework/serverless adaptation remain host-owned.
8
+
9
+ ## When to use it
10
+
11
+ Use it when a Node 20, serverless, worker, or framework host already speaks Web `Request`/`Response` and needs a small Prism API boundary. Wrap it in the platform's native adapter rather than adding Express, Fastify, Hono, Koa, Nest, or Next to Prism.
12
+
13
+ Use `AgentSession` or workflow APIs directly for in-process applications. Do not treat this package as an auth provider, user database, firewall, durable agent-result store, or public listener.
14
+
15
+ ## Inputs / request
16
+
17
+ ```ts
18
+ const handler = createPrismHandler({
19
+ agents?: Record<string, Agent | PrismAgentExposure>,
20
+ workflows?: Record<string, PrismWorkflowExposure>,
21
+ schedules?: WorkflowSchedules | ((authorization, signal) => WorkflowSchedules),
22
+ authorize: async ({ request, operation, capabilityId }) => false | {
23
+ ownership: { tenantId?: string; accountId?: string; userId?: string },
24
+ metadata?: Record<string, unknown>,
25
+ },
26
+ basePath?: "/prism",
27
+ allowedHosts?: string[],
28
+ allowedOrigins?: string[],
29
+ redactor?: SecretRedactor,
30
+ limits?: PrismServerLimits,
31
+ disconnectAborts?: boolean,
32
+ });
33
+ ```
34
+
35
+ At least one non-empty ownership field must come from `authorize()`. Request JSON never chooses ownership.
36
+
37
+ | Method and route | Authorization operation | Body |
38
+ | --- | --- | --- |
39
+ | `POST /prism/agents/:id/runs` | `agent.run` | `{ "input": string | Message | Message[] }` |
40
+ | `POST /prism/agents/:id/stream` | `agent.stream` | same; SSE response |
41
+ | `POST /prism/workflows/:id/runs` | `workflow.run` | `{ "input": unknown, "runId"?: string }` |
42
+ | `POST /prism/workflows/:id/stream` | `workflow.stream` | same; SSE response |
43
+ | `POST /prism/workflows/:id/enqueue` | `workflow.enqueue` | `{ "input": unknown, "runId"?: string }`; returns `202` queued handle |
44
+ | `GET /prism/workflows/:id/runs/:runId` | `workflow.status` | none |
45
+ | `DELETE /prism/workflows/:id/runs/:runId` | `workflow.cancel` | none |
46
+ | `POST /prism/workflows/:id/runs/:runId/resume` | `workflow.resume` | `{ "decision": "approve" | "deny", "input"?: unknown, "expectedVersion": number }` |
47
+ | `POST /prism/workflows/:id/runs/:runId/replay` | `workflow.replay` | `{ "fromNodeId": string, "runId"?: string }` |
48
+ | `POST /prism/schedules/:id` | `schedule.create` | `{ "workflowId", "nextRunAt", "input"?, "intervalMs"?, "calculatorId"?, "paused"?, "metadata"? }` |
49
+ | `GET /prism/schedules?status=&cursor=&limit=` | `schedule.list` | none |
50
+ | `POST /prism/schedules/:id/pause` | `schedule.pause` | `{}` |
51
+ | `POST /prism/schedules/:id/resume` | `schedule.resume` | `{ "nextRunAt"?: string }` |
52
+ | `POST /prism/schedules/:id/trigger` | `schedule.trigger` | `{ "idempotencyKey": string }` |
53
+ | `DELETE /prism/schedules/:id` | `schedule.delete` | none |
54
+
55
+ POST routes require `Content-Type: application/json`. Capability/run IDs are bounded URL-safe identifiers. A custom `PrismAgentExposure.sessionFactory` can build sessions from authorized host context; otherwise an `Agent` creates a fresh session.
56
+
57
+ ## Outputs / response / events
58
+
59
+ Direct routes return bounded JSON. Stream routes return `text/event-stream`; every event is one `data: <AgentEvent|WorkflowEvent>` frame. Status returns the ownership-scoped durable checkpoint record. Resume uses Phase 8 expected-version CAS. Cancel aborts active work or marks eligible durable checkpoints aborted.
60
+
61
+ Errors use `{ "error": { "code", "message" } }`. Unknown routes/capabilities are `404`, authorization/policy denial `403`, malformed input `400`, unsupported content type `415`, body overflow `413`, concurrency overflow `429`, and result overflow `507`. Unexpected errors are generic and never include stacks.
62
+
63
+ ## Request/response example
64
+
65
+ ```json
66
+ {
67
+ "request": { "method": "POST", "path": "/prism/agents/support/runs", "body": { "input": "Summarize this" } },
68
+ "response": { "status": "succeeded", "sessionId": "...", "runId": "...", "text": "Summary" }
69
+ }
70
+ ```
71
+
72
+ ## Implementation example
73
+
74
+ ```ts
75
+ import { createAgent, createMockProvider, providerDone, providerTextDelta } from "@arnilo/prism";
76
+ import { createPrismHandler } from "@arnilo/prism-server";
77
+
78
+ const agent = createAgent({
79
+ model: { provider: "mock", model: "offline" },
80
+ provider: createMockProvider([providerTextDelta("ready"), providerDone()]),
81
+ });
82
+
83
+ const handler = createPrismHandler({
84
+ agents: { support: agent },
85
+ authorize: async ({ request }) => request.headers.get("authorization") === "Bearer host-validated"
86
+ ? { ownership: { tenantId: "tenant-1", userId: "user-1" } }
87
+ : false,
88
+ allowedHosts: ["api.example.test"],
89
+ allowedOrigins: ["https://app.example.test"],
90
+ });
91
+
92
+ // Cloudflare/Bun/Deno-style: export { handler as fetch }.
93
+ // Node/framework hosts adapt their request to Web Request and return Web Response.
94
+ ```
95
+
96
+ ## Extension and configuration notes
97
+
98
+ - `basePath` defaults to `/prism`; URL root exposure is rejected.
99
+ - Agent maps and workflow maps are immutable host selections. No registry/package discovery runs.
100
+ - Workflow exposure requires its existing `WorkflowCheckpointAdapter`; no server-owned database exists.
101
+ - Schedule exposure is optional and may be one service or an authorization-selected resolver. Returned service ownership must exactly match authorized tenant/account/user scope; otherwise request is forbidden.
102
+ - `PrismWorkflowExposure.runOptions` can supply agent/tool/policy/resume-validator wiring. Server-owned ownership, signal, checkpoint, redactor, run ID, and event bus fields cannot be overridden.
103
+ - Host/origin checks and CORS headers activate only when their allow-lists are configured. Hosts still own reverse-proxy trust and canonical host handling.
104
+
105
+ Default/hard ceilings:
106
+
107
+ | Limit | Default | Hard cap |
108
+ | --- | ---: | ---: |
109
+ | JSON request | 64 KiB | 1 MiB |
110
+ | direct response | 1 MiB | 8 MiB |
111
+ | SSE event | 64 KiB | 1 MiB |
112
+ | SSE total | 10 MiB | 64 MiB |
113
+ | SSE event count | 10,000 | 100,000 |
114
+ | concurrent runs | 16 | 256 |
115
+ | subscriber queue | 128 | 4,096 |
116
+ | request/run timeout | 120 s | 30 min |
117
+
118
+ ## Security and performance notes
119
+
120
+ - `authorize()` is required and runs for every matched operation before capability lookup or body execution. Return `false` on missing/invalid credentials. Do not trust caller ownership fields.
121
+ - Use authorization metadata only for non-secret audit context. Never put credentials in metadata, input, route IDs, run IDs, checkpoints, events, or responses.
122
+ - Configure `SecretRedactor` before runs. Redaction matches known secrets; it is not DLP.
123
+ - Agent tools and workflow tool nodes still need their own `PermissionPolicy`, `ToolValidator`, and `ExecutionPolicy`. HTTP authorization does not replace side-effect policy.
124
+ - Host and origin allow-lists are exact string matches. Configure reverse-proxy normalization, TLS, rate limiting, IP policy, CSRF/cookie policy, and authentication outside Prism.
125
+ - SSE uses bounded upstream subscriber queues. Consumer cancellation aborts owned work by default and releases concurrency; set `disconnectAborts: false` only when the host deliberately owns background completion.
126
+ - Source inputs/resource URLs remain host responsibilities and use existing resource/media SSRF policies. Server package does not fetch URLs.
127
+ - Schedule routes never accept ownership from JSON. Services carry mandatory ownership and explicit workflow/calculator registries; route authorization cannot broaden either. Replay applies workflow ownership/hash/approval checks.
128
+ - No agent status/reconnect store is invented. Durable reconnect/status/resume is the workflow path; persistent agent run querying remains a host persistence API.
129
+
130
+ A2A routes are not added to `createPrismHandler()`. Install `@arnilo/prism-supervisor` and explicitly mount `createA2AHandler()` when protocol interoperability is required; this keeps cards and remote invoke absent from ordinary Prism servers.
131
+
132
+ ## Related APIs
133
+
134
+ - [Agent/session runtime](agent-session-runtime.md): direct result and event stream semantics.
135
+ - [Workflows](workflows.md): durable checkpoints, status, cancellation, and exact-once resume.
136
+ - [MCP client and server exposure](mcp-tools.md): selected MCP capabilities and web-standard MCP transport.
137
+ - [Host security guide](host-security.md): remote-boundary checklist.
138
+ - [A2A interoperability](a2a.md): separately mounted A2A 1.0 handler/client.
139
+ - [Release and install](release-and-install.md): optional package installation and profiles.
@@ -7,7 +7,9 @@ Session store conformance helpers are dependency-free assertions for `SessionSto
7
7
  Exported from `@arnilo/prism/testing/session-store-conformance`:
8
8
 
9
9
  - `assertSessionStoreConforms(store, options?)`
10
+ - `runSessionStoreConformance(factory, options?)`
10
11
  - `SessionStoreConformanceOptions`
12
+ - `SessionStoreConformanceFactory`
11
13
 
12
14
  ## When to use it
13
15
 
@@ -20,6 +22,9 @@ Use this helper when implementing a DB-backed `SessionStore` (for example, the r
20
22
  - branching from any existing entry (existence validation, not tip-CAS)
21
23
  - distinct linear appends sharing a run-level `idempotencyKey` are not collapsed
22
24
  - optional `readBranchPath` returns the ancestor chain in root-to-leaf order (when `exerciseReadBranchPath: true`)
25
+ - session ids remain isolated (`assertSessionStoreConforms` always probes a secondary session)
26
+ - optional concurrent fork children of the same parent succeed when `exerciseConcurrentParentAppend: true`
27
+ - optional durable reopen/idempotency survival when `runSessionStoreConformance(..., { exerciseReopen: true })`
23
28
 
24
29
  ## Inputs / request
25
30
 
@@ -28,11 +33,21 @@ import { assertSessionStoreConforms } from "@arnilo/prism/testing/session-store-
28
33
  import type { SessionStore } from "@arnilo/prism";
29
34
 
30
35
  await assertSessionStoreConforms(myDbBackedStore, { exerciseReadBranchPath: true });
36
+
37
+ // Durable adapters: factory reopens the same backing store
38
+ await runSessionStoreConformance(() => createStore(testDatabase), {
39
+ exerciseReadBranchPath: true,
40
+ exerciseReopen: true,
41
+ exerciseConcurrentParentAppend: true,
42
+ });
31
43
  ```
32
44
 
33
45
  `SessionStoreConformanceOptions`:
34
46
  - `sessionId?: string` — stable session id for the run (default `"conformance"`)
47
+ - `otherSessionId?: string` — secondary session for isolation probes
35
48
  - `exerciseReadBranchPath?: boolean` — also probe `readBranchPath` when implemented
49
+ - `exerciseConcurrentParentAppend?: boolean` — also probe concurrent fork appends
50
+ - `exerciseReopen?: boolean` — only on `runSessionStoreConformance`; reopen via the same factory
36
51
 
37
52
  ## Outputs / response / events
38
53
 
@@ -75,4 +90,5 @@ await assertSessionStoreConforms(createMemorySessionStore());
75
90
  - [Session stores and branching](session-stores-and-branching.md)
76
91
  - [Session stores](session-stores.md)
77
92
  - [Database persistence](database-persistence.md)
93
+ - [Run ledger conformance](run-ledger-conformance.md)
78
94
  - [Provider conformance](provider-conformance.md)
@@ -119,5 +119,6 @@ Use `createDefaultCompactionStrategy()` to create compaction entries that `rebui
119
119
  - [Compaction and retry policies](compaction-and-retry.md): default strategy for creating compaction entries.
120
120
  - [Input and prompt assembly](input-and-prompt-assembly.md): provider input assembly consumes rebuilt `messages` and `summaries`.
121
121
  - [Credentials and redaction](credentials-and-redaction.md): security boundary for secrets that must not enter session entries.
122
+ - [Workflows](workflows.md): optional orchestration that reuses session `leafId` on resume rather than reloading full transcripts into the scheduler.
122
123
 
123
124
  Session stores persist the entries they receive. Configure `AgentConfig.redactor` or `RunOptions.redactor` before a run when known secrets must be removed before entries reach durable stores.
@@ -18,7 +18,7 @@ Use these APIs when a host wants one explicit place to compose settings, resolve
18
18
  - Node-only subpaths: `@arnilo/prism/node/settings` for caller-named JSON settings files and `@arnilo/prism/node/trust` for explicit trusted path roots with symlink-aware realpath checks.
19
19
 
20
20
  ## Outputs / response / events
21
- Settings and credential helpers return existing `SettingsProvider` and `CredentialResolver` contracts. `AgentConfig.settings` and `AgentConfig.credentials` are host-owned metadata for compatibility; `createAgent()` / `session.run()` do not call `settings.get()` or `credentials.resolve()`. Permission denial blocks tool execution, extension setup, and resource loader calls before side effects. A configured `AgentConfig.redactor` or `RunOptions.redactor` redacts provider requests, emitted `AgentEvent` payloads, stored `SessionEntry` values, and runtime `InstructionContext` input/history seen by instruction injectors.
21
+ Settings and credential helpers return existing `SettingsProvider` and `CredentialResolver` contracts. These seams are host-owned outside `AgentConfig`; `createAgent()` / `session.run()` do not call `settings.get()` or `credentials.resolve()`. Permission denial blocks tool execution, extension setup, and resource loader calls before side effects. A configured `AgentConfig.redactor` or `RunOptions.redactor` redacts provider requests, emitted `AgentEvent` payloads, stored `SessionEntry` values, and runtime `InstructionContext` input/history seen by instruction injectors.
22
22
 
23
23
  ## Request/response example
24
24
  ```ts
@@ -47,17 +47,17 @@ const apiKey = await resolveCredentialValue(credentials, { name: "api", provider
47
47
 
48
48
  const agent = createAgent({
49
49
  model: { provider: "demo", model: "model" },
50
- // host-owned metadata; runtime does not read/resolve these fields
51
- settings,
52
- credentials,
50
+ // Resolve credentials at the provider edge; register known secrets for redaction.
53
51
  redactor: apiKey ? createSecretRedactor([apiKey]) : undefined,
54
52
  });
53
+ void settings;
54
+ void credentials;
55
55
  void trust;
56
56
  void agent;
57
57
  ```
58
58
 
59
59
  ## Extension and configuration notes
60
- Root imports stay filesystem-free. Node settings files are caller-named and read once; optional missing files are skipped. Trust storage, prompts, approval UI, OAuth token storage, environment-variable selection, and persistent credentials belong in the host or an extension package. Passing `settings` / `credentials` on `AgentConfig` does not wire hidden runtime reads; hosts pass concrete values or resolvers to the provider/request edge that needs them.
60
+ Root imports stay filesystem-free. Node settings files are caller-named and read once; optional missing files are skipped. Trust storage, prompts, approval UI, OAuth token storage, environment-variable selection, and persistent credentials belong in the host or an extension package. For Node.js hosts, [`@arnilo/prism-credentials-node`](credential-storage.md) provides encrypted-file and system-keychain backends. Pass concrete settings values or credential resolvers to the provider/request edge that needs them; do not place them on `AgentConfig`.
61
61
 
62
62
  ## Security and performance notes
63
63
  Prism does not sandbox host tools or extensions. Prism does not read environment variables, keychains, user config files, package manifests, resources, settings providers, credential resolvers, or project-local extensions unless the host explicitly wires those operations. Redaction is exact known-secret replacement only; it is not secret detection. Permission and trust checks are one operation per guarded call and add no workers, watchers, retries, network, or filesystem scans.
@@ -81,6 +81,7 @@ Boundary hardening summary:
81
81
  - `createMemoryCredentialStore`, `createChainedCredentialResolver`, `createExplicitCredentialResolver`, `createEnvCredentialResolver`, `refreshOAuthCredential`, `resolveCredentialValue`
82
82
  - `createStaticTrustPolicy`, `assertTrusted`, `isTrusted`, `TrustDeniedError`
83
83
  - `createStaticPermissionPolicy`, `assertPermission`, `checkPermission`, `PermissionDeniedError`
84
+ - `ExecutionPolicy`, `assertExecutionAllowed`, `checkExecution`, `ExecutionDeniedError` (core); `@arnilo/prism-coding-security` for coding-tool approval adapters — see [Coding execution approval and sandboxing](coding-security.md)
84
85
  - `createSecretRedactor`, `redactMessage`, `redactAgentEvent`, `redactSessionEntry`, `redactProviderRequest`
85
86
  - `@arnilo/prism/node/settings`: `defaultUserSettingsPath`, `readSettingsFile`, `loadSettingsFiles`
86
87
  - `@arnilo/prism/node/trust`: `createPathTrustPolicy`, `isPathInside`, `isPathInsideReal`
@@ -0,0 +1,123 @@
1
+ # SQLite persistence
2
+
3
+ ## What it does
4
+
5
+ The optional `@arnilo/prism-session-store-sqlite` package ships a production-oriented SQLite adapter that implements:
6
+
7
+ - `SessionStore` — atomic `append` / `list` / `get` / `readBranchPath`
8
+ - `RunLedger` — durable run, event, tool-call, and usage rows
9
+ - `ProductionPersistenceStore` — cursor-paginated `query*` reads plus generic `checkpoints` and atomic `leases` capabilities
10
+
11
+ Factory:
12
+
13
+ - `createSqlitePersistence(options)`
14
+ - `SqlitePersistenceOptions`
15
+ - `SqlitePersistence.close()`
16
+
17
+ The adapter uses `better-sqlite3@^12.11.1`, enables WAL and foreign keys, applies versioned migrations from the shared Plan 056 schema model, and passes the full session-store and run-ledger conformance suites including process reopen.
18
+
19
+ ## When to use it
20
+
21
+ Use this package when you want a small, file-backed persistence layer on Node without operating a database server:
22
+
23
+ - local CLI tools and desktop hosts
24
+ - single-writer or low-concurrency deployments
25
+ - integration tests that need durable reopen semantics
26
+
27
+ Do **not** use it as a substitute for PostgreSQL when you need heavy multi-writer concurrency, server-side pooling, or managed TLS. See [`@arnilo/prism-session-store-postgres`](postgres-persistence.md) for that path.
28
+
29
+ ## Inputs / request
30
+
31
+ ```ts
32
+ import { createSqlitePersistence } from "@arnilo/prism-session-store-sqlite";
33
+ ```
34
+
35
+ | Field | Type | Purpose |
36
+ | --- | --- | --- |
37
+ | `filename` | `string` | SQLite database path. Use `:memory:` for ephemeral tests. |
38
+ | `wal` | `boolean` | Enable WAL journal mode. Defaults to `true`. |
39
+ | `busyTimeoutMs` | `number` | SQLite `busy_timeout` in milliseconds. Defaults to `5000`. |
40
+ | `feedbackRedactor` | `SecretRedactor` | Optional redaction for feedback comment/tags/metadata before insert. |
41
+ | `fileMode` | `number` | Unix file mode for newly created database files. Defaults to `0o600`. |
42
+ | `database` | `Database` | Advanced: supply an existing `better-sqlite3` handle (caller owns lifecycle). |
43
+
44
+ ## Outputs / response / events
45
+
46
+ `createSqlitePersistence()` returns one object implementing the three persistence contracts plus generic checkpoints and leases:
47
+
48
+ | Method group | Behavior |
49
+ | --- | --- |
50
+ | `SessionStore.append` | One transaction per append: parent existence check, idempotency dedup, duplicate-id rejection, entry insert. |
51
+ | `SessionStore.list` / `get` | Indexed reads by `session_id` and primary key. |
52
+ | `SessionStore.readBranchPath` | Recursive ancestor query from `leafId` (or latest leaf) in root→leaf order. |
53
+ | `RunLedger.append*` | Inserts run/event/tool/usage rows; events receive monotonic per-run `sequence` values. |
54
+ | `ProductionPersistenceStore.query*` | Parameterized cursor pagination on indexed columns. |
55
+ | `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. |
56
+ | `leases` | Atomic `LeaseStore` backed by `prism_leases`; database-clock expiry, opaque renew/release token, monotonic takeover fence. |
57
+ | `close()` | Closes the underlying database when the adapter opened it. |
58
+
59
+ Migrations run automatically on open and are idempotent across reopen.
60
+
61
+ ## Request/response example
62
+
63
+ ```json
64
+ {
65
+ "filename": "./prism.db",
66
+ "wal": true,
67
+ "busyTimeoutMs": 5000,
68
+ "fileMode": 384
69
+ }
70
+ ```
71
+
72
+ ## Implementation example
73
+
74
+ ```ts
75
+ import { createAgentSession } from "@arnilo/prism";
76
+ import { createSqlitePersistence } from "@arnilo/prism-session-store-sqlite";
77
+ import { runSessionStoreConformance } from "@arnilo/prism/testing/session-store-conformance";
78
+
79
+ const persistence = createSqlitePersistence({ filename: "./prism.db" });
80
+
81
+ await runSessionStoreConformance(
82
+ () => createSqlitePersistence({ filename: "./prism.db" }),
83
+ { exerciseReadBranchPath: true, exerciseReopen: true },
84
+ );
85
+
86
+ const session = createAgentSession({
87
+ sessionStore: persistence,
88
+ runLedger: persistence,
89
+ });
90
+
91
+ await session.run("hello");
92
+ persistence.close();
93
+ ```
94
+
95
+ For resume/timeline flows, use `queryRuns`, `queryEvents`, `queryToolCalls`, and `queryUsage` the same way as the reference mock in [`examples/external-app-db-backed.ts`](../examples/external-app-db-backed.ts).
96
+
97
+ ## Extension and configuration notes
98
+
99
+ - The package is optional and workspace-local; `@arnilo/prism` core has no SQLite dependency.
100
+ - Hosts choose the database path and own backup, retention enforcement, and filesystem permissions.
101
+ - `SessionAppendOptions` idempotency rows are durable in `prism_session_append_idempotency` and survive reopen.
102
+ - Schema version **3** applies `001_init`, additive `002_usage_scope`, and `003_run_feedback`. Migration 003 adds immutable `prism_run_feedback` rows with run FK/cascade deletion and owner/run/trace cursor indexes. `persistence.feedback` validates exact run ownership, bounds/redacts through optional `feedbackRedactor`, queries bounded pages, and deletes only exact-owned IDs. PostgreSQL shares the same model with dialect-local DDL.
103
+ - Pass an existing `better-sqlite3` `Database` via `database` when your host already manages connections.
104
+
105
+ ## Security and performance notes
106
+
107
+ - **Parameterized SQL only.** Session ids, idempotency keys, tenant ids, and JSON payloads are bound parameters.
108
+ - **File ownership.** Create database files on a host-controlled path with restrictive permissions (`0600` default on Unix via `fileMode`).
109
+ - **No path interpolation.** The adapter opens exactly the caller-supplied `filename`; it does not expand environment variables or discover paths.
110
+ - **Redaction upstream.** Event and tool-call payloads may contain secrets; redact before ledger writes. The adapter does not scan or rewrite row contents.
111
+ - **WAL + busy timeout.** WAL is enabled by default; busy timeout defaults to 5 seconds. This meets the Plan 056 local workload target but SQLite still serializes writers — prefer PostgreSQL for high write concurrency.
112
+ - **Indexed operations.** Append, parent validation, idempotency dedup, branch reads, and pagination use the indexes documented in [Database persistence](database-persistence.md); normal paths avoid whole-database scans.
113
+ - **Tenant isolation.** `tenant_id` / `account_id` / `user_id` columns on run and ownership tables participate in query filters; hosts must still scope writes correctly.
114
+
115
+ ## Related APIs
116
+
117
+ - [Database persistence](database-persistence.md): shared schema model, conditional append pattern, indexes.
118
+ - [Session store conformance](session-store-conformance.md): `assertSessionStoreConforms` / `runSessionStoreConformance`.
119
+ - [Run ledger conformance](run-ledger-conformance.md): `assertRunLedgerConforms` / `runRunLedgerConformance`.
120
+ - [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): package matrix and threat model.
121
+ - [Node JSONL session store](node-jsonl-session-store.md): dev-only single-process alternative.
122
+ - [Workflows](workflows.md): adapt `persistence.checkpoints` and pass `persistence.leases` to `createWorkflowCoordinator()` and `createWorkflowSchedules()` for durable background execution and schedules.
123
+ - [Migration guide](migration.md): moving from JSONL/in-memory to database-backed persistence.
@@ -10,6 +10,8 @@ An artifact loop generates provider text, parses it to `T`, validates `T` agains
10
10
 
11
11
  Use `generateValidateReviseLoop` (with host `parser`/`validator`/`repairer`) when a run should produce an artifact that must satisfy a host-owned schema before it is considered complete: structured JSON output, a generated file passing lint, a typed response conforming to a Synapta-defined model. Wrap your existing schema/validation library behind the `Artifact*` callbacks.
12
12
 
13
+ When the model declares `capabilities.structuredOutput` and the host opts into native mode, pass `structuredOutput` on `RunOptions.providerOptions` or on the `generate-validate-revise` loop options so capable providers map the schema to their wire format (`response_format` / Responses `text.format`) and valid output can finish in one turn without repair revisions.
14
+
13
15
  Do not use it to re-implement provider calls, retry, abort, store, or event emission — those stay runtime-owned and are exposed to the loop only through `LoopContext`. Do not use it for runs that need tool calls during revision turns — use `singleShotLoop` or a custom `AgentLoopStrategy` instead. Do not put Synapta domain types into Prism; map them to `ArtifactValidation` in your callbacks.
14
16
 
15
17
  ## Inputs / request
@@ -47,6 +49,11 @@ await session.run(input, {
47
49
  parser, // optional; default treats assistant text as the value
48
50
  repairer, // optional; default stringifies validation.errors[].message
49
51
  maxRevisions: 3, // optional; default 3
52
+ structuredOutput: { name: "answer", schema, strict: true }, // optional native mode
53
+ structuredOutputMode: "native", // or "artifact-loop" to skip provider-native schema
54
+ },
55
+ providerOptions: {
56
+ structuredOutput: { name: "answer", schema, strict: true }, // direct native request
50
57
  },
51
58
  });
52
59
  ```
@@ -222,6 +229,8 @@ Key cross-seam points:
222
229
  ## Extension and configuration notes
223
230
 
224
231
  - `generate-validate-revise` is selected via `AgentConfig.loop` / `RunOptions.loop` (`RunOptions.loop` wins). See [Agent loops](agent-loops.md). `resolveLoop()` maps the options form to the factory; an unknown `strategy` throws before the first turn; a custom `AgentLoopStrategy` instance bypasses the options form.
232
+ - Native structured output uses provider-neutral `StructuredOutputOptions` on `ProviderRequestOptions` / loop options. Capable OpenAI-family providers map to JSON-schema wire fields; unsupported models fail before fetch unless the host sets `structuredOutputMode: "artifact-loop"` and relies on parser/validator/repairer only.
233
+ - `validateStructuredOutputOptions()` enforces JSON-safe schemas, forbidden prototype-pollution keys, and a 64 KiB schema size cap.
225
234
  - The default parser treats assistant text as the value (`{ ok: true, value: text }`); supply a host parser whenever `T` is not `string`.
226
235
  - The default repairer builds a user message from `validation.errors[].message`; supply a host repairer for schema-specific guidance.
227
236
  - `maxRevisions` (default 3) bounds revision turns; budget exhaustion ends the loop and emits `artifact_failed` (it does not throw).
@@ -0,0 +1,71 @@
1
+ # Supervisor delegation
2
+
3
+ ## What it does
4
+
5
+ `@arnilo/prism-supervisor` adds optional runtime-selected delegation to an explicit local child allow-list. It returns normal `AgentRunResult` values and does not modify core `createAgent()` or deterministic workflows.
6
+
7
+ ## When to use it
8
+
9
+ Use a supervisor when a host or agent must choose a child dynamically. Use `@arnilo/prism-workflows` for known DAGs, durable checkpoints, schedules, replay, or human suspension.
10
+
11
+ ## Inputs / request
12
+
13
+ | API/field | Meaning |
14
+ | --- | --- |
15
+ | `createSupervisor({ ownership, children })` | Creates one ownership-scoped supervisor. |
16
+ | `SupervisorChild.createAgent(context)` | Child-owned factory; receives derived resource/thread IDs, narrowed permission, abort signal, and nested `delegate`. |
17
+ | `delegate({ childId, input, threadId?, limits?, signal? })` | Invokes one allow-listed child. Input is text and byte-bounded. |
18
+ | `hooks.before` | May reject, modify redacted input, or narrow limits/policy. |
19
+ | `hooks.after` | Observes redacted terminal summary; failures cannot alter settled result. |
20
+ | `limits` | Depth 4/16, active children 4/32, input 64 KiB/1 MiB, steps 8/64, tools 32/256, tokens 20k/1m, timeout 60s/30m, event queue 128/4096 default/hard. |
21
+
22
+ ## Outputs / response / events
23
+
24
+ `delegate()` returns the child's `AgentRunResult` or throws its `AgentRunError`/a supervisor denial or limit error. `subscribe()` emits bounded `delegation_started`, `delegation_finished`, `delegation_rejected`, and `delegation_error` metadata events.
25
+
26
+ ## Request/response example
27
+
28
+ ```json
29
+ {"childId":"research","input":"Check primary sources","limits":{"maxTokens":4000}}
30
+ ```
31
+
32
+ ## Implementation example
33
+
34
+ ```ts
35
+ import { createSupervisor } from "@arnilo/prism-supervisor";
36
+
37
+ const supervisor = createSupervisor({
38
+ ownership: { tenantId: "tenant", userId: "user" },
39
+ permission: parentPolicy,
40
+ children: {
41
+ research: {
42
+ permission: readOnlyPolicy,
43
+ createAgent: ({ resourceId, threadId, permission, delegate }) =>
44
+ createResearchAgent({ resourceId, threadId, permission, delegate }),
45
+ },
46
+ },
47
+ hooks: { before: ({ input }) => ({ input, limits: { maxTokens: 4000 } }) },
48
+ });
49
+
50
+ const result = await supervisor.delegate({ childId: "research", input: "Check sources" });
51
+ ```
52
+
53
+ ## Extension and configuration notes
54
+
55
+ Child factories resolve their own providers/credentials and construct context/memory using the supplied IDs. Parent, child, returned-agent, budget, and hook permission policies are AND-composed. Child/request/hook limits can only lower inherited limits. A nested factory can call the supplied `delegate()`; immutable path state rejects cycles and depth overflow.
56
+
57
+ ## Security and performance notes
58
+
59
+ - Child IDs are explicit; no package/provider discovery occurs.
60
+ - `resourceId` and `threadId` include supervisor/delegation/child identity. Do not replace them with parent memory IDs.
61
+ - Tool budget is checked before side effects. Token usage is enforced on terminal aggregate usage and can exceed by at most one provider turn because providers report tokens after generation.
62
+ - Abort and timeout cover hooks, child creation, nested delegation, and the run. Host child code must cooperate with `AbortSignal`.
63
+ - Redaction applies before hook input, run metadata/results, completion hooks, and events. Child credentials are never supplied in delegation context.
64
+ - Static workflows remain smaller and more reproducible for known graphs.
65
+
66
+ ## Related APIs
67
+
68
+ - [A2A interoperability](a2a.md): remote protocol boundary.
69
+ - [Workflows](workflows.md): preferred deterministic orchestration.
70
+ - [Working and semantic memory](working-and-semantic-memory.md): child scope construction.
71
+ - [Host security](host-security.md): permission and credential boundaries.
@@ -77,6 +77,7 @@ await assertToolDispatchConforms(createToolRegistry(), {
77
77
  ## Security and performance notes
78
78
 
79
79
  - No credentials, no network required.
80
+ - Supply `validate` to exercise your policy; use `createJsonSchemaToolArgumentValidator()` from `@arnilo/prism-tool-validator-json-schema` for standards-based `parameters` validation.
80
81
  - The helper uses an allow-all permission policy by default; supply `permission` to validate your fail-closed policy.
81
82
  - Blocked calls are proven not to execute by the absence of `tool_execution_started`.
82
83