@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.
- package/CHANGELOG.md +58 -0
- package/README.md +34 -57
- package/dist/agent-definitions.js +4 -1
- package/dist/agent-run-lifecycle.js +4 -0
- package/dist/agent-run-state.d.ts +4 -0
- package/dist/agent-run-state.js +18 -5
- package/dist/agent-session/session.d.ts +7 -0
- package/dist/agent-session/session.js +59 -2
- package/dist/cli-dev.d.ts +29 -0
- package/dist/cli-dev.js +52 -0
- package/dist/cli-init.d.ts +17 -2
- package/dist/cli-init.js +194 -21
- package/dist/cli-runner.d.ts +5 -1
- package/dist/cli-runner.js +12 -1
- package/dist/contracts-core/agent.d.ts +29 -2
- package/dist/contracts-protocol.d.ts +18 -0
- package/dist/contracts-run-state.d.ts +1 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/input.d.ts +8 -0
- package/dist/input.js +4 -0
- package/dist/rpc.d.ts +4 -1
- package/dist/rpc.js +5 -1
- package/dist/testing/persistence-schema.d.ts +1 -1
- package/dist/testing/persistence-schema.js +32 -28
- package/dist/testing/tool-conformance.d.ts +25 -0
- package/dist/testing/tool-conformance.js +128 -1
- package/dist/tool-search.d.ts +76 -0
- package/dist/tool-search.js +199 -0
- package/docs/0.1.0-readiness.md +2 -2
- package/docs/acp-agent.md +1 -1
- package/docs/agent-definitions.md +1 -1
- package/docs/antigravity-agent.md +1 -1
- package/docs/browser-automation.md +5 -5
- package/docs/caveman.md +2 -2
- package/docs/cli-rpc.md +26 -3
- package/docs/coding-agent-tools.md +7 -1
- package/docs/coding-security.md +1 -1
- package/docs/coding-tools.md +82 -0
- package/docs/compaction-and-retry.md +5 -2
- package/docs/compaction-llm.md +4 -4
- package/docs/compaction-observational-memory.md +49 -2
- package/docs/context-and-skills.md +2 -0
- package/docs/core.md +85 -0
- package/docs/credential-storage.md +1 -1
- package/docs/database-persistence.md +4 -0
- package/docs/dev-inspector.md +103 -0
- package/docs/diagrams.md +247 -0
- package/docs/documents.md +213 -0
- package/docs/evaluations.md +35 -1
- package/docs/extension-authoring.md +42 -0
- package/docs/graft.md +3 -3
- package/docs/guardrails.md +1 -1
- package/docs/host-security.md +4 -3
- package/docs/impeccable.md +2 -2
- package/docs/index.md +34 -23
- package/docs/mcp-tools.md +1 -1
- package/docs/migrate-to-0.4.md +312 -0
- package/docs/migration.md +22 -0
- package/docs/model-routing.md +1 -1
- package/docs/multi-agent-patterns.md +177 -0
- package/docs/multimodal-content.md +1 -1
- package/docs/obscura.md +10 -10
- package/docs/openapi-tools.md +1 -1
- package/docs/performance.md +23 -3
- package/docs/persistence-credentials-multimodality-primitives.md +1 -1
- package/docs/policy-and-audit.md +1 -1
- package/docs/ponytail.md +2 -2
- package/docs/prompt-registry.md +106 -0
- package/docs/provider-caching.md +32 -32
- package/docs/provider-conformance.md +1 -1
- package/docs/provider-packages.md +19 -19
- package/docs/provider-primitives.md +4 -4
- package/docs/providers/ai-sdk.md +3 -3
- package/docs/providers/alibaba.md +5 -5
- package/docs/providers/anthropic.md +6 -6
- package/docs/providers/azure.md +3 -3
- package/docs/providers/bedrock.md +3 -3
- package/docs/providers/clinepass.md +3 -3
- package/docs/providers/deepseek.md +3 -3
- package/docs/providers/google.md +4 -4
- package/docs/providers/kimi.md +3 -3
- package/docs/providers/neuralwatt.md +8 -8
- package/docs/providers/ollama.md +3 -3
- package/docs/providers/openai-compatible.md +1 -1
- package/docs/providers/openai.md +5 -5
- package/docs/providers/opencode-go.md +4 -4
- package/docs/providers/openrouter.md +3 -3
- package/docs/providers/vertex.md +5 -5
- package/docs/providers/xai.md +3 -3
- package/docs/providers/zai.md +3 -3
- package/docs/public-contracts.md +1 -1
- package/docs/rag.md +5 -5
- package/docs/release-and-install.md +116 -50
- package/docs/runs-and-usage.md +14 -1
- package/docs/server.md +90 -1
- package/docs/sheets.md +229 -0
- package/docs/supervisors.md +9 -1
- package/docs/thinking-and-reasoning.md +10 -10
- package/docs/tool-conformance.md +27 -2
- package/docs/tools.md +29 -2
- package/docs/web-tools.md +2 -2
- package/docs/wiki.md +24 -10
- package/docs/workflow-orchestration-primitives.md +24 -0
- package/docs/workflows.md +102 -8
- package/docs/working-and-semantic-memory.md +53 -5
- package/package.json +10 -30
- package/templates/README.md +23 -0
- package/templates/deep-research/README.md.tmpl +47 -0
- package/templates/deep-research/env.example.tmpl +12 -0
- package/templates/deep-research/gitignore.tmpl +7 -0
- package/templates/deep-research/manifest.json +12 -0
- package/templates/deep-research/package.json.tmpl +23 -0
- package/templates/deep-research/src/agent.ts.tmpl +81 -0
- package/templates/deep-research/src/index.ts.tmpl +53 -0
- package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
- package/templates/deep-research/src/tools.ts.tmpl +86 -0
- package/templates/deep-research/src/types.ts.tmpl +45 -0
- package/templates/deep-research/src/workflow.ts.tmpl +156 -0
- package/templates/deep-research/tsconfig.json.tmpl +15 -0
- package/templates/init/manifest.json +5 -0
- package/templates/init/package.json.tmpl +2 -1
- package/templates/init/providers.json +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-
|
|
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
|
|
449
|
+
// allowCustom optional — defaults to false (tool-path parity)
|
|
444
450
|
});
|
|
445
451
|
// resumeWorkflow(..., { validateResume: createAskUserDecisionResumeValidator() })
|
|
446
452
|
```
|
package/docs/coding-security.md
CHANGED
|
@@ -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-
|
|
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
|
|
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.
|
package/docs/compaction-llm.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# LLM compaction package
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
|
-
`@arnilo/prism-compaction
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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.
|