@arnilo/prism 0.3.2 → 0.5.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.
Files changed (208) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +42 -62
  3. package/dist/agent-run-lifecycle.js +4 -0
  4. package/dist/agent-run-state.d.ts +5 -2
  5. package/dist/agent-run-state.js +18 -8
  6. package/dist/agent-session/session/assemble.d.ts +6 -0
  7. package/dist/agent-session/session/assemble.js +391 -0
  8. package/dist/agent-session/session/persist.d.ts +28 -0
  9. package/dist/agent-session/session/persist.js +166 -0
  10. package/dist/agent-session/session/provider-round.d.ts +6 -0
  11. package/dist/agent-session/session/provider-round.js +231 -0
  12. package/dist/agent-session/session/tool-round.d.ts +31 -0
  13. package/dist/agent-session/session/tool-round.js +473 -0
  14. package/dist/agent-session/session/types.d.ts +115 -0
  15. package/dist/agent-session/session/types.js +5 -0
  16. package/dist/agent-session/session.d.ts +54 -41
  17. package/dist/agent-session/session.js +23 -1132
  18. package/dist/capture.d.ts +63 -0
  19. package/dist/capture.js +67 -0
  20. package/dist/cli-dev.d.ts +29 -0
  21. package/dist/cli-dev.js +52 -0
  22. package/dist/cli-init.d.ts +34 -3
  23. package/dist/cli-init.js +192 -24
  24. package/dist/cli-runner.d.ts +6 -2
  25. package/dist/cli-runner.js +57 -10
  26. package/dist/content.d.ts +3 -3
  27. package/dist/content.js +3 -1
  28. package/dist/contracts-core/agent.d.ts +8 -0
  29. package/dist/contracts-core/batch.d.ts +97 -0
  30. package/dist/contracts-core/batch.js +65 -0
  31. package/dist/contracts-core/content.d.ts +72 -1
  32. package/dist/contracts-core/embeddings.d.ts +30 -0
  33. package/dist/contracts-core/embeddings.js +17 -0
  34. package/dist/contracts-core/images.d.ts +60 -0
  35. package/dist/contracts-core/images.js +17 -0
  36. package/dist/contracts-core/moderation.d.ts +46 -0
  37. package/dist/contracts-core/moderation.js +34 -0
  38. package/dist/contracts-core/speech.d.ts +39 -0
  39. package/dist/contracts-core/speech.js +17 -0
  40. package/dist/contracts-core/transcription.d.ts +48 -0
  41. package/dist/contracts-core/transcription.js +17 -0
  42. package/dist/contracts-core/video.d.ts +61 -0
  43. package/dist/contracts-core/video.js +17 -0
  44. package/dist/contracts-core.d.ts +7 -0
  45. package/dist/contracts-core.js +7 -0
  46. package/dist/contracts-protocol.d.ts +18 -0
  47. package/dist/contracts-run-state.d.ts +1 -2
  48. package/dist/index.d.ts +7 -3
  49. package/dist/index.js +5 -3
  50. package/dist/input.d.ts +8 -0
  51. package/dist/input.js +4 -0
  52. package/dist/node/agent-definitions.d.ts +1 -8
  53. package/dist/node/agent-definitions.js +0 -34
  54. package/dist/node/settings.d.ts +0 -1
  55. package/dist/node/settings.js +0 -5
  56. package/dist/pinned-fetch.js +29 -3
  57. package/dist/provider-events.js +3 -4
  58. package/dist/providers/media.d.ts +1 -2
  59. package/dist/providers/media.js +1 -4
  60. package/dist/rpc.d.ts +1 -1
  61. package/dist/rpc.js +4 -4
  62. package/dist/testing/persistence-schema.d.ts +1 -1
  63. package/dist/testing/persistence-schema.js +32 -28
  64. package/dist/testing/provider-conformance.d.ts +114 -5
  65. package/dist/testing/provider-conformance.js +342 -0
  66. package/dist/testing/tool-conformance.d.ts +25 -0
  67. package/dist/testing/tool-conformance.js +128 -1
  68. package/dist/testing/tool-effect-store-conformance.d.ts +0 -1
  69. package/dist/testing/tool-effect-store-conformance.js +0 -3
  70. package/dist/thinking.d.ts +48 -9
  71. package/dist/thinking.js +134 -8
  72. package/dist/tool-search.d.ts +76 -0
  73. package/dist/tool-search.js +199 -0
  74. package/docs/0.1.0-readiness.md +3 -3
  75. package/docs/a2a.md +2 -2
  76. package/docs/acp-agent.md +1 -1
  77. package/docs/acp.md +3 -3
  78. package/docs/ag-ui-adoption.md +1 -1
  79. package/docs/ag-ui.md +1 -2
  80. package/docs/agent-definitions.md +1 -1
  81. package/docs/agent-events.md +5 -5
  82. package/docs/agent-identity.md +13 -2
  83. package/docs/audit-export.md +3 -3
  84. package/docs/batch-jobs.md +120 -0
  85. package/docs/browser-automation.md +5 -5
  86. package/docs/caveman.md +2 -2
  87. package/docs/cli-rpc.md +43 -9
  88. package/docs/coding-agent-tools.md +19 -19
  89. package/docs/coding-review-and-diagnostics.md +2 -2
  90. package/docs/coding-security.md +5 -5
  91. package/docs/coding-tools.md +82 -0
  92. package/docs/coding-workspaces.md +2 -2
  93. package/docs/compaction-and-retry.md +2 -2
  94. package/docs/compaction-llm.md +4 -4
  95. package/docs/compaction-observational-memory.md +3 -3
  96. package/docs/computer-use-linux.md +13 -2
  97. package/docs/context-and-skills.md +3 -1
  98. package/docs/conversations.md +4 -4
  99. package/docs/core.md +85 -0
  100. package/docs/credential-storage.md +12 -8
  101. package/docs/credentials-and-redaction.md +1 -1
  102. package/docs/data-classification.md +1 -1
  103. package/docs/database-persistence.md +7 -3
  104. package/docs/dev-inspector.md +103 -0
  105. package/docs/device-adapters.md +2 -2
  106. package/docs/diagrams.md +247 -0
  107. package/docs/document-reader.md +6 -6
  108. package/docs/documents.md +214 -0
  109. package/docs/embeddings.md +112 -0
  110. package/docs/enterprise-postgres-state.md +7 -7
  111. package/docs/evaluations.md +41 -7
  112. package/docs/extensions.md +3 -3
  113. package/docs/forge-integration.md +3 -3
  114. package/docs/graft.md +5 -5
  115. package/docs/guardrails.md +2 -2
  116. package/docs/host-security.md +16 -15
  117. package/docs/image-generation.md +129 -0
  118. package/docs/impeccable.md +7 -5
  119. package/docs/index.md +84 -46
  120. package/docs/indexed-code-search.md +2 -2
  121. package/docs/language-intelligence.md +4 -4
  122. package/docs/live-testing.md +126 -0
  123. package/docs/mcp-tools.md +44 -13
  124. package/docs/middleware-hooks.md +1 -1
  125. package/docs/migrate-to-0.4.md +312 -0
  126. package/docs/migrate-to-0.5.md +122 -0
  127. package/docs/migration.md +51 -1
  128. package/docs/model-registry.md +38 -0
  129. package/docs/model-routing.md +6 -6
  130. package/docs/moderation.md +117 -0
  131. package/docs/multi-agent-patterns.md +177 -0
  132. package/docs/multimodal-content.md +27 -3
  133. package/docs/obscura.md +12 -12
  134. package/docs/observability.md +32 -7
  135. package/docs/openapi-tools.md +14 -4
  136. package/docs/operations.md +11 -0
  137. package/docs/performance.md +30 -10
  138. package/docs/persistence-credentials-multimodality-primitives.md +7 -7
  139. package/docs/policy-and-audit.md +18 -8
  140. package/docs/ponytail.md +3 -3
  141. package/docs/postgres-persistence.md +5 -5
  142. package/docs/process-sessions.md +2 -2
  143. package/docs/prompt-registry.md +106 -0
  144. package/docs/provider-caching.md +36 -32
  145. package/docs/provider-conformance.md +24 -2
  146. package/docs/provider-packages.md +58 -22
  147. package/docs/provider-primitives.md +5 -5
  148. package/docs/provider-request-policies.md +1 -1
  149. package/docs/providers/ai-sdk.md +18 -6
  150. package/docs/providers/alibaba.md +10 -6
  151. package/docs/providers/anthropic.md +10 -6
  152. package/docs/providers/azure.md +20 -4
  153. package/docs/providers/bedrock.md +18 -3
  154. package/docs/providers/clinepass.md +7 -3
  155. package/docs/providers/commandcode.md +253 -0
  156. package/docs/providers/deepseek.md +7 -3
  157. package/docs/providers/google.md +8 -4
  158. package/docs/providers/hyper.md +284 -0
  159. package/docs/providers/kimi.md +7 -3
  160. package/docs/providers/neuralwatt.md +12 -8
  161. package/docs/providers/ollama.md +18 -3
  162. package/docs/providers/openai-compatible.md +5 -1
  163. package/docs/providers/openai.md +9 -5
  164. package/docs/providers/opencode-go.md +8 -4
  165. package/docs/providers/openrouter.md +8 -4
  166. package/docs/providers/vertex.md +21 -5
  167. package/docs/providers/xai.md +7 -3
  168. package/docs/providers/zai.md +7 -3
  169. package/docs/rag.md +31 -9
  170. package/docs/release-and-install.md +181 -76
  171. package/docs/resource-loading.md +1 -1
  172. package/docs/runs-and-usage.md +28 -3
  173. package/docs/server.md +94 -5
  174. package/docs/settings-auth-trust-security.md +7 -5
  175. package/docs/sheets.md +229 -0
  176. package/docs/speech.md +126 -0
  177. package/docs/sqlite-persistence.md +4 -4
  178. package/docs/supervisors.md +4 -3
  179. package/docs/thinking-and-reasoning.md +93 -60
  180. package/docs/tool-conformance.md +28 -3
  181. package/docs/tool-execution-primitives.md +8 -8
  182. package/docs/tools.md +32 -5
  183. package/docs/web-tools.md +3 -3
  184. package/docs/wiki.md +7 -7
  185. package/docs/work-artifacts-and-review.md +17 -6
  186. package/docs/work-connectors.md +4 -4
  187. package/docs/work-tools.md +5 -5
  188. package/docs/workflow-orchestration-primitives.md +35 -11
  189. package/docs/workflows.md +74 -13
  190. package/docs/working-and-semantic-memory.md +53 -5
  191. package/package.json +14 -31
  192. package/templates/README.md +23 -0
  193. package/templates/deep-research/README.md.tmpl +47 -0
  194. package/templates/deep-research/env.example.tmpl +12 -0
  195. package/templates/deep-research/gitignore.tmpl +7 -0
  196. package/templates/deep-research/manifest.json +12 -0
  197. package/templates/deep-research/package.json.tmpl +23 -0
  198. package/templates/deep-research/src/agent.ts.tmpl +81 -0
  199. package/templates/deep-research/src/index.ts.tmpl +53 -0
  200. package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
  201. package/templates/deep-research/src/tools.ts.tmpl +86 -0
  202. package/templates/deep-research/src/types.ts.tmpl +45 -0
  203. package/templates/deep-research/src/workflow.ts.tmpl +156 -0
  204. package/templates/deep-research/tsconfig.json.tmpl +15 -0
  205. package/templates/init/manifest.json +5 -0
  206. package/templates/init/package.json.tmpl +2 -1
  207. package/templates/init/providers.json +40 -24
  208. package/docs/antigravity-agent.md +0 -207
package/docs/server.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
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, cross-replica durable agent-event reconnect, opt-in durable agent status/resume, durable workflow start/enqueue/status/cancel/resume/replay, ownership-scoped schedules, host authorization, ownership propagation, redaction, resource ceilings, and optional deployment seams (health/readiness, drain, host rate-limit adapter, ownership-scoped event replay, worker/coordinator lease election).
5
+ `@arnilo/prism-core/runtime/server` exposes explicitly selected agents and workflows through one framework-free `(Request) => Promise<Response>` handler. It supports direct agent results, bounded agent/workflow SSE, cross-replica durable agent-event reconnect, opt-in durable agent status/resume, durable workflow start/enqueue/status/cancel/resume/replay, ownership-scoped schedules, host authorization, ownership propagation, redaction, resource ceilings, and optional deployment seams (health/readiness, drain, host rate-limit adapter, ownership-scoped event replay, worker/coordinator lease election).
6
6
 
7
7
  No listener starts on import. Empty `agents`/`workflows` maps expose nothing. Authentication, authorization, route selection, durable stores, TLS, distributed rate limiting, queues, and framework/serverless adaptation remain host-owned.
8
8
 
@@ -82,7 +82,7 @@ Errors use `{ "error": { "code", "message" } }`. Unknown routes/capabilities are
82
82
 
83
83
  ```ts
84
84
  import { createAgent, createMockProvider, providerDone, providerTextDelta } from "@arnilo/prism";
85
- import { createPrismHandler } from "@arnilo/prism-server";
85
+ import { createPrismHandler } from "@arnilo/prism-core/runtime/server";
86
86
 
87
87
  const agent = createAgent({
88
88
  model: { provider: "mock", model: "offline" },
@@ -128,6 +128,95 @@ Default/hard ceilings:
128
128
  | drain admit cutoff | 30 s | 5 min |
129
129
  | replay page / cursor | 100 / 4 KiB | 500 / 16 KiB |
130
130
 
131
+ ## Outbound webhooks
132
+
133
+ ### What it does
134
+
135
+ `createWebhookNotifier()` posts selected terminal agent/workflow events to host-registered HTTPS endpoints. Every JSON envelope is redacted before HMAC-SHA-256 signing; `X-Prism-Signature` is `sha256=<hex>` and `X-Prism-Timestamp` carries the signed envelope timestamp.
136
+
137
+ ### When to use it
138
+
139
+ Use it for host-owned PagerDuty, Slack, or application hooks after a run completes, fails, or suspends. Omit targets when no outbound notification is wanted: no target means no queued delivery or network activity.
140
+
141
+ ### Inputs / request
142
+
143
+ | Field | Meaning |
144
+ | --- | --- |
145
+ | `targets` | Host-configured `{ url, events }` entries. URLs must be public HTTPS, without credentials/fragments; private and metadata literals fail during registration. |
146
+ | `allowLoopbackHttp` | Explicit opt-in permitting `http:` for loopback hostnames (development receivers only); never combined with private or metadata targets. Default false. |
147
+ | `signer.key` | Host-held `Uint8Array` HMAC key, at least 32 bytes. |
148
+ | `redactor` | Required `SecretRedactor`; its result is placed in `redactedPayload` before signing. |
149
+ | `limits.maxQueuedEvents` | Global outbound queue cap; default 128, hard cap 4,096. Overflow drops newest and increments `diagnostics().dropped`. |
150
+ | `limits.timeoutMs` / `maxEventBytes` | Per-attempt timeout (5 s default, 30 s hard) and JSON envelope byte cap (64 KiB default, 1 MiB hard). |
151
+ | `limits.retries` / `retryBaseDelayMs` / `retryMaxDelayMs` / `retryJitter` | Retries after the first attempt (3 default, 10 hard); exponential 100 ms→5 s delays (30 s hard), with ±25% jitter by default. Set jitter to `0` only for deterministic tests. |
152
+ | `limits.maxFailureRecords` | Redacted terminal failure-record ring buffer; 32 default, 256 hard. |
153
+
154
+ `notify()` accepts `run.completed`, `run.failed`, `run.suspended`, `workflow.completed`, `workflow.failed`, or `workflow.suspended`. `onAgentEvent()` and `onWorkflowEvent()` translate Prism lifecycle events into those names.
155
+
156
+ ### Outputs / response / events
157
+
158
+ `notify()` returns immediately after bounded enqueue. Pass `{ signal }` as its second argument to cancel queued or retrying delivery for that run. `diagnostics()` returns `{ queued, delivered, failed, dropped, retries, cancelled, failures, lastError? }`: `failed` is the `prism.webhook.failed` terminal-failure counter, `failures` is bounded and redacted, and `lastError` is its latest redacted error.
159
+
160
+ A `2xx` delivery succeeds. `4xx` is terminal except `429`; `429`, `5xx`, and transport failures retry within the configured cap. `Retry-After` is honored within `retryMaxDelayMs`. Delivery is at-least-once: receiver timeouts after processing can yield duplicates.
161
+
162
+ ```json
163
+ {
164
+ "id": "01d2...",
165
+ "event": "run.failed",
166
+ "runId": "run-42",
167
+ "status": "failed",
168
+ "redactedPayload": { "error": "[REDACTED]" },
169
+ "timestamp": "2026-01-01T00:00:00.000Z"
170
+ }
171
+ ```
172
+
173
+ ### Request/response example
174
+
175
+ Receivers verify the raw request body, not a parsed/re-serialized object:
176
+
177
+ ```ts
178
+ const expected = createHmac("sha256", hostHmacKey).update(rawBody).digest("hex");
179
+ const valid = request.headers.get("x-prism-signature") === `sha256=${expected}`;
180
+ ```
181
+
182
+ ### Implementation example
183
+
184
+ ```ts
185
+ import { createSecretRedactor, type AgentSession } from "@arnilo/prism";
186
+ import { createWebhookNotifier } from "@arnilo/prism-core/runtime/server";
187
+
188
+ const notifier = createWebhookNotifier({
189
+ targets: [{ url: "https://ops.example.test/prism", events: ["run.failed", "workflow.suspended"] }],
190
+ signer: { key: Buffer.from(process.env.PRISM_WEBHOOK_HMAC!, "hex") },
191
+ redactor: createSecretRedactor([process.env.PRISM_WEBHOOK_HMAC]),
192
+ });
193
+
194
+ export function wireWebhookAgentSession(session: AgentSession): AgentSession {
195
+ void (async () => {
196
+ for await (const event of session.subscribe()) notifier.onAgentEvent(event);
197
+ })();
198
+ return session;
199
+ }
200
+
201
+ export const webhookWorkflowRunOptions = { onEvent: notifier.onWorkflowEvent };
202
+ ```
203
+
204
+ The agent `sessionFactory` is the server-handler adapter. Workflow `onEvent` receives the same events emitted through its event bus, including `workflow_finished` and `workflow_suspended`.
205
+
206
+ ### Extension and configuration notes
207
+
208
+ Targets are static host configuration, never request JSON, tool output, or extension discovery. Event filters are exact. The notifier is a server-package export; it neither starts a listener nor owns a durable queue, auth provider, or webhook receiver.
209
+
210
+ ### Security and performance notes
211
+
212
+ Delivery uses core `pinnedFetch` only: every attempt DNS-pins a public address and rejects all redirects, including redirects toward private targets. The HMAC key, signature, and delivery errors are never logged by the notifier; bounded failure records redact error text before retention. HTTPS is mandatory; plaintext HTTP is allowed only with explicit `allowLoopbackHttp: true` and a loopback hostname, and still pins/validates DNS. Queue overflow deliberately drops newest deliveries so earlier accepted lifecycle events retain order; use `diagnostics()` to observe loss. The in-memory queue does not survive restart; a durable outbox is required when cross-restart delivery matters. The outbound-webhook threat leg lives in `npm run security:threat-suites` (`scripts/phase46-webhooks-security.test.mjs` plus the package and pinned-fetch fixtures).
213
+
214
+ ### Related APIs
215
+
216
+ - [Multimodal content](multimodal-content.md): shared DNS-pinned outbound fetch primitive.
217
+ - [Workflows](workflows.md): workflow event-bus and `onEvent` lifecycle seam.
218
+ - [Host security guide](host-security.md): remote-boundary controls.
219
+
131
220
  ## Deployment seams (optional)
132
221
 
133
222
  Compose beside `createPrismHandler` — Prism starts no listener, container orchestrator, or queue worker.
@@ -139,7 +228,7 @@ Compose beside `createPrismHandler` — Prism starts no listener, container orch
139
228
  | `rateLimit` on handler | Host adapter after authorize, before session create. Return denial `{ retryAfterMs, code, message }` → `429` + optional `Retry-After`. `createMemoryRateLimiter` is single-process only. |
140
229
  | `createPrismAgentEventReplay` | Shared `AgentEventSource` page/follow semantics for exact-owned runs. |
141
230
  | `createPrismEventReplay` / `createPrismReplayHandler` | Compatible ownership-scoped legacy `queryEvents` pages (`redacted: true`). Does not re-run work. Unauthorized replay denies. |
142
- | `createPrismDeploymentLease` | Lease election under `prism.server.deployment`. Coordinator replica holds `key: "coordinator"` before schedule ticks; workers run `@arnilo/prism-workflows` `createWorkflowCoordinator` for queued runs (fencing tokens). |
231
+ | `createPrismDeploymentLease` | Lease election under `prism.server.deployment`. Coordinator replica holds `key: "coordinator"` before schedule ticks; workers run `@arnilo/prism-core/runtime/workflows` `createWorkflowCoordinator` for queued runs (fencing tokens). |
143
232
  | `createConversationService` / `createConversationHandler` | Durable user-scoped conversation threads (create/list/continue/branch/archive/export/delete) over session + event-ledger seams, with thread-bound reconnectable replay. Mounts beside the handler; see [Conversations](conversations.md). |
144
233
  | `createArtifactService` / `createArtifactHandler` | Durable artifact co-work review (attach/revise/compare/approve/reject/last-validated/delivery-link + authorized download) over the versioned checkpoint store; records persist metadata/revisions/approvals only, never file bodies. Mounts beside the handler; see [Work artifacts and review](work-artifacts-and-review.md). |
145
234
 
@@ -158,11 +247,11 @@ Network-free demo: [`examples/server-deployment-seams.ts`](../examples/server-de
158
247
  - Health endpoints reveal process/liveness only by default; detail flags require host authorize and must omit secrets/tenant dumps.
159
248
  - Drain and event replay require the same ownership/authorize boundary as other routes; replay never invokes providers or tools. Durable event routes exist only on object `PrismAgentExposure` entries with both `events` and `resolveRun`; every reconnect authorizes again, resolves public run ID to exact internal session/run IDs, and opens the shared source without `sessionFactory`.
160
249
  - 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.
161
- - Source inputs/resource URLs remain host responsibilities and use existing resource/media SSRF policies. Server package does not fetch URLs.
250
+ - Source inputs/resource URLs remain host responsibilities and use existing resource/media SSRF policies. Apart from explicitly configured `WebhookNotifier` targets, server package features do not fetch URLs.
162
251
  - 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.
163
252
  - Agent status/resume routes exist only for keys in `agentRuns`. Supply one core `createAgentRunLifecycle({ checkpoints, resolveAgent })` capability per selected agent; its resolver returns current `{ agent, definitionRevision }`. It reuses core checkpoint parsing/CAS/fingerprint checks, returns only public state/version, and needs a durable `SessionStore` as well as checkpoints for restart-safe resume. Empty/default configuration adds no agent lifecycle route, polling, or server cache.
164
253
 
165
- 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.
254
+ A2A routes are not added to `createPrismHandler()`. Install `@arnilo/prism-core/runtime/supervisor` and explicitly mount `createA2AHandler()` when protocol interoperability is required; this keeps cards and remote invoke absent from ordinary Prism servers.
166
255
 
167
256
  ## Related APIs
168
257
 
@@ -34,11 +34,13 @@ console.log(await settings.get("demo.enabled"));
34
34
  ## Implementation example
35
35
  ```ts
36
36
  import { createAgent, createMemoryCredentialStore, createSecretRedactor, resolveCredentialValue } from "@arnilo/prism";
37
- import { loadSettingsFiles, defaultUserSettingsPath } from "@arnilo/prism/node/settings";
37
+ import { loadSettingsFiles } from "@arnilo/prism/node/settings";
38
38
  import { createPathTrustPolicy } from "@arnilo/prism/node/trust";
39
+ import { homedir } from "node:os";
40
+ import { join } from "node:path";
39
41
 
40
42
  const settings = await loadSettingsFiles([
41
- { name: "user", path: defaultUserSettingsPath(), optional: true },
43
+ { name: "user", path: join(homedir(), ".config", "prism", "settings.json"), optional: true },
42
44
  ]);
43
45
  const credentials = createMemoryCredentialStore();
44
46
  credentials.set({ name: "api", provider: "demo", credential: { type: "api_key", value: "token-value" } });
@@ -57,7 +59,7 @@ void agent;
57
59
  ```
58
60
 
59
61
  ## 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. 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`.
62
+ 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-core/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
63
 
62
64
  ## Security and performance notes
63
65
  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,8 +83,8 @@ Boundary hardening summary:
81
83
  - `createMemoryCredentialStore`, `createChainedCredentialResolver`, `createExplicitCredentialResolver`, `createEnvCredentialResolver`, `refreshOAuthCredential`, `resolveCredentialValue`
82
84
  - `createStaticTrustPolicy`, `assertTrusted`, `isTrusted`, `TrustDeniedError`
83
85
  - `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)
86
+ - `ExecutionPolicy`, `assertExecutionAllowed`, `checkExecution`, `ExecutionDeniedError` (core); `@arnilo/prism-coding-tools/security` for coding-tool approval adapters — see [Coding execution approval and sandboxing](coding-security.md)
85
87
  - `createSecretRedactor`, `redactMessage`, `redactAgentEvent`, `redactSessionEntry`, `redactProviderRequest`
86
- - `@arnilo/prism/node/settings`: `defaultUserSettingsPath`, `readSettingsFile`, `loadSettingsFiles`
88
+ - `@arnilo/prism/node/settings`: `readSettingsFile`, `loadSettingsFiles` (`defaultUserSettingsPath` was removed in 0.5.0 — build the path with `node:os`/`node:path`)
87
89
  - `@arnilo/prism/node/trust`: `createPathTrustPolicy`, `isPathInside`, `isPathInsideReal`
88
90
  - [Contribution discovery (workspace)](contribution-discovery.md): `createPathTrustPolicy` + `isPathInsideReal` gate workspace contribution roots fail-closed.
package/docs/sheets.md ADDED
@@ -0,0 +1,229 @@
1
+ # Spreadsheets, CSV parsing, and typed schema inference (`@arnilo/prism-office/sheets`)
2
+
3
+ ## What it does
4
+
5
+ The `@arnilo/prism-office/sheets` package provides fail-closed, high-fidelity spreadsheet (XLSX) and delimiter-separated (CSV/TSV/PSV) data ingestion with automatic dialect sniffing, typed column schema inference, and **strict financial decimal safety**.
6
+
7
+ ### Headline Guarantee: Strict Financial Decimal Safety
8
+
9
+ > [!IMPORTANT]
10
+ > **Zero Float Coercion on Decimal Paths**:
11
+ > In financial and enterprise data processing, floating-point rounding errors (IEEE-754 `double`) silently distort monetary totals, balance ledgers, and transaction reconciliations.
12
+ >
13
+ > In `@arnilo/prism-office/sheets`:
14
+ > - Money-like and decimal values are **never converted to JavaScript numbers (`Number()`, `parseFloat()`, or unary `+`)**.
15
+ > - All decimal and currency values are parsed, normalized, and emitted as exact canonical decimal strings: `{ type: "decimal", value: "1234.56" }`.
16
+ > - Currency markers (`$`, `€`, `£`, `¥`, `₹`, `CHF`, `USD`, `EUR`, etc.) and accounting parentheses `($1,234.56)` are normalized safely into canonical strings (`"-1234.56"`).
17
+ > - Ambiguous numbers (e.g. scientific notation `1.23e5` or inconsistent locale separators) are preserved as raw strings with `flags: ["numeric-ambiguous"]` rather than guessed.
18
+ > - An automated source-scan test in the regression suite enforces that no floating-point conversions exist on decimal paths across the codebase.
19
+
20
+ ### Core Capabilities
21
+
22
+ - **Pure In-Memory Operation**: Accepts `Uint8Array` binary archives or `string | Uint8Array` CSV text. Zero filesystem access, zero network I/O, zero `process.env` lookups, and zero background worker threads.
23
+ - **Fail-Closed Container Gating**: XLSX archives must begin with the standard ZIP container magic signature (`PK\x03\x04`). CSV inputs support UTF-8 (with automatic BOM stripping); UTF-16 encoded buffers are refused fail-closed with `ERR_PRISM_SHEETS_UNSUPPORTED_FORMAT`.
24
+ - **Automatic CSV Dialect Sniffing**: Analyzes the first 4 KiB / 50 lines to detect candidate delimiters (`,`, `;`, `\t`, `|`) using variance-based scoring outside quoted regions, correctly distinguishing delimiters from commas within quoted addresses or company names.
25
+ - **RFC 4180 State Machine**: Handles embedded newlines in quoted fields, doubled-quote escaping (`""`), and CRLF / LF line endings in a single, non-backtracking linear pass (<60 ms for 1 MB CSVs).
26
+ - **Read-Only Formula Preservation**: OpenXML formula cells are extracted as `{ type: "formula", formula: "=SUM(A1:A10)", cachedValue: "100" }` and are **never evaluated or executed**.
27
+ - **Bounded Sampling Window + Full Validation**: Infers column types across an initial window (default 500 rows) and validates all subsequent records against the inferred schema, reporting structured `type-mismatch` warnings without silent coercion.
28
+ - **Privacy-Guaranteed Telemetry Seam**: Optional, dependency-free `SheetsTelemetry` hook emits `sheets.parse` spans carrying byte, row, column, and duration metrics with zero cell text or confidential payload leakage.
29
+
30
+ ## When to use it
31
+
32
+ Use `@arnilo/prism-office/sheets` when autonomous agents, data pipelines, or enterprise workflows need to:
33
+ 1. Ingest untrusted customer XLSX or CSV files with strict, unbypassable byte, row, column, and sheet caps.
34
+ 2. Parse tabular financial records, invoices, ledgers, or pricing sheets with mathematical decimal precision guarantees.
35
+ 3. Automatically determine CSV delimiters, quotes, and headers without manual dialect configuration.
36
+ 4. Extract structural column schemas (`name`, `type`, `nullRate`, `sample`, `flags`) for automated data cataloging, SQL generation, or tool routing.
37
+
38
+ Do **not** use this package for interactive spreadsheet calculation/formula engines, macro execution, chart generation, or real-time collaborative editing.
39
+
40
+ ## Inputs / request
41
+
42
+ ### Primary Parsing Functions
43
+
44
+ | Function | Signature | Description |
45
+ | --- | --- | --- |
46
+ | `parseWorkbook` | `(bytes: Uint8Array, options?: ParseWorkbookOptions) => Promise<WorkbookParse>` | Parses an XLSX workbook binary buffer, enforces caps and ZIP signature gating, extracts raw cells, resolves shared strings and styles, and infers schemas per worksheet. |
47
+ | `parseCsv` | `(input: string \| Uint8Array, options?: ParseCsvOptions) => Promise<CsvParse>` | Parses CSV/TSV/PSV input with automatic dialect sniffing, BOM stripping, RFC 4180 state machine processing, and schema inference. |
48
+ | `inferAndTransformRows` | `(rows: readonly (readonly CellValue[])[], caps: ResolvedSheetsCaps) => InferAndTransformResult` | Pure function that infers column types over a sampling window and validates full rows against the inferred schema. |
49
+
50
+ ### Decimal & Normalization Utilities
51
+
52
+ | Function | Signature | Description |
53
+ | --- | --- | --- |
54
+ | `normalizeDecimal` | `(input: string) => NormalizedDecimalResult \| null` | Normalizes currency strings, accounting negatives, and thousands separators into canonical decimal strings (`^-?\d+(\.\d+)?$`). Returns `null` if ambiguous or invalid. |
55
+ | `isCanonicalDecimal` | `(str: string) => boolean` | Checks if a string is already in canonical decimal format (e.g. `"1234.56"`, `"-0.05"`, `"42"`). |
56
+ | `isCurrencyString` | `(str: string) => boolean` | Checks if a string contains explicit currency symbols or currency codes (`$`, `€`, `USD`, etc.). |
57
+ | `isScientificNotation` | `(str: string) => boolean` | Checks if a string represents scientific notation (e.g. `"1.23e5"`, `"4.56E-3"`). |
58
+
59
+ ### Cap Validation Utilities
60
+
61
+ | Function | Signature | Description |
62
+ | --- | --- | --- |
63
+ | `resolveSheetsCaps` | `(caps?: SheetsCaps) => ResolvedSheetsCaps` | Resolves user-configured limits against safe defaults and hard ceilings. |
64
+ | `validateByteCap` | `(byteLength: number, caps: ResolvedSheetsCaps) => void` | Validates input size against `caps.maxBytes`, throwing `SheetsCapError` if exceeded. |
65
+ | `validateZipSignature` | `(bytes: Uint8Array) => void` | Validates standard PKZIP magic bytes (`0x50, 0x4b, 0x03, 0x04`), throwing `SheetsFormatError` if missing. |
66
+
67
+ ### Capacity Limits and Defaults
68
+
69
+ Limits are verified upfront and enforced progressively to prevent memory exhaustion and denial-of-service from adversarial inputs:
70
+
71
+ | Cap | Default | Hard Ceiling | Description |
72
+ | --- | --- | --- | --- |
73
+ | `maxBytes` | 32 MiB (`33,554,432`) | 512 MiB (`536,870,912`) | Maximum input buffer or string byte length. |
74
+ | `maxSheets` | 100 | 1,000 | Maximum worksheets in a workbook archive. |
75
+ | `maxRows` | 100,000 | 1,000,000 | Maximum rows per worksheet or CSV document. |
76
+ | `maxColumns` | 1,000 | 16,384 | Maximum columns per worksheet or CSV document. |
77
+ | `inferenceWindowRows` | 500 | 5,000 | Number of rows sampled for column schema inference. |
78
+ | `maxWarnings` | 100 | 1,000 | Maximum validation and dialect warnings recorded. |
79
+
80
+ ## Outputs / response / events
81
+
82
+ ### Error Hierarchy
83
+
84
+ All error classes inherit from `SheetsError` and carry structured error codes:
85
+
86
+ | Error Class | Code | Cause / Trigger |
87
+ | --- | --- | --- |
88
+ | `SheetsCapError` | `ERR_PRISM_SHEETS_CAP` | Input size, sheet count, row count, or column count exceeds configured caps. |
89
+ | `SheetsValidationError` | `ERR_PRISM_SHEETS_VALIDATION` | Invalid cap configuration (e.g. non-integer or exceeding hard ceiling). |
90
+ | `SheetsFormatError` | `ERR_PRISM_SHEETS_UNSUPPORTED_FORMAT` | Missing ZIP container signature on XLSX input or unsupported encoding (UTF-16) on CSV input. |
91
+ | `SheetsParseError` | `ERR_PRISM_SHEETS_PARSE_FAILED` | Corrupt archive structure or malformed XML parts. |
92
+
93
+ ### Schema & Warnings Output Shape
94
+
95
+ ```ts
96
+ export interface ColumnSchema {
97
+ readonly name: string;
98
+ readonly type: "string" | "integer" | "number" | "decimal" | "date" | "datetime" | "boolean";
99
+ readonly nullRate: number;
100
+ readonly sample?: string;
101
+ readonly flags?: readonly ("numeric-ambiguous" | "mixed-types")[];
102
+ }
103
+
104
+ export interface InferenceWarning {
105
+ readonly row: number;
106
+ readonly column: number;
107
+ readonly columnName?: string;
108
+ readonly expectedType: string;
109
+ readonly actualValue: string;
110
+ readonly message: string;
111
+ }
112
+ ```
113
+
114
+ ## Request/response example
115
+
116
+ ### Input CSV
117
+
118
+ ```csv
119
+ transaction_id,item_description,total_amount,unit_price,refund_fee
120
+ TXN-1001,"Software Consulting","$1,234.56",1234.56,"($ 50.00)"
121
+ TXN-1002,"Cloud Server Hosting","€ 450.00",450.00,"-$ 10.00"
122
+ TXN-1003,"Hardware Device","£ 2,500.00",2500.00,"$ 0.00"
123
+ ```
124
+
125
+ ### Parsed Output (`CsvParse`)
126
+
127
+ ```json
128
+ {
129
+ "dialect": {
130
+ "delimiter": ",",
131
+ "quote": "\"",
132
+ "hasHeader": true
133
+ },
134
+ "schema": [
135
+ { "name": "transaction_id", "type": "string", "nullRate": 0, "sample": "TXN-1001", "flags": [] },
136
+ { "name": "item_description", "type": "string", "nullRate": 0, "sample": "Software Consulting", "flags": [] },
137
+ { "name": "total_amount", "type": "decimal", "nullRate": 0, "sample": "$1,234.56", "flags": [] },
138
+ { "name": "unit_price", "type": "decimal", "nullRate": 0, "sample": "1234.56", "flags": [] },
139
+ { "name": "refund_fee", "type": "decimal", "nullRate": 0, "sample": "($ 50.00)", "flags": [] }
140
+ ],
141
+ "rows": [
142
+ ["transaction_id", "item_description", "total_amount", "unit_price", "refund_fee"],
143
+ ["TXN-1001", "Software Consulting", { "type": "decimal", "value": "1234.56" }, { "type": "decimal", "value": "1234.56" }, { "type": "decimal", "value": "-50.00" }],
144
+ ["TXN-1002", "Cloud Server Hosting", { "type": "decimal", "value": "450.00" }, { "type": "decimal", "value": "450.00" }, { "type": "decimal", "value": "-10.00" }],
145
+ ["TXN-1003", "Hardware Device", { "type": "decimal", "value": "2500.00" }, { "type": "decimal", "value": "2500.00" }, { "type": "decimal", "value": "0.00" }]
146
+ ],
147
+ "warnings": []
148
+ }
149
+ ```
150
+
151
+ ## Implementation example
152
+
153
+ ```ts
154
+ import { parseWorkbook, parseCsv, type SheetsTelemetry } from "@arnilo/prism-office/sheets";
155
+
156
+ // 1. Parse XLSX workbook with custom caps
157
+ const xlsxBytes = new Uint8Array([...]); // Untrusted file bytes
158
+ const workbook = await parseWorkbook(xlsxBytes, {
159
+ caps: {
160
+ maxRows: 50_000,
161
+ maxColumns: 500,
162
+ maxBytes: 16 * 1024 * 1024,
163
+ },
164
+ });
165
+
166
+ for (const sheet of workbook.sheets) {
167
+ console.log(`Sheet: ${sheet.name} (${sheet.rows.length} rows)`);
168
+ for (const col of sheet.schema) {
169
+ console.log(` Column [${col.name}] inferred as ${col.type} (nullRate: ${col.nullRate})`);
170
+ }
171
+ }
172
+
173
+ // 2. Parse CSV with automatic dialect sniffing and telemetry
174
+ const csvText = `id;name;revenue\n1;"Acme, Corp";1000000.00\n2;"Global, Inc";500000.50\n`;
175
+
176
+ const telemetry: SheetsTelemetry = {
177
+ startSpan(name, attributes) {
178
+ console.log(`Span started: ${name}`, attributes);
179
+ return {
180
+ setAttribute() {},
181
+ addEvent() {},
182
+ recordError() {},
183
+ end() {},
184
+ };
185
+ },
186
+ };
187
+
188
+ const csvResult = await parseCsv(csvText, { telemetry });
189
+ console.log(`Detected delimiter: "${csvResult.dialect.delimiter}"`);
190
+ console.log(`Revenue value:`, csvResult.rows[1][2]);
191
+ // Output: { type: "decimal", value: "1000000.00" }
192
+ ```
193
+
194
+ ## Extension and configuration notes
195
+
196
+ ### Sub-package Pinning
197
+ To avoid pulling in CLI frameworks or extraneous dependencies, `@arnilo/prism-office/sheets` directly pins the exact underlying modular packages:
198
+ - `@office-open/xlsx@0.12.3`
199
+ - `@office-open/xml@0.12.3`
200
+
201
+ ### Custom Telemetry Hook
202
+ The `SheetsTelemetry` seam allows optional OpenTelemetry instrumentation without adding runtime telemetry dependencies:
203
+ ```ts
204
+ const telemetry: SheetsTelemetry = {
205
+ startSpan(name, attributes) {
206
+ // Maps to tracer.startSpan with allow-listed metadata (bytes, rows, columns, sheetCount)
207
+ // Cell contents and user data are NEVER passed to telemetry spans.
208
+ return activeSpan;
209
+ },
210
+ };
211
+ ```
212
+
213
+ ### Self-Hosting & Operational Notes
214
+ - **Zero Network & Storage Dependencies**: `@arnilo/prism-office/sheets` does not write files or contact network services. Host engines own persistence, storage buckets, and lake datasets.
215
+ - **Fail-Closed Container Gating**: Malicious or non-standard files are rejected before allocation or XML decompression occurs.
216
+
217
+ ## Security and performance notes
218
+
219
+ - **Pure In-Memory Operation**: No temporary files, no shell execution, no binary spawning, and zero network sockets.
220
+ - **ZIP Signature Gating**: Buffers must begin with standard PKZIP container signatures (`0x50, 0x4B, 0x03, 0x04`). Extension-based type inference is strictly prohibited.
221
+ - **Fail-Closed Caps**: Input size and element count caps are evaluated before entering XML translation passes, preventing zip-bomb and decompression amplification attacks.
222
+ - **Strict Anti-Corruption Invariant**: Zero floating-point conversions on decimal paths guarantee exact financial calculations and ledger balances.
223
+ - **Performance Budget**: Warm parsing of 1 MB CSV files completes in under 60 ms; workbook parsing completes in under 100 ms.
224
+
225
+ ## Related APIs
226
+
227
+ - [`@arnilo/prism-office/documents`](./documents.md): Specification-compliant OpenXML document generation, parsing, patching, and preview rendering for DOCX, XLSX, and PPTX.
228
+ - [`@arnilo/prism-coding-tools/document-reader`](./document-reader.md): Bounded literal text extraction from PDF and DOCX documents for coding agent tools.
229
+ - [`@arnilo/prism-core/governance/observability`](./observability.md): OpenTelemetry instrumentation and trace adapters.
package/docs/speech.md ADDED
@@ -0,0 +1,126 @@
1
+ # Speech synthesis and transcription
2
+
3
+ ## What it does
4
+
5
+ `SpeechProvider` is the provider-neutral speech contract: text→audio via
6
+ `synthesize` (full bytes) and `synthesizeStream` (first chunk as soon as the
7
+ provider responds). `TranscriptionProvider` is audio→text via `transcribe`
8
+ (one-shot) and `transcribeStream` (partial `TranscriptDelta` events, then one
9
+ `done`), sharing the Realtime `transcript_delta` event naming. Adapters ship in
10
+ [`@arnilo/prism-providers/openai`](providers/openai.md) (`/v1/audio/speech`,
11
+ `/v1/audio/transcriptions`); offline conformance runs via `runSpeechConformance`
12
+ and `runTranscriptionConformance` from `@arnilo/prism/testing/provider-conformance`.
13
+
14
+ ## When to use it
15
+
16
+ Use it for one-shot voice output and batch/stream transcription where the host
17
+ owns playback, capture, and audio storage. Do not use it for interactive
18
+ bidirectional voice — that is the Realtime session contract
19
+ ([`RealtimeSession`](public-contracts.md)), which keeps its own
20
+ `audio_delta`/`transcript_delta` events. Streaming here is one-directional:
21
+ synthesis streams audio out, transcription streams text in.
22
+
23
+ ## Inputs / request
24
+
25
+ | Field | Type | Meaning |
26
+ | --- | --- | --- |
27
+ | `model` | `string` | Speech model (`tts-1`, `gpt-4o-mini-tts`) or transcription model (`whisper-1`, `gpt-4o-transcribe`). |
28
+ | `input` | `string` (speech) | Text to speak; non-empty, within the provider input cap (OpenAI: 4,096 chars). |
29
+ | `audio` | `Uint8Array` (transcription) | Audio bytes; non-empty, within the provider cap (OpenAI: 25 MiB). |
30
+ | `voice` / `format` / `speed` | `string?` / `string?` / `number?` (speech) | Voice id, audio container (`mp3`, `wav`, `opus`), playback speed. Adapter defaults when omitted. |
31
+ | `format` / `language` / `prompt` | `string?` | Audio container hint (maps to the upload filename), ISO-639-1 hint, spelling hint. |
32
+ | `signal` | `AbortSignal?` | Cancellation; observed by the adapter transport. |
33
+
34
+ Adapter options: `apiKey` (`CredentialValueSource` — the existing credential
35
+ seam, resolved per call and redacted from errors), `baseUrl`, `fetch` (inject a
36
+ fake transport for offline tests), `headers`, provider defaults
37
+ (`voice`/`format`), and `maxAudioBytes` (response ceiling for synthesis,
38
+ input ceiling for transcription).
39
+
40
+ ## Outputs / response / events
41
+
42
+ | Field | Type | Meaning |
43
+ | --- | --- | --- |
44
+ | `audio` | `Uint8Array` / `ReadableStream<Uint8Array>` | Full synthesized bytes, or chunks in order — the first chunk is available as soon as the provider responds. |
45
+ | `format` | `string` | Actual audio container (request value or provider default). |
46
+ | `text` | `string` | Transcript; `TranscriptDone.text` is the final full text. |
47
+ | `usage` | `Usage?` | Transcription token usage (streaming `done` reports it; one-shot when the provider includes it). |
48
+
49
+ Streaming transcription yields `TranscriptDelta` (`{ type: "transcript_delta",
50
+ text }`, naming aligned with `RealtimeEvent.transcript_delta` — no `role`) then
51
+ exactly one `TranscriptDone`.
52
+
53
+ Failures throw `SpeechError` / `TranscriptionError` with a stable `code`:
54
+ `empty_input` (no text / no audio), `input_too_large` (speech text over the
55
+ provider cap), `audio_too_large` (transcription audio over the cap),
56
+ `request_failed` (non-2xx or stream error, secret-redacted message),
57
+ `response_malformed` (missing text, over-ceiling audio bytes),
58
+ `unsupported_model` (via `assertSpeechSupported` / `assertTranscriptionSupported`
59
+ when the host checks `ModelCapabilities.speech` / `transcription`).
60
+
61
+ ## Request/response example
62
+
63
+ ```json
64
+ { "model": "tts-1", "input": "hello", "voice": "alloy", "response_format": "mp3" }
65
+ ```
66
+
67
+ ## Implementation example
68
+
69
+ ```ts
70
+ import { createOpenAISpeechProvider, createOpenAITranscriptionProvider } from "@arnilo/prism-providers/openai";
71
+ import { runSpeechConformance, runTranscriptionConformance } from "@arnilo/prism/testing/provider-conformance";
72
+
73
+ const speech = createOpenAISpeechProvider({ apiKey: process.env.OPENAI_API_KEY });
74
+ const { audio } = await speech.synthesize({ model: "tts-1", input: "hi", voice: "alloy" });
75
+ const streamed = await speech.synthesizeStream({ model: "tts-1", input: "hi" });
76
+ // streamed.audio is a ReadableStream<Uint8Array>; first chunk resolves at first response bytes
77
+
78
+ const transcription = createOpenAITranscriptionProvider({ apiKey: process.env.OPENAI_API_KEY });
79
+ const { text } = await transcription.transcribe({ model: "whisper-1", audio, format: "mp3" });
80
+ for await (const event of transcription.transcribeStream({ model: "whisper-1", audio })) {
81
+ if (event.type === "transcript_delta") process.stdout.write(event.text);
82
+ }
83
+
84
+ // Offline conformance (fake transport, no network):
85
+ await runSpeechConformance({
86
+ provider: createOpenAISpeechProvider({ apiKey: "sk-test", fetch: fakeFetch }),
87
+ model: "tts-1",
88
+ maxInputChars: 4096,
89
+ sample: { input: "hi", voice: "alloy" },
90
+ });
91
+ ```
92
+
93
+ ## Extension and configuration notes
94
+
95
+ - Implement `SpeechProvider` / `TranscriptionProvider` for other vendors; the
96
+ contracts are structural — no base class, no registry.
97
+ - Models declare support with `capabilities.speech` / `capabilities.transcription`;
98
+ hosts gate with the `modelSupports*` / `assert*Supported` guards, mirroring the
99
+ structured-output and embeddings guard pattern.
100
+ - `synthesizeStream` returns a Web `ReadableStream` and `transcribeStream` an
101
+ `AsyncIterable` — the repo's streaming conventions; no Node-only stream types.
102
+ - Adapters never truncate: over-cap inputs and responses reject with typed
103
+ errors, so callers own splitting long text or large audio.
104
+
105
+ ## Security and performance notes
106
+
107
+ - API keys resolve through the existing `CredentialValueSource` seam and are
108
+ redacted from every thrown error (`redactSecrets`); no new secret paths.
109
+ - Synthesized audio is consumed under a byte ceiling
110
+ (`DEFAULT_SPEECH_MAX_AUDIO_BYTES`, 25 MiB; `maxAudioBytes` override) — the
111
+ stream errors instead of buffering without bound; transcription input is
112
+ capped before any network I/O.
113
+ - Audio content and transcript text are never logged; error messages carry
114
+ status and a redacted body only.
115
+ - One HTTP request per call; streaming paths forward provider chunks as they
116
+ arrive (first byte at provider RTT, no full-response buffering).
117
+
118
+ ## Related APIs
119
+
120
+ - [Realtime sessions](public-contracts.md): `RealtimeSession` for interactive
121
+ bidirectional voice; `RealtimeEvent.transcript_delta` is this contract's
122
+ naming anchor.
123
+ - [Provider conformance](provider-conformance.md): `runSpeechConformance` /
124
+ `runTranscriptionConformance` and the offline conformance matrix.
125
+ - [Provider packages](provider-packages.md): subpath import rules for
126
+ `@arnilo/prism-providers/openai`.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- The optional `@arnilo/prism-session-store-sqlite` package ships a production-oriented SQLite adapter that implements:
5
+ The optional `@arnilo/prism-core/sessions/sqlite` package ships a production-oriented SQLite adapter that implements:
6
6
 
7
7
  - `SessionStore` — atomic `append` / `list` / `get` / `readBranchPath` / bounded `searchSessions` / bounded `searchSessions`
8
8
  - `RunLedger` — durable run, event, tool-call, and usage rows
@@ -24,12 +24,12 @@ Use this package when you want a small, file-backed persistence layer on Node wi
24
24
  - single-writer or low-concurrency deployments
25
25
  - integration tests that need durable reopen semantics
26
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.
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-core/sessions/postgres`](postgres-persistence.md) for that path.
28
28
 
29
29
  ## Inputs / request
30
30
 
31
31
  ```ts
32
- import { createSqlitePersistence } from "@arnilo/prism-session-store-sqlite";
32
+ import { createSqlitePersistence } from "@arnilo/prism-core/sessions/sqlite";
33
33
  ```
34
34
 
35
35
  | Field | Type | Purpose |
@@ -73,7 +73,7 @@ Migrations run automatically on open and are idempotent across reopen. Under the
73
73
 
74
74
  ```ts
75
75
  import { createAgentSession } from "@arnilo/prism";
76
- import { createSqlitePersistence } from "@arnilo/prism-session-store-sqlite";
76
+ import { createSqlitePersistence } from "@arnilo/prism-core/sessions/sqlite";
77
77
  import { runSessionStoreConformance } from "@arnilo/prism/testing/session-store-conformance";
78
78
 
79
79
  const persistence = createSqlitePersistence({ filename: "./prism.db" });
@@ -2,11 +2,11 @@
2
2
 
3
3
  ## What it does
4
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.
5
+ `@arnilo/prism-core/runtime/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
6
 
7
7
  ## When to use it
8
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.
9
+ Use a supervisor when a host or agent must choose a child dynamically. Use `@arnilo/prism-core/runtime/workflows` for known DAGs, durable checkpoints, schedules, replay, or human suspension.
10
10
 
11
11
  ## Inputs / request
12
12
 
@@ -36,7 +36,7 @@ Use a supervisor when a host or agent must choose a child dynamically. Use `@arn
36
36
  ## Implementation example
37
37
 
38
38
  ```ts
39
- import { createSupervisor } from "@arnilo/prism-supervisor";
39
+ import { createSupervisor } from "@arnilo/prism-core/runtime/supervisor";
40
40
 
41
41
  const supervisor = createSupervisor({
42
42
  ownership: { tenantId: "tenant", userId: "user" },
@@ -78,6 +78,7 @@ Supervisors propagate parent `identity` and `effectStore` to every child agent/r
78
78
 
79
79
  ## Related APIs
80
80
 
81
+ - [Multi-agent patterns](multi-agent-patterns.md): the decision table comparing this delegation pattern with in-session handoff (swarm) and A2A; handoff keeps one transcript, delegation keeps separate child runs — choose by boundary, budget, and telemetry needs.
81
82
  - [Agent identity](agent-identity.md): host-verified identity and narrow delegation.
82
83
  - [A2A interoperability](a2a.md): separate remote protocol boundary. `A2ATaskLifecycle` adapts host durable agent/workflow state directly; it does not route A2A execution through local supervisor child planning.
83
84
  - [Workflows](workflows.md): preferred deterministic orchestration.