@arnilo/prism 0.0.2 → 0.0.4

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 (99) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +32 -20
  3. package/dist/agent-loops.d.ts +8 -1
  4. package/dist/agent-loops.js +57 -11
  5. package/dist/agents.js +70 -17
  6. package/dist/checkpoints.d.ts +11 -0
  7. package/dist/checkpoints.js +144 -0
  8. package/dist/compaction.js +9 -1
  9. package/dist/content.d.ts +102 -0
  10. package/dist/content.js +410 -0
  11. package/dist/contracts.d.ts +142 -2
  12. package/dist/event-multiplexer.d.ts +23 -0
  13. package/dist/event-multiplexer.js +136 -0
  14. package/dist/execution-policy.d.ts +28 -0
  15. package/dist/execution-policy.js +24 -0
  16. package/dist/index.d.ts +17 -5
  17. package/dist/index.js +11 -4
  18. package/dist/input.js +11 -1
  19. package/dist/leases.d.ts +8 -0
  20. package/dist/leases.js +111 -0
  21. package/dist/node/agent-definitions.js +3 -5
  22. package/dist/node/config.d.ts +1 -0
  23. package/dist/node/config.js +5 -3
  24. package/dist/node/contribution-discovery.js +5 -8
  25. package/dist/node/session-store-jsonl.js +8 -5
  26. package/dist/node/settings.js +2 -2
  27. package/dist/node/trust.js +2 -4
  28. package/dist/observability.d.ts +3 -0
  29. package/dist/observability.js +18 -0
  30. package/dist/providers/media.d.ts +42 -0
  31. package/dist/providers/media.js +116 -0
  32. package/dist/providers/openai-compatible.js +18 -119
  33. package/dist/providers/openai-primitives.d.ts +9 -0
  34. package/dist/providers/openai-primitives.js +129 -0
  35. package/dist/providers/transport.d.ts +40 -0
  36. package/dist/providers/transport.js +221 -0
  37. package/dist/redaction.js +40 -13
  38. package/dist/resources.d.ts +5 -0
  39. package/dist/resources.js +4 -0
  40. package/dist/structured-output.d.ts +11 -0
  41. package/dist/structured-output.js +59 -0
  42. package/dist/testing/persistence-schema.d.ts +102 -0
  43. package/dist/testing/persistence-schema.js +457 -0
  44. package/dist/testing/provider-conformance.js +10 -1
  45. package/dist/testing/run-ledger-conformance.d.ts +33 -0
  46. package/dist/testing/run-ledger-conformance.js +172 -0
  47. package/dist/testing/session-store-conformance.d.ts +16 -0
  48. package/dist/testing/session-store-conformance.js +73 -0
  49. package/dist/tools.d.ts +17 -0
  50. package/dist/tools.js +29 -2
  51. package/docs/agent-events.md +13 -4
  52. package/docs/agent-loops.md +10 -4
  53. package/docs/agent-session-runtime.md +1 -0
  54. package/docs/cli-rpc.md +3 -0
  55. package/docs/coding-agent-tools.md +242 -0
  56. package/docs/coding-security.md +84 -0
  57. package/docs/credential-storage.md +177 -0
  58. package/docs/credentials-and-redaction.md +2 -1
  59. package/docs/database-persistence.md +44 -2
  60. package/docs/host-security.md +15 -1
  61. package/docs/index.md +28 -11
  62. package/docs/input-and-prompt-assembly.md +6 -5
  63. package/docs/mcp-tools.md +139 -0
  64. package/docs/middleware-hooks.md +2 -0
  65. package/docs/migration.md +21 -28
  66. package/docs/model-registry.md +5 -3
  67. package/docs/multimodal-content.md +148 -0
  68. package/docs/observability.md +163 -0
  69. package/docs/performance.md +40 -1
  70. package/docs/persistence-credentials-multimodality-primitives.md +303 -0
  71. package/docs/postgres-persistence.md +141 -0
  72. package/docs/provider-conformance.md +17 -0
  73. package/docs/provider-layer.md +1 -1
  74. package/docs/provider-primitives.md +281 -0
  75. package/docs/providers/kimi.md +1 -0
  76. package/docs/providers/neuralwatt.md +1 -0
  77. package/docs/providers/openai-compatible.md +2 -1
  78. package/docs/providers/openai.md +8 -1
  79. package/docs/providers/opencode-go.md +1 -0
  80. package/docs/providers/openrouter.md +1 -0
  81. package/docs/providers/zai.md +1 -0
  82. package/docs/public-contracts.md +9 -2
  83. package/docs/release-and-install.md +209 -23
  84. package/docs/resource-loading.md +14 -4
  85. package/docs/review-coverage-2026-07-14.md +260 -0
  86. package/docs/run-ledger-conformance.md +96 -0
  87. package/docs/runs-and-usage.md +2 -0
  88. package/docs/session-store-conformance.md +16 -0
  89. package/docs/session-stores-and-branching.md +1 -0
  90. package/docs/settings-auth-trust-security.md +2 -1
  91. package/docs/sqlite-persistence.md +122 -0
  92. package/docs/structured-output.md +9 -0
  93. package/docs/tool-conformance.md +1 -0
  94. package/docs/tool-execution-primitives.md +374 -0
  95. package/docs/tools.md +40 -1
  96. package/docs/workflow-orchestration-primitives.md +565 -0
  97. package/docs/workflow-tui-primitives.md +5 -0
  98. package/docs/workflows.md +219 -0
  99. package/package.json +34 -5
package/docs/index.md CHANGED
@@ -3,16 +3,17 @@
3
3
  Prism is a TypeScript/Node.js agent harness. Host apps and extension packages own providers, tools, resources, credentials, storage, UI, and business behavior. Prism supplies contracts, registries, streaming events, and replaceable runtime primitives.
4
4
 
5
5
  ## Public contracts
6
- - [Public contracts](public-contracts.md): type shapes for messages, content, agents, sessions, providers, tools, context, skills, extensions, stores, resources, settings, credentials, and events.
6
+ - [Public contracts](public-contracts.md): type shapes for messages, agents, tools, stores, generic `CheckpointStore`, atomic `LeaseStore`, bounded `EventMultiplexer`, resources, credentials, and events.
7
7
 
8
8
  ## Agent/session runtime
9
9
  - [Agent/session runtime](agent-session-runtime.md): create agents and sessions, run prompts, subscribe to normalized events, and see which `AgentConfig` fields are runtime-consumed vs host-owned metadata. Covers tool-call loop transcript shape and prior-reasoning preservation across turns.
10
10
  - [Agent definitions](agent-definitions.md): resolve declarative `AgentDefinition` values via `resolveAgentDefinition`, and turn app-config `<configRoot>/agents/<name>/AGENT.md` bundles into runnable agents via `discoverAgentBundles` / `resolveAgentBundle` (explicit tool/skill activation by name, fail-closed omitted capabilities, migration-only `activateAllCapabilities`, strict duplicate scope checks, configurable prompt layers, no auto-discovery).
11
11
  - [Agent loops](agent-loops.md): replaceable per-run control loops — `singleShotLoop` default and `generate-validate-revise` with host-supplied `validator`/`parser`/`repairer` callbacks.
12
- - [Agent events](agent-events.md): the `AgentEvent` stream — agent/turn/message (including live `tool_call_delta` fragments), tool execution, queue/subscriber overflow, compaction/retry, artifact validation/refinement, and error variants, redacted via `redactAgentEvent`.
12
+ - [Agent events](agent-events.md): the `AgentEvent` stream — agent/turn/message (including live `tool_call_delta` fragments), provider turn timing, tool execution, queue/subscriber overflow, compaction/retry, artifact validation/refinement, and error variants, redacted via `redactAgentEvent`.
13
+ - [Observability](observability.md): metadata-only `provider_turn_*` events, `ToolExecutionMetadata`, core helpers, and optional `@arnilo/prism-observability-opentelemetry` adapter.
13
14
  - [Runs and usage ledger](runs-and-usage.md): `RunLedger` adapter for durable run, event, tool-call, usage persistence, cache diagnostics, ownership/idempotency, and redaction guidance.
14
15
  - [Performance limits](performance.md): bounded live subscriber queues, branch-read pagination expectations, JSONL/dev-store limits, and production sizing assumptions.
15
- - [Structured output](structured-output.md): the `Artifact*` seam (parser/validator/repairer, host-defined `T`) — the only typed-output path from a loop, with a Synapta-style schema→`ArtifactValidation` mapping example and an end-to-end third-party integration walkthrough.
16
+ - [Structured output](structured-output.md): the `Artifact*` seam plus provider-native `StructuredOutputOptions` / `structuredOutputMode` for capable models.
16
17
 
17
18
  ## Compaction/session memory
18
19
  - [Compaction and retry policies](compaction-and-retry.md): summarize branch history and retry transient provider failures with host-replaceable policies.
@@ -20,11 +21,15 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
20
21
  - [Observational memory compaction package](compaction-observational-memory.md): optional source-backed memory, owned runtime append callback, provider-valid worker transcripts, fast compaction, recall tool, and status/view command package.
21
22
  - [Session stores](session-stores.md): `SessionStore` contract, `SessionAppendOptions`, `SessionAppendConflictError`, branch handles, `readBranchPath`, and dev-vs-production branch reads — start here for session persistence.
22
23
  - [Session stores and branching](session-stores-and-branching.md): detailed branch semantics and helper reference (kept for compatibility; links back to the canonical atomic append / branch-handle sections).
23
- - [Database persistence](database-persistence.md): production persistence contracts, conditional append transaction pattern, idempotency indexes, `readBranchPath`, reference relational schema, retention, migrations, and NoSQL mapping.
24
- - [Migration guide](migration.md): the two cross-cutting app migrations in one place — in-memory/JSONL → database-backed `ProductionPersistenceStore` persistence (+ `RunLedger`) and permissive capability defaults → Phase 38 explicit `tools`/`skills` activation, with before/after shapes and links to the detailed pages.
24
+ - [Database persistence](database-persistence.md): production persistence contracts, shared schema/migration primitives (`@arnilo/prism/testing/persistence-schema`), conditional append transaction pattern, idempotency indexes, `readBranchPath`, reference relational schema, retention, migrations, and NoSQL mapping.
25
+ - [SQLite persistence](sqlite-persistence.md): optional `@arnilo/prism-session-store-sqlite` adapter — `SessionStore`, `RunLedger`, `ProductionPersistenceStore`, and generic durable checkpoints and atomic leases over `better-sqlite3`.
26
+ - [PostgreSQL persistence](postgres-persistence.md): optional pooled adapter — session/run/query persistence plus generic durable checkpoints and atomic leases over `pg`, with advisory-lock migrations and opt-in live conformance.
27
+ - [Migration guide](migration.md): 0.0.3 compatibility and optional 0.0.4 adoption — first-party/custom database persistence plus explicit fail-closed tool/skill activation.
25
28
  - [Node JSONL session store](node-jsonl-session-store.md): development-only JSONL file adapter for single-process Node hosts; no cross-process safety.
29
+ - [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): Plan 056 inventory — session/run-ledger/persistence contracts, credential/OAuth seams, content/resource/model capabilities, package dependency matrix, conformance matrix, and threat model for production adapters.
26
30
 
27
31
  ## Provider and model connection
32
+ - [Provider primitives](provider-primitives.md): shared bounded transport and OpenAI serialization helpers — migrated across first-party providers; native structured-output and observability contracts.
28
33
  - [Provider layer](provider-layer.md): register and resolve host-owned providers/models, choose replace-or-error duplicate policy, create provider events, stream/reconstruct tool-call deltas, use generic provider request options, and test with the mock provider; deprecated provider-level timeout/retry hints point to runtime abort/retry.
29
34
  - [Model registry](model-registry.md): register and resolve `ModelConfig` records with capabilities, limits, cost, cache support metadata, compat data, and duplicate policy.
30
35
  - [Provider caching](provider-caching.md): use `PromptCacheHints`, `PromptCacheBreakpoint`, `ModelCacheCapabilities`, cache-aware stable-prefix guidance, and shared cache diagnostics helpers; includes a per-provider explicit/implicit cache matrix for OpenAI, OpenRouter, OpenCode Go, Z.AI, Kimi, and NeuralWatt; cache hints are best-effort and cache keys are never secrets.
@@ -35,16 +40,22 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
35
40
 
36
41
  ## Input, prompt, and context assembly
37
42
  - [SDK customization guide](customization.md): map provider resolution, middleware, context, builders, injectors, loops, compaction, retry, stores, and skills to explicit host-wired APIs.
38
- - [Input and prompt assembly](input-and-prompt-assembly.md): render tiny prompt templates and turn common host input, history, attachments, explicit resources, summaries, and tool results into messages with replaceable builders, provider-input assembly, legacy default order, and opt-in cache-aware ordering.
43
+ - [Input and prompt assembly](input-and-prompt-assembly.md): render tiny prompt templates and turn common host input, history, attachments, explicit resources, summaries, and tool results into messages with replaceable builders, provider-input assembly, legacy default order, and opt-in cache-aware ordering. Audio/file/document `ContentBlock` types and capability checks are documented there.
44
+ - [Multimodal content](multimodal-content.md): bounded `audio`, `file`, and `document` content blocks, media resolution helpers, SSRF/MIME policy, and `ModelCapabilities.input` tags.
39
45
  - [System prompts](system-prompts.md): compose explicit user/package/app/run system prompt layers, auto-load the standard `AGENTS.md` (workspace) / `SYSTEM.md` prompt files via the Node `loadSystemPromptFiles` loader (trust-gated for `AGENTS.md`), and append `SYSTEM.md` → per-agent `AGENT.md` body → repo `AGENTS.md` layers from a discovered agent bundle via `resolveAgentBundle`.
40
46
  - [Instruction injection](instruction-injection.md): register package injectors that layer redacted instructions/context blocks without granting tools, permissions, or resource escapes.
41
47
  - [Context and skills](context-and-skills.md): resolve ordered context providers and keep context/skill selection host-owned; omitted declarative skills stay inactive by default, `toolNames` fail closed before provider turns, and strict skill registries prevent silent shadowing.
42
48
 
43
49
  ## Tools
44
50
  - [Tools](tools.md): register host-owned active tools with replace-or-error duplicate policy, apply exact allow/deny filtering, and dispatch tool calls.
51
+ - [Tool execution primitives](tool-execution-primitives.md): JSON Schema validation, exclusive-aware bounded parallel dispatch, MCP bridge mapping, coding execution policy, and image-read bounds.
52
+ - [Tool validator JSON Schema package](../packages/tool-validator-json-schema/README.md): optional `@arnilo/prism-tool-validator-json-schema` adapter for `tool.parameters`.
53
+ - [MCP client bridge](mcp-tools.md): optional `@arnilo/prism-mcp` package mapping remote MCP tools to `ToolDefinition`s.
54
+ - [Coding agent tools](coding-agent-tools.md): optional first-party package `@arnilo/prism-coding-agent` providing `shell`, `read`, `write`, and `edit` tools (ported from pi) as `ToolDefinition`s a host registers; pluggable operation backends, per-path mutation serialization, optional `ExecutionPolicy`, bounded image reads (`maxImageBytes`, `transformImage`), and read-only/coding aggregators. Host shell/filesystem access — gate with permission/trust policies and `@arnilo/prism-coding-security` approval.
55
+ - [Coding execution approval and sandboxing](coding-security.md): optional `@arnilo/prism-coding-security` package for path roots, command rules, approval caching, shell-turn exclusivity, and pluggable sandbox adapters for coding tools.
45
56
 
46
57
  ## Extensions/plugins
47
- - [Contribution discovery (workspace)](contribution-discovery.md): opt-in, realpath-contained directory scanner turning `SKILL.md`/`manifest.json` into inert `DiscoveredContribution` envelopes the host registers — no `import()`, no auto-activate, no provider scanning. (Per-agent `AGENT.md` bundles live under an app-controlled `configRoot`; see [Agent definitions](agent-definitions.md).)
58
+ - [Contribution discovery (workspace)](contribution-discovery.md): opt-in, realpath-contained directory scanner turning `SKILL.md`/`manifest.json` into inert `DiscoveredContribution` envelopes the host registers — no `import()`, no auto-activate, no provider scanning. Per-agent bundles remain app-controlled and are documented under Agent/session runtime.
48
59
  - [Contribution registries](contribution-registries.md): explicit host-owned registries for extension/package contributions without hidden globals, with `duplicate: "error"` strict mode for provider/model/tool/skill shadowing prevention.
49
60
  - [Extension kernel and event bus](extensions.md): load host-provided extensions in order, register contributions, emit lifecycle events, and isolate extension errors.
50
61
  - [Extension authoring guide](extension-authoring.md): publish third-party extension packages that register inert contributions and show host-owned activation, trust, permissions, redaction, and no-sandbox boundaries.
@@ -53,25 +64,31 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
53
64
  ## Configuration/manifests
54
65
  - [Configuration and manifests](configuration-and-manifests.md): merge in-memory JSON config layers and validate data-only package manifests with prototype-pollution key rejection.
55
66
  - [Node filesystem config loader](node-filesystem-config.md): explicitly read caller-named JSON config files in Node hosts.
56
- - [Resource loading](resource-loading.md): decode text, JSON, and manifest resources through caller-provided loaders.
67
+ - [Resource loading](resource-loading.md): decode text, JSON, binary, and manifest resources through caller-provided loaders with bounded byte limits.
57
68
 
58
69
  ## CLI/RPC
59
70
  - [CLI/RPC](cli-rpc.md): Run print/json modes and LF-delimited RPC over the public AgentSession runtime, including branch-handle results, fixed `forkSession`, and `checkout`.
71
+ - [Workflows](workflows.md): optional `@arnilo/prism-workflows` typed bounded DAG orchestration — local execution plus SQLite/PostgreSQL multi-process coordination with enqueue, leases, heartbeats, fencing, durable cancel/resume, events, and optional RPC bindings. Interactive TUI (C-012) deferred.
72
+ - [Workflow orchestration primitives](workflow-orchestration-primitives.md): architecture inventory — workflow adapters consume core `CheckpointStore`, `LeaseStore`, and bounded `EventMultiplexer`; run control and optional RPC commands stay package-local.
73
+ - [Workflow/TUI scope](workflow-tui-primitives.md): records why 0.0.4 ships workflow APIs/RPC control but no interactive terminal UI.
60
74
 
61
75
  ## Security and credentials
62
76
  - [Host security guide](host-security.md): fail-closed checklist for credentials, settings, redaction, trust roots, permission policies, persistence, extension loading, and tool validation.
63
77
  - [Security/auth/trust](settings-auth-trust-security.md): settings providers, credential helpers, trust/permission policies, redaction controls, host-owned `AgentConfig.settings`/`credentials`, and security-boundary hardening summary.
64
78
  - [Credentials and redaction](credentials-and-redaction.md): compose explicit credential resolver order, use caller-supplied env objects/OAuth refresh helpers, avoid eager `AgentConfig.credentials` resolution, and redact known secret values.
79
+ - [Credential storage](credential-storage.md): optional `@arnilo/prism-credentials-node` encrypted-file and system-keychain adapters for durable host-owned credentials.
65
80
 
66
81
  ## Testing and examples
67
- - [Provider layer](provider-layer.md): use `createMockProvider()` and provider event helpers for deterministic tests without timers, credentials, or network.
82
+ - Provider test doubles: `createMockProvider()` and provider event helpers are documented on the canonical Provider layer page above.
68
83
  - [Provider conformance](provider-conformance.md): run network-free provider adapter assertions (stream order, abort, tool-call reconstruction, cache usage, content coverage, protected header ownership, secret leak) from `@arnilo/prism/testing/provider-conformance`.
69
84
  - [Session store conformance](session-store-conformance.md): assert any `SessionStore` adapter satisfies append/idempotency/conflict/branch invariants from `@arnilo/prism/testing/session-store-conformance`.
85
+ - [Run ledger conformance](run-ledger-conformance.md): assert any `RunLedger` adapter satisfies durable run/event/tool/usage writes and reopen survival from `@arnilo/prism/testing/run-ledger-conformance`.
70
86
  - [Compaction conformance](compaction-conformance.md): assert any `CompactionStrategy` returns a non-empty redacted summary and observes abort from `@arnilo/prism/testing/compaction-conformance`.
71
87
  - [Tool conformance](tool-conformance.md): assert the tool-dispatch blocked-reason matrix (unknown/denied/invalid/permission/validator) and success path from `@arnilo/prism/testing/tool-conformance`.
72
88
  - [Extension conformance](extension-conformance.md): assert an `Extension` setup runs, contributions stay inert, and setup errors are redacted or rethrown from `@arnilo/prism/testing/extension-conformance`.
73
- - `examples/`: compile-checked typed examples and runnable mock demos (SDK basics, provider registration, auth, tools, cache-aware prompt assembly, NeuralWatt agent run, stores/branching, compaction, observational-memory recall, structured-output/artifact-loop, CLI, RPC).
89
+ - `examples/`: compile-checked typed examples and runnable mock demos (SDK basics, provider registration, auth, tools, cache-aware prompt assembly, NeuralWatt agent run, stores/branching, compaction, observational-memory recall, structured-output/artifact-loop, CLI, RPC, workflow orchestration).
74
90
 
75
91
  ## Release and install
76
- - [Release and install](release-and-install.md): package layout, install specifiers, required `@arnilo/prism` peer, tarball contents and exclusions, the map-retention knob, the release workflow, and the offline test budget.
92
+ - [Release and install](release-and-install.md): 24-package graph and profiles, install/tarball rules, deterministic resumable provenance publication, and offline test budget.
93
+ - [Review coverage (2026-07-14)](review-coverage-2026-07-14.md): traceability matrix linking review findings and bug-report fixes to plan tasks, tests, and documentation for release 0.0.4.
77
94
 
@@ -60,7 +60,7 @@ Useful exported types:
60
60
  - `DefaultInputBuilder`: the default `InputBuilder` with typed default context.
61
61
  - `InputAssemblyLayout`: `"legacy" | "cache_aware"`; legacy is default.
62
62
  - `DefaultInputBuildContext`: optional input layout, instructions, history, summaries, attachments, resource loader/URIs, tool results, middleware, ids, metadata, and abort signal.
63
- - `InputAttachment`: already-loaded text/content or an explicit URI loaded through a caller-provided `ResourceLoader`.
63
+ - `InputAttachment`: already-loaded text/content blocks (including `audio`, `file`, and `document`) or an explicit URI loaded through a caller-provided `ResourceLoader`.
64
64
  - `PromptInstruction`: labeled system instruction text.
65
65
  - `DefaultPromptBuilder`: the default `PromptBuilder`.
66
66
  - `AssembleProviderInputOptions`: model, input, optional builders, context providers, selected skills, active tools, generic provider options, metadata, and signal.
@@ -82,10 +82,10 @@ The builder returns `readonly Message[]`.
82
82
  The default prompt builder still prepends context, selected skills, and tool declarations before those input messages. Cache-aware ordering gives cache-capable providers a stable prefix only while those stable inputs stay byte-stable; changing tools, context, resources, summaries, history, or attachments changes the prefix too.
83
83
  - History is prepended before current input.
84
84
  - Instructions and summaries are system messages; compacted branch summaries from `rebuildSessionContext()` use the same path.
85
- - Text attachments and explicit text resources are user messages.
85
+ - Text attachments and explicit text resources are user messages; inline `audio`/`file`/`document` blocks pass through unchanged on attachments with `content`.
86
86
  - Tool results are tool messages containing `tool_result` content; the agent/session runtime uses this to feed dispatched tool results into the next provider turn, placing the assistant `tool_call` and the matching role `tool` `tool_result` before any final assistant content. Cache-aware layout keeps tool results before the current user suffix so it does not split tool transcripts.
87
87
  - Middleware runs only when `middleware` is supplied in the context.
88
- - `assembleProviderInput()` returns a `ProviderRequest` with the caller's model/tools/provider options/metadata/signal and composed messages/context.
88
+ - `assembleProviderInput()` returns a `ProviderRequest` with the caller's model/tools/provider options/metadata/signal and composed messages/context. It also calls `assertMessagesSupportModelCapabilities()` so unsupported `audio`/`file`/`document`/`image` blocks fail with `UnsupportedModalityError` when the model declares `capabilities.input`.
89
89
  - `renderPromptTemplate()` replaces top-level `{{name}}` variables with caller-supplied JSON-compatible values. Strings are inserted directly; numbers, booleans, `null`, arrays, and objects are stringified deterministically with sorted object keys. Missing variables throw by default or stay unchanged with `{ missing: "preserve" }`.
90
90
 
91
91
  ## Request/response example
@@ -165,7 +165,7 @@ const request = await assembleProviderInput({
165
165
  - The builder is linear in supplied messages, attachments, resources, and tool results. Layout selection is one flattening branch over already-built groups.
166
166
  - Template expansion is dependency-free string replacement over `{{name}}` variables. It does not evaluate expressions, filters, loops, partials, JavaScript, globals, or prototype properties.
167
167
  - It performs no provider calls, tool execution, credential resolution, package discovery, filesystem scan, network access, timers, or watchers.
168
- - URI attachments/resources load only through the caller-provided `ResourceLoader`.
168
+ - URI attachments/resources load only through the caller-provided `ResourceLoader`. Binary media uses `resolveMediaContentBlock()` / `loadBinaryResource()` with bounded bytes, SSRF checks for URLs, and MIME magic validation — see [Multimodal content](multimodal-content.md).
169
169
  - Do not place secrets in templates, variables, instructions, messages, attachments, tool results, metadata, middleware payloads, or docs examples.
170
170
  - Active tools are passed through from the host; prompt middleware cannot grant additional provider tools.
171
171
  - Skill selection is handled by the host/skill registry path; this builder only includes selected skills passed by the caller.
@@ -175,7 +175,8 @@ const request = await assembleProviderInput({
175
175
  - [SDK customization guide](customization.md): high-level map of replaceable provider resolution, middleware, context, builder, injector, loop, compaction, retry, store, and skill seams.
176
176
  - [Public contracts](public-contracts.md): `Message`, `ContentBlock`, `InputBuilder`, `InputBuildContext`, `ToolResult`, and `ResourceLoader` shapes.
177
177
  - [Context and skills](context-and-skills.md): ordered context resolution feeding prompt composition.
178
- - [Resource loading](resource-loading.md): `loadTextResource()` behavior used for explicit URI resources.
178
+ - [Multimodal content](multimodal-content.md): `audio`/`file`/`document` blocks, bounded media resolution, and capability checks.
179
+ - [Resource loading](resource-loading.md): `loadTextResource()` and `loadBinaryResource()` behavior used for explicit URI resources.
179
180
  - [Middleware hooks](middleware-hooks.md): ordered middleware registry and `input_assembly`, `context`, and `prompt_build` hooks.
180
181
  - [System prompts](system-prompts.md): compose layered package/app/user/run prompts before input assembly.
181
182
  - [Contribution registries](contribution-registries.md): inert input, prompt, context, and skill contributions.
@@ -0,0 +1,139 @@
1
+ # MCP client bridge
2
+
3
+ ## What it does
4
+
5
+ `@arnilo/prism-mcp` connects Prism hosts to remote [Model Context Protocol](https://modelcontextprotocol.io) servers and maps discovered tools to ordinary `ToolDefinition`s. Transports are stdio subprocesses and Streamable HTTP. The package wraps the official MCP TypeScript SDK (`@modelcontextprotocol/sdk` v1.29+) and does **not** add MCP-specific branches to core Prism.
6
+
7
+ Primary API:
8
+
9
+ ```ts
10
+ import { connectMcpTools } from "@arnilo/prism-mcp";
11
+
12
+ const bridge = await connectMcpTools({
13
+ serverId: "fs",
14
+ transport: { type: "stdio", command: "node", args: ["server.js"] },
15
+ });
16
+
17
+ // bridge.tools are ToolDefinition[] — register with createToolRegistry / createAgent
18
+ await bridge.refresh(); // re-list after notifications or TTL expiry
19
+ await bridge.close(); // close client + transport
20
+ ```
21
+
22
+ Advanced hosts that manage their own `Client` + `Transport` can call `attachMcpToolBridge(client, transport, options)` after `client.connect(transport)`.
23
+
24
+ ## When to use it
25
+
26
+ - **Integrate external MCP tool servers** (filesystem, databases, SaaS adapters) without reimplementing JSON-RPC transports in your app.
27
+ - **Keep core dispatch gates** — register returned tools and let `dispatchToolCall` enforce permission, JSON Schema validation (`ToolValidator`), middleware, abort, and parallel execution (Plan 055 Tasks 1–2).
28
+ - **Explicit lifecycle** — connect, refresh on `notifications/tools/list_changed`, and `close()` when the session ends.
29
+
30
+ Do **not** use this package as a sandbox, permission engine, or auto-discovery loader. Hosts must trust configured commands/URLs and gate registration.
31
+
32
+ ## Inputs / request
33
+
34
+ `connectMcpTools()` requires a stable `serverId` plus an explicit stdio or Streamable HTTP transport. Optional bounds control list caching, call timeout, result bytes, name prefix, and abort behavior; defaults are listed below.
35
+
36
+ ## Outputs / response / events
37
+
38
+ The resolved `McpToolBridge` exposes `tools`, `refresh()`, and `close()`. Each discovered MCP tool becomes a normal Prism `ToolDefinition`; calls return `ToolResult`, with remote `isError` mapped to `ToolResult.error`. List-change notifications invalidate the cache but register nothing automatically.
39
+
40
+ ## Request/response example
41
+
42
+ ```json
43
+ {
44
+ "request": { "serverId": "docs", "transport": { "type": "stdio", "command": "node", "args": ["server.js"] } },
45
+ "mappedTool": { "name": "mcp:docs:search", "parameters": { "type": "object" } }
46
+ }
47
+ ```
48
+
49
+ ## Implementation example
50
+
51
+ ```ts
52
+ import { createToolRegistry } from "@arnilo/prism";
53
+ import { connectMcpTools } from "@arnilo/prism-mcp";
54
+
55
+ const bridge = await connectMcpTools({
56
+ serverId: "docs",
57
+ transport: { type: "stdio", command: "node", args: ["server.js"] },
58
+ callTimeoutMs: 30_000,
59
+ });
60
+ const registry = createToolRegistry({ duplicate: "error" });
61
+ for (const tool of bridge.tools) registry.register(tool);
62
+ ```
63
+
64
+ ## Tool naming and mapping
65
+
66
+ | MCP | Prism |
67
+ | --- | --- |
68
+ | `tools/list` `inputSchema` | `ToolDefinition.parameters` |
69
+ | `tools/call` arguments | Parsed `ToolCallContent.arguments` |
70
+ | `tools/call` content blocks | `ToolResult.content` (`text`, `image`; resource/audio/link → descriptive `text`) |
71
+ | Tool `name` | Prefixed `mcp:<serverId>:<name>` (override with `namePrefix`) |
72
+ | `isError` results | `ToolResult.error` with summarized text |
73
+ | `structuredContent` | `ToolResult.value` / metadata |
74
+
75
+ Duplicate prefixed names throw `McpToolNameCollisionError` at refresh time.
76
+
77
+ ## Extension and configuration notes
78
+
79
+ | Option | Default | Purpose |
80
+ | --- | --- | --- |
81
+ | `serverId` | required | Stable identifier used in default name prefix |
82
+ | `transport` | required | `stdio` or `streamable-http` config |
83
+ | `namePrefix` | `mcp:<serverId>:` | Registry namespace for remote tools |
84
+ | `listCacheTtlMs` | `30000` | Skip re-listing until TTL expires (invalidated on list-changed) |
85
+ | `callTimeoutMs` | `60000` | Per-call MCP request timeout |
86
+ | `maxResultBytes` | `10000000` | Bound mapped result content |
87
+ | `signal` | none | Abort connect and trigger close on abort |
88
+
89
+ ### Stdio transport
90
+
91
+ ```ts
92
+ {
93
+ type: "stdio",
94
+ command: "node",
95
+ args: ["path/to/server.js"],
96
+ env?: Record<string, string>,
97
+ cwd?: string,
98
+ stderr?: "inherit" | "pipe" | "ignore" | "overlapped",
99
+ }
100
+ ```
101
+
102
+ The host explicitly chooses the executable, arguments, environment, and working directory. Prism does not search `PATH` for unknown servers or inject credentials.
103
+
104
+ ### Streamable HTTP transport
105
+
106
+ ```ts
107
+ {
108
+ type: "streamable-http",
109
+ url: "https://mcp.example.com/mcp",
110
+ requestInit?: RequestInit,
111
+ sessionId?: string,
112
+ }
113
+ ```
114
+
115
+ Only `http:` and `https:` URLs are accepted. Authentication (Bearer tokens, cookies) is supplied through `requestInit.headers` by the host.
116
+
117
+ ## Security and performance notes
118
+
119
+ | Risk | Mitigation |
120
+ | --- | --- |
121
+ | Untrusted subprocess (stdio) | Explicit `command` / `args` / `env` / `cwd`; review before deploy |
122
+ | SSRF / open redirects (HTTP) | Host URL allow-lists, network policy, no implicit discovery |
123
+ | Tool-name shadowing | Prefixed names + `createToolRegistry({ duplicate: "error" })` |
124
+ | Oversized server output | `maxResultBytes` on content mapping |
125
+ | Unvalidated arguments | Register tools with `createJsonSchemaToolArgumentValidator()` at dispatch |
126
+ | Missing permission gate | `PermissionPolicy` on `tool:mcp:<serverId>:<name>:execute` (or broader deny rules) |
127
+
128
+ MCP output is untrusted. Apply `SecretRedactor` and host logging policy to `ToolResult` before persisting or displaying.
129
+
130
+ ## Related APIs
131
+
132
+ - [Tools](tools.md): registry, dispatch, validation
133
+ - [Tool execution primitives](tool-execution-primitives.md): Plan 055 design and conformance matrix
134
+ - [Host security guide](host-security.md): permission, trust, validation checklist
135
+ - Package README: [`@arnilo/prism-mcp`](../packages/mcp/README.md)
136
+
137
+ ## Testing
138
+
139
+ Package tests use in-memory MCP transports (no network). Hosts should integration-test their configured stdio commands and HTTP endpoints in staging before production registration.
@@ -101,6 +101,7 @@ export const extension: Extension = {
101
101
  - `retry` middleware may stop retrying or adjust delay, but runtime still owns retry event emission, abort-aware waiting, and provider-turn boundaries.
102
102
  - The registry does not discover packages, read manifests, load config, call providers, execute tools, read resources, or start sessions.
103
103
  - Hosts may pass a middleware registry into `createExtensionKernel({ middleware })` to share it with direct host code.
104
+ - For OpenTelemetry export, prefer `session.subscribe()` + `@arnilo/prism-observability-opentelemetry` (see [Observability](observability.md)) rather than adding a parallel event bus. Middleware hooks remain for transforming payloads at named boundaries.
104
105
 
105
106
  ## Security and performance notes
106
107
 
@@ -118,6 +119,7 @@ export const extension: Extension = {
118
119
  - [Input and prompt assembly](input-and-prompt-assembly.md): `input_assembly` and `prompt_build` helper call sites.
119
120
  - [Compaction and retry policies](compaction-and-retry.md): compaction/retry middleware payloads and runtime timing.
120
121
  - [Context and skills](context-and-skills.md): `context` helper call site.
122
+ - [Observability](observability.md): optional OpenTelemetry adapter over `AgentEvent` streams.
121
123
  - [Public contracts](public-contracts.md): provider, tool, context, session, and extension contracts that runtimes can pass through hooks.
122
124
 
123
125
  Permission checks for tools, extensions, and resources are hard guards; middleware can transform payloads but cannot bypass a denied `PermissionPolicy`.
package/docs/migration.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- This page is the single navigation entry for the two cross-cutting migrations external apps hit when moving from Prism's development defaults to its production persistence and explicit-capability surfaces:
5
+ Prism 0.0.4 is source-compatible with documented 0.0.3 agent construction; no mandatory code migration is required. New capabilities are additive and inactive until configured. This page covers two optional adoption paths:
6
6
 
7
- 1. **In-memory / JSONL → database-backed persistence** — swap the single-process development `SessionStore` for a host-implemented `ProductionPersistenceStore` / `SessionStore` adapter, and optionally attach a durable `RunLedger`.
8
- 2. **Permissive capability defaults → explicit capability activation** — move from "omitted tool/skill lists activate everything in scope" (pre-Phase 38 behavior) to named, fail-closed tool/skill activation.
7
+ 1. **In-memory / JSONL → database-backed persistence** — replace the single-process development `SessionStore` with `@arnilo/prism-session-store-sqlite`, `@arnilo/prism-session-store-postgres`, or a host implementation, and optionally attach its durable `RunLedger`.
8
+ 2. **Legacy permissive capability configuration → explicit activation** — name tools/skills and keep omitted capabilities fail-closed.
9
9
 
10
- It is a thin, link-first guide: it states before/after shapes and points at the detailed pages for schema, indexes, redaction, branch handles, capability semantics, and security.
10
+ It states before/after shapes and links detailed schema, redaction, branch, capability, and security guidance.
11
11
 
12
12
  ## When to use it
13
13
 
@@ -15,7 +15,7 @@ Read this page when:
15
15
 
16
16
  - you are taking an app from the `createMemorySessionStore()` / `createJsonlSessionStore()` path to a multi-process, multi-tenant, or durable database backend;
17
17
  - you are hardening an agent that previously relied on "every scoped tool/skill is active" and need to name capabilities explicitly;
18
- - you are adopting the Phase 34–40 production surfaces (atomic append, branch handles, run/event/tool/usage ledger, security boundary hardening) for the first time.
18
+ - you are adopting 0.0.4 persistence, checkpoints/leases, workflows, structured output, multimodality, or explicit tool safety for the first time.
19
19
 
20
20
  If you are new to Prism, start at [Session stores](session-stores.md) and [Agent/session runtime](agent-session-runtime.md) instead.
21
21
 
@@ -26,6 +26,8 @@ There is no runtime import for this page. The migrations below use these surface
26
26
  | Surface | Where | Migration role |
27
27
  | --- | --- | --- |
28
28
  | `SessionStore` | `@arnilo/prism` | Runtime seam swapped from memory/JSONL to DB. |
29
+ | `createSqlitePersistence` | `@arnilo/prism-session-store-sqlite` | Local durable session, ledger, query, checkpoint, and lease adapter. |
30
+ | `createPostgresPersistence` | `@arnilo/prism-session-store-postgres` | Multi-process pooled persistence with advisory-lock migrations. |
29
31
  | `ProductionPersistenceStore` | `@arnilo/prism` | Adapter-facing contract for paginated, multi-tenant reads (`query*`, optional `readBranchPath`). |
30
32
  | `RunLedger` / `RunLedgerRecord` | `@arnilo/prism` | Durable run/event/tool-call/usage ledger attached via `AgentConfig.runLedger` / `RunOptions.runLedger`. |
31
33
  | `SessionAppendOptions` / `SessionAppendConflictError` / `SessionBranchHandle` | `@arnilo/prism` | Atomic append, retry dedup, durable branch handles. |
@@ -77,34 +79,23 @@ Capability migration (before/after):
77
79
 
78
80
  ### Migration 1 — in-memory / JSONL → database-backed persistence
79
81
 
80
- A complete, network-free reference adapter that implements these contracts against in-memory tables (and wires a `RunLedger`, branch-handle checkout, fork, and prior-run timeline resume) lives at [`examples/external-app-db-backed.ts`](../examples/external-app-db-backed.ts). The steps below mirror its structure.
82
+ Runnable references: [`examples/workflow-sqlite-resume.ts`](../examples/workflow-sqlite-resume.ts), credential-gated [`examples/workflow-postgres-resume.ts`](../examples/workflow-postgres-resume.ts), and the network-free custom-adapter example [`examples/external-app-db-backed.ts`](../examples/external-app-db-backed.ts).
81
83
 
82
- Step 1: implement a `SessionStore` (or the richer `ProductionPersistenceStore`) against your database. The runtime only requires `append(entry, options?)`, `list(sessionId)`, and optional `get(id)` / `readBranchPath(query)`.
84
+ Step 1: replace the development store with a first-party adapter. Use PostgreSQL instead when multiple processes or sustained concurrent writers matter.
83
85
 
84
86
  ```ts
85
87
  // Before: development store, single process.
86
88
  import { createJsonlSessionStore } from "@arnilo/prism/node/session-store-jsonl";
87
- const store = createJsonlSessionStore("./sessions.jsonl");
88
-
89
- // After: host-implemented database adapter implementing the documented contract, no real DB needed to satisfy the contract.
90
- import type { SessionStore, SessionEntry, SessionAppendOptions, PersistencePage, SessionBranchRead } from "@arnilo/prism";
91
-
92
- const store: SessionStore = {
93
- async append(entry: SessionEntry, options?: SessionAppendOptions) {
94
- // 1. idempotency dedup: insert (session_id, expected_parent_id, idempotency_key, entry_id)
95
- // into prism_session_append_idempotency; unique hit => SessionAppendConflictError { idempotencyDuplicate: true }
96
- // 2. expectedParentId existence check => SessionAppendConflictError { expectedParentId } if missing
97
- // 3. insert prism_session_entries row; duplicate id fails the transaction
98
- // 4. optionally compare-and-swap prism_branches.leaf_entry_id
99
- },
100
- async list(sessionId: string) { /* O(n) development fallback only */ return []; },
101
- async readBranchPath(query: SessionBranchRead): Promise<PersistencePage<SessionEntry>> {
102
- // one recursive CTE / ancestor query — do NOT list(sessionId)+in-memory walk for long sessions
103
- return { items: [] };
104
- },
105
- };
89
+ const oldStore = createJsonlSessionStore("./sessions.jsonl");
90
+
91
+ // After: local durable adapter. The same object implements SessionStore,
92
+ // RunLedger, ProductionPersistenceStore, checkpoints, and leases.
93
+ import { createSqlitePersistence } from "@arnilo/prism-session-store-sqlite";
94
+ const store = createSqlitePersistence({ filename: "./prism.db" });
106
95
  ```
107
96
 
97
+ Custom adapters remain supported through `SessionStore` / `ProductionPersistenceStore`; implement indexed `readBranchPath()` rather than full-session scans.
98
+
108
99
  Step 2: optionally attach a durable run/event/tool/usage ledger and ownership scope so a process exit leaves enough to resume and bill:
109
100
 
110
101
  ```ts
@@ -174,7 +165,7 @@ Runtime skill activation remains explicit: `RunOptions.activeSkills` narrows per
174
165
 
175
166
  ## Extension and configuration notes
176
167
 
177
- - **Persistence is host-owned.** Prism ships no database adapter, no DDL, no migration runner. Hosts own connection pools, transactions, cursor encoding, retention jobs, and tenant isolation. The runtime only talks to `SessionStore` (+ optional `readBranchPath`) and `RunLedger`.
168
+ - **Persistence remains host-configured.** Optional SQLite/PostgreSQL packages ship adapters and versioned setup, but hosts choose connection paths/pools, TLS, credentials, retention, tenant policy, and lifecycle. Core only consumes `SessionStore`, `RunLedger`, checkpoint, and lease contracts.
178
169
  - **`RunLedger` is not a `SessionStore` replacement.** Messages, branches, and session entries still flow through `SessionStore.append()`; the ledger records run/event/tool/usage facts. See [Runs and usage ledger](runs-and-usage.md).
179
170
  - **Capability activation is config over code.** Every seam lives on `AgentDefinition` / `AgentDefinitionResolutionContext` / `RunOptions`; no auto-activation, no privilege grant. A declaration cannot grant permissions or bypass `toolNames`.
180
171
  - **Migration order is decoupled.** You can adopt database persistence without changing capability activation, and vice versa. Both migrations are independent config swaps.
@@ -190,7 +181,9 @@ Runtime skill activation remains explicit: `RunOptions.activeSkills` narrows per
190
181
 
191
182
  ## Related APIs
192
183
 
193
- - [Database persistence](database-persistence.md): production persistence contracts, reference schema, indexes, conditional append, retention, migrations, NoSQL mapping.
184
+ - [Database persistence](database-persistence.md): production contracts, reference schema, indexes, conditional append, retention, migrations, and custom adapters.
185
+ - [SQLite persistence](sqlite-persistence.md): local durable first-party adapter and writer ceiling.
186
+ - [PostgreSQL persistence](postgres-persistence.md): pooled multi-process adapter, TLS/pool ownership, and live gate.
194
187
  - [Session stores](session-stores.md): `SessionStore` contract, `SessionAppendOptions`, `SessionAppendConflictError`, branch handles, `readBranchPath`.
195
188
  - [Session stores and branching](session-stores-and-branching.md): detailed branch semantics and helper reference.
196
189
  - [Runs and usage ledger](runs-and-usage.md): `RunLedger` record shapes, redaction, and event/usage ordering.
@@ -36,7 +36,7 @@ import { createModelRegistry, type ModelConfig } from "@arnilo/prism";
36
36
  | --- | --- |
37
37
  | `provider` / `model` | Required registry key. |
38
38
  | `displayName` | Human-readable label. |
39
- | `capabilities` | Input/output modes plus reasoning/tools/streaming booleans. |
39
+ | `capabilities` | Input/output modes (`text`, `image`, `audio`, `file`, `document`) plus reasoning/tools/streaming booleans and optional `structuredOutput` (`true` or `"json_schema"`) for native JSON-schema requests. |
40
40
  | `limits` | Context and output-token limits. |
41
41
  | `cost` | Input/output/cache read/cache write pricing. |
42
42
  | `cache` | Generic `ModelCacheCapabilities`. |
@@ -74,7 +74,7 @@ The registry emits no events and performs no I/O.
74
74
  "model": {
75
75
  "provider": "demo",
76
76
  "model": "demo-large",
77
- "capabilities": { "input": ["text"], "tools": true, "streaming": true },
77
+ "capabilities": { "input": ["text", "image", "audio", "file", "document"], "tools": true, "streaming": true },
78
78
  "limits": { "contextWindow": 128000, "maxOutputTokens": 8192 },
79
79
  "cost": { "input": 10, "output": 30, "cacheRead": 2, "currency": "USD", "unit": "1M tokens" },
80
80
  "cache": { "kind": "cache_control", "maxBreakpoints": 4, "longRetention": true }
@@ -91,7 +91,7 @@ const model: ModelConfig = {
91
91
  provider: "demo",
92
92
  model: "demo-large",
93
93
  displayName: "Demo Large",
94
- capabilities: { input: ["text"], output: ["text"], tools: true, streaming: true },
94
+ capabilities: { input: ["text", "document"], output: ["text"], tools: true, streaming: true },
95
95
  limits: { contextWindow: 128_000, maxOutputTokens: 8_192 },
96
96
  cost: { input: 10, output: 30, cacheRead: 2, cacheWrite: 12, currency: "USD", unit: "1M tokens" },
97
97
  cache: { kind: "cache_control", maxBreakpoints: 4, minCacheableTokens: 1024, longRetention: true },
@@ -110,12 +110,14 @@ Provider packages register models through `ProviderPackageAPI.registerModel(mode
110
110
  ## Security and performance notes
111
111
 
112
112
  - Model metadata must not contain credentials or secrets.
113
+ - Declare truthful `capabilities.input` tags. Prism core rejects undeclared modalities in `assembleProviderInput()` when the list is present.
113
114
  - Registration is in-memory and O(1) by provider/model key.
114
115
  - `ModelConfig.cache` is declarative capability info only; it does not grant permissions, select tools, or bypass auth.
115
116
  - Provider-specific behavior belongs in provider packages, not Prism core.
116
117
 
117
118
  ## Related APIs
118
119
 
120
+ - [Multimodal content](multimodal-content.md): `audio`/`file`/`document` blocks and `MODEL_INPUT_CAPABILITIES`.
119
121
  - [Provider layer](provider-layer.md): provider/model registry overview.
120
122
  - [Provider caching](provider-caching.md): `ModelCacheCapabilities` and cache helpers.
121
123
  - [Provider packages](provider-packages.md): package registration of model metadata.
@@ -0,0 +1,148 @@
1
+ # Multimodal content
2
+
3
+ ## What it does
4
+
5
+ Prism core ships generic `audio`, `file`, and `document` `ContentBlock` types plus bounded media resolution helpers. Blocks carry MIME type, optional name, and exactly one source: inline base64 `data`, remote `url`, or host `resourceUri`. Optional `transcript` metadata can accompany audio/document blocks.
6
+
7
+ `assembleProviderInput()` calls `assertMessagesSupportModelCapabilities()` so declared `ModelCapabilities.input` tags are enforced before provider calls. First-party provider packages map supported blocks locally; unsupported combinations fail closed with `UnsupportedModalityError` or an explicit provider error.
8
+
9
+ ## When to use it
10
+
11
+ - **Host apps** attaching PDFs, audio clips, or generic files to user messages before a provider turn.
12
+ - **Resource loaders** returning binary payloads for `resourceUri` references under trust/permission policy.
13
+ - **Provider authors** reading truthful `ModelCapabilities.input` tags (`text`, `image`, `audio`, `file`, `document`) before mapping wire formats.
14
+
15
+ Do not embed provider upload IDs, tenant-scoped remote file IDs, or API-specific handles in core content blocks.
16
+
17
+ ## Inputs / request
18
+
19
+ ```ts
20
+ import {
21
+ resolveMediaContentBlock,
22
+ assertSsrfAllowedUrl,
23
+ type AudioContent,
24
+ type FileContent,
25
+ type DocumentContent,
26
+ } from "@arnilo/prism";
27
+
28
+ const file: FileContent = {
29
+ type: "file",
30
+ mediaType: "application/pdf",
31
+ name: "report.pdf",
32
+ data: base64Pdf,
33
+ };
34
+
35
+ const audio: AudioContent = {
36
+ type: "audio",
37
+ mediaType: "audio/wav",
38
+ resourceUri: "package://demo/sample.wav",
39
+ durationMs: 12_000,
40
+ };
41
+
42
+ await resolveMediaContentBlock(file, { bounds: { maxItemBytes: 10_000_000 } });
43
+ ```
44
+
45
+ Known `ModelCapabilities.input` tags are exported as `MODEL_INPUT_CAPABILITIES`:
46
+
47
+ | Tag | Block type | First-party mapping (declared capability required) |
48
+ | --- | --- | --- |
49
+ | `text` | `text` (default) | All providers |
50
+ | `image` | `image` | OpenAI Responses, OpenRouter, OpenCode Go Anthropic route, Kimi, NeuralWatt |
51
+ | `audio` | `audio` | OpenAI Responses (`input_audio`) |
52
+ | `file` | `file` | OpenAI Responses (`input_file`); Anthropic routes map PDF only |
53
+ | `document` | `document` | OpenAI Responses (`input_file`); OpenCode Go Anthropic route; Kimi |
54
+
55
+ ## Outputs / response / events
56
+
57
+ - `resolveMediaContentBlock()` returns `{ mediaType, bytes, name?, durationMs?, transcript?, metadata? }`.
58
+ - `assertModelSupportsContentBlocks()` / `assertMessagesSupportModelCapabilities()` throw `UnsupportedModalityError` when a declared capability list omits the block modality.
59
+ - `assertMediaBlocksWithinBounds()` enforces per-item bytes, total request bytes, item count, and audio duration ceilings.
60
+ - `assertSsrfAllowedUrl()` rejects private/link-local/metadata hosts unless explicitly allow-listed.
61
+ - `sniffMediaMimeType()` / `assertDeclaredMediaTypeMatches()` compare declared MIME types to magic bytes.
62
+ - No events are emitted and no provider calls occur in these helpers.
63
+
64
+ Default ceilings:
65
+
66
+ | Constant | Value |
67
+ | --- | --- |
68
+ | `DEFAULT_MAX_MEDIA_ITEM_BYTES` | 10 MB |
69
+ | `DEFAULT_MAX_MEDIA_REQUEST_BYTES` | 32 MiB |
70
+ | `DEFAULT_MAX_AUDIO_DURATION_MS` | 5 minutes |
71
+ | `DEFAULT_MEDIA_FETCH_TIMEOUT_MS` | 30 seconds |
72
+ | `DEFAULT_MAX_MEDIA_ITEMS_PER_REQUEST` | 32 items |
73
+
74
+ ## Request/response example
75
+
76
+ ```json
77
+ {
78
+ "block": {
79
+ "type": "file",
80
+ "mediaType": "application/pdf",
81
+ "name": "report.pdf",
82
+ "resourceUri": "package://demo/report.pdf"
83
+ },
84
+ "resolved": {
85
+ "mediaType": "application/pdf",
86
+ "bytes": "<Uint8Array>",
87
+ "name": "report.pdf"
88
+ }
89
+ }
90
+ ```
91
+
92
+ ## Implementation example
93
+
94
+ ```ts
95
+ import {
96
+ assembleProviderInput,
97
+ loadBinaryResource,
98
+ resolveMediaContentBlock,
99
+ UnsupportedModalityError,
100
+ } from "@arnilo/prism";
101
+
102
+ const loader = {
103
+ async load(uri) {
104
+ return { uri, mediaType: "application/pdf", data: pdfBytes };
105
+ },
106
+ };
107
+
108
+ const bytes = await loadBinaryResource(loader, "package://demo/report.pdf");
109
+ const resolved = await resolveMediaContentBlock(
110
+ { type: "document", mediaType: "application/pdf", resourceUri: "package://demo/report.pdf" },
111
+ { loader },
112
+ );
113
+
114
+ try {
115
+ await assembleProviderInput({
116
+ model: { provider: "demo", model: "text-only", capabilities: { input: ["text"] } },
117
+ input: [{ role: "user", content: [{ type: "file", mediaType: "application/pdf", data: "..." }] }],
118
+ });
119
+ } catch (error) {
120
+ if (error instanceof UnsupportedModalityError) {
121
+ // Host-visible reject before provider HTTP.
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## Extension and configuration notes
127
+
128
+ - URL fetches use injectable `fetch` for tests and custom transports; production hosts should supply TLS, auth, and logging policy outside Prism core.
129
+ - `resourceUri` resolution requires a caller-provided `ResourceLoader` and optional `ResourceLoadContext.permission` check.
130
+ - Local filesystem paths should use trust policies such as `createPathTrustPolicy()` before exposing URIs to loaders.
131
+ - Provider upload/create/delete lifecycles are provider-package-local. `@arnilo/prism-provider-openai` inlines files under 4 MiB as `data:<mediaType>;base64,...` `file_data`, otherwise uses a bounded per-run upload cache and best-effort `DELETE /v1/files` cleanup after each stream.
132
+ - Shared wire helpers live in `@arnilo/prism/providers/media` (`serializeOpenAIResponsesInputFile`, `serializePdfDocumentWireBlock`, `createBoundedUploadCache`).
133
+
134
+ ## Security and performance notes
135
+
136
+ - SSRF deny-by-default blocks loopback, RFC1918, link-local, and cloud metadata hostnames unless `SsrfPolicy.allowedHostnames` is set.
137
+ - MIME validation rejects common magic-byte spoofing; extensions alone are never trusted.
138
+ - Byte budgets use base64 size estimates before decode and re-check decoded `buffer.length` after read/fetch.
139
+ - Media errors omit raw bytes/base64 payloads from messages.
140
+ - Fetch readers are cancelled promptly after bound violations or abort signals.
141
+
142
+ ## Related APIs
143
+
144
+ - [Input and prompt assembly](input-and-prompt-assembly.md): attachments and `assembleProviderInput()` capability checks.
145
+ - [Resource loading](resource-loading.md): `loadBinaryResource()` and text/JSON helpers.
146
+ - [Model registry](model-registry.md): `ModelCapabilities.input` metadata.
147
+ - [Provider conformance](provider-conformance.md): serialized request coverage for content blocks.
148
+ - [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): Plan 056 inventory and threat model.