@arnilo/prism 0.2.7 → 0.2.8
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 +6 -1
- package/README.md +2 -2
- package/dist/agent-loops.js +4 -0
- package/dist/agent-run-lifecycle.js +2 -2
- package/dist/agent-session/helpers.d.ts +1 -1
- package/dist/agent-session/helpers.js +2 -1
- package/dist/agent-session/session.d.ts +1 -1
- package/dist/agent-session/session.js +28 -20
- package/dist/agent-session.d.ts +1 -1
- package/dist/agent-session.js +1 -1
- package/dist/agents.d.ts +1 -1
- package/dist/agents.js +1 -1
- package/dist/contracts-core/agent.d.ts +5 -5
- package/dist/contracts-core/agent.js +2 -0
- package/dist/contracts-core/extensions.d.ts +3 -3
- package/dist/contracts-core/extensions.js +2 -0
- package/dist/contracts-core/loop.d.ts +6 -1
- package/dist/contracts-core/session.d.ts +1 -1
- package/dist/contracts-core.d.ts +6 -6
- package/dist/contracts-core.js +6 -6
- package/dist/contracts-protocol.d.ts +8 -2
- package/dist/contracts.d.ts +1 -1
- package/dist/contracts.js +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +5 -5
- package/dist/input.js +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -1
- package/docs/0.1.0-readiness.md +7 -7
- package/docs/acp-agent.md +78 -0
- package/docs/acp.md +21 -10
- package/docs/ag-ui.md +1 -1
- package/docs/agent-definitions.md +1 -1
- package/docs/agent-events.md +2 -2
- package/docs/agent-loops.md +2 -2
- package/docs/coding-agent-tools.md +3 -1
- package/docs/coding-security.md +6 -0
- package/docs/index.md +3 -2
- package/docs/migration.md +14 -0
- package/docs/release-and-install.md +29 -8
- package/docs/structured-output.md +10 -10
- package/package.json +3 -2
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Spawnable ACP agent (`@arnilo/prism-acp-agent`)
|
|
2
|
+
|
|
3
|
+
New in 0.2.8 (plan 028 Task 10 / adoption F3). A thin binary that serves [`createPrismAcpAgent`](acp.md) over stdio from a config file — the wiring you would otherwise copy out of [`examples/acp-coding-host.ts`](../examples/acp-coding-host.ts) into every host.
|
|
4
|
+
|
|
5
|
+
## Running
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx prism-acp-agent [--config prism-acp-agent.json]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The agent speaks ACP v1 as newline-delimited JSON on `stdin`/`stdout` (SDK `ndJsonStream` adapter over `Readable.toWeb(process.stdin)` / `Writable.toWeb(process.stdout)`). It serves until the client closes stdin; an `EPIPE` on stdout (client disconnected) is a normal shutdown.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
# a config file must exist; missing/invalid config fails closed with a clear error and exit 1
|
|
15
|
+
printf '%s\n' '{"userId":"local","cwd":"/workspace"}' > prism-acp-agent.json
|
|
16
|
+
npx prism-acp-agent
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Config reference
|
|
20
|
+
|
|
21
|
+
The config file is the trust boundary: unknown keys are rejected (a typo cannot silently disable a security-relevant option), every value is shape-validated, and relative paths resolve against the config file's directory.
|
|
22
|
+
|
|
23
|
+
| Key | Required | Description |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| `userId` | yes | Ownership user id for every session (single-local-user `authorize`). |
|
|
26
|
+
| `cwd` | yes | Workspace root the coding tools are bound to (must be an existing directory). Sessions always operate on this root — a client-supplied `cwd` never moves the tools. |
|
|
27
|
+
| `sessionStore` | no | `{ "type": "sqlite", "path": ".prism/sessions.db" }` or `{ "type": "memory" }` (default). SQLite persists sessions, runs, checkpoints, and leases (`createSqlitePersistence`). |
|
|
28
|
+
| `mcp.allow` | no | MCP allow-list. http/sse servers must have a `url` starting with an allow entry; stdio servers require the marker `"stdio"`. The UNSTABLE `acp` transport is never approved. |
|
|
29
|
+
| `modes` | no | Mode table `{ "modes": [{ "id", "name", "description?" }], "defaultModeId"? }`; ids unique, `defaultModeId` must name a mode. |
|
|
30
|
+
| `configOptions` | no | `{ "options": [{ "type": "boolean" \| "select", "id", "name", "defaultValue", ... }] }`; ids unique. Select options are advertised/settable per the B3 gate (see [acp.md](acp.md)). |
|
|
31
|
+
| `limits` | no | AG-UI/ACP caps passthrough (`AgUiLimitOptions`). |
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"userId": "local",
|
|
38
|
+
"cwd": ".",
|
|
39
|
+
"sessionStore": { "type": "sqlite", "path": ".prism/sessions.db" },
|
|
40
|
+
"mcp": { "allow": ["https://mcp.example.com"] },
|
|
41
|
+
"modes": { "modes": [{ "id": "edit", "name": "Edit" }], "defaultModeId": "edit" },
|
|
42
|
+
"configOptions": [{ "type": "boolean", "id": "verbose", "name": "Verbose", "defaultValue": false }]
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## What it wires
|
|
47
|
+
|
|
48
|
+
The binary is pure wiring (~200 lines) — no protocol code lives here. It builds:
|
|
49
|
+
|
|
50
|
+
- `authorize` — single local user; every inbound call is scoped by session id.
|
|
51
|
+
- `sessionFactory` — real Prism sessions over `createAgent` with the nine coding tools (`createCodingTools(config.cwd)`), durable `runState` (`interruptBeforeTool`, checkpoints), ownership-scoped to `userId`.
|
|
52
|
+
- `lifecycle` — `createAgentRunLifecycle` over the same checkpoint store, so approvals suspend/resume durably.
|
|
53
|
+
- `mcp` — allow-list `select` gate with http/sse transports.
|
|
54
|
+
- `modes` / `configOptions` — from config.
|
|
55
|
+
- Provider — **mock by default** (full lifecycle, no tokens). Wire a real provider programmatically:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { createSpawnableAgent, loadConfig } from "@arnilo/prism-acp-agent";
|
|
59
|
+
import { createOpenAIResponsesProvider } from "@arnilo/prism-provider-openai";
|
|
60
|
+
|
|
61
|
+
const agent = createSpawnableAgent({
|
|
62
|
+
config: loadConfig("prism-acp-agent.json"),
|
|
63
|
+
provider: createOpenAIResponsesProvider({ apiKey: process.env.OPENAI_API_KEY }),
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Library surface
|
|
68
|
+
|
|
69
|
+
- `loadConfig(path)` / `parseConfig(text, baseDir)` — read + validate; throw `ConfigError` (code `PRISM_ACP_AGENT_CONFIG`) with a clear message.
|
|
70
|
+
- `createSpawnableAgent({ config, provider? })` — build the ACP `AgentApp`.
|
|
71
|
+
- `selectMcpServers(allow, servers)` — the allow-list gate, exported for reuse in custom hosts.
|
|
72
|
+
|
|
73
|
+
## Security posture
|
|
74
|
+
|
|
75
|
+
- Config file = trust boundary: validated shape, no arbitrary code execution.
|
|
76
|
+
- MCP servers only from the allow-list; the UNSTABLE `acp` transport is never bridged.
|
|
77
|
+
- Coding tools are bound to `config.cwd` only; session ownership is fixed to `userId`.
|
|
78
|
+
- Session store paths are resolved against the config directory and fail closed on invalid config.
|
package/docs/acp.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- `createPrismAcpAgent(options)` — serves ACP as an **agent**: an editor/AI client connects through the SDK transport and drives host-owned Prism sessions with `session/new`, `session/load`, `session/resume`, `session/prompt`, `session/set_mode`, `session/set_config_option`, `session/list`, `session/delete`, `session/close`, and `session/cancel`. The agent is a thin protocol adapter: every capability, decision, and byte cap is wired from host seams, and there is **no second policy engine** on the agent side.
|
|
8
8
|
- `createAcpEventMapper(options)` — maps a Prism `AgentEvent` stream (or `CoWorkEvent`) to ACP `SessionUpdate`s for hosts that stream through their own transport.
|
|
9
9
|
|
|
10
|
-
The adapter builds on the Phase 8/9 shared machinery: redacted event projection (`AgUiProjection`), the durable pending-decision batch model (`
|
|
10
|
+
The adapter builds on the Phase 8/9 shared machinery: redacted event projection (`AgUiProjection`), the durable pending-decision batch model (keyed by the permission optionIds `allow-once` / `allow-for-run` / `reject-once` / `reject-for-run`), `AgentRunLifecycle` resume, `CodingLifecycleEvent` emission, and the AG-UI/ACP package caps. It never ships experimental ACP v2 or UNSTABLE fields (`providers`, `nes`, `positionEncoding`, `sessionCapabilities.fork`, `mcpCapabilities.acp/auth`, `elicitation` is consumed client-side only and never advertised; the UNSTABLE `plan` surface is consumed client-side only — `plan_update`/`plan_removed` are emitted solely to clients that advertised `ClientCapabilities.plan`, F5).
|
|
11
11
|
|
|
12
12
|
## When to use it
|
|
13
13
|
|
|
@@ -24,11 +24,12 @@ Do **not** use it when the host needs a browser/TUI Web endpoint (use [AG-UI](ag
|
|
|
24
24
|
| `authorize` | `(input) => AcpAuthorization \| Promise` | **Required.** Ownership/identity gate for every inbound call, scoped by `sessionId`; unknown sessions fail. |
|
|
25
25
|
| `sessionFactory` | `(input) => AcpSessionBinding \| Promise` | **Required.** Builds the Prism `AgentSession` for `session/new`. Input carries `authorization`, `cwd`, `additionalDirectories`, `mcpServers` (policy-checked), `signal`, optional pre-generated `sessionId`, and `coding` (built client fs/terminal adapters when the client advertised them). |
|
|
26
26
|
| `lifecycle` | `AgentRunLifecycle` | **Required.** `status`/`resume`/`resumeStream` for durable `session/load` and `session/resume`. |
|
|
27
|
-
| `sessions?` | `AcpSessionStoreSeams` | `load` (advertises `sessionCapabilities.loadSession`), `list` (list), `delete` (delete), `resume` (resume), `additionalDirectories` (policy narrowing of `additionalDirectories`). `close` is always advertised. |
|
|
27
|
+
| `sessions?` | `AcpSessionStoreSeams` | `load` (advertises `sessionCapabilities.loadSession`), `list` (list), `delete` (delete), `resume` (resume), `additionalDirectories` (policy narrowing of `additionalDirectories`), `transcript` (F2: replay source for `session/load`/`session/resume`), `title` (F6: host-owned session titles — see below). `close` is always advertised. |
|
|
28
28
|
| `mcp?` | `AcpMcpSeams` | `transports: ("http" \| "sse")[]` and `select({ servers, signal })` — **required** for any client-supplied MCP server; select must approve before the bridge connects. Advertises `mcpCapabilities.http`/`sse` per transport. |
|
|
29
29
|
| `modes?` | `{ modes: AcpSessionMode[], defaultModeId? }` | `AcpSessionMode { id, name, description?, apply? }`; `apply({ sessionId?, fromModeId?, modeId, signal })` is the host hook run on switch. Advertised in `SessionModeState` on new/load/resume; enables `session/set_mode`. |
|
|
30
|
-
| `configOptions?` | `{ options: AcpConfigOption[], onChange? }` | `boolean`/`select` options with `defaultValue`; enables `session/set_config_option` (requires the client to advertise `session.configOptions.boolean`). |
|
|
31
|
-
| `capabilities?` | `AcpCapabilitiesOptions` | `prompt.media`/`prompt.embedded` policy seams, re-checked **live at prompt time**; presence advertises `promptCapabilities.image`/`audio`/`embeddedContext`. |
|
|
30
|
+
| `configOptions?` | `{ options: AcpConfigOption[], onChange? }` | `boolean`/`select` options with `defaultValue`; enables `session/set_config_option` (requires the client to advertise `session.configOptions.boolean`). **B3:** only `boolean` options are advertised in `session/new`/`load`/`resume` responses and `config_option_update`; `select` options are never settable — `set_config_option` on one fails with `ERR_PRISM_ACP_CAPABILITY` until the ACP spec defines a select capability. |
|
|
31
|
+
| `capabilities?` | `AcpCapabilitiesOptions` | `prompt.media`/`prompt.embedded` policy seams, re-checked **live at prompt time**; presence advertises `promptCapabilities.image`/`audio`/`embeddedContext`. `usage.contextWindow({ model, signal })` reports the model's context window in tokens; `usage_update` is emitted only when it returns a positive finite number — absent/undefined/throw ⇒ the update is omitted (never `size = used`). |
|
|
32
|
+
| `commands?` | `AcpCommandsSeam` | F9: `{ list({ sessionId, signal }) => AcpCommand[] }`. Presence emits `available_commands_update` on `session/new`, `session/load`, and `session/resume`. Absent seam ⇒ no update. |
|
|
32
33
|
| `coding?` | `AcpCodingSeams` | `filesystem(client, sessionId)` / `processes(client, sessionId)` factories building `AcpClientFilesystem` / `AcpClientTerminals` over client methods; `lifecycle?: CodingLifecycleEmitter` subscribes `CodingLifecycleEvent`s into ACP updates. |
|
|
33
34
|
| `name?` | `string` | `agentInfo.name` (default `"Prism"`). |
|
|
34
35
|
|
|
@@ -43,20 +44,26 @@ In-stream `SessionUpdate`s:
|
|
|
43
44
|
| Prism event | ACP update |
|
|
44
45
|
|---|---|
|
|
45
46
|
| Assistant text | `agent_message_chunk` |
|
|
46
|
-
|
|
|
47
|
-
| Tool
|
|
48
|
-
|
|
|
49
|
-
|
|
|
47
|
+
| Assistant thinking | `agent_thought_chunk` (same `messageId` scheme as text; through the shared redactor and byte caps) |
|
|
48
|
+
| Tool lifecycle | `tool_call` / `tool_call_update` (title/status/content) — `tool_call.kind` comes from the session's tool registry `kind` metadata when present (B4), else the name heuristic |
|
|
49
|
+
| Tool result, projected | `tool_call_update` with `locations` (≤ `acpLocationsPerUpdate`) and/or a `diff` block (≤ `acpDiffBytes`) — only from `AgUiProjection.toolLocations`/`toolDiff` allow-lists, at `finish()`. `toolResult` may return a string (text content) or `{ type: "image", data, mimeType }` (F8) — the mapper wraps the image as `{ type: "content", content: { type: "image", data, mimeType } }` and drops payloads over `acpImageBytes` (never truncated). Opt-in turnkey: `createCodingToolProjection()` (F7) recognizes first-party `edit` (path + unified patch as `newText`, `firstChangedLine` location) and `write` (path location only — result has no file body) results; default remains deny-by-default. |
|
|
50
|
+
| Provider usage | `usage_update` (only when the `capabilities.usage.contextWindow` seam reports a valid window — absent/undefined/throw ⇒ the update is omitted, never `size = used`) |
|
|
51
|
+
| Run-level failure | No transcript chunk — the `session/prompt` request rejects with `ERR_PRISM_ACP_RUN` (redacted, byte-capped message). Retryable provider-turn failures stay silent and may recover; only a terminal `error` event fails the request. |
|
|
52
|
+
| Run stop reason | `session/prompt` returns the SDK `StopReason` (F4): `cancelled` when the run was aborted, `max_turn_requests` for the tool-round ceiling (`finishReason: "turn_limit"`), `max_tokens` for `"token_limit"`, `refusal` for `"refusal"`, else `end_turn`. The generic `finishReason` field is set on `agent_finished` by loop strategies (single-shot records `turn_limit` at the `maxToolRounds` ceiling); `token_limit`/`refusal` have no core producer yet — the mapping is ready. |
|
|
53
|
+
| Durable suspension | `session/request_permission` with the four options `allow-once`→`allow_once`, `allow-for-run`→`allow_always`, `reject-once`→`reject_once`, `reject-for-run`→`reject_always` (optionId→SDK kind, as emitted by `permission-elicit.ts`); cancel, unknown options, and request failure deny. Sticky decisions expire at run end. |
|
|
50
54
|
| Elicitation suspension (all-elicitation batch + client advertised `elicitation`) | `elicitation/create` (form mode, bounded schema, redacted reason); `accept` → `allow_once` with the typed payload as `RunDecision.elicitation`, `decline`/`cancel` → `reject_once`. Otherwise falls back to the shared four-option permission path. |
|
|
51
55
|
| `file_changed` lifecycle | `tool_call_update` with `locations: [{ path }]` (needs a `toolCallId`; diff only from `fileDiff` allow-list, capped + redacted) |
|
|
52
56
|
| `worktree_changed` / process events | Projection-gated `agent_message_chunk` (deny-by-default: no `lifecycle` projection hook = no update) |
|
|
53
57
|
| `permission_denied` lifecycle | `tool_call_update` status `failed` (never raw args; synthesized id `prism:denied:<approvalId>` when no `toolCallId`) |
|
|
54
58
|
| `configuration_changed` lifecycle | `config_option_update` with the full current set, per streaming session |
|
|
59
|
+
| Plan lifecycle (F5, UNSTABLE-gated) | `plan_changed` → `plan_update` with `plan: { type: "items", planId = planPath, entries: [{ content, priority: "medium", status }] }` — the complete entry list per update (client replaces its plan wholesale); `plan_removed` → `plan_removed` with `planId = planPath`. Emitted only when the client advertised `ClientCapabilities.plan`; mapper stays capability-agnostic (gate in the agent wiring). Entries come from `writeCodingPlanFile`'s `onEvent` (parsed via `parseCodingPlanTodos`) or host-emitted through their `CodingLifecycleEmitter`; text passes the shared redactor and byte caps. |
|
|
60
|
+
| Session title (F6) | `sessions.title({ sessionId, prompt, signal })` resolves on `session/prompt`; a defined value differing from the last emitted title produces `session_info_update` with `{ sessionUpdate: "session_info_update", title }`. Best-effort: `undefined` or a throw means no title and no update (requests never fail on titles); the host owns title storage. Titles pass the shared redactor and are truncated at `maxTextBytes`/`maxEventBytes`. |
|
|
61
|
+
| Slash commands (F9) | `commands.list({ sessionId, signal })` on `session/new`/`load`/`resume` produces `available_commands_update` with `{ name, description, input?: { hint } }` (SDK `AvailableCommand`; description is required). Names/descriptions/hints pass the shared redactor and `maxTextBytes`; the list is sliced at `acpCommandsPerUpdate`. Best-effort: a throw or non-array omits the update (session start never fails on commands). |
|
|
55
62
|
| Session mode/config switch | `current_mode_update` / `config_option_update` |
|
|
56
63
|
|
|
57
|
-
Frozen caps (default / hard, from the Phase 10 freeze manifest): sessions 32/128, additional directories 8/32 (path 4 KiB/16 KiB), MCP servers 8/32 (config 16 KiB/256 KiB, header values 4 KiB/64 KiB), modes 16/64, config options 16/64, list page 20/100, diff bytes 64 KiB/1 MiB, locations per update 32/128, prompt media parts 16/64 and media bytes 64 KiB/1 MiB (shared AG-UI caps), terminal output chunks 51200 B/1 MiB (Phase 9 `process.outputChunkBytes`), stream events/bytes per AG-UI budgets. `session/load`/`session/resume` of a still-registered session rejects with `ERR_PRISM_ACP_INPUT` ("ACP session already exists"); model reconnect as resume of a pre-seeded stored session.
|
|
64
|
+
Frozen caps (default / hard, from the Phase 10 freeze manifest): sessions 32/128, additional directories 8/32 (path 4 KiB/16 KiB), MCP servers 8/32 (config 16 KiB/256 KiB, header values 4 KiB/64 KiB), modes 16/64, config options 16/64, list page 20/100, diff bytes 64 KiB/1 MiB, locations per update 32/128, projected tool-result images `acpImageBytes` 256 KiB/1 MiB (F8; oversize dropped), slash commands `acpCommandsPerUpdate` 32/128 (F9), prompt media parts 16/64 and media bytes 64 KiB/1 MiB (shared AG-UI caps), terminal output chunks 51200 B/1 MiB (Phase 9 `process.outputChunkBytes`), stream events/bytes per AG-UI budgets. `session/load`/`session/resume` of a still-registered session rejects with `ERR_PRISM_ACP_INPUT` ("ACP session already exists"); model reconnect as resume of a pre-seeded stored session.
|
|
58
65
|
|
|
59
|
-
Errors surface as `AcpError` with codes `ERR_PRISM_ACP_INPUT` (malformed), `ERR_PRISM_ACP_LIMIT` (caps), `ERR_PRISM_ACP_POLICY` (host denied), `ERR_PRISM_ACP_CAPABILITY` (not advertised), `ERR_PRISM_ACP_MCP` (MCP bridging). Over the wire they become JSON-RPC `-32603` with the message in `data.details` (SDK behavior).
|
|
66
|
+
Errors surface as `AcpError` with codes `ERR_PRISM_ACP_INPUT` (malformed), `ERR_PRISM_ACP_LIMIT` (caps), `ERR_PRISM_ACP_POLICY` (host denied), `ERR_PRISM_ACP_CAPABILITY` (not advertised), `ERR_PRISM_ACP_MCP` (MCP bridging), `ERR_PRISM_ACP_RUN` (run-level failure — the `session/prompt` request rejects instead of emitting a fake `Agent error:` chunk). Over the wire they become JSON-RPC `-32603` with the message in `data.details` (SDK behavior).
|
|
60
67
|
|
|
61
68
|
## Request/response example
|
|
62
69
|
|
|
@@ -104,6 +111,7 @@ const agent = createPrismAcpAgent({
|
|
|
104
111
|
## Extension and configuration notes
|
|
105
112
|
|
|
106
113
|
- **Seam = capability.** Wiring `sessions.load` advertises `loadSession`; removing it withdraws the method. There is no separate capability flag to keep in sync — the freeze manifest's advertise-when matrix is enforced by construction and asserted by `scripts/phase10-conformance.test.mjs`.
|
|
114
|
+
- **Transcript replay (F2).** When `sessions.transcript` is wired, `session/load` and `session/resume` replay `user_message_chunk`/`agent_message_chunk` text chunks (from `SessionEntry`s with `kind: "message"` and a user/assistant role, text blocks only) before returning `sessionState`. Each chunk passes the shared redactor and is truncated at `maxTextBytes`; replay stops at `maxReplayEvents` chunks and counts against the stream event/byte caps (an oversized transcript fails the load/resume request closed). Absent seam = no replay, behavior unchanged.
|
|
107
115
|
- **Client fs/terminal are adapters, not a second implementation.** `AcpClientFilesystem` / `AcpClientTerminals` wrap the client's `fs/*` and `terminal/*` methods behind the Phase 9 `ProcessSession`-flavored interfaces; the agent pre-generates the session id so terminal requests can carry it. Host repo operations remain default when the client fs is absent.
|
|
108
116
|
- **Modes and config options are a pure host overlay.** The agent stores only a thin per-session registry; `apply`/`onChange` hooks narrow the host's own behavior. Mode switches can narrow or host-authorized widen — never a parallel policy evaluator, never a client-enabled tool.
|
|
109
117
|
- **Lifecycle wiring.** Pass your `createCodingLifecycleEmitter()` as `coding.lifecycle`; `file_changed` etc. then flow to streaming sessions. `configuration_changed` broadcasts `config_option_update` (agent-message fallback if the SDK rejects the kind).
|
|
@@ -141,6 +149,9 @@ const agent = createPrismAcpAgent({
|
|
|
141
149
|
|
|
142
150
|
- **Untrusted client input.** Client-supplied paths, `additionalDirectories`, MCP server configs, terminal env/args, and media are validated at the boundary: count/byte caps, ownership-scoped sessions, path policy via the `sessions.additionalDirectories` seam, MCP servers only through host `select` (never auto-connected), UNSTABLE `acp` transport always rejected.
|
|
143
151
|
- **Deny-closed by default.** Unknown mode ids, unadvertised methods, unprojected lifecycle events, oversize diffs/locations/media, thrown projection hooks, and failed elicitation all fail closed. Raw tool arguments/results are never sent unless a projection allow-list says otherwise.
|
|
152
|
+
- **Slash commands (F9).** `commands.list` is a host-owned slash-command list (not derived from the tool registry). The agent emits `available_commands_update` on session start (`session/new`, `session/load`, `session/resume`). Mid-session refresh is not in this release — re-list by starting a session. Names, descriptions, and input hints pass the shared redactor; the list is sliced at `acpCommandsPerUpdate`. Absent seam or a thrown list ⇒ no update.
|
|
153
|
+
- **Projected images (F8).** `AgUiProjection.toolResult` may return `{ type: "image", data, mimeType }` (return-type widening — existing string returns stay valid). The mapper emits `{ type: "content", content: { type: "image", data, mimeType } }` (SDK v1 `ToolCallContent` has no top-level image variant). `data` is the host-supplied base64; it is not redacted and not truncated — payloads over `acpImageBytes` are dropped. Default (no hook / non-image return) emits no image.
|
|
154
|
+
- **Coding-tool projection (F7).** `createCodingToolProjection({ maxDiffBytes? })` is an opt-in `AgUiProjection` for first-party `@arnilo/prism-coding-agent` `edit`/`write` results: `edit` → `toolDiff` (`path` + unified `patch` as `newText`) and `toolLocations` (`path` + `firstChangedLine`); `write` → `toolLocations` (`path` only — write metadata has no file body, so no honest diff; use `file_changed` + `fileDiff` when bodies are needed). Pass as `projection: createCodingToolProjection()` on the agent/mapper. Mapper still redacts and enforces `acpDiffBytes` / `acpLocationsPerUpdate`; optional `maxDiffBytes` pre-truncates the patch so a slightly-oversize edit is shortened instead of dropped. Without the factory, behavior is unchanged (deny-by-default).
|
|
144
155
|
- **No secrets.** Updates carry no raw file bodies, terminal output is capped by the Phase 9 chunk budget, and the shared redactor is applied before anything leaves the host. `permission_denied` never includes raw args.
|
|
145
156
|
- **Performance.** The adapter is O(1) per update with no unbounded buffering; p95 targets (fs round trip 250 ms, mode switch 250 ms, terminal chunk ack 1000 ms, prompt first update 2000 ms, prompt end 30 s) are recorded by `scripts/benchmark-0.0.27.mjs` and gated in `scripts/budgets.json` `phase10`.
|
|
146
157
|
|
package/docs/ag-ui.md
CHANGED
|
@@ -202,7 +202,7 @@ const renderer = createA2UiRenderer({
|
|
|
202
202
|
const surface = await renderer.surface("chat"); // detached DOM node, kept in sync
|
|
203
203
|
```
|
|
204
204
|
|
|
205
|
-
The core is a DOM-free state machine (`reduceA2UiOps`): operations become a surface/component model (adjacency list with `id`/`component`/flat props, A2UI v0.9 JSON-Pointer data model, `deleteSurface`); a thin binding layer renders the model through catalog component renderers (framework-free `(props, ctx, dom) => node` functions). The core is also exported as values from the subpath entry (`A2UiSurfaceState`, `reduceA2UiOps`, `readA2UiBatch`, `resolvePointer`, `A2UI_VERSION`, 0.0.27,
|
|
205
|
+
The core is a DOM-free state machine (`reduceA2UiOps`): operations become a surface/component model (adjacency list with `id`/`component`/flat props, A2UI v0.9 JSON-Pointer data model, `deleteSurface`); a thin binding layer renders the model through catalog component renderers (framework-free `(props, ctx, dom) => node` functions). The core is also exported as values from the subpath entry (`A2UiSurfaceState`, `reduceA2UiOps`, `readA2UiBatch`, `resolvePointer`, `A2UI_VERSION`, 0.0.27, host FR) so framework hosts can drive the validated surface state machine and own the view layer; behavior and frozen caps are unchanged. Snapshots replace a surface's model (streaming mode sends cumulative ops); RFC 6902 deltas append. The same frozen caps as the server painter are enforced client-side: 64/512 ops per message, 64 KiB/1 MiB per op, 16/64 surfaces per run, depth 32/64. Invalid or oversized ops drop closed with one bounded `prism.a2ui.error` event (host logging via `onError`); unknown catalog components render an explicit placeholder. The renderer never executes remote HTML: only `createElement`/`createTextNode`/`appendChild`, no HTML-string assignment, no dynamic code evaluation. Data bindings `{"path": "/pointer"}` resolve against the per-surface data model; `deleteSurface` detaches content. The main `@arnilo/prism-ag-ui` entry stays runtime-agnostic — DOM code lives only behind the `renderer` subpath (the root entry re-exports renderer types only, no values). Hosts embedding it should follow the MCP Apps CSP/sandbox guidance (`docs/ag-ui-adoption.md`) for iframe/worker placement.
|
|
206
206
|
|
|
207
207
|
## Security and performance notes
|
|
208
208
|
|
|
@@ -15,7 +15,7 @@ A third helper, `discoverAgentBundles(options)` (same Node subpath), scans an ap
|
|
|
15
15
|
|
|
16
16
|
Use `resolveAgentDefinition` when an app already holds a `AgentDefinition` (from an extension, a manifest, or hand-written config) and wants to turn it into an `Agent` against its registries — without wiring every field by hand.
|
|
17
17
|
|
|
18
|
-
Use `discoverAgentBundles` + `resolveAgentBundle` when a host app keeps per-agent bundles on disk under an app-controlled config root (for example
|
|
18
|
+
Use `discoverAgentBundles` + `resolveAgentBundle` when a host app keeps per-agent bundles on disk under an app-controlled config root (for example `<appRoot>/extensions/prism/agents/<agentName>/AGENT.md`) and wants to honor them as first-class agents. The bundle layout is host-owned: Prism never picks the config root, never touches the user's home directory, and never auto-runs resolution — the host calls `discoverAgentBundles` and then `resolveAgentBundle` explicitly.
|
|
19
19
|
|
|
20
20
|
Do not use the bundle loader to discover providers — provider/model packages stay config/package-driven (Phase 24; see [Provider packages](provider-packages.md)). Do not use it to auto-activate undeclared tools or skills: omitted `tools` / `skills` means no active capabilities by default; named bundle entries are explicit activation, and runtime skill selection can narrow further with `RunOptions.activeSkills`.
|
|
21
21
|
|
package/docs/agent-events.md
CHANGED
|
@@ -88,7 +88,7 @@ Agent / turn / message events:
|
|
|
88
88
|
| Variant | Fields |
|
|
89
89
|
| --- | --- |
|
|
90
90
|
| `agent_started` | `sessionId`, `runId` |
|
|
91
|
-
| `agent_finished` | `sessionId`, `runId`, `usage?: Usage` (aggregate of all usage-bearing provider turns) |
|
|
91
|
+
| `agent_finished` | `sessionId`, `runId`, `usage?: Usage` (aggregate of all usage-bearing provider turns), `finishReason?: "turn_limit" \| "token_limit" \| "refusal"` (why a limit/ceiling ended the run cleanly — F4; absent = natural end) |
|
|
92
92
|
| `agent_suspended` | `sessionId`, `runId`, redacted `interruption`, checkpoint `version`; no tool side effect has started. |
|
|
93
93
|
| `agent_resumed` | `sessionId`, `runId`, checkpoint `version`. |
|
|
94
94
|
| `agent_denied` | `sessionId`, `runId`, redacted `interruption`, checkpoint `version`; no tool side effect runs. |
|
|
@@ -242,4 +242,4 @@ for await (const event of session.stream("draft", { loop: { strategy: "generate-
|
|
|
242
242
|
- [Observability](observability.md): `ProviderTurnMetadata`; optional adapter builds one parented GenAI span tree from metadata-only lifecycle events and ignores message/progress deltas.
|
|
243
243
|
- [Tools](tools.md): `tool_execution_*` variants.
|
|
244
244
|
- [Compaction and retry policies](compaction-and-retry.md): `compaction_*` and `retry_scheduled` variants.
|
|
245
|
-
- [Frontend interoperability (AG-UI and ACP)](ag-ui.md): optional redacted mapping of this stream; durable replay is ledger-backed and at-least-once, never a live-subscriber substitute. [ACP coding-host interop](acp.md) additionally maps `CodingLifecycleEvent`s from `@arnilo/prism-coding-agent` (`file_changed`, `worktree_changed`, `permission_denied`, `configuration_changed`; process events reuse `CodingProcessEvent`) into ACP session updates — locations/diff blocks only through projection allow-lists, terminal chunks under `process.outputChunkBytes
|
|
245
|
+
- [Frontend interoperability (AG-UI and ACP)](ag-ui.md): optional redacted mapping of this stream; durable replay is ledger-backed and at-least-once, never a live-subscriber substitute. [ACP coding-host interop](acp.md) additionally maps `CodingLifecycleEvent`s from `@arnilo/prism-coding-agent` (`file_changed`, `worktree_changed`, `permission_denied`, `configuration_changed`, `plan_changed`, `plan_removed`; process events reuse `CodingProcessEvent`) into ACP session updates — locations/diff blocks only through projection allow-lists, terminal chunks under `process.outputChunkBytes`, plan updates only to clients that advertised the UNSTABLE `plan` capability.
|
package/docs/agent-loops.md
CHANGED
|
@@ -232,7 +232,7 @@ await session.run(input, { loop: twoShotLoop });
|
|
|
232
232
|
- `generateValidateReviseLoop` makes at most `1 + maxRevisions + maxToolRounds` provider turns when bounded tools are enabled (otherwise `maxRevisions + 1`); it cannot loop forever. Each revision costs one provider turn plus one store append.
|
|
233
233
|
- Bounded artifact tool calls run sequentially through `dispatchToolCall` (permission + validation + execute); their assistant call and result are persisted before the next provider request. `singleShotLoop` retains its bounded parallel worker pool and original call-order transcript behavior.
|
|
234
234
|
- The loop is a plain object/factory; no class hierarchy, no background work, no extra dependencies. `LoopContext` is a single object literal of bound arrows built once per run.
|
|
235
|
-
- The
|
|
235
|
+
- The host-domain-free boundary is guarded by tests: `src/` imports no host-domain package, and the `Artifact*`/`AgentLoop*`/`LoopContext` contracts contain no `workflow`/`node`/`step` field names. Hosts supply their own schema; no host domain type is imported by `src/`.
|
|
236
236
|
|
|
237
237
|
## Guardrails
|
|
238
238
|
|
|
@@ -241,7 +241,7 @@ Built-in loops and custom loops that use `LoopContext.generate()` / `LoopContext
|
|
|
241
241
|
## Related APIs
|
|
242
242
|
- [Agent/session runtime](agent-session-runtime.md): `RuntimeAgentSession.run()` builds the `LoopContext` and delegates to the resolved loop.
|
|
243
243
|
- [Agent events](agent-events.md): the `artifact_*` event variants and ordering emitted by `generateValidateReviseLoop`.
|
|
244
|
-
- [Structured output](structured-output.md): the `ArtifactParser<T>`/`ArtifactValidator<T>`/`ArtifactRepairer<T>` seam (host-defined `T`, Prism never instantiates it) and a
|
|
244
|
+
- [Structured output](structured-output.md): the `ArtifactParser<T>`/`ArtifactValidator<T>`/`ArtifactRepairer<T>` seam (host-defined `T`, Prism never instantiates it) and a host schema→`ArtifactValidation` mapping example.
|
|
245
245
|
- [Public contracts](public-contracts.md): `AgentLoopStrategy`, `AgentLoopOptions`, `LoopContext`, `ProviderTurnResult`, and the `Artifact*` contracts.
|
|
246
246
|
- [Input and prompt assembly](input-and-prompt-assembly.md): `assembleProviderInput()`, the primitive behind `LoopContext.assemble`.
|
|
247
247
|
- [Tools](tools.md): `dispatchToolCall()`, the primitive behind `LoopContext.dispatchToolCall`.
|
|
@@ -48,6 +48,8 @@ import { createCodingTools } from "@arnilo/prism-coding-agent";
|
|
|
48
48
|
const tools = createToolRegistry(createCodingTools(process.cwd()));
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
Every tool carries an explicit `kind` (`shell`→`execute`, `read`/`repo_list`→`read`, `write`/`edit`→`edit`, `repo_search`/`glob`→`search`, `delete`→`delete`, `move`→`move`) so ACP `tool_call` updates and other consumers can classify tools without name heuristics.
|
|
52
|
+
|
|
51
53
|
## When to use it
|
|
52
54
|
|
|
53
55
|
Use this package when a host wants ready-made coding tools for an agent, session, or run, registered explicitly into a `ToolRegistry` and dispatched through the normal Prism tool harness. The tools perform **real** shell and filesystem operations on the host — they are not mocked or sandboxed. Use the individual factories when you need per-tool options or custom operation backends; use the aggregators when you want the default set.
|
|
@@ -578,5 +580,5 @@ Every configurable value is a positive safe integer (context may be zero); Prism
|
|
|
578
580
|
- [Public contracts](public-contracts.md): `ToolDefinition`, `ToolResult`, `ToolExecutionContext`, `ContentBlock`, and `JsonObject` shapes.
|
|
579
581
|
- [Host security guide](host-security.md): fail-closed checklist for permission policies, tool validation, and trust boundaries that must gate these tools.
|
|
580
582
|
- [Tool conformance](tool-conformance.md): assertions for the tool-dispatch blocked-reason matrix these tools participate in.
|
|
581
|
-
- [ACP coding-host interop](acp.md): host editors drive these tools through stable ACP v1 — client fs/terminal adapters, `CodingLifecycleEvent` emission (`file_changed` etc. via the `onEvent` options), and permission/elicitation through the shared four-outcome decision model.
|
|
583
|
+
- [ACP coding-host interop](acp.md): host editors drive these tools through stable ACP v1 — client fs/terminal adapters, `CodingLifecycleEvent` emission (`file_changed` etc. via the `onEvent` options; `plan_changed` also fires from `writeCodingPlanFile`'s `onEvent`, F5), and permission/elicitation through the shared four-outcome decision model.
|
|
582
584
|
- [LLM compaction package](compaction-llm.md): optional `createCodingCompactionStrategy()` retains bounded paths, patch intent, checks, plan/todo state, blockers, and next verification—not complete diffs or raw command output.
|
package/docs/coding-security.md
CHANGED
|
@@ -210,6 +210,12 @@ The protected coding journey (0.2.6, plan 026 Task 7) exercises these boundaries
|
|
|
210
210
|
|
|
211
211
|
The egress proxy is a policy enforcer, not a firewall: it cannot stop a container whose Docker network reaches the internet directly. Egress attestation (`denyDirectEgress: true`) is a claim the host must make true by network topology; the adapter records it as evidence and fails closed when it is absent or malformed. The proxy performs no TLS interception, no DNS rebinding of its own beyond pinning, and no content filtering; audit records contain no secrets. Frozen caps: 32 concurrent connections (hard 256), 64 MiB request/response bytes (hard 1 GiB), 600 s transfer time (hard 1 h), 128 rules (hard 1,024), 5 redirect hops (hard 10).
|
|
212
212
|
|
|
213
|
+
## Windows hosts
|
|
214
|
+
|
|
215
|
+
`createNativeSandbox` is Linux-only. On any other platform (including Windows) it throws at creation and does **not** fall back to an unsandboxed process — egress denial cannot be enforced by construction without a network namespace. Do not catch that error and enable `shell` on the host; keep `shell` disabled, or run the agent inside a Docker container using `createDockerSandbox` and the documented [allow-list egress](#allow-list-egress-composition) policy (`network: none` or an attested custom network). Host-mode tools (`read`/`write`/`edit` under `workspaceMode: "host"`) remain available; they never claim containment.
|
|
216
|
+
|
|
217
|
+
A native Windows backend (Job objects / AppContainer) is tracked, not scheduled. Until one exists, Windows hosts that need isolation use Docker. Do not weaken the deny-by-default posture to compensate.
|
|
218
|
+
|
|
213
219
|
## Related APIs
|
|
214
220
|
|
|
215
221
|
- [Coding agent tools](coding-agent-tools.md): durable plan/todo Markdown helpers and `state.coding` checkpoint metadata for restart/resume without a second runtime
|
package/docs/index.md
CHANGED
|
@@ -103,7 +103,8 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
103
103
|
- [Supervisor delegation](supervisors.md): optional explicit child allow-list, derived memory scopes, narrowing-only permissions, lifecycle hooks, nested delegation, cancellation, finite budgets, host-projected delegation telemetry, and separate A2A durable adapter boundary.
|
|
104
104
|
- [A2A interoperability](a2a.md): A2A 1.0 JSON-RPC/HTTPS cards plus host-owned durable task get/list/cancel/subscribe, shared `AgentEventSource` task adapter, bounded rich parts/replay, principal-scoped push configs, exact-origin verified client, rich stream seam for explicit AG-UI fronting, and server-side `createAgUiA2AServer` exposure of a local AG-UI agent (0.0.26).
|
|
105
105
|
- [Frontend interoperability (AG-UI and ACP)](ag-ui.md): optional `@arnilo/prism-ag-ui` full AG-UI 0.0.57 input/event/capability mapper, authorized Web handler/distributed source follow, opt-in A2UI painting middleware, explicit hardened MCP/MCP Apps/remote A2A adapters, a framework-free reference renderer subpath (`@arnilo/prism-ag-ui/renderer`, 0.0.26), and stable ACP sibling over shared redacted event and durable-approval seams; 0.0.14 adds reconnectable co-work events.
|
|
106
|
-
- [ACP coding-host interop](acp.md): stable ACP v1 `createPrismAcpAgent()`/`createAcpEventMapper()` over `@agentclientprotocol/sdk@1.3.0` — capability advertisement is a pure function of host seams (sessions load/list/delete/resume/dirs, close always, prompt media/embedded, MCP http/sse), client fs/terminal adapters, modes and config options as host overlays, `CodingLifecycleEvent` mapping, four-outcome approvals with elicitation, and frozen caps (0.0.27); 0.1.1 adds ownership-scoped persistence guidance for host-persisted modes/config (plan 013 Task 5 — the agent never persists them); 0.1.6 adds the optional host-owned `AcpSessionStore` durability seam — live registry (modes/config/cwd/ownership) survives agent restart, restore is ownership-scoped and fail-closed (plan 018 Task 2). 0.2.6 adds durable run recovery (plan 026 Task 5): bounded `activeRun` refs on persisted sessions, restart re-resolution against `AgentRunLifecycle` (suspended → pending approval ids, terminal → terminal, unprovable in-flight → unknown, never a restarted prompt), and durable ownership/version/fence-checked cancellation that never replays tools; docs/migration.md records the additive-field decision and the 0.2.5 → 0.2.6 downgrade rules.
|
|
106
|
+
- [ACP coding-host interop](acp.md): stable ACP v1 `createPrismAcpAgent()`/`createAcpEventMapper()` over `@agentclientprotocol/sdk@1.3.0` — capability advertisement is a pure function of host seams (sessions load/list/delete/resume/dirs, close always, prompt media/embedded, MCP http/sse), client fs/terminal adapters, modes and config options as host overlays, `CodingLifecycleEvent` mapping, four-outcome approvals with elicitation, and frozen caps (0.0.27); 0.1.1 adds ownership-scoped persistence guidance for host-persisted modes/config (plan 013 Task 5 — the agent never persists them); 0.1.6 adds the optional host-owned `AcpSessionStore` durability seam — live registry (modes/config/cwd/ownership) survives agent restart, restore is ownership-scoped and fail-closed (plan 018 Task 2). 0.2.6 adds durable run recovery (plan 026 Task 5): bounded `activeRun` refs on persisted sessions, restart re-resolution against `AgentRunLifecycle` (suspended → pending approval ids, terminal → terminal, unprovable in-flight → unknown, never a restarted prompt), and durable ownership/version/fence-checked cancellation that never replays tools; docs/migration.md records the additive-field decision and the 0.2.5 → 0.2.6 downgrade rules. 0.2.8 (plan 028) adds `session/load`/`session/resume` transcript replay (bounded, redacted chunks from the `sessions.transcript` seam), truthful `usage_update`, per-type `set_config_option` gates, explicit tool `kind` metadata, run-level error mapping, permission wire alignment, `agent_thought_chunk`, and the spawnable entrypoint; UNSTABLE-gated `plan_update`/`plan_removed` from coding plan lifecycle events (F5, client must advertise `ClientCapabilities.plan`); host-owned `session_info_update` titles and title pass-through in `session/list` (F6, `sessions.title` seam); opt-in `createCodingToolProjection()` for first-party edit/write diffs+locations (F7, deny-by-default unchanged); projected `toolResult` images as ACP content/image blocks (F8, `acpImageBytes` cap); host-owned slash commands as `available_commands_update` (F9, `acpCommandsPerUpdate` cap).
|
|
107
|
+
- [Spawnable ACP agent](acp-agent.md): `@arnilo/prism-acp-agent` — a ~200-line bin serving `createPrismAcpAgent` over stdio from a validated config file (single local user, coding tools bound to one workspace, sqlite/memory session store, MCP allow-list, modes/config options, mock provider by default; 0.2.8 plan 028 Task 10).
|
|
107
108
|
- [AG-UI adoption evaluation](ag-ui-adoption.md): official 0.0.57 input/event/capability matrix and shipped hardened MCP/MCP Apps/A2A handshake boundaries.
|
|
108
109
|
|
|
109
110
|
## CLI/RPC
|
|
@@ -134,7 +135,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
134
135
|
|
|
135
136
|
## Release and install
|
|
136
137
|
- [0.2.7 Task 0 scope evidence](release-0.2.7-evidence.md): frozen ERP primitives, demand decisions, threat mappings, budgets, protected-gate policy, and API ownership; not a production-readiness claim.
|
|
137
|
-
- [Release and install](release-and-install.md): current **0.2.7** 50-package graph (root + 49 workspace packages) — plan 026 the fully-featured coding-agent-readiness cut: **host-selected PTY** (`pty: true` delegates only to the host `ptyBackend`, fails closed as unsupported when absent, bounded resize/TERM/attach caps), **indexed code search** (host-owned incremental index seam with explicit `indexed_literal`/`semantic` modes, literal remains the default, stale/failed/untrusted indexes fail closed `ERR_PRISM_INDEX_*`, results labeled `untrusted_index`), **coding workspaces** (`createCodingWorkspaceLifecycle`: durable CheckpointStore CAS records + LeaseStore fencing, locked worktrees, credential-free fingerprints, cleanup refusal matrix), **durable recovery** (process intent/ACP `activeRun` refs over Postgres/SQLite stores with attach-if-attested `recover()` and durable fence-checked cancellation, never fabricated exits), **patch review and diagnostics** (`createCodingPatchReviewManifest` + `assertCodingPatchAccepted` with pending/accepted/rejected/superseded bound to digest + revision + identity, opt-in LSP `syncDocument`/`diagnosticDelta`), and the **protected real coding journey** (packed consumer through real provider/Docker/Postgres/GitHub/Playwright/PTY services with retained evidence report; forge breadth GitLab/Bitbucket stays demand-gated); then plan 025 the maintainability-and-bounded-performance cut: **god-module splits** (the six remaining implementation monoliths — `src/contracts-core.ts` 1,719 L, `src/agent-session.ts` 2,049 L, `workflows/src/run.ts` 1,227 L, `server/src/handler.ts` 1,005 L, `coding-agent/src/repository.ts` 974 L, `ag-ui/src/acp/agent.ts` 836 L — split into cohesive family files behind preserved barrels, compat-preserving with zero breaking deltas, no `exports`-map subpath, `RuntimeAgentSession` kept as one class with a recorded reason), **persistence-mechanics dedup** (21 pure ownership/cursor/checkpoint/lifecycle/search helpers moved into the dependency-free `session-store-codecs`; postgres/sqlite adapters shrank 273 lines; SQL dialect stays per-adapter; no schema/shape change; cross-store conformance green), **bounded accumulation removed** (per-push `Buffer.concat` in language framing + tar parsing → chunk-array readers; framing ~100–200× faster at 4,000 chunks, tar linear at 8 MiB, caps fail-closed byte-identical; CLI `collectOutput` audited already linear), **dead-code cleanup internal-only** (62 candidates triaged: 2 internal removals + 60 allow-listed in `docs/_evidence/phase25-dead-exports-triage.md`), and **coverage close** (76 behavior-backed regressions; core 90.53/84.20/90.54 → 91.43/84.80/91.60); additive-only compat (105 helper exports), no migration; then plan 024 the package-documentation-and-compatibility-truth cut: **umbrella wording matches manifests** (`@arnilo/prism-providers` installs 11 of 14 provider adapters — Azure/Bedrock/Vertex are added separately by `prism-all`; `prism-all` installs 20 direct / 43 transitive packages and omits document-reader, OpenAPI tools, NATS, Caveman, Ponytail; membership unchanged in 0.2.x), **manifest-derived package truth** (`scripts/package-truth.mjs` → `scripts/package-truth.json` is the single source for counts, provider membership, and closures; docs literals regenerate from it and drift fails the gates), **peer-version policy Decision A** (exact `@arnilo/prism: 0.2.4` pins, atomic-upgrade rule, ERESOLVE refusal for partial upgrades, `^1.0.0` widening at 1.x), and **current-line truth** (`docs/0.1.0-readiness.md` at the 0.2.x line with 0.1.7 as the terminal 0.1.x baseline); no runtime contract delta (compat gate at 0.2.4: version literal only), no migration; then plan 023 the build-coverage-and-release-evidence-integrity cut: **build serialization** (dependency-free `scripts/with-build-lock.mjs` — one O_EXCL lockfile at `node_modules/.prism-build.lock` serializing every emit/test leaf so concurrent compilers can never expose a partial live `dist/`, stale-PID reclaim, env-overridable `PRISM_BUILD_LOCK_TIMEOUT_MS`, fail-closed; documented direct-`tsc` caveat), **corrected workspace coverage denominators** (package-local `--test-coverage-include=dist/**` so imported core `dist` no longer pollutes workspace rows — `mcp` 45.47→90.25, `rag` 19.70→94.82; evidence-based per-package thresholds in `scripts/coverage-thresholds.json` with `protectedException` for durable-leg packages shown separately, machine-readable `scripts/coverage-summary.json`), **machine-auditable release skip manifest** (`scripts/release-skip-manifest.mjs` → `scripts/release-evidence.json`: every surface recorded `pass`/`skip`/`blocked`/`protected` with reason and required env; the 33 protected/live skips named; a required surface without evidence records `blocked` and fails the release gate fail-closed — missing credentials/services can never convert into a green release), and **stabilized quality gates** (Biome 2.x `preset` config migration with zero lint diagnostics, the racy 150ms MCP bridge timing assert replaced by a deterministic barrier, load-sensitive guards carry documented `ponytail:` ceilings, machine-readable `lint-report.sarif` + `unused-report.json` retained by CI); no runtime contract delta (compat gate at 0.2.3: version literal only), no migration; then plan 022 the concurrent-state-and-durability-integrity cut: atomic model-budget reservation (`ModelRouterStateStore.reserveBudget`/`commitBudget`/`releaseBudget` with fencing tokens, `reservationTtlMs` expiry and unknown-usage reconciliation, rate/budget key-map caps with LRU eviction that never drops a held reservation), atomic conversation metadata (`SessionRecord.version` + `appendSession` `expectedVersion` CAS across Postgres/SQLite — create-only `0`, exact-version `N>0`, legacy last-write-wins when omitted; `SessionMetadataConflictError` `metadata_conflict` with versions only, HTTP 409; concurrent create/branch/archive single-statement with branch caps inside the CAS, archive wins, deleted rows never resurrect), single-consumer `EventMultiplexer` (`EventMultiplexerError` `ERR_PRISM_EVENT_MULTIPLEXER_SINGLE_CONSUMER` instead of silent queue sharing), restart-stable NATS durable consumer identity (`prism_<hmac16>` with no random suffix — crash-resumed subscribe continues from the last ack, orphaned 0.2.1 consumers reclaimed on clean stop), and bounded non-durable active-run registries (sweep + fail-closed 512 cap `ERR_PRISM_WORKFLOW_RUN_REGISTRY_OVERFLOW`); new regression surface `scripts/phase22-security.test.mjs` (4 blockers + gate accounting over built public entrypoints) + packed plain-JS `security22.mjs` consumer + the `@arnilo/prism/testing/state-concurrency-conformance` harness (7 probes across memory/Postgres/SQLite/NATS legs, no timing-only sleeps) + the `scripts/phase22-conformance.test.mjs` gate; additive-only compat (new exports only, no removals); forward-only migrations 008 (`prism_sessions.version`) and 003 (`prism_model_router_budgets.reservations`); migration `0.2.1 → 0.2.2`; then plan 021 the provider-completion-and-outbound-trust-boundaries cut: strict stream completion is the shared OpenAI-compatible default (truncated streams fail `incomplete_delta`, explicit `strictCompletion: false` opt-out), bounded success bodies via `readBoundedResponseJson` on all discovery/quota/embeddings/upload/OAuth JSON endpoints (65,536-byte ceiling, depth/property/shape caps), DNS-pinned OIDC JWKS/OPA/content fetches through the core `pinnedFetch` primitive with 3xx redirects rejected outright (private/metadata answers fail closed `ssrf_denied`), shared bounded OAuth device/token polling (`pollDeviceCodeToken`) across provider-openai and credentials-node, and the four edge fixes (Azure/Vertex credential-once, Bedrock duplicate-case/repeated-query SigV4 canonicalization, OpenAI upload failed-DELETE retention, cache `__overflow__` tokens-only); public-entrypoint threat-suite `scripts/phase21-security.test.mjs` + packed plain-JS consumer; additive-only compat (MCP transport helpers re-exported from core, no removals); migration `0.2.0 → 0.2.1`; then plan 020 the fail-closed runtime-and-sandbox-security cut on the 0.2.x review-remediation line: durable-resume decision validation in core (`assertValidAgentRunResume` — unknown decisions/malformed batches fail closed with `ERR_PRISM_DECISION_*` before any state claim, checkpoint write, or tool execution; server parser remains defense in depth), isolated work-tool subprocess environments (`@arnilo/prism-work-tools` — fixed base allow-list + explicit env + forced HOME/telemetry + late-bound per-identity tokens, 64-name/64-KiB caps, absolute binary/configDir, linear output capture), and explicit sandbox capabilities (`@arnilo/prism-coding-security` — `SandboxAdapter.capabilities` with omission-is-false fail-closed resolution, `SandboxCodingComposition.capabilities` from verified wiring, `containmentClaim` deprecated as the conservative projection; Docker reports only verified controls, native reports filesystem/process/privilege `false`); public-entrypoint security conformance (`scripts/phase20-security.test.mjs`, wired into `security:threat-suites`), packed plain-JS consumer regressions, and the sandbox-browser workflow's fail-loud Docker/native capability evidence gate — 0.2.0 never ships while a blocker is skipped; migration and rollback notes in `docs/migration.md` `0.1.7 → 0.2.0`, store-compatible with 0.1.7 in both directions; 0.1.7 was the performance-and-DX patch — dependency-free `createCacheTelemetry()` per-provider/model cache hit/miss aggregator (bounded cardinality with `__overflow__`, token counters/rates only, host-activated), host-configurable `ModelRouterSelectionPolicy` on `createModelRouter` with the reference `createCostLatencySelection` (ModelCost rank then in-memory latency EMA, default ordered behavior byte-identical), `prism providers add <name>` OpenAI-compatible provider scaffold (manifest/provider/models/cache/conformance test/docs stub, npm-name + traversal + symlink-escape validation, placeholders only), and the async `AgUiProjection` verification closeout (plan 009 Task 15 evidence recorded, no new code); plan 017 the documented breaking cut — deprecated-option removal with `docs/migration.md` `0.1.4 → 0.1.5` section and reviewed compat-baseline regeneration via `--allow-break` then `--update-baseline`: the inert provider request knobs, `RunOptions.maxToolRounds`, observational-memory flat settings keys + top-level worker aliases, `ReadToolOptions.autoResizeImages`, `INIT_PROVIDERS`; all removals fail closed naming their replacement; plan 016 internal god-module split — `agents.ts`/`contracts.ts` reorganized behind barrel re-exports with a byte-identical public entry surface, measured tree-shaking improvement in `scripts/phase16-baseline.json`, and additive `@arnilo/prism-browser` Chrome DevTools Protocol capabilities — `browser_evaluate`/`browser_observe` and `block_urls`/`unblock_urls`/`throttle`/`emulate` act actions; plan 015 dead-code and deprecation hygiene on the frozen 0.1.x line — parameterized benchmark runner `scripts/benchmark.mjs` absorbing the per-version runners, archived review-coverage evidence in `docs/_evidence/`, non-blocking unused-code sweep `npm run sweep:unused`, opt-in checkpoint persistence for loaded-skill names and read-path sets; plan 014 Alibaba provider enrichment — embeddings, video input, verified compatible-mode surface decision table; plan 013 post-release hardening — build single-flight, MCP SSE relay test, combined coverage summary, canonical manifest-count narrative, ACP modes/config persistence guidance; Phase 12 release-candidate hardening; plan 012 — freeze manifest, compatibility matrix, upgrade matrix, packed-install e2e journeys, restart-recovery evidence, capacity envelopes, security policy), exact-peer/install/tarball rules, deterministic resumable publication and publish dry-run, frozen 0.1.x compatibility and support matrix (Node/PostgreSQL/platform/provider/protocol pins and unsupported combinations, machine-checked against `scripts/phase12-freeze-manifest.json`), protected PostgreSQL gate, pinned supply-chain gates, offline tests, the 0.0.15 provider/AI-SDK/RAG/memory protected live-canary matrix, and sandbox-browser Docker/Playwright gates. 0.2.6 (plan 026 Task 7) adds the protected coding journey: `scripts/phase26-coding-journey.test.mjs` runs a packed consumer through real provider calls, a digest-pinned Docker sandbox, the durable Postgres worktree lifecycle, provider-driven ACP edits with policy approval, named checks with `diagnosticDelta`, patch review over the server ArtifactService, cross-replica process recovery, durable cancellation, real GitHub PR push/reconcile/cleanup, host Playwright inspection, and the host PTY adapter (frozen profile) — the retained `scripts/phase26-coding-journey-report.json` gates release evidence (pass/blocked/protected, never a passing skip).
|
|
138
|
+
- [Release and install](release-and-install.md): current **0.2.8** 51-package graph (root + 50 workspace packages) — plan 026 the fully-featured coding-agent-readiness cut: **host-selected PTY** (`pty: true` delegates only to the host `ptyBackend`, fails closed as unsupported when absent, bounded resize/TERM/attach caps), **indexed code search** (host-owned incremental index seam with explicit `indexed_literal`/`semantic` modes, literal remains the default, stale/failed/untrusted indexes fail closed `ERR_PRISM_INDEX_*`, results labeled `untrusted_index`), **coding workspaces** (`createCodingWorkspaceLifecycle`: durable CheckpointStore CAS records + LeaseStore fencing, locked worktrees, credential-free fingerprints, cleanup refusal matrix), **durable recovery** (process intent/ACP `activeRun` refs over Postgres/SQLite stores with attach-if-attested `recover()` and durable fence-checked cancellation, never fabricated exits), **patch review and diagnostics** (`createCodingPatchReviewManifest` + `assertCodingPatchAccepted` with pending/accepted/rejected/superseded bound to digest + revision + identity, opt-in LSP `syncDocument`/`diagnosticDelta`), and the **protected real coding journey** (packed consumer through real provider/Docker/Postgres/GitHub/Playwright/PTY services with retained evidence report; forge breadth GitLab/Bitbucket stays demand-gated); then plan 025 the maintainability-and-bounded-performance cut: **god-module splits** (the six remaining implementation monoliths — `src/contracts-core.ts` 1,719 L, `src/agent-session.ts` 2,049 L, `workflows/src/run.ts` 1,227 L, `server/src/handler.ts` 1,005 L, `coding-agent/src/repository.ts` 974 L, `ag-ui/src/acp/agent.ts` 836 L — split into cohesive family files behind preserved barrels, compat-preserving with zero breaking deltas, no `exports`-map subpath, `RuntimeAgentSession` kept as one class with a recorded reason), **persistence-mechanics dedup** (21 pure ownership/cursor/checkpoint/lifecycle/search helpers moved into the dependency-free `session-store-codecs`; postgres/sqlite adapters shrank 273 lines; SQL dialect stays per-adapter; no schema/shape change; cross-store conformance green), **bounded accumulation removed** (per-push `Buffer.concat` in language framing + tar parsing → chunk-array readers; framing ~100–200× faster at 4,000 chunks, tar linear at 8 MiB, caps fail-closed byte-identical; CLI `collectOutput` audited already linear), **dead-code cleanup internal-only** (62 candidates triaged: 2 internal removals + 60 allow-listed in `docs/_evidence/phase25-dead-exports-triage.md`), and **coverage close** (76 behavior-backed regressions; core 90.53/84.20/90.54 → 91.43/84.80/91.60); additive-only compat (105 helper exports), no migration; then plan 024 the package-documentation-and-compatibility-truth cut: **umbrella wording matches manifests** (`@arnilo/prism-providers` installs 11 of 14 provider adapters — Azure/Bedrock/Vertex are added separately by `prism-all`; `prism-all` installs 20 direct / 43 transitive packages and omits document-reader, OpenAPI tools, NATS, Caveman, Ponytail; membership unchanged in 0.2.x), **manifest-derived package truth** (`scripts/package-truth.mjs` → `scripts/package-truth.json` is the single source for counts, provider membership, and closures; docs literals regenerate from it and drift fails the gates), **peer-version policy Decision A** (exact `@arnilo/prism: 0.2.4` pins, atomic-upgrade rule, ERESOLVE refusal for partial upgrades, `^1.0.0` widening at 1.x), and **current-line truth** (`docs/0.1.0-readiness.md` at the 0.2.x line with 0.1.7 as the terminal 0.1.x baseline); no runtime contract delta (compat gate at 0.2.4: version literal only), no migration; then plan 023 the build-coverage-and-release-evidence-integrity cut: **build serialization** (dependency-free `scripts/with-build-lock.mjs` — one O_EXCL lockfile at `node_modules/.prism-build.lock` serializing every emit/test leaf so concurrent compilers can never expose a partial live `dist/`, stale-PID reclaim, env-overridable `PRISM_BUILD_LOCK_TIMEOUT_MS`, fail-closed; documented direct-`tsc` caveat), **corrected workspace coverage denominators** (package-local `--test-coverage-include=dist/**` so imported core `dist` no longer pollutes workspace rows — `mcp` 45.47→90.25, `rag` 19.70→94.82; evidence-based per-package thresholds in `scripts/coverage-thresholds.json` with `protectedException` for durable-leg packages shown separately, machine-readable `scripts/coverage-summary.json`), **machine-auditable release skip manifest** (`scripts/release-skip-manifest.mjs` → `scripts/release-evidence.json`: every surface recorded `pass`/`skip`/`blocked`/`protected` with reason and required env; the 33 protected/live skips named; a required surface without evidence records `blocked` and fails the release gate fail-closed — missing credentials/services can never convert into a green release), and **stabilized quality gates** (Biome 2.x `preset` config migration with zero lint diagnostics, the racy 150ms MCP bridge timing assert replaced by a deterministic barrier, load-sensitive guards carry documented `ponytail:` ceilings, machine-readable `lint-report.sarif` + `unused-report.json` retained by CI); no runtime contract delta (compat gate at 0.2.3: version literal only), no migration; then plan 022 the concurrent-state-and-durability-integrity cut: atomic model-budget reservation (`ModelRouterStateStore.reserveBudget`/`commitBudget`/`releaseBudget` with fencing tokens, `reservationTtlMs` expiry and unknown-usage reconciliation, rate/budget key-map caps with LRU eviction that never drops a held reservation), atomic conversation metadata (`SessionRecord.version` + `appendSession` `expectedVersion` CAS across Postgres/SQLite — create-only `0`, exact-version `N>0`, legacy last-write-wins when omitted; `SessionMetadataConflictError` `metadata_conflict` with versions only, HTTP 409; concurrent create/branch/archive single-statement with branch caps inside the CAS, archive wins, deleted rows never resurrect), single-consumer `EventMultiplexer` (`EventMultiplexerError` `ERR_PRISM_EVENT_MULTIPLEXER_SINGLE_CONSUMER` instead of silent queue sharing), restart-stable NATS durable consumer identity (`prism_<hmac16>` with no random suffix — crash-resumed subscribe continues from the last ack, orphaned 0.2.1 consumers reclaimed on clean stop), and bounded non-durable active-run registries (sweep + fail-closed 512 cap `ERR_PRISM_WORKFLOW_RUN_REGISTRY_OVERFLOW`); new regression surface `scripts/phase22-security.test.mjs` (4 blockers + gate accounting over built public entrypoints) + packed plain-JS `security22.mjs` consumer + the `@arnilo/prism/testing/state-concurrency-conformance` harness (7 probes across memory/Postgres/SQLite/NATS legs, no timing-only sleeps) + the `scripts/phase22-conformance.test.mjs` gate; additive-only compat (new exports only, no removals); forward-only migrations 008 (`prism_sessions.version`) and 003 (`prism_model_router_budgets.reservations`); migration `0.2.1 → 0.2.2`; then plan 021 the provider-completion-and-outbound-trust-boundaries cut: strict stream completion is the shared OpenAI-compatible default (truncated streams fail `incomplete_delta`, explicit `strictCompletion: false` opt-out), bounded success bodies via `readBoundedResponseJson` on all discovery/quota/embeddings/upload/OAuth JSON endpoints (65,536-byte ceiling, depth/property/shape caps), DNS-pinned OIDC JWKS/OPA/content fetches through the core `pinnedFetch` primitive with 3xx redirects rejected outright (private/metadata answers fail closed `ssrf_denied`), shared bounded OAuth device/token polling (`pollDeviceCodeToken`) across provider-openai and credentials-node, and the four edge fixes (Azure/Vertex credential-once, Bedrock duplicate-case/repeated-query SigV4 canonicalization, OpenAI upload failed-DELETE retention, cache `__overflow__` tokens-only); public-entrypoint threat-suite `scripts/phase21-security.test.mjs` + packed plain-JS consumer; additive-only compat (MCP transport helpers re-exported from core, no removals); migration `0.2.0 → 0.2.1`; then plan 020 the fail-closed runtime-and-sandbox-security cut on the 0.2.x review-remediation line: durable-resume decision validation in core (`assertValidAgentRunResume` — unknown decisions/malformed batches fail closed with `ERR_PRISM_DECISION_*` before any state claim, checkpoint write, or tool execution; server parser remains defense in depth), isolated work-tool subprocess environments (`@arnilo/prism-work-tools` — fixed base allow-list + explicit env + forced HOME/telemetry + late-bound per-identity tokens, 64-name/64-KiB caps, absolute binary/configDir, linear output capture), and explicit sandbox capabilities (`@arnilo/prism-coding-security` — `SandboxAdapter.capabilities` with omission-is-false fail-closed resolution, `SandboxCodingComposition.capabilities` from verified wiring, `containmentClaim` deprecated as the conservative projection; Docker reports only verified controls, native reports filesystem/process/privilege `false`); public-entrypoint security conformance (`scripts/phase20-security.test.mjs`, wired into `security:threat-suites`), packed plain-JS consumer regressions, and the sandbox-browser workflow's fail-loud Docker/native capability evidence gate — 0.2.0 never ships while a blocker is skipped; migration and rollback notes in `docs/migration.md` `0.1.7 → 0.2.0`, store-compatible with 0.1.7 in both directions; 0.1.7 was the performance-and-DX patch — dependency-free `createCacheTelemetry()` per-provider/model cache hit/miss aggregator (bounded cardinality with `__overflow__`, token counters/rates only, host-activated), host-configurable `ModelRouterSelectionPolicy` on `createModelRouter` with the reference `createCostLatencySelection` (ModelCost rank then in-memory latency EMA, default ordered behavior byte-identical), `prism providers add <name>` OpenAI-compatible provider scaffold (manifest/provider/models/cache/conformance test/docs stub, npm-name + traversal + symlink-escape validation, placeholders only), and the async `AgUiProjection` verification closeout (plan 009 Task 15 evidence recorded, no new code); plan 017 the documented breaking cut — deprecated-option removal with `docs/migration.md` `0.1.4 → 0.1.5` section and reviewed compat-baseline regeneration via `--allow-break` then `--update-baseline`: the inert provider request knobs, `RunOptions.maxToolRounds`, observational-memory flat settings keys + top-level worker aliases, `ReadToolOptions.autoResizeImages`, `INIT_PROVIDERS`; all removals fail closed naming their replacement; plan 016 internal god-module split — `agents.ts`/`contracts.ts` reorganized behind barrel re-exports with a byte-identical public entry surface, measured tree-shaking improvement in `scripts/phase16-baseline.json`, and additive `@arnilo/prism-browser` Chrome DevTools Protocol capabilities — `browser_evaluate`/`browser_observe` and `block_urls`/`unblock_urls`/`throttle`/`emulate` act actions; plan 015 dead-code and deprecation hygiene on the frozen 0.1.x line — parameterized benchmark runner `scripts/benchmark.mjs` absorbing the per-version runners, archived review-coverage evidence in `docs/_evidence/`, non-blocking unused-code sweep `npm run sweep:unused`, opt-in checkpoint persistence for loaded-skill names and read-path sets; plan 014 Alibaba provider enrichment — embeddings, video input, verified compatible-mode surface decision table; plan 013 post-release hardening — build single-flight, MCP SSE relay test, combined coverage summary, canonical manifest-count narrative, ACP modes/config persistence guidance; Phase 12 release-candidate hardening; plan 012 — freeze manifest, compatibility matrix, upgrade matrix, packed-install e2e journeys, restart-recovery evidence, capacity envelopes, security policy), exact-peer/install/tarball rules, deterministic resumable publication and publish dry-run, frozen 0.1.x compatibility and support matrix (Node/PostgreSQL/platform/provider/protocol pins and unsupported combinations, machine-checked against `scripts/phase12-freeze-manifest.json`), protected PostgreSQL gate, pinned supply-chain gates, offline tests, the 0.0.15 provider/AI-SDK/RAG/memory protected live-canary matrix, and sandbox-browser Docker/Playwright gates. 0.2.6 (plan 026 Task 7) adds the protected coding journey: `scripts/phase26-coding-journey.test.mjs` runs a packed consumer through real provider calls, a digest-pinned Docker sandbox, the durable Postgres worktree lifecycle, provider-driven ACP edits with policy approval, named checks with `diagnosticDelta`, patch review over the server ArtifactService, cross-replica process recovery, durable cancellation, real GitHub PR push/reconcile/cleanup, host Playwright inspection, and the host PTY adapter (frozen profile) — the retained `scripts/phase26-coding-journey-report.json` gates release evidence (pass/blocked/protected, never a passing skip).
|
|
138
139
|
- [0.1.0 / 1.0 readiness gates](0.1.0-readiness.md): command-per-gate 1.0 readiness table — frozen API surface + compat gate, migration/docs tripwires, budget table, live-suite matrix, security matrix, current-line status (**0.2.5** current line; 0.1.7 terminal 0.1.x baseline), signed-publication/live-canary prerequisites for 1.0, and Phase 12 demand-evidence entry criteria.
|
|
139
140
|
- [Review coverage archive](_evidence/): per-phase evidence freezes (plans 067–079, releases 0.0.4–0.0.16) — traceability matrices, provider validation, capability/primitive/limit matrices, benchmark budgets, and artifact-diet findings; tarball-excluded, kept in-repo for audit.
|
|
140
141
|
|
package/docs/migration.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Migration guide
|
|
2
2
|
|
|
3
|
+
## 0.2.7 → 0.2.8 ACP adoption fixes (additive)
|
|
4
|
+
|
|
5
|
+
Release **0.2.8** (plan 028) tightens ACP coding-host interop and adds the spawnable `@arnilo/prism-acp-agent` entrypoint. **Additive-only: no exported declaration removed or changed, no persisted 0.2.7 shape repurposed.**
|
|
6
|
+
|
|
7
|
+
Hosts that already speak ACP should re-check these wire behaviors (deny-by-default unchanged unless a new seam is wired):
|
|
8
|
+
|
|
9
|
+
- `usage_update` is omitted when the host cannot report a context window (never `size = used`).
|
|
10
|
+
- A terminal run `error` rejects `session/prompt` with `ERR_PRISM_ACP_RUN` instead of an `Agent error:` transcript chunk.
|
|
11
|
+
- Only boolean config options are advertised; `set_config_option` on a select option fails `ERR_PRISM_ACP_CAPABILITY`.
|
|
12
|
+
- Permission option kinds on the wire are `allow_once` / `allow_always` / `reject_once` / `reject_always`.
|
|
13
|
+
- New optional seams (`sessions.transcript`, `sessions.title`, `commands.list`, `capabilities.usage.contextWindow`, `createCodingToolProjection`, image `toolResult`) emit nothing when unwired.
|
|
14
|
+
|
|
15
|
+
No store migration. Rollback = restore the 0.2.7 manifests/tag. The added exports and `@arnilo/prism-acp-agent` simply disappear.
|
|
16
|
+
|
|
3
17
|
## 0.2.6 → 0.2.7 enterprise ERP production readiness (additive)
|
|
4
18
|
|
|
5
19
|
Release **0.2.7** (plan 027) adds the enterprise ERP production-readiness primitives behind optional host-activated seams: the transactional outbox/inbox + bounded dispatcher, the durable saga compensation/reconciliation engine, multi-party separation-of-duties approvals, signed hash-chained audit export with WORM/SIEM sinks, field-level classification + fail-closed redaction, and the deterministic ERP invariant evals. **Additive-only: no exported declaration removed or changed, no persisted 0.2.6 shape repurposed.**
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
Prism is published as **
|
|
5
|
+
Prism is published as **51 publishable manifests**: the root `@arnilo/prism` core package plus **50 workspace packages** — 14 provider adapters, 9 `prism-*` family/profile packages, and 27 capability packages. (Generated by `node scripts/package-truth.mjs` → `scripts/package-truth.json` — the manifest-derived single source for counts, provider membership, umbrella closures, and profile closures.) The 51st manifest is the 0.2.8 plan 028 spawnable ACP agent package `@arnilo/prism-acp-agent` (thin stdio wiring over `@arnilo/prism-ag-ui`; new packages land only when a closeout demands them). This page describes how they are packed, what each tarball contains, how to install them, the required `@arnilo/prism` peer dependency, the release workflow, and the offline test budget. The measurable 1.0 readiness gates (command-per-gate) live in [`0.1.0-readiness.md`](./0.1.0-readiness.md).
|
|
6
6
|
|
|
7
|
-
Core `@arnilo/prism` ships runtime, CLI, templates, and docs. Every code package has a required `@arnilo/prism@0.2.
|
|
7
|
+
Core `@arnilo/prism` ships runtime, CLI, templates, and docs. Every code package has a required `@arnilo/prism@0.2.8` peer; profiles are pure manifests. Installation activates no provider, listener, database, browser, credential, or tool capability.
|
|
8
8
|
|
|
9
|
-
Current **
|
|
9
|
+
Current **51** publishable manifests (root + 50 workspace packages):
|
|
10
10
|
|
|
11
11
|
`@arnilo/prism`, `@arnilo/prism-ag-ui`, `@arnilo/prism-browser`, `@arnilo/prism-coding-agent`, `@arnilo/prism-coding-security`, `@arnilo/prism-compaction-llm`
|
|
12
12
|
`@arnilo/prism-compaction-observational-memory`, `@arnilo/prism-credentials-node`, `@arnilo/prism-enterprise-postgres`, `@arnilo/prism-evals`, `@arnilo/prism-mcp`, `@arnilo/prism-memory`
|
|
@@ -94,7 +94,7 @@ A packed tarball contains only public compiled output and release files:
|
|
|
94
94
|
- Code packages ship `README.md`, `LICENSE`, and `CHANGELOG.md`; family/profile packages ship `README.md` and `CHANGELOG.md`.
|
|
95
95
|
- The core tarball additionally ships the full `docs/` directory (the docs hub) and `templates/init/` used by `prism init`.
|
|
96
96
|
- `dist/cli.js` and the `bin` link in core.
|
|
97
|
-
- **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.2.
|
|
97
|
+
- **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.2.8.tgz`; first-party packages produce `arnilo-prism-provider-<name>-0.2.8.tgz` / `arnilo-prism-compaction-<name>-0.2.8.tgz` / `arnilo-prism-coding-agent-0.2.8.tgz`; family/profile packages produce `arnilo-prism-{providers,compaction,base,code,sdk,all}-0.2.8.tgz`. The CLI bin name `prism` is unaffected by the package name (`npx prism` still works; npm allows the bin field to differ from the package name).
|
|
98
98
|
|
|
99
99
|
Excluded from every tarball by `files` negation:
|
|
100
100
|
|
|
@@ -348,6 +348,27 @@ npm run release:publish -- --version 0.2.6 --dry-run --allow-dirty --allow-untag
|
|
|
348
348
|
|
|
349
349
|
Protected evidence (never a passing skip): the durable recovery/workspace conformance legs (real Postgres two-replica split-brain fence, cross-replica cancellation, terminal-before-recovery), the protected PTY leg (real PTY host adapter), the protected real coding journey (`scripts/phase26-coding-journey-report.json` — pass/blocked/protected, never a passing skip; runs in `.github/workflows/coding-journey.yml` with real provider/Docker/Playwright/GitHub/Postgres/PTY services), and the live canaries (provider OIDC/OPA, MCP, A2A, Brave — always `protected` rows in the manifest, never `pass`). The release skip manifest names every skip class with its required env; missing protected evidence records 0.2.6 as **blocked**, never a passing skip.
|
|
350
350
|
|
|
351
|
+
### 0.2.8 publish handoff (plan 028 Task 18)
|
|
352
|
+
|
|
353
|
+
**Decision: GO when the operator prerequisites below are recorded.** Release **0.2.8** (plan 028) is the ACP adoption-fixes cut on the 0.2.x review-remediation line. API surface **additive-only** (plain reviewed compat gate at 0.2.8: expected deltas are the version literal plus the plan 028 additive exports — `ToolKind`/`kind` on `ToolDefinition`, `AgentFinishReason`, `createCodingToolProjection`/`AgUiProjectedImage`/`AgUiProjectedToolResult`, `AcpCommand`/`AcpCommandsSeam`, `ERR_PRISM_ACP_RUN`, `acpImageBytes`/`acpCommandsPerUpdate`, and the new `@arnilo/prism-acp-agent` package; zero removals; baselines regenerated with `--update-baseline`, no `--allow-break`). Client names are scrubbed; `scripts/check-client-neutrality.mjs` is part of `release:gate`. ACP B1–B5 and F1–F10 as recorded in `plans/028-Release-0-2-8-ACP-Adoption-Fixes.md`. Release graph is **51** publishable manifests at exact **0.2.8** (root + 50 workspace). Store compatibility with 0.2.7: **compatible, no migration**.
|
|
354
|
+
|
|
355
|
+
**Rollback notes.** Rollback = restore the 0.2.7 manifests/tag. No persisted 0.2.7 shape changed; the added exports and `@arnilo/prism-acp-agent` simply disappear.
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
node scripts/release.mjs bump --from 0.2.7 --to 0.2.8 # already applied by Task 18; idempotent
|
|
359
|
+
npm test
|
|
360
|
+
PRISM_CLIENT_NAMES=<names> node scripts/check-client-neutrality.mjs
|
|
361
|
+
npm run sdk:ready
|
|
362
|
+
node scripts/release.mjs gate --version 0.2.8
|
|
363
|
+
npm run pack:dry-run
|
|
364
|
+
npm audit --audit-level=moderate
|
|
365
|
+
node scripts/scan-secrets.mjs && npm sbom --sbom-format spdx > security-artifacts/sbom.spdx.json && node scripts/verify-sbom.mjs
|
|
366
|
+
npm run release:check -- --version 0.2.8 --report /tmp/prism-0.2.8-preflight.json
|
|
367
|
+
npm run release:publish -- --version 0.2.8 --dry-run --allow-dirty --allow-untagged --report /tmp/prism-0.2.8-dry-run.json
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Protected evidence stays the same classes as 0.2.7 (Postgres durable legs, live canaries). Missing protected evidence records **blocked**, never a passing skip. Publication remains the operator handoff (signed `v0.2.8` tag + npm OIDC).
|
|
371
|
+
|
|
351
372
|
### 0.2.7 publish handoff (plan 027 Task 10)
|
|
352
373
|
|
|
353
374
|
**Decision: GO when the operator prerequisites below are recorded.** Release **0.2.7** (plan 027) is the enterprise ERP production-readiness cut on the 0.2.x review-remediation line. API surface **additive-only** (plain reviewed compat gate at 0.2.7: expected deltas are the version literal plus the plan 027 additive exports — ERP outbox/inbox + dispatcher, saga engine, SoD approvals, audit export, field policy, ERP invariant evals; zero removals; baselines regenerated with `--update-baseline`, no `--allow-break`; freeze manifest `scripts/phase27-freeze-manifest.json` records per-task evidence tokens, state machines, caps, the demand registry, measured HA/DR/classification/journey numbers, and the explicit 0.3.0 blocker). Nine roadmap items, **no exactly-once claim, additive forward-only migrations**: (1) **transactional outbox/inbox** (`erp-messaging`, Task 1) — `ErpOutboxStore`/`ErpInboxStore` + bounded `ErpOutboxDispatcher` with claim-token CAS, `FOR UPDATE SKIP LOCKED`, `ON CONFLICT DO NOTHING` idempotent append, at-least-once delivery with explicit unknown-outcome, dead-letter/replay requiring verified tenant `AgentIdentity`; migration `004_erp_messaging` (`prism_erp_outbox`/`prism_erp_inbox`, 14+4 columns, 3 partial indexes). (2) **saga compensation and reconciliation** (`saga`, Task 2) — `defineSaga`/`runSaga`/`resumeSaga` over existing CheckpointStore + LeaseStore (`prism.workflow.saga`), reverse-order compensation, unknown-outcome detection, manual resolution requiring verified identity + bounded reason + audit ref, stable tenant-scoped operation keys, redacted snapshots, `MAX_SAGA_STEPS=100`. (3) **multi-party and separation-of-duties approvals** (`approvals`, Task 3) — `ApprovalStore` with role/quorum rules, requester/approver separation, any-party-veto rejection, delegated authority (max depth 8), expiry checked at every protected transition, atomic grant consumption in the host transaction, `policyRevision` pin denying on mismatch; migration `005_erp_approvals` (`prism_erp_approvals`, JSONB decisions, `FOR UPDATE` row lock). (4) **tamper-evident audit export** (`audit-export`, Task 4) — `createAuditExporter` with WORM-then-SIEM ordering, hash-chained record envelopes (genesis 0x64 zeros), `verifyAuditBatch` independent verification, `AuditCursorStore` CAS, SIEM best-effort pending replay (8-entry cap), legal-hold flag preservation, RFC 8785 canonical JSON for digests; Prism does not certify NIST/SIEM/WORM compliance programs. (5) **secret-manager adapters demand-gated** (Task 5) — Vault/AWS/Azure/GCP stay **deferred** behind the demand gate (no named consumer; no adapter ships; `scripts/phase27-demand-gate.mjs` enforces zero ambient discovery). (6) **HA registries and recovery** (`ha-dr`, Task 6) — two-replica drill on real Postgres proves failover within lease TTL+5s (measured 4100 ms vs 9000 ms ceiling), idempotent outbox re-append on uncertain-commit replay, stale fence/revision write rejection, exactly-one lease owner, tenant isolation fail-closed. (7) **backup, restore, and migration rollback evidence** (Task 7) — `pg_dump`/`pg_restore` custom-format backup (108,291 B / 122 ms / 382 ms restore), 0.2.6→0.2.7 migration forward+rollback rehearsed (5 migrations), PITR RPO 0 s / RTO 1 s (recovery 1163 ms); production rollback is roll-forward repair only (no down migrations). (8) **field-level data classification and redaction** (`field-policy`, Task 8) — `applyFieldPolicy`/`FieldPolicy`/`createProtectedFieldPolicy` at the redaction, audit-export, and OpenTelemetry seams; unknown-label deny-on-outbound fail-closed default, sparse-copy walker, measured overhead peak 99.8% of the redactor-walk baseline (cap 110%). (9) **ERP release journey** (`erp-evals`, Task 9) — `erpInvariantDataset` + `createErpInvariantScorers` (8 hard 0/1 gates consuming structured facts only) + `scripts/phase27-erp-journey.test.mjs` exercising identity/policy/budget/SoD-approval/outbox/saga-compensation/audit-export/legal-hold/classification/failover/restore end-to-end (4815 ms, all 8 invariants pass). Release graph stays **50** publishable manifests at exact **0.2.7**; zero new runtime dependency names (core remains dependency-free); 43 code packages + 6 pure-manifest family/profile.
|
|
@@ -500,7 +521,7 @@ git push origin v0.1.7 # tag push triggers release.yml publish job (prove
|
|
|
500
521
|
|
|
501
522
|
### 0.1.6 publish handoff (plan 018 Task 7)
|
|
502
523
|
|
|
503
|
-
**Decision: GO when the operator prerequisites below are recorded.** Release **0.1.6** (plan 018) is the coding-agent capability-closeouts patch on the frozen 0.1.x line — **additive-only** vs 0.1.5 (plain compat gate at 0.1.6 passed with 0 breaking declaration deltas; the baseline text was regenerated with `--update-baseline` for the version literal only, no `--allow-break` anywhere). Five demand-gated closeouts shipped, each flipped to `demanded` by named demand evidence (operator `arn` for native-sandbox/doc-reader/delete-glob/checkpoint-bodies, user
|
|
524
|
+
**Decision: GO when the operator prerequisites below are recorded.** Release **0.1.6** (plan 018) is the coding-agent capability-closeouts patch on the frozen 0.1.x line — **additive-only** vs 0.1.5 (plain compat gate at 0.1.6 passed with 0 breaking declaration deltas; the baseline text was regenerated with `--update-baseline` for the version literal only, no `--allow-break` anywhere). Five demand-gated closeouts shipped, each flipped to `demanded` by named demand evidence (operator `arn` for native-sandbox/doc-reader/delete-glob/checkpoint-bodies, a consuming-app user for acp-session-store) before its task landed; the demand-gate registry (`scripts/phase18-freeze-manifest.json`) machine-checks demanded ⇒ implemented, deferred ⇒ untouched. Shipped: (1) **durable ACP session store** — `@arnilo/prism-ag-ui` `AcpSessionStore` host seam (`save`/`loadAll`/`evict`), persisted `{sessionId, ownership, modeId, configValues, cwd, additionalDirectories, updatedAt}`, lazy ownership-scoped restore, fail-closed drops, absent seam = 0.1.5 behavior; (2) **network-free native sandbox** — `createNativeSandbox` in `@arnilo/prism-coding-security` (fresh netns per command via the OS `unshare` binary, chained ulimits with `|| exit 126`, argv-only exec, cwd containment, process-group kill, env allow-list, Linux-only fail-closed); (3) **bounded PDF/Office document reader** — new optional package `@arnilo/prism-document-reader` (the 50th manifest, graph 49 → 50) with optional `pdf-parse`/`mammoth` peers fail-closed at creation, magic-byte gating, null fall-through, caps + redaction at the adapter boundary; (4) **recursive delete + brace-expanding glob** — per-call `recursive: true` with fan-out cap and symlink-unlink-not-follow, host-selected/per-call `braceExpansion` bounded to 128 alternatives / 4096 expanded bytes, fail-closed on overflow/malformed braces; (5) **checkpoint persistence for loaded-skill bodies** — opt-in `includeSkillBodies` on run + resume options (names-only stays default, 0.1.3 shapes byte-identical), ≤64 bodies / ≤256-char names / ≤262144-byte bodies / ≤1 MiB total, `maxStateBytes` refusal, redacted at rest, registry-independent resume render. Store compatibility with 0.1.5: **compatible, no migration** (additive-only; no persisted-shape change; `docs/migration.md` gains no entries). Exit gate green: npm test core 1,433/1,433 + 190 script gates (incl. phase18-freeze done-phase), `sdk:ready` exit 0, audit 0 moderate, pack dry-run 50/50 twice byte-identical, budget/benchmark gates green; evidence in `scripts/phase18-baseline.json` `exitGate`. Rollback = restore the 0.1.5 manifests/tag.
|
|
504
525
|
|
|
505
526
|
```bash
|
|
506
527
|
# Operator prerequisites recorded: clean tree at the v0.1.6 tag candidate, GPG key, npm OIDC publisher.
|
|
@@ -817,8 +838,8 @@ Audit fixes, dependency updates, and security patches land only for the supporte
|
|
|
817
838
|
|
|
818
839
|
## Extension and configuration notes
|
|
819
840
|
|
|
820
|
-
- **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional **exact** `@arnilo/prism@0.2.
|
|
821
|
-
- **Public access.** All
|
|
841
|
+
- **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional **exact** `@arnilo/prism@0.2.8` peer (`peerDependenciesMeta` must not mark `@arnilo/prism` optional; other peers such as `playwright-core` may be optional). **Peer-version policy (plan 024, Decision A — exact pins):** the peer spec is the bare exact current version — no `~`/`^`/`>=` range, no `*` — for the whole 0.2.x line, and all `@arnilo/prism-*` packages move at the same version (the **atomic-upgrade rule**). A partial upgrade (e.g. `@arnilo/prism@0.2.8` installed with a package peering `@arnilo/prism@0.2.9`) is unsupported and fails clearly at install time with `ERESOLVE unable to resolve dependency tree` naming the conflicting peer — never a silent install of a pair that was never tested together. A third-party `@arnilo/prism-*` adapter declares the same exact peer on the documented current version; an unsupported mixture fails at install time, not at runtime. The range widens to `^1.0.0` at the 1.x stable release (the 1.0 readiness gates go operator-green on the 0.2.x line); rollback of a release moves the pins back atomically with the manifests/tag. Inside the workspace each package also declares `"@arnilo/prism": "file:../.."` in `devDependencies` so `npm install` resolves the peer locally; that devDependency is stripped from consumer installs and is not a runtime dependency.
|
|
842
|
+
- **Public access.** All 51 manifests (root + 50 workspace packages: 44 code packages + 6 pure-manifest family/profile packages — the 9 `prism-*` family/profile set is the 6 pure-manifest profiles plus the 3 code packages `prism-caveman`, `prism-openapi-tools`, `prism-ponytail`) declare `"publishConfig": { "access": "public" }`; the publisher also passes `--access public` explicitly because scoped packages otherwise default to restricted on first publish.
|
|
822
843
|
- **Map retention knob.** Source maps are emitted locally but stripped from tarballs by `!dist/**/*.map`. Removing that `files` negation ships maps in releases (larger tarballs, better consumer stack traces).
|
|
823
844
|
- **Release workflow.** `.github/workflows/release.yml` has six jobs. `verify` runs network-free SDK readiness on Node 24; `node20-compat` builds/imports every public root `exports` default target on Node 20 for declared `engines.node >=20` (docs examples need Node >=22.6 native TypeScript stripping); `postgres-integration` uses `pgvector/pgvector:pg16`; `supply-chain` runs high-severity audit, SPDX/license policy, and tracked-source secret scanning; and tag-only `codeql-release` runs SAST. Tag-only `publish` needs all five gates, preserves clean exact-tag/version/topological publication, and alone receives `NPM_TOKEN`, `id-token: write`, and `attestations: write`. Before npm publish it packs all current tarballs, generates checksums plus SPDX, scans unpacked public artifacts, creates GitHub attestations for tarballs and SBOM, then retains artifacts for 30 days. Registry state remains the resumable journal. Local `npm run release:dry-run` remains network-free SDK readiness; local PostgreSQL coverage is `PRISM_TEST_POSTGRES_URL=... npm run test:postgres`.
|
|
824
845
|
- **Adding a package.** New workspace packages are picked up automatically by `npm run build --workspaces`, `npm test --workspaces`, `npm run pack:dry-run`, the packaging guard (`src/__tests__/packaging.test.ts`), and the install-smoke test (`src/__tests__/install-smoke.test.ts`) via the workspace glob; add the package to both tests' config arrays for explicit per-package assertions.
|
|
@@ -992,7 +1013,7 @@ Every release gate maps to an exact enforcement test or command, so the checklis
|
|
|
992
1013
|
| Root SDK export surface freeze | `public-export-contract.test.ts` `root export surface is frozen` snapshots every value and type export of `src/index.ts` (107 value + 69 type) so any add/remove is a deliberate test update; `every frozen value export resolves at runtime` rebuilds `dist/index.js` and asserts each value export is present (catches build drift), and `every frozen type export appears in the built type declarations` asserts each type export is in `dist/index.d.ts`. |
|
|
993
1014
|
| Examples compile and are listed; runnable demos execute | `npm run typecheck` runs `tsc -p examples --noEmit`; `docs.test.ts` checks every `examples/*.ts` file is listed in `examples/README.md`, then runs demos offline and scans output for secrets. |
|
|
994
1015
|
| Examples run to completion with no secret leakage | `docs.test.ts` `examples_demos_run_to_completion_and_emit_no_secret` runs each demo (Node strips TypeScript types natively) with exit-0 and real-secret scans; `external_app_example_*` pins the DB-backed adapter reference exercising the `RunLedger`, branch-handle checkout, fork, and prior-run resume. |
|
|
995
|
-
| Tarball excludes built tests, source maps, and source | `packaging.test.ts` rejects `dist/__tests__/`, `*.map`, `src/`, `plans/`, and internal files; confirms every package ships README/changelog (and code packages ship LICENSE), core ships docs + CLI, and every export target exists. `prism-all` reaches
|
|
1016
|
+
| Tarball excludes built tests, source maps, and source | `packaging.test.ts` rejects `dist/__tests__/`, `*.map`, `src/`, `plans/`, and internal files; confirms every package ships README/changelog (and code packages ship LICENSE), core ships docs + CLI, and every export target exists. `prism-all` reaches 44 of the 50 workspace packages (21 direct + 23 transitive); the deliberate Caveman/Ponytail opt-outs and the other non-closure packages (document-reader, OpenAPI tools, NATS) are not in its install set. |
|
|
996
1017
|
| NeuralWatt package/docs/examples release gate | `packaging.test.ts` pins `@arnilo/prism-provider-neuralwatt` package exports/type declarations and `@arnilo/prism-providers`/`@arnilo/prism-all` membership; `docs.test.ts` asserts `docs/index.md` links `providers/neuralwatt.md` and `provider-caching.md`, and that `examples/cache-aware-prompt-assembly.ts` plus `examples/neuralwatt-agent-run.ts` exist and are listed. |
|
|
997
1018
|
| Enterprise PostgreSQL package/docs/example gate | Packaging/install/public-contract tests include `@arnilo/prism-enterprise-postgres`; `docs.test.ts` pins its API page, four-store migration/ownership/unknown-outcome/async-router guidance, and `examples/enterprise-postgres-state.ts`; `npm run test:postgres` exercises migration, restart, contention, and cleanup with an explicit database URL. |
|
|
998
1019
|
| Version graph and resumable publication | `release.test.ts` covers exact package/lock/range validation, topological order, registry collisions, dry-run, interrupted reports/resume, clean tagged git state, provenance/public/tag arguments, and token-safe errors. `release:check` and `release:publish` derive the workspace graph without a manual package list. |
|