@arnilo/prism 0.3.1 → 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 (123) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +34 -57
  3. package/dist/agent-definitions.js +4 -1
  4. package/dist/agent-run-lifecycle.js +4 -0
  5. package/dist/agent-run-state.d.ts +4 -0
  6. package/dist/agent-run-state.js +18 -5
  7. package/dist/agent-session/session.d.ts +7 -0
  8. package/dist/agent-session/session.js +59 -2
  9. package/dist/cli-dev.d.ts +29 -0
  10. package/dist/cli-dev.js +52 -0
  11. package/dist/cli-init.d.ts +17 -2
  12. package/dist/cli-init.js +194 -21
  13. package/dist/cli-runner.d.ts +5 -1
  14. package/dist/cli-runner.js +12 -1
  15. package/dist/contracts-core/agent.d.ts +29 -2
  16. package/dist/contracts-protocol.d.ts +18 -0
  17. package/dist/contracts-run-state.d.ts +1 -2
  18. package/dist/index.d.ts +3 -1
  19. package/dist/index.js +2 -1
  20. package/dist/input.d.ts +8 -0
  21. package/dist/input.js +4 -0
  22. package/dist/rpc.d.ts +4 -1
  23. package/dist/rpc.js +5 -1
  24. package/dist/testing/persistence-schema.d.ts +1 -1
  25. package/dist/testing/persistence-schema.js +32 -28
  26. package/dist/testing/tool-conformance.d.ts +25 -0
  27. package/dist/testing/tool-conformance.js +128 -1
  28. package/dist/tool-search.d.ts +76 -0
  29. package/dist/tool-search.js +199 -0
  30. package/docs/0.1.0-readiness.md +2 -2
  31. package/docs/acp-agent.md +1 -1
  32. package/docs/agent-definitions.md +1 -1
  33. package/docs/antigravity-agent.md +1 -1
  34. package/docs/browser-automation.md +5 -5
  35. package/docs/caveman.md +2 -2
  36. package/docs/cli-rpc.md +26 -3
  37. package/docs/coding-agent-tools.md +7 -1
  38. package/docs/coding-security.md +1 -1
  39. package/docs/coding-tools.md +82 -0
  40. package/docs/compaction-and-retry.md +5 -2
  41. package/docs/compaction-llm.md +4 -4
  42. package/docs/compaction-observational-memory.md +49 -2
  43. package/docs/context-and-skills.md +2 -0
  44. package/docs/core.md +85 -0
  45. package/docs/credential-storage.md +1 -1
  46. package/docs/database-persistence.md +4 -0
  47. package/docs/dev-inspector.md +103 -0
  48. package/docs/diagrams.md +247 -0
  49. package/docs/documents.md +213 -0
  50. package/docs/evaluations.md +35 -1
  51. package/docs/extension-authoring.md +42 -0
  52. package/docs/graft.md +3 -3
  53. package/docs/guardrails.md +1 -1
  54. package/docs/host-security.md +4 -3
  55. package/docs/impeccable.md +2 -2
  56. package/docs/index.md +34 -23
  57. package/docs/mcp-tools.md +1 -1
  58. package/docs/migrate-to-0.4.md +312 -0
  59. package/docs/migration.md +22 -0
  60. package/docs/model-routing.md +1 -1
  61. package/docs/multi-agent-patterns.md +177 -0
  62. package/docs/multimodal-content.md +1 -1
  63. package/docs/obscura.md +10 -10
  64. package/docs/openapi-tools.md +1 -1
  65. package/docs/performance.md +23 -3
  66. package/docs/persistence-credentials-multimodality-primitives.md +1 -1
  67. package/docs/policy-and-audit.md +1 -1
  68. package/docs/ponytail.md +2 -2
  69. package/docs/prompt-registry.md +106 -0
  70. package/docs/provider-caching.md +32 -32
  71. package/docs/provider-conformance.md +1 -1
  72. package/docs/provider-packages.md +19 -19
  73. package/docs/provider-primitives.md +4 -4
  74. package/docs/providers/ai-sdk.md +3 -3
  75. package/docs/providers/alibaba.md +5 -5
  76. package/docs/providers/anthropic.md +6 -6
  77. package/docs/providers/azure.md +3 -3
  78. package/docs/providers/bedrock.md +3 -3
  79. package/docs/providers/clinepass.md +3 -3
  80. package/docs/providers/deepseek.md +3 -3
  81. package/docs/providers/google.md +4 -4
  82. package/docs/providers/kimi.md +3 -3
  83. package/docs/providers/neuralwatt.md +8 -8
  84. package/docs/providers/ollama.md +3 -3
  85. package/docs/providers/openai-compatible.md +1 -1
  86. package/docs/providers/openai.md +5 -5
  87. package/docs/providers/opencode-go.md +4 -4
  88. package/docs/providers/openrouter.md +3 -3
  89. package/docs/providers/vertex.md +5 -5
  90. package/docs/providers/xai.md +3 -3
  91. package/docs/providers/zai.md +3 -3
  92. package/docs/public-contracts.md +1 -1
  93. package/docs/rag.md +5 -5
  94. package/docs/release-and-install.md +116 -50
  95. package/docs/runs-and-usage.md +14 -1
  96. package/docs/server.md +90 -1
  97. package/docs/sheets.md +229 -0
  98. package/docs/supervisors.md +9 -1
  99. package/docs/thinking-and-reasoning.md +10 -10
  100. package/docs/tool-conformance.md +27 -2
  101. package/docs/tools.md +29 -2
  102. package/docs/web-tools.md +2 -2
  103. package/docs/wiki.md +24 -10
  104. package/docs/workflow-orchestration-primitives.md +24 -0
  105. package/docs/workflows.md +102 -8
  106. package/docs/working-and-semantic-memory.md +53 -5
  107. package/package.json +10 -30
  108. package/templates/README.md +23 -0
  109. package/templates/deep-research/README.md.tmpl +47 -0
  110. package/templates/deep-research/env.example.tmpl +12 -0
  111. package/templates/deep-research/gitignore.tmpl +7 -0
  112. package/templates/deep-research/manifest.json +12 -0
  113. package/templates/deep-research/package.json.tmpl +23 -0
  114. package/templates/deep-research/src/agent.ts.tmpl +81 -0
  115. package/templates/deep-research/src/index.ts.tmpl +53 -0
  116. package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
  117. package/templates/deep-research/src/tools.ts.tmpl +86 -0
  118. package/templates/deep-research/src/types.ts.tmpl +45 -0
  119. package/templates/deep-research/src/workflow.ts.tmpl +156 -0
  120. package/templates/deep-research/tsconfig.json.tmpl +15 -0
  121. package/templates/init/manifest.json +5 -0
  122. package/templates/init/package.json.tmpl +2 -1
  123. package/templates/init/providers.json +16 -16
package/docs/caveman.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- `@arnilo/prism-caveman` is an optional package that wires [juliusbrussee/caveman](https://github.com/juliusbrussee/caveman) into Prism contribution contracts.
5
+ `@arnilo/prism-coding-tools/caveman` is an optional package that wires [juliusbrussee/caveman](https://github.com/juliusbrussee/caveman) into Prism contribution contracts.
6
6
 
7
7
  It registers upstream skills and commands, injects active level prompt slices via `InstructionInjector`, and persists level as session custom `caveman-level` entries. Import and extension `setup` without a resolvable upstream path fail closed with a bounded redacted error and register zero contributions.
8
8
 
@@ -66,7 +66,7 @@ Deactivation phrases `stop caveman` and `normal mode` clear active injection wit
66
66
  ## Implementation example
67
67
 
68
68
  ```ts
69
- import { createCavemanExtension } from "@arnilo/prism-caveman";
69
+ import { createCavemanExtension } from "@arnilo/prism-coding-tools/caveman";
70
70
  import {
71
71
  createExtensionKernel,
72
72
  createLoadSkillTool,
package/docs/cli-rpc.md CHANGED
@@ -8,6 +8,7 @@ The `prism` bin is a thin adapter over `AgentSession` plus a tiny project scaffo
8
8
  - `prism --mode json -p "prompt"`: write one normalized event envelope per line.
9
9
  - `prism --mode rpc`: read LF-delimited JSON requests from stdin and write correlated JSON responses/events to stdout.
10
10
  - `prism init <dir>`: create a minimal TypeScript project with one selected provider, `.env.example`, and one offline mock test.
11
+ - `prism dev`: boot the loopback dev inspector over the scaffolded agent (delegates into `@arnilo/prism-dev` when resolvable; plan 040 Task 4).
11
12
 
12
13
  It does not add a TUI, app tools, provider globals, extension discovery, resource discovery, or credential storage. `init` uses Node standard-library filesystem APIs and checked-in templates only — no interactive prompts or template-engine dependency.
13
14
 
@@ -22,19 +23,22 @@ Use the SDK directly when an app needs custom providers, tools, resources, crede
22
23
  ### `prism init`
23
24
 
24
25
  ```bash
25
- prism init <dir> [--provider <name>] [--with-workflows] [--with-evals] [--force]
26
+ prism init <dir> [--template <name>] [--list-templates] [--provider <name>] [--with-workflows] [--with-evals] [--force]
26
27
  ```
27
28
 
28
29
  | Flag / arg | Purpose |
29
30
  | --- | --- |
30
- | `<dir>` | Destination directory (created if missing). |
31
+ | `<dir>` | Destination directory (created if missing). Required unless `--list-templates` is specified. |
32
+ | `--template <name>` | Template starter name (`init` [default], `deep-research`). |
33
+ | `--list-templates` | List available starter templates from the templates gallery. |
31
34
  | `--provider <name>` | `mock` (default), `openai`, `openrouter`, `kimi`, `zai`, `opencode-go`, or `neuralwatt`. |
32
35
  | `--with-workflows` | Add `@arnilo/prism-workflows` and `src/workflows-example.ts`. |
33
36
  | `--with-evals` | Add `@arnilo/prism-evals` and `src/evals-example.ts`. |
34
37
  | `--force` | Overwrite generated files when the destination already exists. |
35
38
  | `-h`, `--help` | Print init usage. |
36
39
 
37
- Default generation installs only `@arnilo/prism` (mock provider). Selecting a real provider adds exactly one `@arnilo/prism-provider-*` package. Storage, telemetry, memory, and server packages are never added unless a later phase introduces an explicit flag for them. Rerunning without `--force` refuses non-empty destinations and existing generated files. `.env.example` contains placeholders only; `.gitignore` excludes `.env` and local stores.
40
+ Default generation (`init` template) installs only `@arnilo/prism` (mock provider). Selecting a real provider adds exactly one dependency: the `@arnilo/prism-providers` family package (the selected adapter imports from `@arnilo/prism-providers/<id>`). Specifying `--template deep-research` scaffolds a flagship deep research agent pipeline (`@arnilo/prism`, `@arnilo/prism-web-tools`, `@arnilo/prism-rag`, `@arnilo/prism-workflows`) with planning, attributable citations, bounded refine loops, and HITL decision clarification. Rerunning without `--force` refuses non-empty destinations and existing generated files. `.env.example` contains placeholders only; `.gitignore` excludes `.env` and local stores.
41
+
38
42
 
39
43
  ### `prism providers add` (0.1.7)
40
44
 
@@ -68,6 +72,22 @@ header ownership, secret-leak redaction, and serialized-content coverage
68
72
  against the base provider. Replace the starter model metadata and the docs
69
73
  stub with docs-verified values before publishing.
70
74
 
75
+ ### `prism dev` (0.3.2)
76
+
77
+ ```bash
78
+ prism dev [--port <n>] [--host <addr>]
79
+ ```
80
+
81
+ Runs the local dev inspector over the current `prism init` scaffold's agent. Delegation, not duplication: the subcommand resolves `@arnilo/prism-dev` from the project's own `node_modules` (falling back to the CLI's own installation) and hands over to its `runDevCli` entry; unresolvable → install hint, exit `2`.
82
+
83
+ | Flag / arg | Purpose |
84
+ | --- | --- |
85
+ | `--port <n>` | Bind port. Default `4311`. |
86
+ | `--host <addr>` | Bind host. Default `127.0.0.1`; any non-loopback value is refused before binding (`ERR_PRISM_DEV_REMOTE_BIND`) unless remote authorization is wired programmatically. |
87
+ | `-h`, `--help` | Print dev usage. |
88
+
89
+ Manifests of the negotiation are simple: the agent is loaded from the scaffold contract `dist/agent.js` exporting `createAppAgent()` (missing → "build the project first", exit `2`); the inspector binds loopback, prints `prism dev → http://127.0.0.1:<port>/…`, and `SIGINT` drains and closes (exit `0`). Credentials stay in the scaffold's agent config — the CLI never reads environment secrets itself. Scaffolded projects ship a matching `"dev": "prism dev"` npm script.
90
+
71
91
  ### Run/RPC CLI flags
72
92
 
73
93
  | Flag | Purpose |
@@ -178,7 +198,10 @@ printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' | prism --provi
178
198
  prism init my-agent
179
199
  prism init my-agent --provider openai
180
200
  prism init my-agent --provider openrouter --with-workflows --with-evals
201
+ prism init my-research --template deep-research
202
+ prism init --list-templates
181
203
  cd my-agent && npm install && npm test
204
+
182
205
  ```
183
206
 
184
207
  Programmatic hosts should use the public runtime directly:
@@ -417,6 +417,12 @@ Opt-in `ask_user_decision` for ambiguous, high-impact direction choices. Model m
417
417
  | Agent durable adapter | `validateAskUserDecisionAgentResume({ request, answer })` — same validation; **no** new `AgentRunInterruption` kinds in 0.0.11 |
418
418
 
419
419
  Custom-text caps match question defaults (2 KiB / hard 8 KiB). Options default max 6 (hard 16).
420
+ `allowCustom` defaults to `false` on **both** paths when omitted — the tool
421
+ path (`parseAllowCustom`) and the workflow suspend path
422
+ (`toAskUserDecisionSuspendData`) normalize at accept time, so the persisted
423
+ suspension always carries a boolean and survives JSON checkpoint round-trips;
424
+ a non-boolean value throws `allowCustom must be a boolean` at accept time,
425
+ never at resume time.
420
426
 
421
427
  ```ts
422
428
  import { createToolRegistry } from "@arnilo/prism";
@@ -440,7 +446,7 @@ return suspendAskUserDecision({
440
446
  question: "Ship sqlite or postgres?",
441
447
  options: [/* ≥2 with 3 pros + 3 cons each */],
442
448
  selectionMode: "single",
443
- allowCustom: false,
449
+ // allowCustom optional — defaults to false (tool-path parity)
444
450
  });
445
451
  // resumeWorkflow(..., { validateResume: createAskUserDecisionResumeValidator() })
446
452
  ```
@@ -194,7 +194,7 @@ await sandbox.close({
194
194
 
195
195
  Policies are ordinary host values: attach one globally through `createCodingTools()`/`createReadOnlyTools()`/`createSandboxCodingComposition()` or per tool. A per-tool policy overrides the shared policy. `SandboxAdapter` / `DisposableSandbox` are replaceable and host-owned; approval policy and sandboxing are separate layers. Custom remote sandboxes can implement `DisposableSandbox` without using Docker.
196
196
 
197
- `createSandboxCodingComposition()` requires `workspaceMode`. Sandbox mode auto-wires FS/list/search through `DisposableSandbox.execFile` (or host-supplied custom operations) so mutations stay on the disposable tree until export. Host mode runs every coding tool against the host cwd and reports no isolation capability (`containmentClaim` deprecated false). Sandbox shell + host FS throws unless `allowMixedWorkspaceWiring: true` (warnings + all capabilities false). Opt-in structured Git tools (`createGitTools(composition.workspaceRoot, { execFile: sandbox.execFile, commitIdentity })`) share the same tree/cwd; Prism still never pushes or opens PRs. Optional `@arnilo/prism-browser` can share the same disposable boundary: use `assertBrowserSandboxNetwork()` before browse-ready custom networks, and `createSharedSandboxBrowserOptions({ workspaceRoot, downloadsRoot, containedProxyAttestation })` so uploads/downloads align with `/workspace` and `/downloads`. Close the browser context before disposing the sandbox.
197
+ `createSandboxCodingComposition()` requires `workspaceMode`. Sandbox mode auto-wires FS/list/search through `DisposableSandbox.execFile` (or host-supplied custom operations) so mutations stay on the disposable tree until export. Host mode runs every coding tool against the host cwd and reports no isolation capability (`containmentClaim` deprecated false). Sandbox shell + host FS throws unless `allowMixedWorkspaceWiring: true` (warnings + all capabilities false). Opt-in structured Git tools (`createGitTools(composition.workspaceRoot, { execFile: sandbox.execFile, commitIdentity })`) share the same tree/cwd; Prism still never pushes or opens PRs. Optional `@arnilo/prism-web-tools/browser` can share the same disposable boundary: use `assertBrowserSandboxNetwork()` before browse-ready custom networks, and `createSharedSandboxBrowserOptions({ workspaceRoot, downloadsRoot, containedProxyAttestation })` so uploads/downloads align with `/workspace` and `/downloads`. Close the browser context before disposing the sandbox.
198
198
 
199
199
  The Docker reference adapter starts by recorded container ID/label, uses argument arrays only, mounts source read-only, populates a size-bounded tmpfs `/workspace`, drops all capabilities, enables `no-new-privileges`, runs with `--init`, and never exposes the Docker socket, privileged mode, or host PID/IPC namespaces. Image pull/build/update stays outside Prism. Protected real-Docker checks are opt-in via `PRISM_TEST_DOCKER_SANDBOX=1` with host-supplied `PRISM_TEST_DOCKER_BIN` and digest-pinned `PRISM_TEST_DOCKER_IMAGE`.
200
200
 
@@ -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.
@@ -93,6 +93,8 @@ createDefaultRetryPolicy(options?: DefaultRetryPolicyOptions): RetryPolicy
93
93
 
94
94
  `session.compact(options?)` emits `compaction_started`, runs the strategy on the current branch, runs `middleware.run("compaction", { context, result })` when middleware is configured, appends one standard `kind: "compaction"` entry under the current leaf, emits `compaction_finished`, and returns the appended result. Manual compaction rejects while a run is active.
95
95
 
96
+ > **Contract — compact at the task boundary.** `session.compact()` throws `Error("Agent session already has an active run")` while `run()`/`stream()` is in flight. Intended model: one `run()` per task, then compact. Do not design mid-run compaction. Auto-compaction (when `thresholdEntries` is set) already runs **before** provider input, not during the turn. Live demo: [`examples/autonomous-coding-loop.ts`](../examples/autonomous-coding-loop.ts) (`compact` node after execute/validate/gate).
97
+
96
98
  Auto-compaction checks at most once per `run()`, after input/model-change entries are appended and before provider input assembly. It runs only when `AgentConfig.compaction` or `RunOptions.compaction` supplies `thresholdEntries`, and it is skipped by `RunOptions.compaction: false`.
97
99
 
98
100
  `rebuildSessionContext()` detects the latest compaction entry on a branch. Its returned `entries` still contains the raw full branch, while `messages` contains only messages after the compaction boundary plus `keepEntryIds`, and `summaries` contains the compaction summary plus later summary entries.
@@ -147,11 +149,11 @@ await session.compact({ keepRecentEntries: 4 });
147
149
 
148
150
  ## Extension and configuration notes
149
151
 
150
- 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.
151
153
 
152
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.
153
155
 
154
- 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).
155
157
 
156
158
  ## Security and performance notes
157
159
 
@@ -170,6 +172,7 @@ The default strategy does not call a provider. Hosts that need model-generated s
170
172
  - [Session stores and branching](session-stores-and-branching.md): branch entries, compaction entries, and `rebuildSessionContext()` behavior.
171
173
  - [Input and prompt assembly](input-and-prompt-assembly.md): compacted summaries become default summary messages for provider input.
172
174
  - [Agent/session runtime](agent-session-runtime.md): `session.compact()`, opt-in auto-compaction, `RunOptions.retry`, and `retry_scheduled` runtime behavior.
175
+ - Example: [`examples/autonomous-coding-loop.ts`](../examples/autonomous-coding-loop.ts) — task-boundary compact after each iteration.
173
176
  - [Middleware hooks](middleware-hooks.md): `compaction` and `retry` middleware payload timing.
174
177
  - [Contribution registries](contribution-registries.md): compaction strategy and retry policy contributions.
175
178
  - [Configuration and manifests](configuration-and-manifests.md): `compactionStrategy` and `retryPolicy` manifest contribution kinds.
@@ -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 },
@@ -166,6 +166,52 @@ The runtime requires host-supplied `session`, an `appendEntry` callback bound to
166
166
 
167
167
  `createObservationalMemoryExtension()` registers only inert contributions. It does not start workers, compact sessions, read settings, resolve credentials, call providers, or execute tools/commands during setup.
168
168
 
169
+ ## Cross-session / delegation-tree recall (opt-in pattern)
170
+
171
+ Default is per-session: `attach()` + `appendEntry` bind one store/branch, and `recallObservationalMemory(entries, id)` / `createRecallMemoryTool({ getEntries })` see only the entries the host passes for that session. Supervisor children therefore produce observations the parent cannot recall. That is acceptable for v1 — the parent transcript already contains `delegate()` results, so parent OM covers milestones. There is no package primitive for a shared workspace scope (a namespaced multi-tenant store key is out of scope).
172
+
173
+ Hosts that need parent recall of child *source* work compose it themselves: wrap the shared `SessionStore.append` so eligible child messages (`isEligibleObservationSourceEntry`) are copied onto a workspace (or parent) session with a **new entry id** and that session's `sessionId`/`parentId`. Parent OM then observes those copies and mints **new** observation ids. Child OM, if attached, stays on the child session with its own ids.
174
+
175
+ ```ts
176
+ import { createId, type SessionStore } from "@arnilo/prism";
177
+ import { isEligibleObservationSourceEntry } from "@arnilo/prism-memory/compaction/observational-memory";
178
+
179
+ function funnelChildMessagesToWorkspace(store: SessionStore, workspaceSessionId: string): SessionStore {
180
+ return {
181
+ async append(entry, options) {
182
+ await store.append(entry, options);
183
+ if (entry.sessionId === workspaceSessionId) return;
184
+ if (!isEligibleObservationSourceEntry(entry)) return;
185
+ const leaf = (await store.list(workspaceSessionId)).at(-1);
186
+ await store.append({
187
+ ...entry,
188
+ id: createId("entry"),
189
+ sessionId: workspaceSessionId,
190
+ parentId: leaf?.id,
191
+ });
192
+ },
193
+ list: (sessionId) => store.list(sessionId),
194
+ get: (id) => store.get?.(id) ?? Promise.resolve(undefined),
195
+ searchSessions: (query) => store.searchSessions?.(query) ?? Promise.reject(new Error("searchSessions unsupported")),
196
+ readBranchPath: store.readBranchPath?.bind(store),
197
+ };
198
+ }
199
+ ```
200
+
201
+ Wire the wrapped store into both the parent session and each supervisor child factory (`createAgent({ store })`). Parent `attach({ appendEntry: (entry, options) => store.append(entry, options) })` and `createRecallMemoryTool({ getEntries: () => parentSession.entries() })` then see funneled child messages plus parent-minted observations. Recreate the parent session with the store `leafId` after a restart so the workspace branch is the one that received the copies. [`examples/autonomous-coding-loop.ts`](../examples/autonomous-coding-loop.ts) shows parent OM attach/compact/recall in the supervisor loop; it records child outcomes on the parent session (same recall, no extra store wrap).
202
+
203
+ Rules that keep exact-id recall unambiguous:
204
+
205
+ - Recall always takes **one** branch (`session.entries()` / `getEntries(sessionId)`). Never concatenate parent + child lists into one `recallObservationalMemory()` call.
206
+ - Copies mint a new `entry.id`. `createMemorySessionStore` rejects duplicate ids globally; JSONL/DB adapters do too.
207
+ - Do **not** rewrite the child's OM `appendEntry` onto the workspace session. After each memory append the runtime checks the entry is visible at the **child** leaf and fails closed on a session/store mismatch. Funnel messages; let parent OM observe them.
208
+ - Do **not** copy `om.*` custom entries across. Their `sourceEntryIds` point at the origin session and would dangle on the workspace branch.
209
+ - Serialize funnel copies if concurrent children share the workspace tip (the sketch's `list().at(-1)` is not a lock).
210
+
211
+ Cost: the workspace branch grows with every funneled child message; parent `compactAfterTokens` / observation-pool caps still apply but fire sooner. Keep the per-session default unless parent recall of child sources is required.
212
+
213
+ Ownership: funnel only within the `OwnershipScope` already on the parent agent/store. Child factories receive that ownership from the supervisor; do not share a store across tenants or identities. Observations never leave the store the host scoped.
214
+
169
215
  ## Security and performance notes
170
216
 
171
217
  - Recall is exact-id only; there is no semantic search, vector store, or transcript browser.
@@ -187,6 +233,7 @@ The runtime requires host-supplied `session`, an `appendEntry` callback bound to
187
233
  - [Compaction and retry policies](compaction-and-retry.md): replaceable compaction strategy boundary.
188
234
  - [LLM compaction package](compaction-llm.md): existing optional compaction-package pattern.
189
235
  - [Session stores and branching](session-stores-and-branching.md): branch entries that observational memory reads and appends to.
236
+ - [Supervisor delegation](supervisors.md): child sessions whose messages this page's opt-in funnel can copy onto a workspace branch.
190
237
  - [Extensions](extensions.md): inert registration pattern for optional package contributions.
191
238
  - [Tools](tools.md): host activation and dispatch for optional recall tool contributions.
192
239
  - [CLI/RPC](cli-rpc.md): command contributions through explicitly wired RPC hosts.
@@ -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.