@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.
- package/CHANGELOG.md +50 -1
- package/README.md +42 -62
- package/dist/agent-run-lifecycle.js +4 -0
- package/dist/agent-run-state.d.ts +5 -2
- package/dist/agent-run-state.js +18 -8
- package/dist/agent-session/session/assemble.d.ts +6 -0
- package/dist/agent-session/session/assemble.js +391 -0
- package/dist/agent-session/session/persist.d.ts +28 -0
- package/dist/agent-session/session/persist.js +166 -0
- package/dist/agent-session/session/provider-round.d.ts +6 -0
- package/dist/agent-session/session/provider-round.js +231 -0
- package/dist/agent-session/session/tool-round.d.ts +31 -0
- package/dist/agent-session/session/tool-round.js +473 -0
- package/dist/agent-session/session/types.d.ts +115 -0
- package/dist/agent-session/session/types.js +5 -0
- package/dist/agent-session/session.d.ts +54 -41
- package/dist/agent-session/session.js +23 -1132
- package/dist/capture.d.ts +63 -0
- package/dist/capture.js +67 -0
- package/dist/cli-dev.d.ts +29 -0
- package/dist/cli-dev.js +52 -0
- package/dist/cli-init.d.ts +34 -3
- package/dist/cli-init.js +192 -24
- package/dist/cli-runner.d.ts +6 -2
- package/dist/cli-runner.js +57 -10
- package/dist/content.d.ts +3 -3
- package/dist/content.js +3 -1
- package/dist/contracts-core/agent.d.ts +8 -0
- package/dist/contracts-core/batch.d.ts +97 -0
- package/dist/contracts-core/batch.js +65 -0
- package/dist/contracts-core/content.d.ts +72 -1
- package/dist/contracts-core/embeddings.d.ts +30 -0
- package/dist/contracts-core/embeddings.js +17 -0
- package/dist/contracts-core/images.d.ts +60 -0
- package/dist/contracts-core/images.js +17 -0
- package/dist/contracts-core/moderation.d.ts +46 -0
- package/dist/contracts-core/moderation.js +34 -0
- package/dist/contracts-core/speech.d.ts +39 -0
- package/dist/contracts-core/speech.js +17 -0
- package/dist/contracts-core/transcription.d.ts +48 -0
- package/dist/contracts-core/transcription.js +17 -0
- package/dist/contracts-core/video.d.ts +61 -0
- package/dist/contracts-core/video.js +17 -0
- package/dist/contracts-core.d.ts +7 -0
- package/dist/contracts-core.js +7 -0
- package/dist/contracts-protocol.d.ts +18 -0
- package/dist/contracts-run-state.d.ts +1 -2
- package/dist/index.d.ts +7 -3
- package/dist/index.js +5 -3
- package/dist/input.d.ts +8 -0
- package/dist/input.js +4 -0
- package/dist/node/agent-definitions.d.ts +1 -8
- package/dist/node/agent-definitions.js +0 -34
- package/dist/node/settings.d.ts +0 -1
- package/dist/node/settings.js +0 -5
- package/dist/pinned-fetch.js +29 -3
- package/dist/provider-events.js +3 -4
- package/dist/providers/media.d.ts +1 -2
- package/dist/providers/media.js +1 -4
- package/dist/rpc.d.ts +1 -1
- package/dist/rpc.js +4 -4
- package/dist/testing/persistence-schema.d.ts +1 -1
- package/dist/testing/persistence-schema.js +32 -28
- package/dist/testing/provider-conformance.d.ts +114 -5
- package/dist/testing/provider-conformance.js +342 -0
- package/dist/testing/tool-conformance.d.ts +25 -0
- package/dist/testing/tool-conformance.js +128 -1
- package/dist/testing/tool-effect-store-conformance.d.ts +0 -1
- package/dist/testing/tool-effect-store-conformance.js +0 -3
- package/dist/thinking.d.ts +48 -9
- package/dist/thinking.js +134 -8
- package/dist/tool-search.d.ts +76 -0
- package/dist/tool-search.js +199 -0
- package/docs/0.1.0-readiness.md +3 -3
- package/docs/a2a.md +2 -2
- package/docs/acp-agent.md +1 -1
- package/docs/acp.md +3 -3
- package/docs/ag-ui-adoption.md +1 -1
- package/docs/ag-ui.md +1 -2
- package/docs/agent-definitions.md +1 -1
- package/docs/agent-events.md +5 -5
- package/docs/agent-identity.md +13 -2
- package/docs/audit-export.md +3 -3
- package/docs/batch-jobs.md +120 -0
- package/docs/browser-automation.md +5 -5
- package/docs/caveman.md +2 -2
- package/docs/cli-rpc.md +43 -9
- package/docs/coding-agent-tools.md +19 -19
- package/docs/coding-review-and-diagnostics.md +2 -2
- package/docs/coding-security.md +5 -5
- package/docs/coding-tools.md +82 -0
- package/docs/coding-workspaces.md +2 -2
- package/docs/compaction-and-retry.md +2 -2
- package/docs/compaction-llm.md +4 -4
- package/docs/compaction-observational-memory.md +3 -3
- package/docs/computer-use-linux.md +13 -2
- package/docs/context-and-skills.md +3 -1
- package/docs/conversations.md +4 -4
- package/docs/core.md +85 -0
- package/docs/credential-storage.md +12 -8
- package/docs/credentials-and-redaction.md +1 -1
- package/docs/data-classification.md +1 -1
- package/docs/database-persistence.md +7 -3
- package/docs/dev-inspector.md +103 -0
- package/docs/device-adapters.md +2 -2
- package/docs/diagrams.md +247 -0
- package/docs/document-reader.md +6 -6
- package/docs/documents.md +214 -0
- package/docs/embeddings.md +112 -0
- package/docs/enterprise-postgres-state.md +7 -7
- package/docs/evaluations.md +41 -7
- package/docs/extensions.md +3 -3
- package/docs/forge-integration.md +3 -3
- package/docs/graft.md +5 -5
- package/docs/guardrails.md +2 -2
- package/docs/host-security.md +16 -15
- package/docs/image-generation.md +129 -0
- package/docs/impeccable.md +7 -5
- package/docs/index.md +84 -46
- package/docs/indexed-code-search.md +2 -2
- package/docs/language-intelligence.md +4 -4
- package/docs/live-testing.md +126 -0
- package/docs/mcp-tools.md +44 -13
- package/docs/middleware-hooks.md +1 -1
- package/docs/migrate-to-0.4.md +312 -0
- package/docs/migrate-to-0.5.md +122 -0
- package/docs/migration.md +51 -1
- package/docs/model-registry.md +38 -0
- package/docs/model-routing.md +6 -6
- package/docs/moderation.md +117 -0
- package/docs/multi-agent-patterns.md +177 -0
- package/docs/multimodal-content.md +27 -3
- package/docs/obscura.md +12 -12
- package/docs/observability.md +32 -7
- package/docs/openapi-tools.md +14 -4
- package/docs/operations.md +11 -0
- package/docs/performance.md +30 -10
- package/docs/persistence-credentials-multimodality-primitives.md +7 -7
- package/docs/policy-and-audit.md +18 -8
- package/docs/ponytail.md +3 -3
- package/docs/postgres-persistence.md +5 -5
- package/docs/process-sessions.md +2 -2
- package/docs/prompt-registry.md +106 -0
- package/docs/provider-caching.md +36 -32
- package/docs/provider-conformance.md +24 -2
- package/docs/provider-packages.md +58 -22
- package/docs/provider-primitives.md +5 -5
- package/docs/provider-request-policies.md +1 -1
- package/docs/providers/ai-sdk.md +18 -6
- package/docs/providers/alibaba.md +10 -6
- package/docs/providers/anthropic.md +10 -6
- package/docs/providers/azure.md +20 -4
- package/docs/providers/bedrock.md +18 -3
- package/docs/providers/clinepass.md +7 -3
- package/docs/providers/commandcode.md +253 -0
- package/docs/providers/deepseek.md +7 -3
- package/docs/providers/google.md +8 -4
- package/docs/providers/hyper.md +284 -0
- package/docs/providers/kimi.md +7 -3
- package/docs/providers/neuralwatt.md +12 -8
- package/docs/providers/ollama.md +18 -3
- package/docs/providers/openai-compatible.md +5 -1
- package/docs/providers/openai.md +9 -5
- package/docs/providers/opencode-go.md +8 -4
- package/docs/providers/openrouter.md +8 -4
- package/docs/providers/vertex.md +21 -5
- package/docs/providers/xai.md +7 -3
- package/docs/providers/zai.md +7 -3
- package/docs/rag.md +31 -9
- package/docs/release-and-install.md +181 -76
- package/docs/resource-loading.md +1 -1
- package/docs/runs-and-usage.md +28 -3
- package/docs/server.md +94 -5
- package/docs/settings-auth-trust-security.md +7 -5
- package/docs/sheets.md +229 -0
- package/docs/speech.md +126 -0
- package/docs/sqlite-persistence.md +4 -4
- package/docs/supervisors.md +4 -3
- package/docs/thinking-and-reasoning.md +93 -60
- package/docs/tool-conformance.md +28 -3
- package/docs/tool-execution-primitives.md +8 -8
- package/docs/tools.md +32 -5
- package/docs/web-tools.md +3 -3
- package/docs/wiki.md +7 -7
- package/docs/work-artifacts-and-review.md +17 -6
- package/docs/work-connectors.md +4 -4
- package/docs/work-tools.md +5 -5
- package/docs/workflow-orchestration-primitives.md +35 -11
- package/docs/workflows.md +74 -13
- package/docs/working-and-semantic-memory.md +53 -5
- package/package.json +14 -31
- package/templates/README.md +23 -0
- package/templates/deep-research/README.md.tmpl +47 -0
- package/templates/deep-research/env.example.tmpl +12 -0
- package/templates/deep-research/gitignore.tmpl +7 -0
- package/templates/deep-research/manifest.json +12 -0
- package/templates/deep-research/package.json.tmpl +23 -0
- package/templates/deep-research/src/agent.ts.tmpl +81 -0
- package/templates/deep-research/src/index.ts.tmpl +53 -0
- package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
- package/templates/deep-research/src/tools.ts.tmpl +86 -0
- package/templates/deep-research/src/types.ts.tmpl +45 -0
- package/templates/deep-research/src/workflow.ts.tmpl +156 -0
- package/templates/deep-research/tsconfig.json.tmpl +15 -0
- package/templates/init/manifest.json +5 -0
- package/templates/init/package.json.tmpl +2 -1
- package/templates/init/providers.json +40 -24
- 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.
|
|
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
|
|
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:
|
|
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
|
|
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`: `
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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" });
|
package/docs/supervisors.md
CHANGED
|
@@ -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.
|