@arnilo/prism 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +42 -62
  3. package/dist/agent-run-lifecycle.js +4 -0
  4. package/dist/agent-run-state.d.ts +5 -2
  5. package/dist/agent-run-state.js +18 -8
  6. package/dist/agent-session/session/assemble.d.ts +6 -0
  7. package/dist/agent-session/session/assemble.js +391 -0
  8. package/dist/agent-session/session/persist.d.ts +28 -0
  9. package/dist/agent-session/session/persist.js +166 -0
  10. package/dist/agent-session/session/provider-round.d.ts +6 -0
  11. package/dist/agent-session/session/provider-round.js +231 -0
  12. package/dist/agent-session/session/tool-round.d.ts +31 -0
  13. package/dist/agent-session/session/tool-round.js +473 -0
  14. package/dist/agent-session/session/types.d.ts +115 -0
  15. package/dist/agent-session/session/types.js +5 -0
  16. package/dist/agent-session/session.d.ts +54 -41
  17. package/dist/agent-session/session.js +23 -1132
  18. package/dist/capture.d.ts +63 -0
  19. package/dist/capture.js +67 -0
  20. package/dist/cli-dev.d.ts +29 -0
  21. package/dist/cli-dev.js +52 -0
  22. package/dist/cli-init.d.ts +34 -3
  23. package/dist/cli-init.js +192 -24
  24. package/dist/cli-runner.d.ts +6 -2
  25. package/dist/cli-runner.js +57 -10
  26. package/dist/content.d.ts +3 -3
  27. package/dist/content.js +3 -1
  28. package/dist/contracts-core/agent.d.ts +8 -0
  29. package/dist/contracts-core/batch.d.ts +97 -0
  30. package/dist/contracts-core/batch.js +65 -0
  31. package/dist/contracts-core/content.d.ts +72 -1
  32. package/dist/contracts-core/embeddings.d.ts +30 -0
  33. package/dist/contracts-core/embeddings.js +17 -0
  34. package/dist/contracts-core/images.d.ts +60 -0
  35. package/dist/contracts-core/images.js +17 -0
  36. package/dist/contracts-core/moderation.d.ts +46 -0
  37. package/dist/contracts-core/moderation.js +34 -0
  38. package/dist/contracts-core/speech.d.ts +39 -0
  39. package/dist/contracts-core/speech.js +17 -0
  40. package/dist/contracts-core/transcription.d.ts +48 -0
  41. package/dist/contracts-core/transcription.js +17 -0
  42. package/dist/contracts-core/video.d.ts +61 -0
  43. package/dist/contracts-core/video.js +17 -0
  44. package/dist/contracts-core.d.ts +7 -0
  45. package/dist/contracts-core.js +7 -0
  46. package/dist/contracts-protocol.d.ts +18 -0
  47. package/dist/contracts-run-state.d.ts +1 -2
  48. package/dist/index.d.ts +7 -3
  49. package/dist/index.js +5 -3
  50. package/dist/input.d.ts +8 -0
  51. package/dist/input.js +4 -0
  52. package/dist/node/agent-definitions.d.ts +1 -8
  53. package/dist/node/agent-definitions.js +0 -34
  54. package/dist/node/settings.d.ts +0 -1
  55. package/dist/node/settings.js +0 -5
  56. package/dist/pinned-fetch.js +29 -3
  57. package/dist/provider-events.js +3 -4
  58. package/dist/providers/media.d.ts +1 -2
  59. package/dist/providers/media.js +1 -4
  60. package/dist/rpc.d.ts +1 -1
  61. package/dist/rpc.js +4 -4
  62. package/dist/testing/persistence-schema.d.ts +1 -1
  63. package/dist/testing/persistence-schema.js +32 -28
  64. package/dist/testing/provider-conformance.d.ts +114 -5
  65. package/dist/testing/provider-conformance.js +342 -0
  66. package/dist/testing/tool-conformance.d.ts +25 -0
  67. package/dist/testing/tool-conformance.js +128 -1
  68. package/dist/testing/tool-effect-store-conformance.d.ts +0 -1
  69. package/dist/testing/tool-effect-store-conformance.js +0 -3
  70. package/dist/thinking.d.ts +48 -9
  71. package/dist/thinking.js +134 -8
  72. package/dist/tool-search.d.ts +76 -0
  73. package/dist/tool-search.js +199 -0
  74. package/docs/0.1.0-readiness.md +3 -3
  75. package/docs/a2a.md +2 -2
  76. package/docs/acp-agent.md +1 -1
  77. package/docs/acp.md +3 -3
  78. package/docs/ag-ui-adoption.md +1 -1
  79. package/docs/ag-ui.md +1 -2
  80. package/docs/agent-definitions.md +1 -1
  81. package/docs/agent-events.md +5 -5
  82. package/docs/agent-identity.md +13 -2
  83. package/docs/audit-export.md +3 -3
  84. package/docs/batch-jobs.md +120 -0
  85. package/docs/browser-automation.md +5 -5
  86. package/docs/caveman.md +2 -2
  87. package/docs/cli-rpc.md +43 -9
  88. package/docs/coding-agent-tools.md +19 -19
  89. package/docs/coding-review-and-diagnostics.md +2 -2
  90. package/docs/coding-security.md +5 -5
  91. package/docs/coding-tools.md +82 -0
  92. package/docs/coding-workspaces.md +2 -2
  93. package/docs/compaction-and-retry.md +2 -2
  94. package/docs/compaction-llm.md +4 -4
  95. package/docs/compaction-observational-memory.md +3 -3
  96. package/docs/computer-use-linux.md +13 -2
  97. package/docs/context-and-skills.md +3 -1
  98. package/docs/conversations.md +4 -4
  99. package/docs/core.md +85 -0
  100. package/docs/credential-storage.md +12 -8
  101. package/docs/credentials-and-redaction.md +1 -1
  102. package/docs/data-classification.md +1 -1
  103. package/docs/database-persistence.md +7 -3
  104. package/docs/dev-inspector.md +103 -0
  105. package/docs/device-adapters.md +2 -2
  106. package/docs/diagrams.md +247 -0
  107. package/docs/document-reader.md +6 -6
  108. package/docs/documents.md +214 -0
  109. package/docs/embeddings.md +112 -0
  110. package/docs/enterprise-postgres-state.md +7 -7
  111. package/docs/evaluations.md +41 -7
  112. package/docs/extensions.md +3 -3
  113. package/docs/forge-integration.md +3 -3
  114. package/docs/graft.md +5 -5
  115. package/docs/guardrails.md +2 -2
  116. package/docs/host-security.md +16 -15
  117. package/docs/image-generation.md +129 -0
  118. package/docs/impeccable.md +7 -5
  119. package/docs/index.md +84 -46
  120. package/docs/indexed-code-search.md +2 -2
  121. package/docs/language-intelligence.md +4 -4
  122. package/docs/live-testing.md +126 -0
  123. package/docs/mcp-tools.md +44 -13
  124. package/docs/middleware-hooks.md +1 -1
  125. package/docs/migrate-to-0.4.md +312 -0
  126. package/docs/migrate-to-0.5.md +122 -0
  127. package/docs/migration.md +51 -1
  128. package/docs/model-registry.md +38 -0
  129. package/docs/model-routing.md +6 -6
  130. package/docs/moderation.md +117 -0
  131. package/docs/multi-agent-patterns.md +177 -0
  132. package/docs/multimodal-content.md +27 -3
  133. package/docs/obscura.md +12 -12
  134. package/docs/observability.md +32 -7
  135. package/docs/openapi-tools.md +14 -4
  136. package/docs/operations.md +11 -0
  137. package/docs/performance.md +30 -10
  138. package/docs/persistence-credentials-multimodality-primitives.md +7 -7
  139. package/docs/policy-and-audit.md +18 -8
  140. package/docs/ponytail.md +3 -3
  141. package/docs/postgres-persistence.md +5 -5
  142. package/docs/process-sessions.md +2 -2
  143. package/docs/prompt-registry.md +106 -0
  144. package/docs/provider-caching.md +36 -32
  145. package/docs/provider-conformance.md +24 -2
  146. package/docs/provider-packages.md +58 -22
  147. package/docs/provider-primitives.md +5 -5
  148. package/docs/provider-request-policies.md +1 -1
  149. package/docs/providers/ai-sdk.md +18 -6
  150. package/docs/providers/alibaba.md +10 -6
  151. package/docs/providers/anthropic.md +10 -6
  152. package/docs/providers/azure.md +20 -4
  153. package/docs/providers/bedrock.md +18 -3
  154. package/docs/providers/clinepass.md +7 -3
  155. package/docs/providers/commandcode.md +253 -0
  156. package/docs/providers/deepseek.md +7 -3
  157. package/docs/providers/google.md +8 -4
  158. package/docs/providers/hyper.md +284 -0
  159. package/docs/providers/kimi.md +7 -3
  160. package/docs/providers/neuralwatt.md +12 -8
  161. package/docs/providers/ollama.md +18 -3
  162. package/docs/providers/openai-compatible.md +5 -1
  163. package/docs/providers/openai.md +9 -5
  164. package/docs/providers/opencode-go.md +8 -4
  165. package/docs/providers/openrouter.md +8 -4
  166. package/docs/providers/vertex.md +21 -5
  167. package/docs/providers/xai.md +7 -3
  168. package/docs/providers/zai.md +7 -3
  169. package/docs/rag.md +31 -9
  170. package/docs/release-and-install.md +181 -76
  171. package/docs/resource-loading.md +1 -1
  172. package/docs/runs-and-usage.md +28 -3
  173. package/docs/server.md +94 -5
  174. package/docs/settings-auth-trust-security.md +7 -5
  175. package/docs/sheets.md +229 -0
  176. package/docs/speech.md +126 -0
  177. package/docs/sqlite-persistence.md +4 -4
  178. package/docs/supervisors.md +4 -3
  179. package/docs/thinking-and-reasoning.md +93 -60
  180. package/docs/tool-conformance.md +28 -3
  181. package/docs/tool-execution-primitives.md +8 -8
  182. package/docs/tools.md +32 -5
  183. package/docs/web-tools.md +3 -3
  184. package/docs/wiki.md +7 -7
  185. package/docs/work-artifacts-and-review.md +17 -6
  186. package/docs/work-connectors.md +4 -4
  187. package/docs/work-tools.md +5 -5
  188. package/docs/workflow-orchestration-primitives.md +35 -11
  189. package/docs/workflows.md +74 -13
  190. package/docs/working-and-semantic-memory.md +53 -5
  191. package/package.json +14 -31
  192. package/templates/README.md +23 -0
  193. package/templates/deep-research/README.md.tmpl +47 -0
  194. package/templates/deep-research/env.example.tmpl +12 -0
  195. package/templates/deep-research/gitignore.tmpl +7 -0
  196. package/templates/deep-research/manifest.json +12 -0
  197. package/templates/deep-research/package.json.tmpl +23 -0
  198. package/templates/deep-research/src/agent.ts.tmpl +81 -0
  199. package/templates/deep-research/src/index.ts.tmpl +53 -0
  200. package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
  201. package/templates/deep-research/src/tools.ts.tmpl +86 -0
  202. package/templates/deep-research/src/types.ts.tmpl +45 -0
  203. package/templates/deep-research/src/workflow.ts.tmpl +156 -0
  204. package/templates/deep-research/tsconfig.json.tmpl +15 -0
  205. package/templates/init/manifest.json +5 -0
  206. package/templates/init/package.json.tmpl +2 -1
  207. package/templates/init/providers.json +40 -24
  208. package/docs/antigravity-agent.md +0 -207
@@ -26,17 +26,17 @@ Start from explicit host inputs. Do not let runtime code discover security state
26
26
  | Tool allow-list | active tools for this agent/session/run | `createToolRegistry`, `filterTools()`, `dispatchToolCall()` |
27
27
  | Tool argument rules | host validator | `AgentConfig.validator`, `RunOptions.validate`, `ToolValidator` |
28
28
  | Guardrail decisions | host callback allow/block/tripwire policy | `Guardrails`, `Guardrail`, `GuardrailError` |
29
- | Coding execution policy | path/command approval adapter | `ExecutionPolicy`, `@arnilo/prism-coding-security` |
29
+ | Coding execution policy | path/command approval adapter | `ExecutionPolicy`, `@arnilo/prism-coding-tools/security` |
30
30
  | Remote media policy | public/default pinned DNS or explicit trusted transport | `SsrfPolicy`, `resolveMediaContentBlock()` |
31
31
  | Durable history | host database adapter | `SessionStore`, `assertSessionStoreConforms()` |
32
32
  | Durable audit | host ledger adapter | `RunLedger`, `redactRunLedgerRecord()` |
33
33
  | Telemetry | host OpenTelemetry SDK/exporter | metadata-only adapter, controlled metric labels, `onTraceReference` |
34
34
  | Durable interruption | host checkpoint + session stores, exact ownership | `RunOptions.runState`, `resumeAgentRun()`, `createAgentRunLifecycle()`, `createSecureAgent()` |
35
35
  | Extensions | explicit package imports only | `createExtensionKernel`, `ExtensionAPI` |
36
- | Remote agent/workflow API | host authentication + ownership mapping | `@arnilo/prism-server`, `createPrismHandler()` |
37
- | Authenticated identity | host `IdentityVerifier` → verified `AgentIdentity`; optional OIDC/JWKS reference adapter (`@arnilo/prism-credentials-node/oidc`, pinned issuer/audience/JWKS, fail closed) | [Agent identity](agent-identity.md), `assertIdentityActive`, `narrowIdentity` |
38
- | Policy decision audit | optional redacted ledger + host WORM sink | [Policy and audit](policy-and-audit.md), `@arnilo/prism-policy` |
39
- | Durable enterprise state | host PostgreSQL pool, TLS, exact owner/principal projection, migration/runtime database roles, backup and explicit cleanup schedule | [Enterprise PostgreSQL state](enterprise-postgres-state.md), `@arnilo/prism-enterprise-postgres` |
36
+ | Remote agent/workflow API | host authentication + ownership mapping | `@arnilo/prism-core/runtime/server`, `createPrismHandler()` |
37
+ | Authenticated identity | host `IdentityVerifier` → verified `AgentIdentity`; optional OIDC/JWKS reference adapter (`@arnilo/prism-core/credentials/node/oidc`, pinned issuer/audience/JWKS, fail closed) | [Agent identity](agent-identity.md), `assertIdentityActive`, `narrowIdentity` |
38
+ | Policy decision audit | optional redacted ledger + host WORM sink | [Policy and audit](policy-and-audit.md), `@arnilo/prism-core/governance/policy` |
39
+ | Durable enterprise state | host PostgreSQL pool, TLS, exact owner/principal projection, migration/runtime database roles, backup and explicit cleanup schedule | [Enterprise PostgreSQL state](enterprise-postgres-state.md), `@arnilo/prism-core/enterprise/postgres` |
40
40
  | MCP server exposure | host MCP auth + selected capability list | `createPrismMcpServer()`, `createPrismMcpWebHandler()` |
41
41
 
42
42
  ## Outputs / response / events
@@ -139,7 +139,7 @@ Wire those values where they matter: provider adapters receive the resolved cred
139
139
  - Redaction is exact known-secret replacement only. It is not arbitrary secret detection, entropy scanning, or DLP.
140
140
  - Known secrets must be passed into redactors before data is emitted or persisted. Redact again in host adapters if they transform records after Prism redaction.
141
141
  - OpenAI Realtime sessions require a stable host owner identifier, use header-only credentials, and bind to the server `session.created` id. Treat returned audio/transcripts as untrusted; use a `SecretRedactor`, retain finite event/byte/wall caps, and close on disconnect or an identity/budget breach.
142
- - Tool `parameters` metadata is not validated by default. Add a `ToolValidator`, use `createToolParameterValidator()` with a schema adapter, or install `@arnilo/prism-tool-validator-json-schema` before side effects. Its untrusted-schema adapter rejects non-local refs, forbidden keys/cycles/non-finite values and bounds bytes/depth/properties/keywords/refs plus its LRU cache before Ajv compilation; do not raise caps above documented hard limits.
142
+ - Tool `parameters` metadata is not validated by default. Add a `ToolValidator`, use `createToolParameterValidator()` with a schema adapter, or install `@arnilo/prism-core/validation/json-schema` before side effects. Its untrusted-schema adapter rejects non-local refs, forbidden keys/cycles/non-finite values and bounds bytes/depth/properties/keywords/refs plus its LRU cache before Ajv compilation; do not raise caps above documented hard limits.
143
143
  - RAG `replaceSource()` only accepts a store with scoped `getBySource()` plus a real transaction; it stages bounded embeddings before mutation and otherwise fails closed. `deleteSource()` rechecks returned tenant/resource/corpus/source metadata. `createResourceDocumentLoader()` receives only a host-authorized `ResourceLoader`; `createWebFetchDocumentLoader()` never opens I/O and rejects local/private/IP-literal URLs before delegating to host-configured web-tools. HTML scripts/styles are stripped, PDF parsing has byte/page/time caps and rejects compressed PDFs.
144
144
  - RAG retrieval always emits `trust: { untrusted: true, inert: true, injectionCapable: true }` plus attributable citation provenance. Context blocks repeat this metadata and never gain tool authority. Host `Reranker`s see redacted finite candidates, are hard-capped by bytes/time/concurrency, must return only a permutation of candidate IDs, and cannot overwrite provenance/trust. Ingestion status errors are redacted; status storage/listing stays exact-scope and capped.
145
145
  - Treat embeddings as untrusted numeric input. `@arnilo/prism-memory` rejects empty, non-number, NaN, and infinite vectors before in-memory similarity, pgvector parameters, export, or rebuild; custom `Embedder`/`VectorStore` implementations must retain the same boundary. Memory entries carry consent/source/visibility; revoked/invisible entries never enter prompts, events, exports, or telemetry. `exportMemory()` additionally excludes consent-less legacy records regardless of recall mode and requires exact host identity equal to its tenant/resource/thread scope. Save rebuild cursors only in host-authorized storage; `rebuildIndex()` is one abortable capped page, never an implicit corpus job. `forget`/`applyRetention` are real bounded deletes.
@@ -149,19 +149,19 @@ Wire those values where they matter: provider adapters receive the resolved cred
149
149
  - AG-UI fields stay untrusted after schema validation. `input.project` returns host-selected messages/handoffs only; never merge state/tools/context/props into ownership, identity, permissions, provider options, or media policy. Apply Prism media SSRF/MIME bounds before resolution; output projectors are bounded allow-lists; interrupt edits deny rather than mutate persisted calls.
150
150
  - AG-UI MCP Apps requires negotiated `mcpApps`, exact proxy origin/auth, owned-run context, approval, one bridge, separate-origin sandbox (`allow-scripts allow-same-origin`), and no-wider CSP. Never execute HTML in host origin or retry a UI mutation; Task 4 adds recovery.
151
151
  - AG-UI A2A requires exact-origin verified client, host-owned task selection/correlation, explicit data/tool/A2UI projection, and reauthorized follow/cancel.
152
- - `@arnilo/prism-server` exposes no agent/workflow by default and requires `authorize()` for every matched operation. Derive complete tenant/account/user ownership from validated host identity, never request JSON. Workflow active identity and cancellation compare exact ownership; a tenant-only scope intentionally cannot cancel a checkpoint/run carrying account or user identity. The artifact review service (`createArtifactService`) requires authenticated identity + thread ownership on every attach/revise/compare/approve/reject/download, resolves concurrent reviewers via checkpoint CAS (no lost approvals), rejects local filesystem paths in `uri`/citations, redacts records before persist and on response, and serves downloads only through signed expiring links that are reauthorized against the token's ownership per request. When a blob store is wired (`bodies: ArtifactBodyStore`, 0.0.28), delivery links additionally resolve through `bodies.presign`; the reference `createS3ArtifactBodyStore` verifies ownership on every operation, verifies size/SHA-256/MIME on put and get (fail closed), refuses delete under legal hold (host `isHeld` callback), keeps credentials host-resolved, and never discloses bucket/path/key in errors, telemetry, or artifact records. Pass the current explicitly revised workflow definition so recursive hash mismatch fails before abort or durable mutation. Configure exact host/origin allow-lists where needed, wire redaction before execution, retain tool/workflow policy checks, and adapt the Web handler behind host TLS/rate limits. Disconnect abort is default; persistent reconnect/status belongs to durable workflow checkpoints, not an invented in-memory agent result cache.
153
- - Coding tools from `@arnilo/prism-coding-agent` accept an optional `ExecutionPolicy` checked inside each tool before side effects; shared policy propagation includes `createReadOnlyTools()`. They enforce finite text-scan/image/edit/write/shell limits, repository list/search depth/entry/match/scan/time caps, structured Git path/ref/message/output/patch/worktree caps, named-check concurrency/output caps, a 600-second default shell wall time, and a 64 MiB default total-output ceiling. Opt-in `createGitTools()` uses argument arrays with hooks/credential prompts/external diff disabled, requires host `commitIdentity` for commits, and never pushes or opens PRs. Successful truncated shell output leaves a host-owned exclusive `0600` temp file; delete `metadata.fullOutputPath` after use. Error/abort/timeout/overflow removes unpublished spills. Custom read/edit/shell/repository backends must honor supplied caps/signals. Use `@arnilo/prism-coding-security` for path roots, command rules, identity-scoped approval caching, required `workspaceMode` on `createSandboxCodingComposition()` / `createSandboxCodingTools()`, and the optional `createDockerSandbox()` reference adapter. **Host mode is never contained execution** (every isolation capability false). Sandbox mode reports isolation only from validated adapter capability metadata: `composition.capabilities` carries the frozen `SandboxCapabilities` object (`workspaceCoherent`, `filesystemIsolated`, `networkIsolated`, `processIsolated`, `privilegeIsolated`, `egressRestricted`); the deprecated `containmentClaim` is a conservative projection and must never be used alone. Authorize security-sensitive actions from the individual capabilities the policy actually needs — e.g. require `filesystemIsolated` before hosting untrusted coding tasks, and `egressRestricted` before any network-capable run. Mixed wiring requires `allowMixedWorkspaceWiring` and still reports no isolation. Limits alone are not containment: construct the Docker adapter (absolute CLI, digest-pinned image, network none by default) or an equivalent host sandbox before treating coding execution as production-safe. Docker daemon/image trust, egress firewall/proxy, and artifact retention remain host-owned.
154
- - Allow-list egress (0.0.26, `@arnilo/prism-coding-security`): `createEgressPolicy()` is deny-all with exact host/port/protocol rules and frozen `npm-registry`/`github` presets; `createAllowListEgressProxy()` is an HTTP forward proxy + CONNECT tunnel that pins DNS answers and verifies the connected address (rebinding defense), denies private/link-local/metadata ranges unless a rule opts in, re-validates every redirect hop against policy, and cuts oversized/slow transfers at frozen byte/time caps. TLS passes through without interception. Every allow/deny writes an audit record with no secrets. The proxy is inert until `start()`; `reloadPolicy()` is the only rule change path. `composeEgressSandboxNetwork(proxy.attestation(), name)` records validated attestation as `prism.egress.*` container labels — evidence, not enforcement: the host must restrict the Docker network so the proxy is the only reachable path, and `denyDirectEgress: true` is a claim the host makes true by topology. The proxy is not a firewall and cannot stop a container whose network reaches the internet directly.
155
- - Optional `@arnilo/prism-browser` requires a host-supplied Playwright Browser (`playwright-core@1.61.0` peer). Import is inert. One non-persistent context belongs to one run; actions serialize; refs are snapshot-scoped; CSS/evaluate/CDP/persistent profiles are denied. Context routing + `serviceWorkers: "block"` deny file/data/blob/devtools/private/loopback by default and require contained-proxy attestation for external egress (Playwright routing is defense in depth, not DNS containment). Uploads are realpath-rooted; downloads quarantine with hash/MIME until host `approveRelease`; screenshots return bounded `ImageContent`. Observation vs mutation/high-impact actions map to `ExecutionPolicy`. Treat snapshot/page text as untrusted external content. Close contexts with `browser_close` or `manager.closeRun(runId)` on abort/terminal. Browser control endpoint, binary/image pin, and real egress firewall/proxy remain host-owned. Shared sandbox: `createSharedSandboxBrowserOptions()` + `assertBrowserSandboxNetwork()`.
152
+ - `@arnilo/prism-core/runtime/server` exposes no agent/workflow by default and requires `authorize()` for every matched operation. Derive complete tenant/account/user ownership from validated host identity, never request JSON. Workflow active identity and cancellation compare exact ownership; a tenant-only scope intentionally cannot cancel a checkpoint/run carrying account or user identity. The artifact review service (`createArtifactService`) requires authenticated identity + thread ownership on every attach/revise/compare/approve/reject/download, resolves concurrent reviewers via checkpoint CAS (no lost approvals), rejects local filesystem paths in `uri`/citations, redacts records before persist and on response, and serves downloads only through signed expiring links that are reauthorized against the token's ownership per request. When a blob store is wired (`bodies: ArtifactBodyStore`, 0.0.28), delivery links additionally resolve through `bodies.presign`; the reference `createS3ArtifactBodyStore` verifies ownership on every operation, verifies size/SHA-256/MIME on put and get (fail closed), refuses delete under legal hold (host `isHeld` callback), keeps credentials host-resolved, and never discloses bucket/path/key in errors, telemetry, or artifact records. Pass the current explicitly revised workflow definition so recursive hash mismatch fails before abort or durable mutation. Configure exact host/origin allow-lists where needed, wire redaction before execution, retain tool/workflow policy checks, and adapt the Web handler behind host TLS/rate limits. Disconnect abort is default; persistent reconnect/status belongs to durable workflow checkpoints, not an invented in-memory agent result cache. Outbound lifecycle webhooks (`createWebhookNotifier`, 0.3.2) share the same boundary posture: host-registered public HTTPS (or explicitly opted-in loopback HTTP) targets only, private/metadata literals rejected at registration, every attempt DNS-pinned and redirect-free through core `pinnedFetch`, redaction before HMAC signing, and the key held by the host only. Pass a known-secret `SecretRedactor`.
153
+ - Coding tools from `@arnilo/prism-coding-tools/agent` accept an optional `ExecutionPolicy` checked inside each tool before side effects; shared policy propagation includes `createReadOnlyTools()`. They enforce finite text-scan/image/edit/write/shell limits, repository list/search depth/entry/match/scan/time caps, structured Git path/ref/message/output/patch/worktree caps, named-check concurrency/output caps, a 600-second default shell wall time, and a 64 MiB default total-output ceiling. Opt-in `createGitTools()` uses argument arrays with hooks/credential prompts/external diff disabled, requires host `commitIdentity` for commits, and never pushes or opens PRs. Successful truncated shell output leaves a host-owned exclusive `0600` temp file; delete `metadata.fullOutputPath` after use. Error/abort/timeout/overflow removes unpublished spills. Custom read/edit/shell/repository backends must honor supplied caps/signals. Use `@arnilo/prism-coding-tools/security` for path roots, command rules, identity-scoped approval caching, required `workspaceMode` on `createSandboxCodingComposition()` / `createSandboxCodingTools()`, and the optional `createDockerSandbox()` reference adapter. **Host mode is never contained execution** (every isolation capability false). Sandbox mode reports isolation only from validated adapter capability metadata: `composition.capabilities` carries the frozen `SandboxCapabilities` object (`workspaceCoherent`, `filesystemIsolated`, `networkIsolated`, `processIsolated`, `privilegeIsolated`, `egressRestricted`); the deprecated `containmentClaim` is a conservative projection and must never be used alone. Authorize security-sensitive actions from the individual capabilities the policy actually needs — e.g. require `filesystemIsolated` before hosting untrusted coding tasks, and `egressRestricted` before any network-capable run. Mixed wiring requires `allowMixedWorkspaceWiring` and still reports no isolation. Limits alone are not containment: construct the Docker adapter (absolute CLI, digest-pinned image, network none by default) or an equivalent host sandbox before treating coding execution as production-safe. Docker daemon/image trust, egress firewall/proxy, and artifact retention remain host-owned.
154
+ - Allow-list egress (0.0.26, `@arnilo/prism-coding-tools/security`): `createEgressPolicy()` is deny-all with exact host/port/protocol rules and frozen `npm-registry`/`github` presets; `createAllowListEgressProxy()` is an HTTP forward proxy + CONNECT tunnel that pins DNS answers and verifies the connected address (rebinding defense), denies private/link-local/metadata ranges unless a rule opts in, re-validates every redirect hop against policy, and cuts oversized/slow transfers at frozen byte/time caps. TLS passes through without interception. Every allow/deny writes an audit record with no secrets. The proxy is inert until `start()`; `reloadPolicy()` is the only rule change path. `composeEgressSandboxNetwork(proxy.attestation(), name)` records validated attestation as `prism.egress.*` container labels — evidence, not enforcement: the host must restrict the Docker network so the proxy is the only reachable path, and `denyDirectEgress: true` is a claim the host makes true by topology. The proxy is not a firewall and cannot stop a container whose network reaches the internet directly.
155
+ - Optional `@arnilo/prism-web-tools/browser` requires a host-supplied Playwright Browser (`playwright-core@1.61.0` peer). Import is inert. One non-persistent context belongs to one run; actions serialize; refs are snapshot-scoped; CSS/evaluate/CDP/persistent profiles are denied. Context routing + `serviceWorkers: "block"` deny file/data/blob/devtools/private/loopback by default and require contained-proxy attestation for external egress (Playwright routing is defense in depth, not DNS containment). Uploads are realpath-rooted; downloads quarantine with hash/MIME until host `approveRelease`; screenshots return bounded `ImageContent`. Observation vs mutation/high-impact actions map to `ExecutionPolicy`. Treat snapshot/page text as untrusted external content. Close contexts with `browser_close` or `manager.closeRun(runId)` on abort/terminal. Browser control endpoint, binary/image pin, and real egress firewall/proxy remain host-owned. Shared sandbox: `createSharedSandboxBrowserOptions()` + `assertBrowserSandboxNetwork()`.
156
156
  - Browser verified-state checkpoints (0.0.14, `createBrowserCheckpointLedger()`) store URL + domain-state hash + host data refs only — never serialized browser internals (cookies/storage/contexts). After any resume/interruption the ledger fails closed (`assertVerifiedBeforeSideEffect`) until the host reloads + verifies, so side effects never replay on stale state.
157
157
  - Device adapters (0.0.14, `resolveDevicePolicy`/`assertDeviceAdmit`) are deny-by-default: admission fails closed without explicit `enabled`, an explicit sandbox, approval (when required), an under-budget session count, and shared `RunLimits`. Stream chunks over the frozen cap are dropped with a marker; telemetry is redacted before emit/persist. No vendor voice/desktop package ships in 0.0.14 (demand-gated 0.1.x); device adapters cannot broaden consent/memory/network/file/browser/connector/tool permissions (gate 8).
158
- - Optional `@arnilo/prism-wiki` tools treat agent-supplied input as untrusted at the first-party `.wiki/` filesystem boundary. `wiki_read_page` enforces lexical containment (`path.relative` with separator-aware `..`/absolute checks) plus `fs.realpath` containment for the wiki root and every successfully read file, so sibling-prefix (`.wiki-evil`), `..`, absolute, alternate-separator, and symlink escapes are denied before content is returned; missing contained pages report `found: false` while denied paths throw an access-denied error (never mapped to not-found). `wiki_record_insight` rejects empty titles/content, caps titles at 200 characters and content at 65,536 bytes, and collapses control characters and newlines in titles to single-line display text before any page/frontmatter/index/log write, so titles cannot inject Markdown headings, index entries, or log entries; slugs are allow-listed to `[a-z0-9-_]` with a non-empty fallback. See [LLM Wiki](wiki.md).
159
- - `@arnilo/prism-credentials-node` rejects oversized/malformed envelopes and excessive scrypt work before KDF allocation, uses async scrypt, and requires restrictive existing/new Unix vault modes. Keep vault ownership and parent-directory access host-controlled; review before `chmod 600`, never auto-weaken a file policy. Keychain calls use abort-aware native async work with finite timeout/payload caps and sanitized errors. OS prompts, service availability, and whether a native backend promptly honors cancellation remain host/platform boundaries; no plaintext fallback is attempted.
158
+ - Optional `@arnilo/prism-memory/wiki` tools treat agent-supplied input as untrusted at the first-party `.wiki/` filesystem boundary. `wiki_read_page` enforces lexical containment (`path.relative` with separator-aware `..`/absolute checks) plus `fs.realpath` containment for the wiki root and every successfully read file, so sibling-prefix (`.wiki-evil`), `..`, absolute, alternate-separator, and symlink escapes are denied before content is returned; missing contained pages report `found: false` while denied paths throw an access-denied error (never mapped to not-found). `wiki_record_insight` rejects empty titles/content, caps titles at 200 characters and content at 65,536 bytes, and collapses control characters and newlines in titles to single-line display text before any page/frontmatter/index/log write, so titles cannot inject Markdown headings, index entries, or log entries; slugs are allow-listed to `[a-z0-9-_]` with a non-empty fallback. See [LLM Wiki](wiki.md).
159
+ - `@arnilo/prism-core/credentials/node` rejects oversized/malformed envelopes and excessive scrypt work before KDF allocation, uses async scrypt, and requires restrictive existing/new Unix vault modes. Keep vault ownership and parent-directory access host-controlled; review before `chmod 600`, never auto-weaken a file policy. Keychain calls use abort-aware native async work with finite timeout/payload caps and sanitized errors. OS prompts, service availability, and whether a native backend promptly honors cancellation remain host/platform boundaries; no plaintext fallback is attempted.
160
160
  - LLM compaction always sends finite summary `maxTokens`, retains bounded deltas/events, and bounds/redacts provider/factory/policy error detail. Observational-memory workers cap turns, calls, arguments, results, transcript, and surfaced errors; unknown tools fail before execution, while invalid results can only be rejected after a host tool returns and may therefore follow side effects. Pass all known provider/credential/tool secrets into compaction/runtime options; exact replacement is not secret discovery.
161
161
  - Default remote-media loading resolves every DNS answer, rejects the hostname if any address is non-public, and pins one validated address through the request. Explicit `allowedHostnames` can trust private destinations. A host-supplied `fetch` owns DNS/rebinding/proxy/redirect safety; a custom `requestUrl` must connect to its supplied validated address.
162
162
  - Permission checks happen before tool validation and before `tool.execute()`. Middleware cannot grant permission by renaming a tool.
163
163
  - Session stores and ledgers receive redacted values when a redactor is active, but durable storage remains host-owned. Enforce tenant/account/user ownership, retention, legal hold, and quotas via `ProductionPersistenceStore.lifecycle` (or host-equivalent DB controls). Hold always blocks delete.
164
- - `@arnilo/prism-enterprise-postgres` request paths require exact tenant scope plus principal for work/router state, use bound SQL values, and retain no prompts, connector request bodies, raw provider results, tokens, or credentials. Configure TLS/credential rotation/connection limits with the host `pg` pool. Run checksum/catalog migration setup with a controlled migration principal; keep request-path SQL least-privilege (`USAGE`, `SELECT`, `INSERT`, `UPDATE`, `DELETE` on six state tables) and do not grant request workers `CREATE`, `ALTER`, `DROP`, `TRUNCATE`, `GRANT`, or `COPY PROGRAM`. Back up and restore-test the schema; run bounded owner-scoped `state.cleanup()` from an authorized host job. `unknown` connector outcomes require reconciliation and must never auto-replay.
164
+ - `@arnilo/prism-core/enterprise/postgres` request paths require exact tenant scope plus principal for work/router state, use bound SQL values, and retain no prompts, connector request bodies, raw provider results, tokens, or credentials. Configure TLS/credential rotation/connection limits with the host `pg` pool. Run checksum/catalog migration setup with a controlled migration principal; keep request-path SQL least-privilege (`USAGE`, `SELECT`, `INSERT`, `UPDATE`, `DELETE` on six state tables) and do not grant request workers `CREATE`, `ALTER`, `DROP`, `TRUNCATE`, `GRANT`, or `COPY PROGRAM`. Back up and restore-test the schema; run bounded owner-scoped `state.cleanup()` from an authorized host job. `unknown` connector outcomes require reconciliation and must never auto-replay.
165
165
  - Prefer `createExtensionKernel({ loadPolicy })` allow-list/signature checks before loading third-party extension packages.
166
166
  - Provider-owned auth/content/session/cache/security headers win over caller headers in adapters that merge headers.
167
167
  - Security checks are bounded explicit calls on the active path. Prism adds no hidden global middleware, background workers, watchers, network calls, or filesystem scans.
@@ -190,7 +190,7 @@ PostgreSQL TLS/network policy, MCP endpoint trust/credentials and egress policy
190
190
  ## Web research boundaries
191
191
 
192
192
  - Construct `@arnilo/prism-web-tools` with one host-selected Brave or Exa adapter; never expose adapter/provider/credential/schema selection to model arguments.
193
- - Construct `@arnilo/prism-work-tools` with host-pinned CLI binary + isolated `configDir` + verified `AgentIdentity` (M365 and/or GWS). Never pass model-built command strings, `login`/`setup`/`auth`/`schema`/`--debug`, or credentials in argv. Mutations require draft approval; external recipients and anonymous/`anyone` shares fail closed.
193
+ - Construct `@arnilo/prism-core/integrations/work` with host-pinned CLI binary + isolated `configDir` + verified `AgentIdentity` (M365 and/or GWS). Never pass model-built command strings, `login`/`setup`/`auth`/`schema`/`--debug`, or credentials in argv. Mutations require draft approval; external recipients and anonymous/`anyone` shares fail closed.
194
194
  - Provider API origins are fixed exact HTTPS origins and redirects fail. Credentials resolve immediately before I/O; remote bodies and secrets are excluded from errors/results/telemetry.
195
195
  - Firecrawl targets reject userinfo, non-HTTP(S), private literals, and policy-denied hosts. Supply `validateUrl` for host DNS/rebinding/egress checks. Firecrawl performs remote retrieval, so Prism cannot pin target DNS after handoff.
196
196
  - Treat every snippet, highlight, Markdown byte, metadata field, and extracted JSON value as prompt-injection-capable untrusted data. Never elevate it into system instructions or let it modify tools, permissions, trust, credentials, routing, or extraction schema.
@@ -211,7 +211,8 @@ PostgreSQL TLS/network policy, MCP endpoint trust/credentials and egress policy
211
211
  ### 0.1.0 security evidence (plan 012 Task 6)
212
212
 
213
213
  - **Audit policy.** `npm audit --audit-level=moderate` is enforced in both `security.yml` and the `release.yml` supply-chain job (freeze-manifest `releasePolicy.auditLevelTarget`); recorded 0.1.0 tree: 0 vulnerabilities at every severity (317 locked dependencies, MCP SDK at the 1.30.0 fix baseline).
214
- - **Named threat-suites leg.** `npm run security:threat-suites` aggregates the Phase 8–11 conformance suites (durable-loop/HITL approval, coding sandbox/egress/forge, ACP protocol, OIDC/OPA/MCP-OAuth/OpenAPI/artifact) into one named 0.1.0 security evidence leg — same scripts as `npm test`, no rewrite; the Phase 7 tenant-isolation suite is its protected counterpart under `npm run test:postgres` (missing `PRISM_TEST_POSTGRES_URL` is a named blocked gate).
214
+ - **Named threat-suites leg.** `npm run security:threat-suites` aggregates the Phase 8–11 conformance suites (durable-loop/HITL approval, coding sandbox/egress/forge, ACP protocol, OIDC/OPA/MCP-OAuth/OpenAPI/artifact) into one named 0.1.0 security evidence leg — same scripts as `npm test`, no rewrite; the Phase 7 tenant-isolation suite is its protected counterpart under `npm run test:postgres` (missing `PRISM_TEST_POSTGRES_URL` is a named blocked gate). The dev-inspector leg (`scripts/phase40-security.test.mjs`, plan 040 Task 5) exercises the `@arnilo/prism-coding-tools/dev` local playground through built public entrypoints: D1 non-loopback binds refuse before any listener exists (`ERR_PRISM_DEV_REMOTE_BIND`) and loopback is the only bindable surface; D2 the host redactor scrubs secret literals from server-rendered replay payloads; D3 replay selectors outside the `resolveRun` seam's ownership fail closed 404 and replay without a durable event_source is a documented 404, never a re-execution; D4 unknown decision outcome discriminants reject `400` without consuming a run version or executing the tool, and a valid `allow_once` applies exactly once.
215
+ - **Dev-surface boundary (plan 040 Task 5).** The `@arnilo/prism-coding-tools/dev` inspector is a developer-time tool: loopback-only by default, non-loopback requires an explicit programmatic `remoteAuthorize` callback plus a real authorizer (the CLI/bin offers no remote-bind flag at all), and it must never be the production API boundary (that stays `@arnilo/prism-core/runtime/server` under host authorization). It carries no credential storage — the host agent config owns credentials and the inspector never reads environment secrets; rendered tool args/results pass the host redactor on both the live and replay paths, and HITL decision resume composes the core `createAgentRunLifecycle` validation (fail-closed on unknown discriminants/versions/runs).
215
216
  - **Supply-chain negative fixtures.** `scripts/release-gate.test.mjs` verifies the tarball deny list rejects tampered content (plans/reviews/maps/tests), unexpected file types and credential material (native binaries, `.pem`/`.key`/`.p12`), and that a provenance flag suppressed in CI is detectable in the `release.mjs` publish dry-run arguments (`--provenance` mandatory under `GITHUB_ACTIONS`, never claimed on local OIDC-less publishes).
216
217
  - **Mandatory gate stack.** CodeQL/SAST, PR dependency review (fail on high), secret scan (source + unpacked tarballs), SPDX SBOM + license policy, tarball allow/deny content checks, and provenance (npm OIDC + GitHub build attestations on tarballs and SBOM) all run in `security.yml`/`release.yml`; evidence for the 0.1.0 tree is recorded in [0.1.0 readiness](0.1.0-readiness.md).
217
218
  - **CodeQL query suite.** `.github/codeql/codeql-config.yml` selects the `security-extended` suite for `javascript-typescript` (with the default suite) on push/PR/schedule in `security.yml` (10-minute job bound; measured runtime ~3m22s on the audited SHA, last successful main run `33059128198`). The ignore list covers only generated `dist`, `node_modules`, and release/security artifact directories — first-party packages, threat suites, and fixtures that ship or execute are always scanned, so new alerts enter the same plan-038 ledger/remediation loop (config + guardrails asserted in `scripts/phase38-codeql-regression.test.mjs`). Local Task 6 gates (typecheck/lint/format/threat suites/audit/secret scan/SBOM) pass on the remediations; GitHub `state=open` stays non-zero until those remediations are the analyzed head. Groups G (`js/insufficient-password-hash` on RFC 7636 S256) and H (`js/incomplete-url-substring-sanitization` on a negative docs assertion) are maintainer-reviewed false positives queued for narrow dismissal after that analyze, not code changes.
@@ -0,0 +1,129 @@
1
+ # Image generation and editing
2
+
3
+ ## What it does
4
+
5
+ `ImageGenerationProvider` is the provider-neutral image contract: `generate`
6
+ (prompt→image[s] with size/format/quality/count options) and `edit`
7
+ (image+mask+prompt, reusing the existing `ImageContent` binary content parts).
8
+ Results are `Uint8Array` bytes plus provenance — hosts own persistence; no
9
+ disk writes, no URL-only contract. Adapters ship in
10
+ [`@arnilo/prism-providers/openai`](providers/openai.md) (`/v1/images/generations`,
11
+ `/v1/images/edits`, always `b64_json`) and
12
+ [`@arnilo/prism-providers/alibaba`](providers/alibaba.md) (DashScope wanx async
13
+ task API); offline conformance runs via `runImageGenerationConformance` from
14
+ `@arnilo/prism/testing/provider-conformance`.
15
+
16
+ ## When to use it
17
+
18
+ Use it when the host owns image storage and lifecycle and wants portable
19
+ generate/edit calls with typed errors. Do not use it for image *input* to a chat
20
+ model — that is the existing `ImageContent` content part on
21
+ [`ModelConfig`](public-contracts.md) — and do not use the Alibaba adapter for
22
+ edits (DashScope has no first-party image-edit route; it rejects with
23
+ `unsupported_operation`).
24
+
25
+ ## Inputs / request
26
+
27
+ | Field | Type | Meaning |
28
+ | --- | --- | --- |
29
+ | `model` | `string` | e.g. `gpt-image-1` (OpenAI), `wanx2.1-t2i-turbo` (DashScope). |
30
+ | `prompt` | `string` | Non-empty, within the provider cap (OpenAI 32,000 chars, wanx 800). |
31
+ | `images` / `mask` | `ImageContent[]` / `ImageContent?` (edit) | Base64 `data` parts decode inline; `url` parts resolve through `pinnedFetch` (SSRF-guarded, DNS-pinned, byte-bounded). |
32
+ | `size` / `format` / `quality` / `count` | `string?` / `string?` / `string?` / `number?` | Provider-defined vocabulary (`1024x1024`, `png`, `standard`/`hd`, 1–10 OpenAI / 1–4 wanx). |
33
+ | `signal` | `AbortSignal?` | Cancellation; observed by the adapter transport and the DashScope poll loop. |
34
+
35
+ Adapter options: `apiKey` (`CredentialValueSource` — the existing credential
36
+ seam, resolved per call and redacted from errors), `baseUrl`, `fetch` (inject a
37
+ fake transport for offline tests), `fetchUrl` (inject a result/input-image
38
+ downloader; defaults to `pinnedFetch`), `headers`, and `maxImageBytes`.
39
+
40
+ ## Outputs / response / events
41
+
42
+ | Field | Type | Meaning |
43
+ | --- | --- | --- |
44
+ | `images` | `GeneratedImage[]` | One entry per generated image, in provider order. |
45
+ | `images[].bytes` | `Uint8Array` | Decoded image bytes (one allocation per image; no re-encode loops). |
46
+ | `images[].mimeType` | `string` | e.g. `image/png` (from the provider's `output_format` or the request format). |
47
+ | `images[].provider` / `.model` | `string` | Preserved provenance — hosts can attribute stored output. |
48
+ | `images[].url` / `.revisedPrompt` | `string?` | Provider-native passthrough when returned. |
49
+ | `usage` | `Usage?` | Provider-reported usage when available. |
50
+
51
+ Failures throw `ImageGenerationError` with a stable `code`: `empty_input`
52
+ (no prompt / edit without image parts), `input_too_large` (prompt or count over
53
+ cap, over-ceiling input image), `unsupported_operation` (edit on providers
54
+ without an edit route), `request_failed` (non-2xx, failed task, failed download,
55
+ secret-redacted message), `response_malformed` (missing `b64_json`, invalid
56
+ base64, no result URLs), `unsupported_model` (via `assertImageGenerationSupported`
57
+ when the host checks `ModelCapabilities.imageGeneration`).
58
+
59
+ ## Request/response example
60
+
61
+ ```json
62
+ { "model": "gpt-image-1", "prompt": "a red cube", "size": "1024x1024", "n": 1, "response_format": "b64_json" }
63
+ ```
64
+
65
+ ## Implementation example
66
+
67
+ ```ts
68
+ import { createOpenAIImageGenerationProvider } from "@arnilo/prism-providers/openai";
69
+ import { createAlibabaImageGenerationProvider } from "@arnilo/prism-providers/alibaba";
70
+ import { runImageGenerationConformance } from "@arnilo/prism/testing/provider-conformance";
71
+
72
+ const images = createOpenAIImageGenerationProvider({ apiKey: process.env.OPENAI_API_KEY });
73
+ const { images: generated } = await images.generate({ model: "gpt-image-1", prompt: "a red cube", size: "1024x1024" });
74
+ // generated[0] = { bytes: Uint8Array, mimeType: "image/png", provider: "openai", model: "gpt-image-1" }
75
+
76
+ const edited = await images.edit({
77
+ model: "gpt-image-1",
78
+ prompt: "make it blue",
79
+ images: [{ type: "image", data: base64Png, mimeType: "image/png" }],
80
+ });
81
+
82
+ // DashScope wanx: submit + poll + download, wrapped in one call:
83
+ const alibaba = createAlibabaImageGenerationProvider({ apiKey: process.env.DASHSCOPE_API_KEY });
84
+ const wanx = await alibaba.generate({ model: "wanx2.1-t2i-turbo", prompt: "a red cube" });
85
+
86
+ // Offline conformance (fake transport, no network):
87
+ await runImageGenerationConformance({
88
+ provider: createOpenAIImageGenerationProvider({ apiKey: "sk-test", fetch: fakeFetch }),
89
+ model: "gpt-image-1",
90
+ maxPromptChars: 32000,
91
+ sample: { prompt: "a red cube", count: 1 },
92
+ });
93
+ ```
94
+
95
+ ## Extension and configuration notes
96
+
97
+ - Implement `ImageGenerationProvider` for other vendors; the contract is
98
+ structural — no base class, no registry. Providers without an edit route
99
+ reject `edit` with `ImageGenerationError("unsupported_operation")` rather
100
+ than pretending.
101
+ - Models declare support with `capabilities.imageGeneration`; hosts gate with
102
+ `modelSupportsImageGeneration` / `assertImageGenerationSupported`, mirroring
103
+ the embeddings/speech guard pattern.
104
+ - The DashScope adapter owns the async-task lifecycle (submit with
105
+ `X-DashScope-Async: enable`, poll until terminal status, download result
106
+ URLs); `pollIntervalMs` and `timeoutMs` tune the loop, and the abort signal
107
+ is honored between polls.
108
+
109
+ ## Security and performance notes
110
+
111
+ - API keys resolve through the existing `CredentialValueSource` seam and are
112
+ redacted from every thrown error (`redactSecrets`); no new secret paths.
113
+ - All URL fetches (edit inputs, DashScope result images) go through
114
+ `pinnedFetch` — DNS-pinned, SSRF-guarded, byte-bounded (`maxImageBytes`,
115
+ 25 MiB default); responses over the ceiling reject instead of buffering.
116
+ - Prompts and image bytes are never logged; error messages carry status and a
117
+ redacted body only. The contract never touches local disk — bytes return to
118
+ the host.
119
+ - Base64 payloads decode exactly once per image; response mapping allocates
120
+ per-image and nothing else.
121
+
122
+ ## Related APIs
123
+
124
+ - [Multimodal content](public-contracts.md): `ImageContent` parts — the edit
125
+ input vocabulary and the chat image-input path.
126
+ - [Provider conformance](provider-conformance.md): `runImageGenerationConformance`
127
+ and the offline conformance matrix.
128
+ - [Provider packages](provider-packages.md): subpath import rules for
129
+ `@arnilo/prism-providers/openai` and `@arnilo/prism-providers/alibaba`.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- `@arnilo/prism-impeccable` is an optional package that wires a host-supplied
5
+ `@arnilo/prism-coding-tools/impeccable` is an optional package that wires a host-supplied
6
6
  [Impeccable](https://github.com/pbakaus/impeccable) `SKILL.md` into Prism skill
7
7
  and command registries.
8
8
 
@@ -30,8 +30,9 @@ iteration — those stay host-owned.
30
30
  | Field | Type | Required | Purpose |
31
31
  | --- | --- | --- | --- |
32
32
  | `upstreamPath` | `string` | yes | Path to a tree with `skills/impeccable/SKILL.md` or `SKILL.md` at the path (e.g. `dist/universal/impeccable`). |
33
+ | `expectedSnapshotDigest` | `string` | no | sha256 hex of the resolved `SKILL.md` bytes. When set, `kernel.load` fails closed on drift before any registration — the vendored-snapshot pin. Record it when you vendor an upstream checkout; bump it when you deliberately pull upstream fixes. |
33
34
 
34
- No optional peer. npm `impeccable` is the detector CLI, not a skill tree.
35
+ No optional peer. npm `impeccable` is the detector CLI, not a skill tree. Ownership model: **host-owned vendored snapshot, pinned** (decision + evidence: `docs/_evidence/impeccable-ownership-2026-09-04.md`; upstream commit at decision time `695df68a…`).
35
36
 
36
37
  ## Outputs / response / events
37
38
 
@@ -56,7 +57,7 @@ No instruction injector. No session persistence. No 23 Prism-native craft/polish
56
57
  ## Implementation example
57
58
 
58
59
  ```ts
59
- import { createImpeccableExtension } from "@arnilo/prism-impeccable";
60
+ import { createImpeccableExtension } from "@arnilo/prism-coding-tools/impeccable";
60
61
  import {
61
62
  createExtensionKernel,
62
63
  createLoadSkillTool,
@@ -85,14 +86,15 @@ Keep `skillsDisclosure: "progressive"` so the full `SKILL.md` stays catalog-only
85
86
  - `kernel.load` resolves `SKILL.md` first; failure throws before any `register*`.
86
87
  - Point `upstreamPath` at a compiled skill dir or a parent that contains `skills/impeccable/SKILL.md`.
87
88
  - Do not invent per-command Prism wrappers for upstream `craft` / `polish` / `live`.
88
- - Not in `@arnilo/prism-all` / `prism-code` / `prism-sdk`.
89
+ - Not installed by any family profile (the 0.3-era profiles were removed in 0.4).
89
90
 
90
91
  ## Security and performance notes
91
92
 
92
93
  - Upstream `SKILL.md` is untrusted host content; reads capped at `MAX_SKILL_FILE_BYTES` (256 KiB).
93
94
  - Path escape rejected. Errors redact home and absolute paths.
95
+ - `expectedSnapshotDigest` is a content pin (sha256 of the parsed artifact): a vendored snapshot cannot be swapped silently — drift fails `kernel.load` with a redacted, bounded error naming the refresh step.
94
96
  - No `npx`, hook install, env scan, or network on import/setup.
95
- - Setup is one bounded file read.
97
+ - Setup is one bounded file read (+ one hash when pinned).
96
98
 
97
99
  ## Related APIs
98
100