@arnilo/prism 0.3.2 → 0.4.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 (116) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +34 -57
  3. package/dist/agent-run-lifecycle.js +4 -0
  4. package/dist/agent-run-state.d.ts +4 -0
  5. package/dist/agent-run-state.js +18 -5
  6. package/dist/agent-session/session.d.ts +7 -0
  7. package/dist/agent-session/session.js +59 -2
  8. package/dist/cli-dev.d.ts +29 -0
  9. package/dist/cli-dev.js +52 -0
  10. package/dist/cli-init.d.ts +17 -2
  11. package/dist/cli-init.js +194 -21
  12. package/dist/cli-runner.d.ts +5 -1
  13. package/dist/cli-runner.js +12 -1
  14. package/dist/contracts-core/agent.d.ts +6 -0
  15. package/dist/contracts-protocol.d.ts +18 -0
  16. package/dist/contracts-run-state.d.ts +1 -2
  17. package/dist/index.d.ts +3 -1
  18. package/dist/index.js +2 -1
  19. package/dist/input.d.ts +8 -0
  20. package/dist/input.js +4 -0
  21. package/dist/testing/persistence-schema.d.ts +1 -1
  22. package/dist/testing/persistence-schema.js +32 -28
  23. package/dist/testing/tool-conformance.d.ts +25 -0
  24. package/dist/testing/tool-conformance.js +128 -1
  25. package/dist/tool-search.d.ts +76 -0
  26. package/dist/tool-search.js +199 -0
  27. package/docs/0.1.0-readiness.md +2 -2
  28. package/docs/acp-agent.md +1 -1
  29. package/docs/antigravity-agent.md +1 -1
  30. package/docs/browser-automation.md +5 -5
  31. package/docs/caveman.md +2 -2
  32. package/docs/cli-rpc.md +26 -3
  33. package/docs/coding-security.md +1 -1
  34. package/docs/coding-tools.md +82 -0
  35. package/docs/compaction-and-retry.md +2 -2
  36. package/docs/compaction-llm.md +4 -4
  37. package/docs/compaction-observational-memory.md +3 -3
  38. package/docs/context-and-skills.md +2 -0
  39. package/docs/core.md +85 -0
  40. package/docs/credential-storage.md +1 -1
  41. package/docs/database-persistence.md +4 -0
  42. package/docs/dev-inspector.md +103 -0
  43. package/docs/diagrams.md +247 -0
  44. package/docs/documents.md +213 -0
  45. package/docs/evaluations.md +35 -1
  46. package/docs/graft.md +3 -3
  47. package/docs/guardrails.md +1 -1
  48. package/docs/host-security.md +4 -3
  49. package/docs/impeccable.md +2 -2
  50. package/docs/index.md +31 -20
  51. package/docs/mcp-tools.md +1 -1
  52. package/docs/migrate-to-0.4.md +312 -0
  53. package/docs/migration.md +22 -0
  54. package/docs/model-routing.md +1 -1
  55. package/docs/multi-agent-patterns.md +177 -0
  56. package/docs/multimodal-content.md +1 -1
  57. package/docs/obscura.md +10 -10
  58. package/docs/openapi-tools.md +1 -1
  59. package/docs/performance.md +23 -3
  60. package/docs/persistence-credentials-multimodality-primitives.md +1 -1
  61. package/docs/policy-and-audit.md +1 -1
  62. package/docs/ponytail.md +2 -2
  63. package/docs/prompt-registry.md +106 -0
  64. package/docs/provider-caching.md +32 -32
  65. package/docs/provider-conformance.md +1 -1
  66. package/docs/provider-packages.md +19 -19
  67. package/docs/provider-primitives.md +4 -4
  68. package/docs/providers/ai-sdk.md +3 -3
  69. package/docs/providers/alibaba.md +5 -5
  70. package/docs/providers/anthropic.md +6 -6
  71. package/docs/providers/azure.md +3 -3
  72. package/docs/providers/bedrock.md +3 -3
  73. package/docs/providers/clinepass.md +3 -3
  74. package/docs/providers/deepseek.md +3 -3
  75. package/docs/providers/google.md +4 -4
  76. package/docs/providers/kimi.md +3 -3
  77. package/docs/providers/neuralwatt.md +8 -8
  78. package/docs/providers/ollama.md +3 -3
  79. package/docs/providers/openai-compatible.md +1 -1
  80. package/docs/providers/openai.md +5 -5
  81. package/docs/providers/opencode-go.md +4 -4
  82. package/docs/providers/openrouter.md +3 -3
  83. package/docs/providers/vertex.md +5 -5
  84. package/docs/providers/xai.md +3 -3
  85. package/docs/providers/zai.md +3 -3
  86. package/docs/rag.md +5 -5
  87. package/docs/release-and-install.md +98 -50
  88. package/docs/runs-and-usage.md +14 -1
  89. package/docs/server.md +90 -1
  90. package/docs/sheets.md +229 -0
  91. package/docs/supervisors.md +1 -0
  92. package/docs/thinking-and-reasoning.md +10 -10
  93. package/docs/tool-conformance.md +27 -2
  94. package/docs/tools.md +29 -2
  95. package/docs/web-tools.md +2 -2
  96. package/docs/wiki.md +6 -6
  97. package/docs/workflow-orchestration-primitives.md +24 -0
  98. package/docs/workflows.md +70 -9
  99. package/docs/working-and-semantic-memory.md +53 -5
  100. package/package.json +10 -30
  101. package/templates/README.md +23 -0
  102. package/templates/deep-research/README.md.tmpl +47 -0
  103. package/templates/deep-research/env.example.tmpl +12 -0
  104. package/templates/deep-research/gitignore.tmpl +7 -0
  105. package/templates/deep-research/manifest.json +12 -0
  106. package/templates/deep-research/package.json.tmpl +23 -0
  107. package/templates/deep-research/src/agent.ts.tmpl +81 -0
  108. package/templates/deep-research/src/index.ts.tmpl +53 -0
  109. package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
  110. package/templates/deep-research/src/tools.ts.tmpl +86 -0
  111. package/templates/deep-research/src/types.ts.tmpl +45 -0
  112. package/templates/deep-research/src/workflow.ts.tmpl +156 -0
  113. package/templates/deep-research/tsconfig.json.tmpl +15 -0
  114. package/templates/init/manifest.json +5 -0
  115. package/templates/init/package.json.tmpl +2 -1
  116. package/templates/init/providers.json +16 -16
@@ -0,0 +1,82 @@
1
+ # Coding Tools, Sandboxing, and Personas (@arnilo/prism-coding-tools)
2
+
3
+ The `@arnilo/prism-coding-tools` family package unifies Prism's coding agent tools, security sandboxing, document reading, OpenAPI integration, Linux desktop automation, Dev inspector, and persona extensions into explicit, import-isolated subpaths.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @arnilo/prism @arnilo/prism-coding-tools
9
+ ```
10
+
11
+ For document reading or specialized persona integrations, install the optional peer dependencies as needed:
12
+
13
+ ```bash
14
+ # PDF and DOCX document extraction
15
+ npm install pdf-parse mammoth
16
+
17
+ # Ponytail upstream integration
18
+ npm install @dietrichgebert/ponytail
19
+ ```
20
+
21
+ ## Subpaths Map
22
+
23
+ | Subpath | Description | Optional Peers |
24
+ |---|---|---|
25
+ | `@arnilo/prism-coding-tools/agent` | Core coding tools (read, write, edit, search, bash, git, diagnostics, check, ast-grep, lsp) | — |
26
+ | `@arnilo/prism-coding-tools/security` | Sandbox execution adapters (Docker/OCI, native disposable sandbox, approval policies, egress proxy) | — |
27
+ | `@arnilo/prism-coding-tools/document-reader` | Bounded PDF/DOCX literal-text extraction adapter with fail-closed loading | `pdf-parse`, `mammoth` |
28
+ | `@arnilo/prism-coding-tools/openapi` | OpenAPI 3.x tool generator and executor with SSRF protection and parameter validation | — |
29
+ | `@arnilo/prism-coding-tools/computer-use-linux` | Linux desktop observation and targeting tool bridge | — |
30
+ | `@arnilo/prism-coding-tools/dev` | Loopback-only developer inspector, event timeline visualizer, and local replay server | — |
31
+ | `@arnilo/prism-coding-tools/dev/cli` | Command-line entrypoint for `prism dev` | — |
32
+ | `@arnilo/prism-coding-tools/caveman` | Caveman ultra-terse engineering persona extension | — |
33
+ | `@arnilo/prism-coding-tools/ponytail` | Ponytail multi-agent planning and delegation persona extension | `@dietrichgebert/ponytail` |
34
+ | `@arnilo/prism-coding-tools/impeccable` | Impeccable high-precision frontend engineering persona extension | — |
35
+
36
+ ## CLI
37
+
38
+ ```bash
39
+ # Start the loopback dev inspector
40
+ npx prism-dev --port 4311
41
+ ```
42
+
43
+ ## Usage Examples
44
+
45
+ ### Creating Coding Tools
46
+ ```ts
47
+ import { createCodingTools } from "@arnilo/prism-coding-tools/agent";
48
+
49
+ const tools = createCodingTools({
50
+ workspaceRoot: process.cwd(),
51
+ });
52
+ ```
53
+
54
+ ### Sandboxed Execution
55
+ ```ts
56
+ import { createDockerSandbox, createSandboxCodingComposition } from "@arnilo/prism-coding-tools/security";
57
+
58
+ const composition = createSandboxCodingComposition({
59
+ workspaceMode: "sandbox",
60
+ sandbox: createDockerSandbox({
61
+ image: "node:20-alpine@sha256:...",
62
+ workspaceRoot: process.cwd(),
63
+ }),
64
+ });
65
+ ```
66
+
67
+ ### Persona Extensions
68
+ ```ts
69
+ import { createCavemanExtension } from "@arnilo/prism-coding-tools/caveman";
70
+ import { createPonytailExtension } from "@arnilo/prism-coding-tools/ponytail";
71
+ import { createImpeccableExtension } from "@arnilo/prism-coding-tools/impeccable";
72
+
73
+ const caveman = createCavemanExtension();
74
+ const ponytail = createPonytailExtension();
75
+ const impeccable = createImpeccableExtension();
76
+ ```
77
+
78
+ ## Security & Import Isolation
79
+
80
+ - Importing `@arnilo/prism-coding-tools/agent` never loads Docker sandbox adapters, desktop MCP bridges, document parser peers, or Dev inspector modules.
81
+ - Document parser peers (`pdf-parse`, `mammoth`) and Ponytail optional peer fail closed when absent.
82
+ - Persona extensions are pure prompt and behavior modifiers and never gain implicit host privileges.
@@ -149,11 +149,11 @@ await session.compact({ keepRecentEntries: 4 });
149
149
 
150
150
  ## Extension and configuration notes
151
151
 
152
- Retry policies are ordinary `RetryPolicy` implementations and can be registered as `retryPolicy` contributions. Hosts must still pass the selected policy/config to `createAgent()` or `session.run()`. Retry middleware receives `{ context, decision }` before a retry is scheduled and can reduce delay or stop retrying. First-party providers emit `ErrorInfo.code` as the numeric HTTP status so the default policy's transient-code set (`429`/`500`/`502`/`503`) classifies retryability without provider-specific core branches; `@arnilo/prism-provider-neuralwatt` additionally exports `classifyNeuralWattError()` for hosts that want structured `Retry-After`/`retry_strategy` metadata.
152
+ Retry policies are ordinary `RetryPolicy` implementations and can be registered as `retryPolicy` contributions. Hosts must still pass the selected policy/config to `createAgent()` or `session.run()`. Retry middleware receives `{ context, decision }` before a retry is scheduled and can reduce delay or stop retrying. First-party providers emit `ErrorInfo.code` as the numeric HTTP status so the default policy's transient-code set (`429`/`500`/`502`/`503`) classifies retryability without provider-specific core branches; `@arnilo/prism-providers/neuralwatt` additionally exports `classifyNeuralWattError()` for hosts that want structured `Retry-After`/`retry_strategy` metadata.
153
153
 
154
154
  Compaction strategies are ordinary `CompactionStrategy` implementations. Extensions can register strategies through the existing compaction strategy contribution registry, but registration is inert until a host explicitly selects and passes a strategy to runtime code. Extensions can also register `compaction` middleware; the runtime calls it only when the agent/session has that middleware registry configured.
155
155
 
156
- The default strategy does not call a provider. Hosts that need model-generated summaries can use the optional [`@arnilo/prism-compaction-llm` package](compaction-llm.md); its `maxOutputTokens`/`maxSummaryTokens` budget is passed through `model.parameters.maxTokens` and first-party providers serialize that to provider output-token fields. Coding sessions can select that package's `createCodingCompactionStrategy()` preset for paths, patch intent, checks, plans/todos, blockers, and next verification steps; it remains an ordinary `CompactionStrategy` and does not retain complete diffs or add a coding runtime. Hosts that need prepared source-backed memory without a compaction-time model call can use [`@arnilo/prism-compaction-observational-memory`](compaction-observational-memory.md).
156
+ The default strategy does not call a provider. Hosts that need model-generated summaries can use the optional [`@arnilo/prism-memory/compaction/llm` subpath](compaction-llm.md); its `maxOutputTokens`/`maxSummaryTokens` budget is passed through `model.parameters.maxTokens` and first-party providers serialize that to provider output-token fields. Coding sessions can select that package's `createCodingCompactionStrategy()` preset for paths, patch intent, checks, plans/todos, blockers, and next verification steps; it remains an ordinary `CompactionStrategy` and does not retain complete diffs or add a coding runtime. Hosts that need prepared source-backed memory without a compaction-time model call can use [`@arnilo/prism-memory/compaction/observational-memory`](compaction-observational-memory.md).
157
157
 
158
158
  ## Security and performance notes
159
159
 
@@ -1,7 +1,7 @@
1
1
  # LLM compaction package
2
2
 
3
3
  ## What it does
4
- `@arnilo/prism-compaction-llm` is an optional provider-backed compaction package. It prepares branch history, calls an explicit summary provider/model, and returns a standard Prism `CompactionStrategy`. The core default compaction remains local and conservative.
4
+ `@arnilo/prism-memory/compaction/llm` is an optional provider-backed compaction subpath. It prepares branch history, calls an explicit summary provider/model, and returns a standard Prism `CompactionStrategy`. The core default compaction remains local and conservative.
5
5
 
6
6
  ## When to use it
7
7
  Use it when a host wants model-generated summaries while preserving raw append-only session history. Do not use it as a core default, provider SDK loader, hidden credential discovery layer, vector memory, or store rewrite.
@@ -59,7 +59,7 @@ Provider `error` events, empty summaries, or abort signals throw before returnin
59
59
 
60
60
  ## Implementation example
61
61
  ```ts
62
- import { createLlmCompactionStrategy } from "@arnilo/prism-compaction-llm";
62
+ import { createLlmCompactionStrategy } from "@arnilo/prism-memory/compaction/llm";
63
63
 
64
64
  const strategy = createLlmCompactionStrategy({
65
65
  provider: summaryProvider,
@@ -80,7 +80,7 @@ await session.compact({ strategy, secrets: [apiKey] });
80
80
  Coding-session example:
81
81
 
82
82
  ```ts
83
- import { createCodingCompactionStrategy } from "@arnilo/prism-compaction-llm";
83
+ import { createCodingCompactionStrategy } from "@arnilo/prism-memory/compaction/llm";
84
84
 
85
85
  const strategy = createCodingCompactionStrategy({
86
86
  provider: summaryProvider,
@@ -110,7 +110,7 @@ This package is inert until imported. Direct strategy use works with `session.co
110
110
 
111
111
  ```ts
112
112
  import { createAgent, createExtensionKernel } from "@arnilo/prism";
113
- import { createLlmCompactionExtension } from "@arnilo/prism-compaction-llm";
113
+ import { createLlmCompactionExtension } from "@arnilo/prism-memory/compaction/llm";
114
114
 
115
115
  const kernel = createExtensionKernel();
116
116
  await kernel.load([createLlmCompactionExtension({ provider: summaryProvider, model: summaryModel })]);
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- `@arnilo/prism-compaction-observational-memory` is an optional package for source-backed observational memory and fast compaction.
5
+ `@arnilo/prism-memory/compaction/observational-memory` is an optional subpath for source-backed observational memory and fast compaction.
6
6
 
7
7
  Current status: ledger/projection/render/recall utilities, explicit worker runtime, fast compaction strategy, inert extension helper, recall tool, and status/view command factories are available.
8
8
 
@@ -107,7 +107,7 @@ import {
107
107
  createRecallMemoryTool,
108
108
  recallObservationalMemory,
109
109
  renderObservationalMemory,
110
- } from "@arnilo/prism-compaction-observational-memory";
110
+ } from "@arnilo/prism-memory/compaction/observational-memory";
111
111
 
112
112
  const om = createObservationalMemory({
113
113
  observation: { provider: observerProvider, model: observerModel, messageTokens: 10_000 },
@@ -174,7 +174,7 @@ Hosts that need parent recall of child *source* work compose it themselves: wrap
174
174
 
175
175
  ```ts
176
176
  import { createId, type SessionStore } from "@arnilo/prism";
177
- import { isEligibleObservationSourceEntry } from "@arnilo/prism-compaction-observational-memory";
177
+ import { isEligibleObservationSourceEntry } from "@arnilo/prism-memory/compaction/observational-memory";
178
178
 
179
179
  function funnelChildMessagesToWorkspace(store: SessionStore, workspaceSessionId: string): SessionStore {
180
180
  return {
@@ -158,6 +158,8 @@ Skill selection grants no tool access and cannot bypass permissions — a skill'
158
158
 
159
159
  ### Progressive skill disclosure
160
160
 
161
+ Skills apply the same progressive-disclosure discipline as plan 041's [tool disclosure](tools.md) (toolsDisclosure "search") — one mechanism applied to skills (prompt text) and tools (provider tool arrays).
162
+
161
163
  `skillsDisclosure` on `AgentConfig` / `RunOptions` (`"progressive"` default, `"eager"` opt-in; run wins) controls how active skills render in provider input:
162
164
 
163
165
  | Mode | Provider view per active skill |
package/docs/core.md ADDED
@@ -0,0 +1,85 @@
1
+ # Core Runtime, Sessions, and Governance (@arnilo/prism-core)
2
+
3
+ The `@arnilo/prism-core` family package unifies Prism's privileged runtime, sessions, governance, credentials, enterprise persistence, and work integrations into explicit, import-isolated subpaths.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @arnilo/prism @arnilo/prism-core
9
+ ```
10
+
11
+ For database persistence and distributed event streams, install the required optional peer dependencies:
12
+
13
+ ```bash
14
+ # SQLite sessions & prompt storage
15
+ npm install better-sqlite3
16
+
17
+ # PostgreSQL sessions, enterprise persistence & prompt storage
18
+ npm install pg
19
+
20
+ # NATS JetStream distributed event source
21
+ npm install @nats-io/jetstream @nats-io/transport-node
22
+ ```
23
+
24
+ ## Subpaths Map
25
+
26
+ | Subpath | Description | Optional Peers |
27
+ |---|---|---|
28
+ | `@arnilo/prism-core/runtime/server` | HTTP server handler, SSE streaming, artifact delivery, replay, webhook delivery | — |
29
+ | `@arnilo/prism-core/runtime/supervisor` | Agent-to-Agent (A2A) protocol server, client, event source, and multi-agent supervisor | — |
30
+ | `@arnilo/prism-core/runtime/workflows` | Multi-step DAG workflow coordinator, saga recovery, checkpoints, and loop nodes | — |
31
+ | `@arnilo/prism-core/sessions/codecs` | Checkpoint, cursor, feedback, and search serialization codecs | — |
32
+ | `@arnilo/prism-core/sessions/sqlite` | SQLite session store, leases, lifecycle, and schema migrations | `better-sqlite3` |
33
+ | `@arnilo/prism-core/sessions/postgres` | PostgreSQL session store, event source, and migrations | `pg` |
34
+ | `@arnilo/prism-core/sessions/nats` | NATS JetStream distributed event source | `@nats-io/jetstream`, `@nats-io/transport-node` |
35
+ | `@arnilo/prism-core/governance/policy` | Capability admission, tool execution approvals, audit log exporter, and OPA evaluator | — |
36
+ | `@arnilo/prism-core/governance/evals` | Offline evaluation runs, scorers, judges, threshold assertions, and trace curation | — |
37
+ | `@arnilo/prism-core/governance/prompts` | Versioned prompt registry, promotion gating, rollback, and storage | `better-sqlite3`, `pg` |
38
+ | `@arnilo/prism-core/governance/model-router` | Cost- and latency-aware model routing, token reservations, and failover | — |
39
+ | `@arnilo/prism-core/governance/observability` | OpenTelemetry instrumentation and event tracing | `@opentelemetry/api` |
40
+ | `@arnilo/prism-core/credentials/node` | Keyring-backed encrypted credential store, scrypt envelope encryption, OAuth2 PKCE providers, and OIDC identity verification | `@napi-rs/keyring` (bundled) |
41
+ | `@arnilo/prism-core/enterprise/postgres` | Unified multi-tenant enterprise PostgreSQL state (approvals, evaluations, model-router, policy, tool effects, work idempotency) | `pg` |
42
+ | `@arnilo/prism-core/integrations/work` | Microsoft 365 and Google Workspace CLI tool adapters with approval gates and idempotency | — |
43
+ | `@arnilo/prism-core/validation/json-schema` | Ajv-backed JSON Schema tool argument validation | `ajv` (bundled) |
44
+
45
+ ## Usage Examples
46
+
47
+ ### Workflow Runtime
48
+ ```ts
49
+ import { createWorkflowCoordinator, defineWorkflow, functionNode } from "@arnilo/prism-core/runtime/workflows";
50
+
51
+ const wf = defineWorkflow({
52
+ name: "order-processing",
53
+ initial: "validate",
54
+ nodes: {
55
+ validate: functionNode(async ({ input }) => ({ next: "process", output: input })),
56
+ },
57
+ });
58
+ ```
59
+
60
+ ### Policy & Approvals
61
+ ```ts
62
+ import { createMemoryApprovalStore, evaluateApproval } from "@arnilo/prism-core/governance/policy";
63
+
64
+ const approvals = createMemoryApprovalStore();
65
+ ```
66
+
67
+ ### SQLite Sessions
68
+ ```ts
69
+ import { createSqlitePersistence } from "@arnilo/prism-core/sessions/sqlite";
70
+
71
+ const persistence = createSqlitePersistence({ filename: "./prism.db" });
72
+ ```
73
+
74
+ ### JSON Schema Validation
75
+ ```ts
76
+ import { createJsonSchemaToolArgumentValidator } from "@arnilo/prism-core/validation/json-schema";
77
+
78
+ const validator = createJsonSchemaToolArgumentValidator();
79
+ ```
80
+
81
+ ## Security & Import Isolation
82
+
83
+ - Subpaths never load database drivers (`pg`, `better-sqlite3`) unless the specific database subpath is imported.
84
+ - All database and network drivers fail closed with clear actionable error messages when peers are omitted.
85
+ - Root `@arnilo/prism` remains dependency-free contracts and CLI runner.
@@ -199,7 +199,7 @@ import {
199
199
  createKeychainCredentialStore,
200
200
  createStoredCredentialResolver,
201
201
  } from "@arnilo/prism-credentials-node";
202
- import { createOpenAIProviderPackage } from "@arnilo/prism-provider-openai";
202
+ import { createOpenAIProviderPackage } from "@arnilo/prism-providers/openai";
203
203
 
204
204
  const keychain = createKeychainCredentialStore({
205
205
  service: "com.example.my-app",
@@ -10,6 +10,8 @@ Plan 056 Task 1 adds dialect-neutral shared primitives under `@arnilo/prism/test
10
10
 
11
11
  Release 0.0.23 additionally ships [`@arnilo/prism-enterprise-postgres`](enterprise-postgres-state.md), a separate PostgreSQL composition for policy decisions, evaluations, work-mutation claims, and model-router state. It is not a `ProductionPersistenceStore` replacement and does not store sessions/runs. Its fixed `prism_enterprise_migrations` history is independent of `prism_migrations`; hosts may use both compositions against the same validated schema.
12
12
 
13
+ The optional [`@arnilo/prism-prompts`](prompt-registry.md) package is another independent persistence surface: its SQLite/PostgreSQL adapters own `prism_prompts`, `prism_prompt_labels`, and `prism_prompt_migrations`, apply a checked `001_init` history, and filter every read/write by exact prompt ownership. It does not extend the shared session schema or place prompt bodies in run/session metadata.
14
+
13
15
  ## When to use it
14
16
 
15
17
  Use these contracts when you write a database-backed `SessionStore` or a separate persistence adapter that needs:
@@ -80,6 +82,8 @@ Metadata CAS (0.2.2): the write seam accepts an additive `expectedVersion` guard
80
82
 
81
83
  Artifact co-work review (0.0.14) reuses the generic `CheckpointStore` rather than adding a dedicated table: the [artifact service](work-artifacts-and-review.md) stores each artifact as a versioned checkpoint value (namespace `prism.artifact`, key `threadId:artifactId`, category `artifact`). The checkpoint `version` is the compare-and-swap counter that resolves concurrent reviewers; revision numbers, approvals, and `lastValidatedVersion` live inside the JSON value. SQLite/Postgres already persist checkpoints durably, so there is no separate artifact schema or migration, and records carry metadata/hashes/refs only — never file bodies.
82
84
 
85
+ Run prompt provenance (plan 042): `RunRecord` gains an optional typed `promptVersion` ref (`{ name, version, hash }`) that the first-party adapters persist as a nullable `prompt_version` JSON column on run rows (schema migration `009_run_prompt_version`, shared schema version 9). Rows written before the migration stay `NULL` and read back without the field; a host that never sets `RunOptions.promptVersion` sees byte-identical rows. The ref is an opaque identity (`sha256:` body hash), never prompt content, and flows through the same ledger redaction as every other run field. Prompt bodies themselves live only in the separate [prompt registry tables](prompt-registry.md) — never in run rows or run metadata.
86
+
83
87
  ## Outputs / response / events
84
88
 
85
89
  Each `query*` method returns a `PersistencePage<T>`:
@@ -0,0 +1,103 @@
1
+ # Dev inspector
2
+
3
+ ## What it does
4
+
5
+ `@arnilo/prism-dev` is a **loopback-only local dev inspector** over a host's already-configured Prism agent — the `prism dev` playground server (plan 040). It is a **composition-only consumer**: it adds zero core primitives and imports no core internals. Every capability is an existing public seam consumed verbatim:
6
+
7
+ - `@arnilo/prism-server` `createPrismHandler` — direct `POST /prism/agents/:id/runs` and SSE `POST /prism/agents/:id/stream` agent routes, authorization, ownership propagation, and the durable `Last-Event-ID` event route when an exposure carries `events` + `resolveRun`.
8
+ - Core durable `AgentEventSource` contract (`page`/`subscribe`) — replay and reconnect without re-execution.
9
+ - `@arnilo/prism-ag-ui/renderer` — event projection for the served UI page (plan 040 Task 3).
10
+ - Run-ledger records (`RunRecord`/`AgentEventRecord`/`ToolCallRecord`/`UsageRecord`) surfaced only through the seams above — the package never touches a ledger.
11
+ - Pending-decision resume through the server's fail-closed decision validation (plan 040 Task 2) — the inspector adds none.
12
+
13
+ ## When to use it
14
+
15
+ Use it when iterating on prompts in a local Prism host and you want a inspectable timeline (events, tool calls, usage, HITL decisions, run replay) instead of building your own trace viewer. Do not deploy it: it is a developer-time surface, intentionally omitted from `@arnilo/prism-all` and the profile packages, and it must never be the production API boundary — that stays `@arnilo/prism-server` under host authorization.
16
+
17
+ ### Quickstart — `prism dev` (plan 040 Task 4)
18
+
19
+ ```bash
20
+ npm install --save-dev @arnilo/prism-dev
21
+ cd my-agent && npm run dev # → prism dev → http://127.0.0.1:4311
22
+ ```
23
+
24
+ `prism dev` (and the standalone `prism-dev` bin, plus the programmatic `runDevCli` from `@arnilo/prism-dev/cli`) boots the inspector over the current `prism init` scaffold: it imports `dist/agent.js` and calls its `createAppAgent()` export — the scaffold's own agent, with its own credentials. It defaults to `127.0.0.1:4311`, prints the loopback URL once listening (start-to-listen under 1s excluding provider network), and `Ctrl+C` drains and closes. A non-loopback `--host` is refused before binding (`ERR_PRISM_DEV_REMOTE_BIND`); the CLI never reads environment secrets itself. See `docs/cli-rpc.md` for the flag table.
25
+
26
+ ## Inputs / request
27
+
28
+ `createPrismDevInspector(options)`:
29
+
30
+ | Field | Purpose |
31
+ | --- | --- |
32
+ | `agent` | Required host-built `Agent` (mock or provider-backed). The inspector never constructs the agent or reads credentials. |
33
+ | `eventSource` | Optional durable `AgentEventSource`; opts the server exposure into durable event routes, SSE reconnect, and the paged replay endpoint. Requires `resolveRun`. |
34
+ | `resolveRun` | Required with `eventSource`: resolves a public run selector to exact internal session/run IDs. Refusing a selector (foreign/unknown run) fails closed with `404`. |
35
+ | `checkpoints` | Optional host checkpoint store backing the agent's `runState`; wiring it enables the durable status/resume capability behind the decision endpoint. Host-owned — the inspector only composes the core lifecycle seam (`createAgentRunLifecycle`) over the host's own agent. |
36
+ | `definitionRevision` | Definition revision declared for the lifecycle resolve; default `"1"`. |
37
+ | `authorize` | Optional per-operation authorizer. Loopback default: single synthetic local user (`local`, ownership `tenantId`/`userId` both `local` so durable event scoping passes); request JSON can never widen ownership (server seam enforces). |
38
+ | `host` | Bind host, default `127.0.0.1`. **Non-loopback fails closed** unless `remoteAuthorize` resolves `true` and a real `authorize` callback is supplied. |
39
+ | `port` | Default `4311`; `0` picks an ephemeral port. |
40
+ | `remoteAuthorize` | Explicit opt-in callback for a non-loopback bind, consulted by `listen()`. |
41
+ | `redactor` | Host `SecretRedactor` passed through to the server handler and the replay pager; rendered tool args/results stay host-redacted on both the SSE and replay paths. |
42
+ | `limits` / `basePath` | Server limits and route base path passthrough. |
43
+
44
+ ## Outputs / response / events
45
+
46
+ The inspector exposes `handler` (the composed `PrismRequestHandler`), `listen()`, `close()`, and once listening `url`/`host`/`port`. Boot (create + bind, excluding host model calls) stays under the 1s envelope. Configuration refusals throw `DevInspectorError` (`ERR_PRISM_DEV_INSPECTOR`, or `ERR_PRISM_DEV_REMOTE_BIND` for bind-policy failures).
47
+
48
+ Per-task surface (plan 040): Task 1 wires agent routes + bind policy; Task 2 (shipped) adds the data-defined inspector routes below; Task 3 serves the static UI page; Task 4 ships the `prism dev` CLI composition.
49
+
50
+ ## HTTP surface (plan 040 Task 2)
51
+
52
+ Data-defined route table over the server seam — each route either rewrites the URL into the already conformance-tested `PrismRequestHandler` or pages the durable event source. Unmatched requests forward unchanged to the raw `/{basePath}/*` server surface on the same listener.
53
+
54
+ | Route | Purpose | Adapts to |
55
+ | --- | --- | --- |
56
+ | `POST /prompt` | Runs the agent (direct run). | server handler direct agent run. |
57
+ | `GET /events?runId=<id>` | Durable SSE stream of normalized events. `Last-Event-ID` header reconnect and `?cursor=` are honored by the server seam; missing `runId` → `400 ERR_PRISM_DEV_ROUTE`. |
58
+ | `GET /runs/:id/replay?cursor=…` | Paged replay of a stored run from the durable `AgentEventSource` — **no session, no provider, no re-execution** (`createPrismAgentEventReplay` page). Returns `{ items, nextCursor?, terminal }`; unknown/foreign run ids → `404`. |
59
+ | `POST /runs/:runId/decisions/:decisionId` | Resumes/denies one suspended approval. Body `{ outcome: "allow_once" \| "allow_always" \| "deny", expectedVersion? }` → forwarded as a single-entry core decision batch; unknown discriminants and stale versions fail closed (`400`) at the core boundary **before any state write**. |
60
+
61
+ Reconnect semantics: every SSE frame carries `id: <cursor>`; a reconnecting client sends `Last-Event-ID: <cursor>` and receives exactly the post-cursor events — no duplicates, no loss (server conformance-tested). Replay pages are bounded by the deployment limits (`maxReplayEvents`, `maxReplayCursorBytes`) and ownership-scoped by the source seam itself.
62
+
63
+ ## UI walkthrough (plan 040 Task 3)
64
+
65
+ Opening the inspector URL serves one static page (`GET /` → `page` + `GET /assets/inspector.js`; bootstrap via same-origin `GET /config` → `{ basePath, agentId }`). No external fetches — the bundle is offline-capable, served with a strict CSP (`default-src 'none'; script-src 'self'; connect-src 'self'`, `nosniff`, `no-store`), and every dynamic payload reaches the DOM through text nodes only (redacted strings render as-is, never parsed as markup).
66
+
67
+ Panels:
68
+
69
+ - **Prompt box** — `POST {basePath}/agents/{id}/stream` (server SSE seam); frames arrive as redacted `AgentEvent` JSON and fold into the timeline live.
70
+ - **Event timeline** — message deltas merged into per-stream text items, thinking separately, turn boundaries as separators. Rows render incrementally through a **windowed list** (last `MAX_RENDERED_WINDOW` = 400 rows; older rows collapse into a counter line) so 1k+ event runs never lock the page. Tool calls are expandable `<details>`: streamed args, finished results, blocked/error state.
71
+ - **Usage** — per-run totals summed from `provider_turn_finished.usage` and the terminal `agent_finished.usage` (input/output/total tokens, cost when the model reports it).
72
+ - **Decisions** — `agent_suspended` renders one card per pending decision (`PendingDecision.approvalId`, tool name, redacted reason, `expectedVersion` from the event's run version). Buttons post `POST /runs/:runId/decisions/:approvalId` ({ outcome: `allow_once` | `allow_always` | `deny`, expectedVersion }); rejections show the seam's fail-closed error verbatim, and a remaining-multi-decision suspension re-renders from the response's `runState.interruption`.
73
+ - **Run selector** — session runs (live + loaded) with status; a durable view of any past run loads via `GET {basePath}/events?runId=…` over `EventSource` — the seam's own `Last-Event-ID` reconnect applies. Without a durable event source wired, loading by runId surfaces that fact instead of pretending to replay.
74
+
75
+ ## Request/response example
76
+
77
+ ```json
78
+ POST /prompt
79
+ { "input": "Summarize the release notes" }
80
+ ```
81
+
82
+ Suspended approval surfaced by that response (`runState.interruption.pendingDecisions`) resumes via:
83
+
84
+ ```json
85
+ POST /runs/<runId>/decisions/<approvalId>
86
+ { "outcome": "allow_once", "expectedVersion": 1 }
87
+ ```
88
+
89
+ ## Implementation example
90
+
91
+ ```ts
92
+ import { createPrismDevInspector } from "@arnilo/prism-dev";
93
+
94
+ const inspector = createPrismDevInspector({
95
+ agent, // host-built agent (mock or provider-backed)
96
+ eventSource, // optional durable AgentEventSource for replay
97
+ host: "127.0.0.1",
98
+ port: 4311,
99
+ });
100
+ await inspector.listen(); // http://127.0.0.1:4311 — loopback only
101
+ ```
102
+
103
+ Loopback policy: default bind is `127.0.0.1:4311`; a non-loopback bind is refused unless an explicit `remoteAuthorize` callback opts in and a real `authorize` callback is supplied; loopback default authorization is one synthetic local user; the inspector stores no secrets and never reads `process.env` for credentials.
@@ -0,0 +1,247 @@
1
+ # Diagramming, draw.io embed client, and mxGraph XML validation (`@arnilo/prism-office/diagrams`)
2
+
3
+ ## What it does
4
+
5
+ The `@arnilo/prism-office/diagrams` package provides an origin-enforced draw.io / diagrams.net iframe embed client, XXE-safe mxGraph XML validation, and byte-stable deterministic XML canonicalization for content hashing and visual artifact workflows in Prism applications and agent runtimes.
6
+
7
+ ### Core Capabilities
8
+
9
+ - **Origin-Enforced Embed Client**: `createDrawioEmbed({ iframe, origin })` establishes a typed, secure postMessage bridge between host applications and embedded draw.io editor iframes adhering to the diagrams.net `proto=json` embed protocol.
10
+ - **Dual Inbound Verification Boundary**: Inbound `message` events are verified against **both** the configured origin and `iframe.contentWindow` prior to JSON parsing. Foreign origins, rogue windows, and malformed frames are dropped immediately at the boundary.
11
+ - **Strict Outbound Target Enforcement**: Prohibits wildcard `targetOrigin: "*"` on all outbound actions. Every postMessage transmits exclusively to the exact validated origin.
12
+ - **No Public SaaS Default**: Construction requires an explicit, validated origin string (`https://` or `http://`). No default fallback to public SaaS endpoints (`embed.diagrams.net`) exists; self-hosting is the documented deployment.
13
+ - **XXE & Billion-Laughs Defenses**: `validateDrawioXml` rejects DOCTYPE and ENTITY declarations up-front (`UNSAFE_XML_DECLARATION_PATTERN = /<!\s*(?:DOCTYPE|ENTITY)/i`) with `ERR_PRISM_DIAGRAMS_XXE`, and operates with XML parser entity and HTML entity expansion disabled under strict element, attribute, and byte caps.
14
+ - **Deterministic XML Canonicalization**: `canonicalizeDrawioXml` sorts element attributes lexicographically (`a-z`), standardizes double quotes and entity escaping, and normalizes insignificant whitespace while preserving document sequence, producing byte-identical outputs for SHA-256 content hashing.
15
+ - **Visio Format Exclusion (P12 Guard)**: Detects and rejects Microsoft Visio binary (`.vsd`) and OpenXML (`.vsdx`) inputs across all entrypoints with `ERR_PRISM_DIAGRAMS_UNSUPPORTED_FORMAT`.
16
+ - **Dependency-Free Telemetry Seam (P15)**: Pluggable `DiagramsTelemetry` interface emits `diagrams.validate` and `diagrams.canonicalize` spans tracking byte sizes, element/cell counts, and durations without leaking diagram contents or node text.
17
+
18
+ ## When to use it
19
+
20
+ Use `@arnilo/prism-office/diagrams` when applications, host workspaces, or autonomous agents need to:
21
+ 1. Embed an interactive, self-hosted draw.io / diagrams.net editor inside a web or Electron iframe with strictly enforced cross-origin security.
22
+ 2. Coordinate diagram editing lifecycles (`init` handshake, `load`, `save`, `autosave`, `merge`, and `export`).
23
+ 3. Execute save-with-preview workflows generating SVG (`xmlsvg`) or PNG (`xmlpng`) visual snapshots from the active editor session.
24
+ 4. Validate untrusted agent-generated or user-uploaded mxGraph XML models against structural and memory boundaries before persistence.
25
+ 5. Compute deterministic content hashes (`sha256(canonicalizeDrawioXml(xml))`) for version control, caching, and change detection.
26
+
27
+ Do **not** use this package for:
28
+ - Microsoft Visio format conversion (`.vsd` / `.vsdx` files are explicitly excluded and rejected per P12).
29
+ - Server-side headless diagram rendering without an editor instance (use headless browser automation or containerized export services).
30
+
31
+ ## Inputs / request
32
+
33
+ ### Primary Functions
34
+
35
+ | Function | Signature | Description |
36
+ | --- | --- | --- |
37
+ | `createDrawioEmbed` | `(options: DrawioEmbedOptions) => DrawioEmbed` | Creates an origin-enforced embed client bound to an iframe element or structural frame. |
38
+ | `validateDrawioXml` | `(xml: string \| Uint8Array, options?: DrawioXmlOptions) => DrawioModelSummary` | Validates mxGraph XML well-formedness, caps, and structure, extracting page/cell/edge metrics. |
39
+ | `canonicalizeDrawioXml` | `(xml: string \| Uint8Array, options?: DrawioCanonicalizeOptions) => string` | Produces byte-stable, attribute-sorted, whitespace-normalized XML for content hashing. |
40
+ | `validateDiagramsOrigin` | `(origin: unknown) => string` | Validates that an origin string is a valid `https:` or `http:` URL origin without paths, queries, hashes, or wildcards. |
41
+ | `assertNotVisio` | `(input: string \| Uint8Array) => void` | Asserts that input is not a Microsoft Visio file, throwing `DiagramsFormatError` if Visio signatures are detected. |
42
+
43
+ ### Message & Options Types
44
+
45
+ ```ts
46
+ export interface DrawioEmbedOptions {
47
+ readonly iframe: DrawioEmbedFrame;
48
+ readonly origin: string;
49
+ readonly messageSource?: DrawioMessageSource;
50
+ readonly onProtocolError?: (error: DiagramsProtocolError) => void;
51
+ readonly defaultExportTimeoutMs?: number;
52
+ }
53
+
54
+ export interface DrawioLoadOptions {
55
+ readonly xml: string;
56
+ readonly autosave?: boolean;
57
+ readonly saveAndExit?: boolean;
58
+ readonly noSaveBtn?: boolean;
59
+ readonly noExitBtn?: boolean;
60
+ readonly title?: string;
61
+ }
62
+
63
+ export interface DrawioExportOptions {
64
+ readonly format: "xml" | "xmlsvg" | "xmlpng" | "json" | "png" | "svg";
65
+ readonly scale?: number;
66
+ readonly border?: number;
67
+ readonly xml?: string;
68
+ readonly embedImages?: boolean;
69
+ readonly timeoutMs?: number;
70
+ }
71
+
72
+ export interface DrawioXmlCaps {
73
+ readonly maxBytes?: number;
74
+ readonly maxElements?: number;
75
+ readonly maxAttributes?: number;
76
+ }
77
+ ```
78
+
79
+ ### Capacity Limits and Defaults
80
+
81
+ | Cap | Default | Hard Ceiling | Description |
82
+ | --- | --- | --- | --- |
83
+ | `maxBytes` | 32 MiB (`33,554,432`) | 512 MiB (`536,870,912`) | Maximum input XML string or byte length. |
84
+ | `maxElements` | 100,000 | 500,000 | Maximum total XML element count in diagram tree. |
85
+ | `maxAttributes` | 500,000 | 2,000,000 | Maximum total XML attribute count across elements. |
86
+ | `defaultExportTimeoutMs` | 30,000 ms | Unlimited | Timeout waiting for editor export responses. |
87
+
88
+ ## Outputs / response / events
89
+
90
+ ### Error Hierarchy
91
+
92
+ All error classes extend `DiagramsError` and carry structured `ERR_PRISM_DIAGRAMS_*` error codes:
93
+
94
+ | Error Class | Code | Cause / Trigger |
95
+ | --- | --- | --- |
96
+ | `DiagramsOriginError` | `ERR_PRISM_DIAGRAMS_ORIGIN_INVALID` | Origin is empty, wildcard (`*`), malformed URL, or contains forbidden path/query/hash components. |
97
+ | `DiagramsProtocolError` | `ERR_PRISM_DIAGRAMS_PROTOCOL` | Inbound message failed JSON parsing, missing event discriminator, or postMessage issued without contentWindow. |
98
+ | `DiagramsXxeError` | `ERR_PRISM_DIAGRAMS_XXE` | XML input contains forbidden DOCTYPE or ENTITY declaration. |
99
+ | `DiagramsCapError` | `ERR_PRISM_DIAGRAMS_XML_CAP` | XML input exceeds byte length, total element count, or total attribute count caps. |
100
+ | `DiagramsXmlMalformedError` | `ERR_PRISM_DIAGRAMS_XML_MALFORMED` | XML input is truncated or violates XML well-formedness rules. |
101
+ | `DiagramsModelInvalidError` | `ERR_PRISM_DIAGRAMS_XML_INVALID_MODEL` | XML root is neither `<mxfile>` nor `<mxGraphModel>`. |
102
+ | `DiagramsFormatError` | `ERR_PRISM_DIAGRAMS_UNSUPPORTED_FORMAT` | Visio format (.vsd/.vsdx) detected in input (P12). |
103
+ | `DiagramsTimeoutError` | `ERR_PRISM_DIAGRAMS_TIMEOUT` | Export operation timed out waiting for editor response. |
104
+
105
+ ### Output Types
106
+
107
+ ```ts
108
+ export interface DrawioModelSummary {
109
+ readonly pages: number;
110
+ readonly cells: number;
111
+ readonly edges: number;
112
+ readonly width?: number;
113
+ readonly height?: number;
114
+ readonly compressed?: boolean;
115
+ }
116
+
117
+ export interface DrawioExportResult {
118
+ readonly format: string;
119
+ readonly data: string;
120
+ readonly xml?: string;
121
+ readonly bounds?: DrawioExportBounds;
122
+ }
123
+ ```
124
+
125
+ ## Request/response example
126
+
127
+ ### Protocol Envelope (`proto=json`)
128
+
129
+ Inbound editor-to-host `message` event:
130
+ ```json
131
+ {
132
+ "event": "save",
133
+ "xml": "<mxfile host=\"drawio.internal\"><diagram id=\"1\">...</diagram></mxfile>",
134
+ "exit": false
135
+ }
136
+ ```
137
+
138
+ Outbound host-to-editor action postMessage:
139
+ ```json
140
+ {
141
+ "action": "load",
142
+ "xml": "<mxfile host=\"drawio.internal\"><diagram id=\"1\">...</diagram></mxfile>",
143
+ "autosave": 1,
144
+ "title": "System Architecture"
145
+ }
146
+ ```
147
+
148
+ ## Implementation example
149
+
150
+ ```ts
151
+ import { createDrawioEmbed, validateDrawioXml, canonicalizeDrawioXml } from "@arnilo/prism-office/diagrams";
152
+
153
+ // 1. Initialize embed client with strict origin binding
154
+ const embed = createDrawioEmbed({
155
+ iframe: document.getElementById("drawio-frame") as HTMLIFrameElement,
156
+ origin: "https://drawio.internal.example",
157
+ onProtocolError(error) {
158
+ console.error("Protocol error:", error.message);
159
+ },
160
+ });
161
+
162
+ // 2. Register typed event listeners
163
+ embed.on("init", () => {
164
+ const initialXml = `<mxfile host="drawio.internal"><diagram id="d1" name="Architecture"><mxGraphModel dx="800" dy="600"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" value="Core Agent" vertex="1" parent="1"><mxGeometry x="100" y="100" width="120" height="60" as="geometry"/></mxCell></root></mxGraphModel></diagram></mxfile>`;
165
+ embed.load({ xml: initialXml, autosave: true });
166
+ });
167
+
168
+ embed.on("save", async ({ xml, exit }) => {
169
+ // Validate model before persisting
170
+ const summary = validateDrawioXml(xml);
171
+ console.log(`Validated diagram with ${summary.cells} cells and ${summary.edges} edges`);
172
+
173
+ // Compute canonical hash for content-addressed storage
174
+ const canonical = canonicalizeDrawioXml(xml);
175
+ console.log("Canonical XML ready for persistence");
176
+
177
+ if (exit) {
178
+ console.log("Editor exit requested by user");
179
+ }
180
+ });
181
+
182
+ embed.on("autosave", ({ xml }) => {
183
+ console.log("Draft autosaved:", xml.length, "bytes");
184
+ });
185
+
186
+ // 3. Save-with-preview flow: export SVG snapshot
187
+ const preview = await embed.export({ format: "xmlsvg" });
188
+ console.log("Exported preview format:", preview.format, "data:", preview.data.slice(0, 40));
189
+ ```
190
+
191
+ ## Extension and configuration notes
192
+
193
+ ### Self-Hosted draw.io Deployment
194
+
195
+ The recommended and supported deployment is a self-hosted instance of the Apache-2.0 `jgraph/drawio` container:
196
+
197
+ ```bash
198
+ docker run -d -p 8080:8080 -e DRAWIO_SERVER_URL="http://localhost:8080" jgraph/drawio
199
+ ```
200
+
201
+ Iframe embed URLs are formed with query parameters configuring json protocol mode:
202
+ ```text
203
+ http://localhost:8080/?embed=1&proto=json&spin=1
204
+ ```
205
+
206
+ ### Decoupled Structural DOM Interface
207
+
208
+ `DrawioEmbedFrame` is typed structurally:
209
+ ```ts
210
+ export interface DrawioEmbedFrame {
211
+ readonly contentWindow: {
212
+ postMessage(message: unknown, targetOrigin: string): void;
213
+ } | null;
214
+ }
215
+ ```
216
+ This enables use with vanilla DOM elements, React/Svelte/Vue refs, Electron webviews, and headless test doubles without requiring browser globals or `@types/dom`.
217
+
218
+ ### OpenTelemetry Telemetry Seam
219
+
220
+ Pass an optional `DiagramsTelemetry` implementation to record spans without runtime overhead:
221
+ ```ts
222
+ const summary = validateDrawioXml(xml, {
223
+ telemetry: {
224
+ startSpan(name, attributes) {
225
+ // Maps to OTel tracer.startSpan("diagrams.validate", { attributes })
226
+ // Diagram text and labels are NEVER passed to spans.
227
+ return activeSpan;
228
+ },
229
+ },
230
+ });
231
+ ```
232
+
233
+ ## Security and performance notes
234
+
235
+ - **Trust Boundary Placement**: Origin and `event.source` checks execute inside the embed client before JSON parsing. Rogue cross-origin messages and foreign window messages are dropped without triggering listeners or error handlers.
236
+ - **Wildcard Prohibition**: `targetOrigin: "*"` is blocked at construction and runtime. Outbound messages are posted exclusively to the verified origin.
237
+ - **XXE Prevention**: DOCTYPE and ENTITY declarations are rejected up-front by regex pre-checks; `fast-xml-parser` is configured with entity expansion disabled.
238
+ - **Memory Caps**: Progressive limits on byte size, element counts, and attribute counts prevent XML decompression bombs and heap exhaustion.
239
+ - **Single-Pass Performance**: Canonicalization and validation process 1 MB XML models in under 10 ms.
240
+
241
+ ## Related APIs
242
+
243
+ - [`@arnilo/prism-office/documents`](./documents.md): Specification-compliant OpenXML document generation and preview rendering for DOCX, XLSX, and PPTX.
244
+ - [`@arnilo/prism-office/sheets`](./sheets.md): Spreadsheet and CSV parsing engine with strict financial decimal safety guarantees.
245
+ - [`@arnilo/prism-web-tools/browser`](./browser-automation.md): Browser automation tools and quarantine lifecycle.
246
+ - [`@arnilo/prism-ag-ui`](./ag-ui.md): Agent-User Interface projection and timeline components.
247
+ - [`@arnilo/prism-observability-opentelemetry`](./observability.md): OpenTelemetry instrumentation and trace adapters.