@arnilo/prism 0.2.5 → 0.2.6
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 +5 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/docs/0.1.0-readiness.md +10 -10
- package/docs/acp.md +2 -0
- package/docs/browser-automation.md +1 -1
- package/docs/coding-agent-tools.md +5 -4
- package/docs/coding-review-and-diagnostics.md +76 -0
- package/docs/coding-security.md +2 -0
- package/docs/coding-workspaces.md +69 -0
- package/docs/forge-integration.md +6 -0
- package/docs/index.md +4 -4
- package/docs/indexed-code-search.md +82 -0
- package/docs/language-intelligence.md +15 -0
- package/docs/migration.md +16 -0
- package/docs/process-sessions.md +58 -3
- package/docs/release-and-install.md +48 -3
- package/docs/work-artifacts-and-review.md +4 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.6] - 2026-08-16
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- **Release 0.2.6 (plan 026)** is the fully-featured coding-agent-readiness cut on the 0.2.x review-remediation line. API surface **additive-only** (plain reviewed compat gate at 0.2.6: expected deltas are the version literal plus the Task 1-6 additive exports — PTY backend/handle types, the indexed-search seam, the workspace lifecycle, process/ACP recovery, the review manifest + diagnostics; zero removals; no `--allow-break`; freeze manifest `scripts/phase26-freeze-manifest.json` records per-task evidence tokens, state machines, caps, and the demand registry). Seven roadmap items with threat model T1-T8, ownership tests, no implicit activation, package budget, docs entry, and protected end-to-end evidence: (1) **host-selected PTY/interactive terminal backend** (`pty-backend`, plan 026 Task 1) — `createProcessSessions` gains an optional `ptyBackend` seam with explicit `capabilities.resize` (never duck-typed); `pty: true` without a backend fails byte-compatibly with `ERR_PRISM_PROCESS_PTY_UNSUPPORTED` before spawn; bounded terminal geometry/TERM/attach-timeout/resize-rate/metadata caps (`ERR_PRISM_PROCESS_PTY_LIMIT`), generic `ERR_PRISM_PROCESS_PTY_BACKEND` errors that never leak backend text, NUL rejected in PTY input as a policy error, terminal output stays untrusted with no parser/emulator, backend loss surfaces as `unknown` with `exitCode: null`. (2) **scalable indexed code-search seam** (`indexed-search`, Task 2) — `createIndexedRepositoryOperations` composes a host-owned incremental index backend (`update/remove/search/status/dispose`, `capabilities.semantic` explicit) with the bounded literal search as the unchanged default; `indexed_literal`/`semantic` modes fail closed on stale/failed/unsupported/untrusted indexes (`ERR_PRISM_INDEX_*`), no silent semantic-to-literal downgrade, results labeled `untrusted_index`, containment/score/snippet caps enforced, 100k-entry benchmark p95 <= 250 ms. (3) **ownership-scoped multi-repository and worktree lifecycle** (`workspace-lifecycle`, Task 3) — `createCodingWorkspaceLifecycle` over CheckpointStore CAS + LeaseStore fencing (`prism.coding-agent.workspace.v1` records, schemaVersion 1): deterministic `ws-` ids, locked worktrees with `prism-workspace:` reasons, credential-free remote fingerprints (sha256 of redacted URL + default branch, never the URL), idempotent create, verify revalidating repository/worktree identity, and a cleanup refusal matrix (dirty/locked/unowned/missing/mismatched/main) with `ERR_PRISM_WORKSPACE_*` codes; `GitOperations` gains worktree `lock`/`unlock` and `fingerprint()`. (4) **forge breadth demand-gated** (Task 4) — GitLab/Bitbucket adapters stay **deferred** in the demand registry (no named consumer/date/use case recorded), so no adapter source ships and the forge barrel contains no provider name; activation requires a recorded named consumer. (5) **durable ACP/live-task and managed-process recovery** (`durable-recovery`, Task 5) — `ProcessSessions` persists bounded intent/metadata before spawn in `prism.coding-agent.process.v1` records (never handles/env/tokens/raw output), serializes per-record transition CAS writes, and `recover()` is attach-if-attested reporting `attached|terminal|unknown` with no fabricated exit code and no PID probing; per-record leases (30 s/300 s) fence two replicas (split-brain conformance green on real Postgres); `PersistedAcpSession` gains an additive optional `activeRun` ref (0.2.5 records stay readable) and `createAcpRunRecovery` re-resolves status against `AgentRunLifecycle` (suspended keeps pending approval ids, unprovable in-flight -> unknown, never a restarted prompt) with durable ownership/version/fence-checked cancellation in `prism.coding-agent.cancel.v1` markers that never replays a pending/dispatched tool; `ERR_PRISM_RECOVERY_*` codes. (6) **bounded patch review and incremental diagnostics** (`review-diagnostics`, Task 6) — `createCodingPatchReviewManifest` builds a digest-bound manifest (patch sha256 + repository/worktree/base/head identity + changed paths/diffstat + checks + diagnostic summaries, never a raw patch body) composed over the server `ArtifactService`; `assertCodingPatchAccepted` derives `pending|accepted|rejected|superseded` bound to the exact artifact revision and digest, refuses stale acceptance, and never applies/commits/pushes/merges; `LanguageIntelligence` gains opt-in full-content `syncDocument` (monotonic versions) and `diagnosticDelta` (push/pull with resultId reuse, stale-version guards) plus `normalizeDiagnostics` with deterministic added/removed/unchanged deltas and host-supplied check parsers; LSP stays strictly opt-in (nothing spawns from tool factories or agent assembly). (7) **protected real coding journey** (`coding-journey`, Task 7) — `scripts/phase26-coding-journey.test.mjs` runs a packed consumer through real host services only (provider call, digest-pinned Docker sandbox, Postgres worktree lifecycle, provider-driven ACP edit with policy approval, named check + `diagnosticDelta`, patch review over the server artifact store, cross-replica process recovery, durable cancellation, real GitHub push/lookup-before-create PR/reconcile/cleanup, host Playwright inspection, host PTY adapter in the frozen profile) under the frozen wall/cleanup ceilings with run-suffix side effects and per-step idempotent cleanup; missing credentials/services or skipped substeps record `blocked`, never a passing skip; the retained `scripts/phase26-coding-journey-report.json` carries timings/states/ids only and gates release evidence (pass/blocked/protected). Release graph stays **50** publishable manifests at exact **0.2.6**; zero new runtime dependency names (core remains dependency-free). Store compatibility with 0.2.5: **compatible, additive** (three new versioned record namespaces + the optional ACP `activeRun` field; rollback = stop 0.2.6 workers, mark active PTY/process/recovery records unknown, restore the 0.2.5 manifests/tag — see `docs/migration.md` `0.2.5 → 0.2.6`). Exit gate green (core + script gates incl. `phase26-freeze` 11, `phase26-index-benchmark`, coding-agent 348 + ag-ui 203 workspace suites, coverage gate, `sdk:ready`, audit 0 moderate, pack dry-run 50/50 twice byte-identical, plain reviewed compat gate at 0.2.6 — version literal + additive exports only, protected Postgres recovery/workspace conformance 8/8, protected PTY leg 4/4, protected real coding journey per the retained report, release-evidence manifest with zero blocked surfaces, evidence in `scripts/phase26-baseline.json`). **Publication remains the operator handoff** (`docs/release-and-install.md` `0.2.6 publish handoff` — signed `v0.2.6` tag + npm OIDC).
|
|
7
|
+
|
|
3
8
|
## [0.2.5] - 2026-08-15
|
|
4
9
|
|
|
5
10
|
### Changed
|
package/dist/index.d.ts
CHANGED
|
@@ -109,5 +109,5 @@ export { createToolParameterValidator, createToolRegistry, dispatchToolCall, fil
|
|
|
109
109
|
export type { ResolvedUseCaseModel, ResolveUseCaseModelInput, UseCaseModelBinding, } from "./use-case-model.js";
|
|
110
110
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
111
111
|
export declare const name = "prism";
|
|
112
|
-
export declare const version = "0.2.
|
|
112
|
+
export declare const version = "0.2.6";
|
|
113
113
|
export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,6 @@ export { DEFAULT_TOOL_RESULT_FOLD_MAX_SUMMARY_BYTES, DEFAULT_TOOL_RESULT_FOLD_MI
|
|
|
60
60
|
export { createToolParameterValidator, createToolRegistry, dispatchToolCall, filterTools } from "./tools.js";
|
|
61
61
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
62
62
|
export const name = "prism";
|
|
63
|
-
export const version = "0.2.
|
|
63
|
+
export const version = "0.2.6";
|
|
64
64
|
export const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
|
65
65
|
//# sourceMappingURL=index.js.map
|
package/docs/0.1.0-readiness.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 0.1.0 / 1.0 Readiness Gates
|
|
2
2
|
|
|
3
|
-
Status: **0.2.
|
|
3
|
+
Status: **0.2.6** is the current release line (the 0.2.x review-remediation line: fail-closed runtime/sandbox security, provider completion and outbound trust boundaries, concurrent-state/durability integrity, build/coverage/release-evidence integrity, package/documentation/compatibility truth, maintainability and bounded performance, fully featured coding-agent readiness); **0.1.7** was the terminal 0.1.x baseline; **1.0** readiness remains operator-gated, not automatic.
|
|
4
4
|
|
|
5
5
|
This page distills runnable readiness gates into one command-per-gate table.
|
|
6
6
|
The **Last evidence** column records the 0.1.0-tree snapshot (plan 012 Tasks
|
|
@@ -17,19 +17,19 @@ Evidence trail: [`docs/_evidence/review-coverage-2026-07-26-phase-11.md`](./_evi
|
|
|
17
17
|
The per-phase review-coverage evidence archive lives in [`docs/_evidence/`](./_evidence/)
|
|
18
18
|
(plans 067–079, releases 0.0.4–0.0.16; tarball-excluded, kept in-repo for audit).
|
|
19
19
|
Historical release lines (0.0.16 floor → 0.0.27 Phase 10 ACP interop → 0.1.0)
|
|
20
|
-
keep their per-phase evidence in the pages above; this page records the 0.2.
|
|
21
|
-
snapshot (plan
|
|
20
|
+
keep their per-phase evidence in the pages above; this page records the 0.2.6
|
|
21
|
+
snapshot (plan 026) with the 0.1.x tables below as the historical record.
|
|
22
22
|
|
|
23
|
-
## Current line (0.2.
|
|
23
|
+
## Current line (0.2.6)
|
|
24
24
|
|
|
25
25
|
| Item | Status |
|
|
26
26
|
|---|---|
|
|
27
|
-
| Published graph | **50** publishable manifests at exact **0.2.
|
|
28
|
-
| Current-line cut | The 0.2.x review-remediation line, additive-only vs the frozen 0.1.x contract: 0.2.0 fail-closed runtime/sandbox security (durable-resume decision validation, work-tool env isolation, explicit sandbox capabilities), 0.2.1 provider completion + outbound trust boundaries (strict stream completion, bounded success bodies, DNS-pinned OIDC/OPA fetches), 0.2.2 concurrent-state/durability integrity (model-budget reservation, conversation-metadata CAS, single-consumer EventMultiplexer, NATS durable identity), 0.2.3 build/coverage/release-evidence integrity (build single-flight, corrected coverage denominators, release skip manifest, stabilized quality gates), 0.2.4 package/documentation/compatibility truth (umbrella wording matches manifests, manifest-derived package truth, peer-version policy Decision A, current-line truth), 0.2.5 maintainability and bounded performance (god-module splits into cohesive family files behind preserved barrels, persistence-mechanics dedup into `session-store-codecs`, quadratic `Buffer.concat` removed from framing/tar, dead-code cleanup internal-only, 76 behavior-backed coverage regressions) |
|
|
29
|
-
| Upgrade path | `docs/migration.md` `0.2.
|
|
30
|
-
| Compat promise | Additive-only vs the frozen 0.1.x contract; `scripts/compat-baseline` regenerated at 0.2.
|
|
31
|
-
| Security policy | `npm audit --audit-level=moderate` 0 at 0.2.
|
|
32
|
-
| Docs freeze | tripwires green including the canonical manifest-count tripwire (50/49/14/9/26), the plan 024 package-truth tests (generator reproducibility + artifact equality + closure asserts + derived docs truth),
|
|
27
|
+
| Published graph | **50** publishable manifests at exact **0.2.6** (root + 49 workspace packages: 14 provider adapters + 9 `prism-*` family/profile + 26 capability; generated by `node scripts/package-truth.mjs` → `scripts/package-truth.json`) |
|
|
28
|
+
| Current-line cut | The 0.2.x review-remediation line, additive-only vs the frozen 0.1.x contract: 0.2.0 fail-closed runtime/sandbox security (durable-resume decision validation, work-tool env isolation, explicit sandbox capabilities), 0.2.1 provider completion + outbound trust boundaries (strict stream completion, bounded success bodies, DNS-pinned OIDC/OPA fetches), 0.2.2 concurrent-state/durability integrity (model-budget reservation, conversation-metadata CAS, single-consumer EventMultiplexer, NATS durable identity), 0.2.3 build/coverage/release-evidence integrity (build single-flight, corrected coverage denominators, release skip manifest, stabilized quality gates), 0.2.4 package/documentation/compatibility truth (umbrella wording matches manifests, manifest-derived package truth, peer-version policy Decision A, current-line truth), 0.2.5 maintainability and bounded performance (god-module splits into cohesive family files behind preserved barrels, persistence-mechanics dedup into `session-store-codecs`, quadratic `Buffer.concat` removed from framing/tar, dead-code cleanup internal-only, 76 behavior-backed coverage regressions), 0.2.6 fully featured coding-agent readiness (host-selected PTY backend, scalable indexed code-search seam, multi-worktree/repository lifecycle, forge breadth demand-gated, durable ACP/process recovery, patch review + incremental diagnostics, protected real coding journey) |
|
|
29
|
+
| Upgrade path | `docs/migration.md` `0.2.5 → 0.2.6` (additive; three new versioned record namespaces + optional ACP `activeRun` ref; rollback = stop 0.2.6 workers, mark active records unknown, restore 0.2.5 manifests/tag); store-compatible throughout 0.2.x |
|
|
30
|
+
| Compat promise | Additive-only vs the frozen 0.1.x contract; `scripts/compat-baseline` regenerated at 0.2.6 (version literal + additive PTY/index/workspace/recovery/review exports), zero breaking deltas |
|
|
31
|
+
| Security policy | `npm audit --audit-level=moderate` 0 at 0.2.6; threat-suites legs (phase8–11 + phase20–26) green; protected Postgres recovery/workspace conformance, PTY, real coding journey, NATS/live-canary legs operator-gated |
|
|
32
|
+
| Docs freeze | tripwires green including the canonical manifest-count tripwire (50/49/14/9/26), the plan 024 package-truth tests (generator reproducibility + artifact equality + closure asserts + derived docs truth), the plan 025 bounded-accumulation near-limit probe, and the plan 026 freeze tripwires (per-task markers, threat T1–T8 test mapping, exit gate green) |
|
|
33
33
|
| 0.1.x line | **0.1.7** (plan 019) is the terminal 0.1.x baseline; the 0.1.1 table below keeps the plan 013 snapshot; the 0.1.0 table keeps the plan 012 snapshot; the **0.0.16** values remain the historical network-free floor |
|
|
34
34
|
|
|
35
35
|
## Previous line (0.1.1)
|
package/docs/acp.md
CHANGED
|
@@ -111,6 +111,8 @@ const agent = createPrismAcpAgent({
|
|
|
111
111
|
|
|
112
112
|
### Persistence and ownership
|
|
113
113
|
|
|
114
|
+
- **Active-run recovery (0.2.6, plan 026 Task 5).** When `sessionStore` and the `recovery` seam (checkpoints + leases + ownerId, all three together) are wired, the agent records a bounded `activeRun` reference on `PersistedAcpSession` while a durable run is live (first run event → `running`, suspension → `suspended` + version, finish/deny/error → `terminal`; frozen 512-byte cap; advisory only — the authoritative status is re-queried from `AgentRunLifecycle.status`). After a restart, `restore` re-attaches the ref to the live session and hosts re-resolve it with `createAcpRunRecovery` (exported from `@arnilo/prism-ag-ui/acp`): suspended runs report their pending approval ids and durable version, terminal runs report terminal, and unprovable in-flight streams report `unknown` — the prompt is never restarted automatically. Durable cancellation (`recovery.cancel`) is ownership/version/fence checked, terminal/idempotent, aborts no unrelated run, and never replays a pending/dispatched tool: a cancelled run reports `cancelled` and must not be resumed. `session/cancel` on a live agent aborts the controller (0.2.5 parity) and, for restored runs, writes the durable marker under the session's ownership. Cancel markers live in `prism.coding-agent.cancel.v1` (schemaVersion 1, CAS + lease fenced). A host-side terminal whose managed process is unattestable after restart reports `unknown` (exitCode null); the agent never fabricates an exit or replays input (`terminal-client`).
|
|
115
|
+
|
|
114
116
|
- **Without the durability seam the agent never persists `modeId`/`configValues`.** Defaults are recomputed per session from the `modes`/`configOptions` seams — a fresh `session/new`, `load`, or `resume` always starts from `defaultModeId` / option `defaultValue`, and the agent's per-session registry is in-memory only. Persisting mode/config across sessions is a **host** decision, and host-side persistence MUST be ownership-scoped.
|
|
115
117
|
- **Host persistence MUST key by `sessions.ownership`.** `authorize` binds transport identity to ownership; a host store that persists `modeId`/`configValues` must refuse any restore whose stored ownership differs from the current session's ownership — a `sessionId` alone is never a sufficient key (session ids may collide across tenants). A cross-tenant restore rejects with `ERR_PRISM_ACP_INPUT` and never returns the other tenant's mode/config.
|
|
116
118
|
- **Ownership-scoped restore (host-owned store).** The store is keyed by `sessionId` and records the owning `userId`; restore refuses on mismatch (this exact pattern is asserted in `packages/ag-ui/src/__tests__/acp-modes-config.test.ts`):
|
|
@@ -118,7 +118,7 @@ Observation tools declare `kind: none`; mutations are `external_mutation`/`unsup
|
|
|
118
118
|
|
|
119
119
|
Import is inert. Construction fails clearly when neither `browser` nor `manager` is supplied. Browser installation, launch, version, and control endpoint are host-owned. Prism never exposes init scripts, extensions, persistent profiles, or model-supplied Playwright launch options; CDP exposure is limited to the allowlisted Runtime/Network/Emulation surface above (evaluate is policy-gated arbitrary code execution — treat results as untrusted). Secrets and storage state must not appear in snapshots, tool results, logs, or checkpoints. Finite caps charge before context/page/action/queue/snapshot/network/artifact retention; snapshots retain no unbounded DOM, console, request, response, or trace history. Unreleased downloads are deleted on context close.
|
|
120
120
|
|
|
121
|
-
Default tests use fake Playwright APIs only. Protected live gate: `PRISM_LIVE_PLAYWRIGHT=1` (or `PRISM_TEST_PLAYWRIGHT=1`) `npm run test:live -w @arnilo/prism-browser` exercises a local loopback hostile HTML fixture for snapshot refs, stale-ref rejection, css/xpath targets, private/file deny, upload containment, screenshot bounds, download quarantine/release, and the CDP leg (real evaluate, observe, and emulate). Missing browser binaries fail closed when the gate is enabled. Adversarial network-free fixtures live in `eval-fixtures.test.ts`; see [Evaluations](evaluations.md) and `examples/coding-browser-evaluation.ts`.
|
|
121
|
+
Default tests use fake Playwright APIs only. Protected live gate: `PRISM_LIVE_PLAYWRIGHT=1` (or `PRISM_TEST_PLAYWRIGHT=1`) `npm run test:live -w @arnilo/prism-browser` exercises a local loopback hostile HTML fixture for snapshot refs, stale-ref rejection, css/xpath targets, private/file deny, upload containment, screenshot bounds, download quarantine/release, and the CDP leg (real evaluate, observe, and emulate). Missing browser binaries fail closed when the gate is enabled. The protected coding journey (0.2.6, plan 026 Task 7) additionally runs a real browser inspection leg (local loopback fixture page, snapshot text assertion, run-owned context closed before the host browser) inside the packed consumer as part of scripts/phase26-coding-journey.test.mjs, gated by PRISM_LIVE_PLAYWRIGHT with the pinned playwright-core installed into the consumer; browser storage never appears in the retained report. Adversarial network-free fixtures live in `eval-fixtures.test.ts`; see [Evaluations](evaluations.md) and `examples/coding-browser-evaluation.ts`.
|
|
122
122
|
|
|
123
123
|
## Related APIs
|
|
124
124
|
|
|
@@ -97,7 +97,7 @@ These are **out of scope** for the 0.0.21 package baseline (see roadmap Phase 9
|
|
|
97
97
|
|
|
98
98
|
- **No PDF / document reader** — text and supported images only via `read`.
|
|
99
99
|
- **No trash / recycle daemon** — `delete` / `move` are permanent; host undo is not automatic.
|
|
100
|
-
- **No PTY / interactive process control in `shell`** — `shell` stays one-shot; optional `createProcessSessions` covers long-running attach/input
|
|
100
|
+
- **No PTY / interactive process control in `shell`** — `shell` stays one-shot; optional `createProcessSessions` covers long-running attach/input with a host-selected PTY backend (`pty: true` requires the `ptyBackend` host option; without one it fails closed as unsupported — see [Process sessions](process-sessions.md)).
|
|
101
101
|
- **LSP language-server tools** — not in default aggregators; optional `createLanguageIntelligence` is Phase 9 (see [Language intelligence](language-intelligence.md)).
|
|
102
102
|
- **Managed process sessions** — not in default aggregators; optional `createProcessSessions` is Phase 9 (see [Process sessions](process-sessions.md)).
|
|
103
103
|
- **GitHub forge adapter** — not in default aggregators; optional `createGitHubForge` is Phase 9 (see [Forge integration](forge-integration.md)); no octokit dependency, no multi-forge abstraction.
|
|
@@ -285,7 +285,7 @@ Search text files under the workspace using literal substring match. Binary file
|
|
|
285
285
|
| --- | --- | --- |
|
|
286
286
|
| `query` | `string` | Literal substring (required). |
|
|
287
287
|
| `path` | `string` | Workspace-relative start path. |
|
|
288
|
-
| `mode` | `"literal"` | Literal only (
|
|
288
|
+
| `mode` | `"literal"` (default) \| `"indexed_literal"` \| `"semantic"` | Literal substring by default. Indexed modes exist only when the host enables them (`createRepoSearchTool({ modes })` with an indexed operations composite); missing capability, stale/failed index, or disabled mode returns a stable `ERR_PRISM_INDEX_*` error — never a silent fallback that changes query meaning. `regex` removed in 0.0.18. |
|
|
289
289
|
| `caseSensitive` | `boolean` | Default false. |
|
|
290
290
|
| `includeHidden` | `boolean` | Default false. |
|
|
291
291
|
| `context` | `number` | Context lines before/after each match (default 5, hard 20). Ignored for non-content `outputMode`. |
|
|
@@ -297,7 +297,7 @@ Search text files under the workspace using literal substring match. Binary file
|
|
|
297
297
|
- `files_with_matches`: unique matching paths only.
|
|
298
298
|
- `count`: totals (`N matches in M files`) without line bodies.
|
|
299
299
|
|
|
300
|
-
Metadata includes `matches`, `truncated`, scan/skip counts; non-content modes also expose `fileCount`.
|
|
300
|
+
Metadata includes `matches`, `truncated`, scan/skip counts; non-content modes also expose `fileCount`. Indexed modes add `untrusted_index`, `indexMode`, `indexState`, `indexRevision`, `indexUpdatedAt` and per-match `[score N.NNN]` suffixes — index text is untrusted and must be re-read before mutation. Full contract: see [Indexed code search](indexed-code-search.md).
|
|
301
301
|
|
|
302
302
|
### `glob`
|
|
303
303
|
|
|
@@ -354,10 +354,11 @@ Opt-in tools over a host-pinned Git executable (`gitPath`, default `/usr/bin/git
|
|
|
354
354
|
| `git_status` | `status --porcelain=v2 -z --branch` → structured branch + entries + `dirty`. |
|
|
355
355
|
| `git_diff` | Bounded `--no-ext-diff --no-textconv` diff; oversized output may spill via `artifactWriter`. |
|
|
356
356
|
| `git_branch` | `validate` / `list` / `create` / `switch` with `git check-ref-format --branch`. Switch refuses unrelated dirty trees unless `createCheckpoint=true`. |
|
|
357
|
-
| `git_worktree` | `list` / `add` / `remove` within finite worktree caps. |
|
|
357
|
+
| `git_worktree` | `list` / `add` / `lock` / `unlock` / `remove` within finite worktree caps; list exposes `locked`/`lockReason` from porcelain. One-shot tool: durable multi-repository worktree lifecycle (create/verify/cleanup with ownership, fencing, and cleanup policy) lives in `createCodingWorkspaceLifecycle` — see [Coding workspaces](coding-workspaces.md). |
|
|
358
358
|
| `git_apply` | `check` / `apply` / `reverse`; always `--check` before mutating apply. Apply requires clean/checkpoint; failures restore. |
|
|
359
359
|
| `git_commit` | Explicit-path `add` + `commit --no-verify -F <tempfile>`; requires host `commitIdentity`. Allows dirty entries that are exactly the requested paths; unrelated dirt requires checkpoint. Never pushes. |
|
|
360
360
|
| `git_pr_handoff` | Bounded `{ base, head, commits, changedPaths, diffstat, checks, artifact? }` for host PR creation. Never authenticates or opens a PR. |
|
|
361
|
+
| `git_pr_handoff` (0.2.6 review) | Handoff output feeds `createCodingPatchReviewManifest` — the review binds to base/head, the patch artifact digest, check summaries, and diagnostic summaries (`diagnosticDelta` output) with pending/accepted/rejected/superseded states; see [Coding review and diagnostics](coding-review-and-diagnostics.md). |
|
|
361
362
|
| `coding_check` | Included when `checks` are declared: model selects only a name; executable/args/env are host-fixed. |
|
|
362
363
|
|
|
363
364
|
```ts
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Coding review and diagnostics
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
Bounded patch-review manifests plus normalized LSP/check diagnostics for the coding agent runtime (plan 026 Task 6). The review side composes over the existing server `ArtifactService` — no second approval engine, no review database, no raw patch bodies persisted. The diagnostics side normalizes LSP push/pull results and host-parsed check output into one bounded shape with deterministic added/removed/unchanged deltas.
|
|
6
|
+
|
|
7
|
+
| Export | Purpose |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `createCodingPatchReviewManifest(input)` | Build a bounded review manifest + structural artifact input (`preview.review` embeds the manifest). |
|
|
10
|
+
| `assertCodingPatchAccepted({ review, artifact })` | Derive `pending\|accepted\|rejected\|superseded` from the artifact record — digest/revision/identity checked. |
|
|
11
|
+
| `CodingPatchReviewError` | Typed fail-closed errors (`ERR_PRISM_REVIEW_*`). |
|
|
12
|
+
| `normalizeDiagnostics(raw, options)` | Validate/bound host-parsed check diagnostics into `NormalizedDiagnostic[]`. |
|
|
13
|
+
| `diagnosticDelta({ next, previous })` | Deterministic `added` / `removed` / `unchanged` across generations. |
|
|
14
|
+
| `diagnosticIdentity(diagnostic)` | Stable per-diagnostic key (`file:source:line:character:code`). |
|
|
15
|
+
| `LanguageIntelligence.syncDocument(file)` / `.diagnosticDelta({ files, previous })` | LSP document re-sync and bounded diagnostic refresh. |
|
|
16
|
+
|
|
17
|
+
## Review lifecycle
|
|
18
|
+
|
|
19
|
+
A review is created per patch handoff. The manifest binds: repository identity (credential-free remote fingerprint + default branch + optional worktree path), `base`/`head`, the patch artifact reference (`kind`, `uri`, `sha256`, `bytes`), changed paths, diffstat, named-check summaries, and diagnostic summaries. The `digest` is SHA-256 over the canonical manifest JSON; the structural artifact input carries the manifest in `preview.review` and the patch SHA-256 as the artifact hash.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { createCodingPatchReviewManifest, assertCodingPatchAccepted } from "@arnilo/prism-coding-agent";
|
|
23
|
+
import { createArtifactService } from "@arnilo/prism-server";
|
|
24
|
+
|
|
25
|
+
const { review, artifactInput } = createCodingPatchReviewManifest({
|
|
26
|
+
threadId: "thread-1",
|
|
27
|
+
artifactId: "patch-1",
|
|
28
|
+
identity: { repositoryId: "app", remoteFingerprint: sha256Fingerprint, defaultBranch: "main" },
|
|
29
|
+
base: "main",
|
|
30
|
+
head: "feature-1",
|
|
31
|
+
patch: { kind: "patch", uri: "artifacts/patch-1.patch", sha256: patchSha, bytes: 4096 },
|
|
32
|
+
changedPaths: ["src/a.ts"],
|
|
33
|
+
diffstat: [{ file: "src/a.ts", additions: 10, deletions: 2 }],
|
|
34
|
+
checks: [{ name: "build", exitCode: 0, summary: "ok" }],
|
|
35
|
+
});
|
|
36
|
+
const record = await artifacts.attach({ ...artifactInput, ownership, identity });
|
|
37
|
+
|
|
38
|
+
// later, after a human approve/reject on the artifact:
|
|
39
|
+
const outcome = assertCodingPatchAccepted({ review, artifact: record });
|
|
40
|
+
// outcome.state: "accepted" | "rejected" | "pending" | "superseded"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
State derivation rules:
|
|
44
|
+
|
|
45
|
+
- `pending` — no decision recorded for the bound revision.
|
|
46
|
+
- `accepted` — an `approved` decision exists for the exact artifact revision whose hash equals the patch digest, the revision is still the latest, and the embedded review digest and repository/worktree/base/head identity still match. Acceptance is a state assertion only — it never applies, commits, pushes, or merges automatically.
|
|
47
|
+
- `rejected` — a `rejected` decision exists for the bound revision (bounded reviewer reason).
|
|
48
|
+
- `superseded` — any change invalidates a prior acceptance: a new patch digest (no revision matches), a newer patch revision attached after the decision (stale acceptance refused), a changed review digest (patch/identity/base/head changed), or a tampered identity in the preview.
|
|
49
|
+
|
|
50
|
+
Caps (default / hard): review revisions 8/32, diagnostic summaries 500/5000, manifest bytes 64 KiB/256 KiB, delta entries 2000/10000; check summaries 8 KiB each; artifact URIs 2048 bytes. Every identity field is validated at manifest creation (`ERR_PRISM_REVIEW_INPUT`); caps charge before retention (`ERR_PRISM_REVIEW_LIMIT`); a record bound to another thread/artifact is refused (`ERR_PRISM_REVIEW_OWNERSHIP`). Raw patch bodies, commands, env, and secrets are never embedded in the manifest or artifact preview — the artifact hash is the patch digest, the body stays in the host artifact store.
|
|
51
|
+
|
|
52
|
+
## Diagnostics
|
|
53
|
+
|
|
54
|
+
`normalizeDiagnostics` accepts host-parsed raw diagnostics (hosts own the check parsers; there is no language/tool-specific parser catalog). Each entry is validated: workspace-relative path (absolute paths must stay inside the workspace root), non-negative finite positions, valid severity, non-empty message. Control characters are stripped, messages are UTF-8-truncated at the byte cap (4 KiB default / 16 KiB hard), and the per-file cap charges before retention (500 default / 5000 hard). Malformed entries are dropped fail-closed — never partially normalized.
|
|
55
|
+
|
|
56
|
+
`diagnosticDelta` computes a deterministic `added` / `removed` / `unchanged` view between generations using `diagnosticIdentity`. Duplicate identities in one side dedupe; previous views with a generation newer than the incoming view are treated as stale and ignored (a stale-version response never overwrites newer results). Same-generation views diff normally, so repeated refreshes yield `unchanged` without churn.
|
|
57
|
+
|
|
58
|
+
## LSP synchronization (opt-in)
|
|
59
|
+
|
|
60
|
+
`LanguageIntelligence` stays a standalone host-activated factory — no LSP server is spawned by construction and nothing is baked into `createCodingTools`/`createAllTools` or any agent assembly. Hosts wire it explicitly:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const lang = createLanguageIntelligence({ workspaceRoot, servers: { ts: {...} }, policy });
|
|
64
|
+
await lang.syncDocument("src/app.ts"); // full-content didChange, monotonic version
|
|
65
|
+
const delta = await lang.diagnosticDelta({ files: ["src/app.ts", "src/lib.ts"], previous });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- `syncDocument(file)` reads the file and sends a full-content `textDocument/didChange` (protocol-valid LSP 3.17; no diff engine). Versions are monotonic per document: didOpen stamps 1, each didChange increments.
|
|
69
|
+
- `diagnosticDelta({ files, previous })` refreshes each changed file: pull diagnostics (`textDocument/diagnostic` with `previousResultId` reuse, `kind: full|unchanged`) when the server advertises `diagnosticProvider`, otherwise the push cache (`textDocument/publishDiagnostics`, which always replaces the full set — publish `[]` to clear). Results are normalized, generation-stamped with the document version, and diffed against `previous`. Stale views (previous generation newer than the refresh) are dropped per file.
|
|
70
|
+
- Refresh is bounded to the requested files (never a whole-workspace pull) and to the standard LSP caps (message bytes, diagnostics/file, pending requests, results/query, timeout, servers).
|
|
71
|
+
|
|
72
|
+
## Related APIs
|
|
73
|
+
|
|
74
|
+
- `docs/work-artifacts-and-review.md`: artifact revisions and approve/reject semantics the manifest composes over.
|
|
75
|
+
- `docs/language-intelligence.md`: full LSP contract.
|
|
76
|
+
- `docs/coding-agent-tools.md`: `coding_check` named checks the manifest summarizes.
|
package/docs/coding-security.md
CHANGED
|
@@ -206,6 +206,8 @@ Containment resolves symlinks and rejects paths outside roots. Command rules are
|
|
|
206
206
|
|
|
207
207
|
Docker sandbox containment—not command regexes—enforces filesystem/network/process boundaries for the reference adapter. Network defaults to none; a custom Docker network still requires a host firewall/proxy for DNS/egress claims. Import rejects symlink escapes, devices, FIFOs, and sockets; export counts entries/bytes and hashes before host retention. Secrets in `secrets` are redacted from adapter errors and never exported as environment metadata. Unified workspace mode reuses existing sandbox/repo/coding hard caps and does not introduce unbounded host↔container sync loops. Host mode and `allowMixedWorkspaceWiring` never claim disposable containment. Durable workflow denial/cancellation is terminal and attributable; approved resume still fails if roots, command rules, read-only mode, or other policy changed while suspended. Cache keys are fixed-size SHA-256 digests of selected identity plus action shape; caches remain process-local, retain at most 1,000 decisions with oldest-entry eviction, and have no default/global mode. Path checks and cache lookup are local; sandbox latency belongs to the supplied adapter and Docker daemon.
|
|
208
208
|
|
|
209
|
+
The protected coding journey (0.2.6, plan 026 Task 7) exercises these boundaries for real at release time: scripts/phase26-coding-journey.test.mjs packs the published packages into a fresh consumer and runs the digest-pinned Docker sandbox, the host Playwright browser, the real forge, durable Postgres checkpoints/leases, and the host PTY adapter (frozen profile) — every missing service records blocked, never a passing skip, and the retained phase26-coding-journey-report.json carries timings/states/ids only (no prompts, source bodies, terminal output, tokens, or browser storage). See docs/release-and-install.md for the operator runbook.
|
|
210
|
+
|
|
209
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).
|
|
210
212
|
|
|
211
213
|
## Related APIs
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Coding workspaces
|
|
2
|
+
|
|
3
|
+
Ownership-scoped multi-repository and worktree lifecycle (plan 026 Task 3, `@arnilo/prism-coding-agent`). A durable coding workspace correlates task/session/run identity with host repositories and linked worktrees so that resume, cleanup, artifacts, and recovery stay bounded and reconcilable.
|
|
4
|
+
|
|
5
|
+
The lifecycle composes existing bounded primitives only: `CheckpointStore` CAS records in a separate versioned namespace (`prism.coding-agent.workspace.v1`), `LeaseStore` fencing, and cwd-bound `GitOperations` runners. There is no clone manager, Git library, watcher, new database schema, or second task runtime.
|
|
6
|
+
|
|
7
|
+
## Activation
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { createCodingWorkspaceLifecycle } from "@arnilo/prism-coding-agent";
|
|
11
|
+
|
|
12
|
+
const workspaces = createCodingWorkspaceLifecycle({
|
|
13
|
+
checkpoints, // CheckpointStore (ownership-scoped)
|
|
14
|
+
leases, // LeaseStore
|
|
15
|
+
ownerId: replicaId, // worker/replica identity
|
|
16
|
+
ownership: { tenantId }, // part of the trust boundary
|
|
17
|
+
repositories: {
|
|
18
|
+
app: { root: "/src/app", git: appGit }, // git must be cwd-bound to root
|
|
19
|
+
api: { root: "/src/api", git: apiGit },
|
|
20
|
+
},
|
|
21
|
+
worktreeRoots: ["/work/prism"], // host-approved linked-worktree roots
|
|
22
|
+
policy: { allowDirtyCleanup: false }, // all cleanup refusals default to refuse
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const workspace = await workspaces.create({
|
|
26
|
+
taskId: "task-42",
|
|
27
|
+
repositories: [{ repositoryId: "app", branch: "agent/task-42" }],
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Nothing starts on import or construction; worktrees are created only by explicit `create` calls. Repository roots and worktree roots are canonicalized (`realpath`) and containment-checked; the main worktree of every registered repository is immutable through this service.
|
|
32
|
+
|
|
33
|
+
## Record
|
|
34
|
+
|
|
35
|
+
`CodingWorkspaceRecord` (schemaVersion 1) holds: stable `workspaceId` (deterministic from `taskId`) and `taskId`; `ownerId`; frozen state `active | cleaning | closed | unknown`; per-repository legs with repository id, canonical root, credential-free remote fingerprint (sha256 of the redacted remote URL plus default branch — never a URL), default branch, task branch, base/head shas, worktree id/path, repository state `active | removed | unknown`, created timestamp; artifact references only (kind/uri/sha256/bytes, never contents); fencing token; created/updated/cleanup timestamps. Records are bounded (64 KiB default / 256 KiB hard).
|
|
36
|
+
|
|
37
|
+
## Operations
|
|
38
|
+
|
|
39
|
+
- `create({ taskId, repositories, artifactRefs? })` — validates task/repository/branch identity, acquires a lease, captures fingerprints, adds one linked worktree per repository (`git worktree add -b`), locks each with `prism-workspace:<id>` reason, and persists the record with a fencing-token CAS write. Duplicate create with an identical active record returns it as-is with no Git mutation; a conflicting request, a live foreign lease, or a CAS/fence conflict fails with `ERR_PRISM_WORKSPACE_FENCE`. A worktree left behind by a crashed earlier attempt is reused.
|
|
40
|
+
- `get({ taskId })` / `list({ cursor?, limit? })` — bounded reads; malformed or escaped records fail closed.
|
|
41
|
+
- `verify({ taskId })` — resume gate: revalidates repository root containment, worktree containment, worktree presence and head, and the remote/default-branch fingerprint before tools, processes, index results, patches, or artifacts are reused. Any change fails with `ERR_PRISM_WORKSPACE_FINGERPRINT` / `ERR_PRISM_WORKSPACE_PATH_ESCAPE`.
|
|
42
|
+
- `attachArtifacts({ taskId, artifactRefs })` — bounded CAS update of artifact refs (16 default / 64 hard refs).
|
|
43
|
+
- `cleanup({ taskId })` — removes owned linked worktrees and closes the record. Idempotent on `closed`; refuses while another worker cleans (`cleaning`); every mutation takes the lease and writes with a monotonic fencing token.
|
|
44
|
+
- `remove({ taskId })` — deletes the durable record only; never touches Git.
|
|
45
|
+
|
|
46
|
+
## Cleanup refusals
|
|
47
|
+
|
|
48
|
+
Cleanup refuses, unless the host policy explicitly allows the documented action:
|
|
49
|
+
|
|
50
|
+
- dirty worktree — `ERR_PRISM_WORKSPACE_DIRTY` (`allowDirtyCleanup` → forced removal, potential data loss);
|
|
51
|
+
- externally locked worktree — `ERR_PRISM_WORKSPACE_LOCKED` (`allowLockedCleanup`); locks owned by this service (`prism-workspace:<id>` reason) are always released first;
|
|
52
|
+
- missing worktree — `ERR_PRISM_WORKSPACE_UNKNOWN` (`allowMissingCleanup` → claim as removed);
|
|
53
|
+
- unowned path (exists on disk but is not a registered worktree) — `ERR_PRISM_WORKSPACE_UNKNOWN` (`allowUnownedCleanup` → unclaim without touching the foreign directory);
|
|
54
|
+
- mismatched head — `ERR_PRISM_WORKSPACE_FINGERPRINT` (`allowMismatchedCleanup` → forced removal);
|
|
55
|
+
- main-worktree path — always `ERR_PRISM_WORKSPACE_MAIN`, no policy overrides.
|
|
56
|
+
|
|
57
|
+
Partial failure persists state `unknown` with per-repository `unknown`/`removed` legs and remains reconcilable: retrying cleanup converges to `closed`.
|
|
58
|
+
|
|
59
|
+
## Ownership and fencing
|
|
60
|
+
|
|
61
|
+
Ownership scopes are part of the trust boundary: records are read and written under the configured `tenantId`/`accountId`/`userId`, and lease acquisition under another scope fails closed as `ERR_PRISM_WORKSPACE_OWNERSHIP`. Every mutation runs under a `LeaseStore` lease (`tryAcquireLease`/`releaseLease`, TTL 30 s default / 300 s hard); the lease fencing token is stored in the record and each `CheckpointStore` save is a version CAS plus a monotonic fencing-token check, so a worker whose lease lapsed or was fenced out cannot overwrite newer state. Stale workers reject deterministically with `ERR_PRISM_WORKSPACE_FENCE`.
|
|
62
|
+
|
|
63
|
+
## Errors
|
|
64
|
+
|
|
65
|
+
`ERR_PRISM_WORKSPACE_UNKNOWN`, `ERR_PRISM_WORKSPACE_LIMIT`, `ERR_PRISM_WORKSPACE_OWNERSHIP`, `ERR_PRISM_WORKSPACE_FENCE`, `ERR_PRISM_WORKSPACE_DIRTY`, `ERR_PRISM_WORKSPACE_LOCKED`, `ERR_PRISM_WORKSPACE_MAIN`, `ERR_PRISM_WORKSPACE_PATH_ESCAPE`, `ERR_PRISM_WORKSPACE_FINGERPRINT` (`WorkspaceError`).
|
|
66
|
+
|
|
67
|
+
## Caps
|
|
68
|
+
|
|
69
|
+
Repositories per task 4 / 16; worktrees 4 / 16 (git caps); record bytes 65536 / 262144; lease TTL 30000 / 300000 ms; cleanup operations 100 / 1000; artifact refs 16 / 64; artifact uri 2048 bytes; task id 128 bytes; repository id 64 bytes. Cleanup is O(worktrees owned by one task); there is no per-file worktree scan and no global timer.
|
|
@@ -98,12 +98,18 @@ if (!report.alreadyMerged && report.pushed) {
|
|
|
98
98
|
|
|
99
99
|
## Extension and configuration notes
|
|
100
100
|
|
|
101
|
+
Forge breadth is demand-gated (plan 026 Task 4): GitLab and Bitbucket adapters stay deferred while no named consumer is recorded in the phase26 freeze manifest's demand registry (`scripts/phase26-freeze-manifest.json`). A deferred adapter has no source file, docs page, or export; activation requires recording a named host/consumer/date/use case and shipping at most one adapter (GitLab or Bitbucket) against the existing `ForgeOperations` contract. Unsupported provider operations fail with a stable typed error — no fake capability, no catalog/factory.
|
|
102
|
+
|
|
101
103
|
Credentials resolve per call through the host resolver; GitHub App installation tokens and PATs are both supported (same `Bearer` REST header and `x-access-token` git header). Least-privilege guidance: App installation tokens with `contents: write` + `pull_requests: write` + `issues: read` cover the six operations; PATs should be fine-grained to the single repository and read/write scope needed. Policy denials propagate as the core `ExecutionDeniedError` (`ERR_PRISM_EXECUTION_DENIED`) — no forge request is attempted — so hosts can distinguish refusal from forge failure. Pagination is sequential (per-request `pagesPerOperation` cap); `requestConcurrency` is a validated ceiling, not a target. The adapter performs no DNS/egress control itself — sandboxed hosts route forge traffic through the Phase 9 egress policy (Task 6).
|
|
102
104
|
|
|
103
105
|
## Security and performance notes
|
|
104
106
|
|
|
105
107
|
Tokens never appear in argv, git config files, logs, model context, or stored events: REST uses the `Authorization` header on a bounded `fetch`, and git uses `GIT_CONFIG_*` environment variables scoped to the single push process. Request bodies and responses are bounded by `payloadBytes` (streamed, content-length pre-checked); timeouts and rate-limit backoff respect `requestTimeoutMs` and `Retry-After`; page fetches stop at `pagesPerOperation`. Repository binding is fixed at construction; tenant binding is checked per mutation; ownership mismatch fails closed. Rate-limit responses map to `ERR_PRISM_FORGE_RATE_LIMIT`, 404 to `ERR_PRISM_FORGE_API`, 422 to `ERR_PRISM_FORGE_STALE`, 401/403 to `ERR_PRISM_FORGE_AUTH`, and cap violations to `ERR_PRISM_FORGE_LIMIT`.
|
|
106
108
|
|
|
109
|
+
## Protected journey cross-link (0.2.6, plan 026 Task 7)
|
|
110
|
+
|
|
111
|
+
The protected coding journey runs the real forge leg end to end: the packed consumer clones `PRISM_CODING_FORGE_REPOSITORY`, pushes the run-suffixed branch, creates the PR with lookup-before-create idempotency (the ToolEffectStore dedupes replays), reads check runs, reconciles the handoff, and cleans up by closing the PR (`PATCH state=closed`) and deleting the branch — credentials late-bound via the resolver and `GIT_CONFIG_*` env, never argv or logs. See [Release and install](release-and-install.md).
|
|
112
|
+
|
|
107
113
|
## Related APIs
|
|
108
114
|
|
|
109
115
|
- [Tool effects](tool-effects.md): `ToolEffectStore` idempotency and unknown-outcome recovery used by every forge mutation
|
package/docs/index.md
CHANGED
|
@@ -74,9 +74,9 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
74
74
|
- [Work connectors](work-connectors.md): connector principles, capability gates, scoped OAuth establishment (0.0.14), and out-of-scope boundaries (Slack/Teams channels not shipped) for Microsoft 365 / Google Workspace.
|
|
75
75
|
- [Browser automation](browser-automation.md): optional `@arnilo/prism-browser` with host-supplied Playwright contexts, AI-mode snapshots/refs, ordered `browser_open`/`browser_snapshot`/`browser_act`/`browser_close` plus (0.1.4) `browser_evaluate`/`browser_observe` and CDP `block_urls`/`unblock_urls`/`throttle`/`emulate` act actions on Chromium hosts, egress/side-effect/upload/download/screenshot policy, finite page/action/snapshot/network/artifact caps, and 0.0.14 verified-state checkpoints with reload/verify-before-side-effect.
|
|
76
76
|
- [Device adapters](device-adapters.md): deny-by-default realtime voice / desktop-control contract + conformance (0.0.14); no vendor package — admission fails closed without explicit consent+sandbox+approval, stream bounds, shared `RunLimits`, redacted telemetry.
|
|
77
|
-
- [Coding agent tools](coding-agent-tools.md): optional `shell`, `read`, `write`, `edit`, `repo_list`, `repo_search`, `glob`, `delete`, and `move` definitions plus opt-in `createGitTools()` / `coding_check`, opt-in `createAskUserDecisionTool` (single/multi/free-text + durable suspend glue), and `runCodingGoalVerify`; durable plan/todo Markdown helpers with workflow `state.coding` checkpoint metadata; streamed text pages, `repo_search` `outputMode`, bounded glob, optional read-before-write, optional Git-aware (`createGitAwareRepositoryOperations`) ignore-aware enumeration with native fallback, finite Git/check/plan/ask caps, bounded image/edit reads and write/edit payloads, finite shell wall/total-output limits, secure host-owned spill cleanup, pluggable bounded operation contracts, per-path mutation serialization, and optional `ExecutionPolicy`. 0.1.6 adds the optional [document reader](document-reader.md) slot (`@arnilo/prism-document-reader`, plan 018 closeout `doc-reader`): bounded PDF/DOCX literal-text extraction behind `createReadTool({ documentReader })` with magic-byte format gating, input/page/text caps, fail-closed optional peer parsers, and no embedded-content execution or external fetching. 0.1.6 also adds opt-in recursive `delete` (`recursive: true`, bounded fan-out, symlink children never followed) and bounded `{a,b}` glob expansion (`braceExpansion`, max 128 alternatives / 4096 bytes, fail-closed) behind plan 018 closeout `delete-glob`. No PDF/trash/PTY in the 0.0.21 baseline (0.1.6's document reader is the demand-gated optional exception); Phase 9 adds optional language intelligence (separate page). Limits do not sandbox host access—gate with permission/trust policy and `@arnilo/prism-coding-security`.
|
|
77
|
+
- [Coding agent tools](coding-agent-tools.md): optional `shell`, `read`, `write`, `edit`, `repo_list`, `repo_search`, `glob`, `delete`, and `move` definitions plus opt-in `createGitTools()` / `coding_check`, opt-in `createAskUserDecisionTool` (single/multi/free-text + durable suspend glue), and `runCodingGoalVerify`; durable plan/todo Markdown helpers with workflow `state.coding` checkpoint metadata; streamed text pages, `repo_search` `outputMode`, bounded glob, optional read-before-write, optional Git-aware (`createGitAwareRepositoryOperations`) ignore-aware enumeration with native fallback, finite Git/check/plan/ask caps, bounded image/edit reads and write/edit payloads, finite shell wall/total-output limits, secure host-owned spill cleanup, pluggable bounded operation contracts, per-path mutation serialization, and optional `ExecutionPolicy`. 0.1.6 adds the optional [document reader](document-reader.md) slot (`@arnilo/prism-document-reader`, plan 018 closeout `doc-reader`): bounded PDF/DOCX literal-text extraction behind `createReadTool({ documentReader })` with magic-byte format gating, input/page/text caps, fail-closed optional peer parsers, and no embedded-content execution or external fetching. 0.1.6 also adds opt-in recursive `delete` (`recursive: true`, bounded fan-out, symlink children never followed) and bounded `{a,b}` glob expansion (`braceExpansion`, max 128 alternatives / 4096 bytes, fail-closed) behind plan 018 closeout `delete-glob`. No PDF/trash/PTY in the 0.0.21 baseline (0.1.6's document reader is the demand-gated optional exception); Phase 9 adds optional language intelligence (separate page). 0.2.6 adds the optional [Indexed code search](indexed-code-search.md) seam: host-owned incremental index (`update/remove/search/status/dispose`) with explicit `indexed_literal`/`semantic` modes behind `createIndexedRepositoryOperations`, literal remains the default, stale/failed/unsupported indexes fail closed with `ERR_PRISM_INDEX_*` and results are labeled `untrusted_index`. 0.2.6 also adds [Coding workspaces](coding-workspaces.md) (plan 026 Task 3): `createCodingWorkspaceLifecycle` registers host repositories and creates/lists/locks/removes linked worktrees with CheckpointStore CAS records, LeaseStore fencing, credential-free remote fingerprints, and a cleanup policy that refuses dirty/locked/unowned/mismatched trees unless the host allows it. 0.2.6 adds [Coding review and diagnostics](coding-review-and-diagnostics.md) (plan 026 Task 6): bounded patch-review manifests (`createCodingPatchReviewManifest` + `assertCodingPatchAccepted`, pending/accepted/rejected/superseded bound to patch digest + artifact revision + repository/worktree/base/head identity, composed over the server ArtifactService, never applying/committing automatically), normalized LSP/check diagnostics with deterministic added/removed/unchanged deltas, and opt-in LSP document synchronization (`syncDocument`, pull diagnostics with resultId reuse, stale-version guards). Limits do not sandbox host access—gate with permission/trust policy and `@arnilo/prism-coding-security`.
|
|
78
78
|
- [Language intelligence](language-intelligence.md): optional host-activated `createLanguageIntelligence` — bounded in-package LSP 3.17 JSON-RPC client (Content-Length framing), host-selected server command/args per language, workspace symbols/definitions/references/diagnostics/hover/rename; lazy spawn; URI root confinement; rename gated by `ExecutionPolicy` + atomic write/mutation queue; frozen message/diagnostic/pending/result/timeout/server caps. No `vscode-languageserver-protocol` dependency.
|
|
79
|
-
- [Process sessions](process-sessions.md): optional host-activated `createProcessSessions` — long-running process registry (start/cursor-paged output/input/wait/signal/kill/release), native or sandbox `startProcess` backend (fail closed when absent), ownership/identity + expiry sweep on access, `reconcile` / sandbox-loss → `unknown` (never fabricates exitCode), durable command fingerprint metadata, `CodingProcessEvent` host sink, `ExecutionPolicy` before spawn and on mutate, frozen session/input/lifetime/output caps; PTY fails closed as unsupported.
|
|
79
|
+
- [Process sessions](process-sessions.md): optional host-activated `createProcessSessions` — long-running process registry (start/cursor-paged output/input/wait/signal/kill/release), native or sandbox `startProcess` backend (fail closed when absent), ownership/identity + expiry sweep on access, `reconcile` / sandbox-loss → `unknown` (never fabricates exitCode), durable command fingerprint metadata, `CodingProcessEvent` host sink, `ExecutionPolicy` before spawn and on mutate, frozen session/input/lifetime/output caps; host-selected PTY (`pty: true` delegates only to the host `ptyBackend`, fails closed as unsupported when absent, bounded resize/TERM/attach caps). Durable process recovery (plan 026 Task 5): with `checkpoints`+`leases`+`ownerId`, intent is persisted before spawn and transitions are CAS/fence-written; `recover()` is attach-if-attested via a host `recoveryBackend`, otherwise starting/running records atomically become `unknown` (no fabricated exit, no PID probing), fenced so two replicas cannot both own a process.
|
|
80
80
|
- [Forge integration](forge-integration.md): optional host-activated `createGitHubForge` — reference GitHub adapter (issue context, authenticated push via `BoundGitRunner` + `GIT_CONFIG_*` credential injection, PR create/update, review comments, check/status retrieval, bounded `reconcileHandoff`), every mutation gated by `ExecutionPolicy` and recorded in `ToolEffectStore` (retry never duplicates PRs/comments), typed `ForgeError` codes (auth/API/stale/rate-limit/limit/ownership), frozen page/payload/comment/concurrency/timeout caps, no octokit dependency, tokens never in argv/logs/events.
|
|
81
81
|
- [Coding execution approval and sandboxing](coding-security.md): path/command approval, identity-scoped caching, shell-turn exclusivity, required `workspaceMode` (`host`/`sandbox`) with fail-closed mixed wiring, `createSandboxCodingComposition()` sandbox capability metadata — 0.2.0 plan 020 Task 4 ships explicit `SandboxCapabilities` (`workspaceCoherent`/`filesystemIsolated`/`networkIsolated`/`processIsolated`/`privilegeIsolated`/`egressRestricted`) with omission resolving false, truthful Docker/native metadata, and `containmentClaim` retained only as a deprecated conservative projection — disposable Docker/OCI sandbox reference with bounded workspace import/export (0.1.6 adds the Linux-only network-free `createNativeSandbox` backend — fresh netns per command via `unshare`, `ulimit` hard caps, cwd containment, fails closed where egress denial is impossible), optional `DisposableSandbox.startProcess` / `SandboxProcessHandle` for process-session backends, and allow-list egress (`createEgressPolicy` deny-all exact rules + frozen presets, `createAllowListEgressProxy` HTTP/CONNECT proxy with pinned-DNS rebinding defense, private/metadata IP denial, redirect re-validation + hop cap, byte/time caps, per-decision audit, `composeEgressSandboxNetwork` attestation recorded as `prism.egress.*` labels; TLS pass-through, no interception).
|
|
82
82
|
|
|
@@ -99,7 +99,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
99
99
|
- [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.
|
|
100
100
|
- [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).
|
|
101
101
|
- [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.
|
|
102
|
-
- [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).
|
|
102
|
+
- [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.
|
|
103
103
|
- [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.
|
|
104
104
|
|
|
105
105
|
## CLI/RPC
|
|
@@ -129,7 +129,7 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
129
129
|
- [Ponytail behavior integration](ponytail.md): optional `@arnilo/prism-ponytail` — upstream Ponytail skills/commands, `ponytail-mode` injector, session `ponytail-mode` persistence; resolves peer `@dietrichgebert/ponytail` or `upstreamPath`; opt-in (not in code/sdk profiles).
|
|
130
130
|
|
|
131
131
|
## Release and install
|
|
132
|
-
- [Release and install](release-and-install.md): current **0.2.5** 50-package graph (root + 49 workspace packages) — 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.
|
|
132
|
+
- [Release and install](release-and-install.md): current **0.2.6** 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).
|
|
133
133
|
- [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.
|
|
134
134
|
- [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.
|
|
135
135
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Indexed code search
|
|
2
|
+
|
|
3
|
+
Optional host-owned incremental search index for `repo_search` (plan 026 Task 2, `@arnilo/prism-coding-agent`). The index is a seam: Prism defines the contract, validates requests and results, and scopes identity and freshness — the host owns persistence, build, watch, and any embedding/ranking engine. There is no bundled index engine, vector store, watcher daemon, or embedding SDK.
|
|
4
|
+
|
|
5
|
+
## Activation
|
|
6
|
+
|
|
7
|
+
Nothing starts on import or construction. A host builds and updates the index explicitly through the facade:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { createIndexedRepositoryOperations, createGitAwareRepositoryOperations } from "@arnilo/prism-coding-agent";
|
|
11
|
+
|
|
12
|
+
const operations = createIndexedRepositoryOperations(cwd, {
|
|
13
|
+
index: hostIndex, // RepositoryIndexBackend
|
|
14
|
+
fallback: createGitAwareRepositoryOperations(cwd),
|
|
15
|
+
allowedModes: ["literal", "indexed_literal", "semantic"],
|
|
16
|
+
stale: { maxAgeMs: 60_000, requireSourceRevision: true },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
await operations.index.update({ repositoryId, worktreeId, sourceRevision, changes });
|
|
20
|
+
await operations.index.remove({ paths });
|
|
21
|
+
await operations.index.status();
|
|
22
|
+
await operations.index.dispose();
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The returned object is a `RepositoryOperations` with an added `index` facade. `mode: "literal"` routes to the fallback unchanged; `indexed_literal` and `semantic` are served only by the host backend.
|
|
26
|
+
|
|
27
|
+
## Backend contract
|
|
28
|
+
|
|
29
|
+
`RepositoryIndexBackend`:
|
|
30
|
+
|
|
31
|
+
- `capabilities.semantic` — explicit declaration; semantic mode is never duck-typed.
|
|
32
|
+
- `update(request)` — add/edit changes with `repositoryId`/`worktreeId`/`sourceRevision` (bounded, never credential-bearing).
|
|
33
|
+
- `remove(request)` — drop paths.
|
|
34
|
+
- `search(request)` — bounded query with `mode`, optional repo-relative `path` scope, `maxResults`, `signal`, `deadlineMs`.
|
|
35
|
+
- `status()` — state `empty | building | ready | stale | failed`, `sourceRevision`, `updatedAt`.
|
|
36
|
+
- `dispose()`.
|
|
37
|
+
|
|
38
|
+
Index state machine is frozen: `empty | building | ready | stale | failed`.
|
|
39
|
+
|
|
40
|
+
## Mode semantics
|
|
41
|
+
|
|
42
|
+
- `literal` (default): native bounded substring search; byte-identical behavior to a host without an index.
|
|
43
|
+
- `indexed_literal`: host index result, relevance scores.
|
|
44
|
+
- `semantic`: host semantic search; requires `capabilities.semantic === true`.
|
|
45
|
+
|
|
46
|
+
Missing capability, mode not in `allowedModes`, stale revision, or failed index returns a stable `ERR_PRISM_INDEX_*` error. There is **no silent fallback** that changes query meaning — an index result is never replaced by a literal scan behind the caller's back.
|
|
47
|
+
|
|
48
|
+
`createRepoSearchTool(cwd, { operations, modes })` exposes exactly the modes listed in `modes` (default `["literal"]`); the JSON schema `enum` matches.
|
|
49
|
+
|
|
50
|
+
## Stale-index and freshness
|
|
51
|
+
|
|
52
|
+
`stale: { maxAgeMs, requireSourceRevision }`:
|
|
53
|
+
|
|
54
|
+
- `maxAgeMs` (default 60 000, hard 300 000): queries fail with `ERR_PRISM_INDEX_STALE` when `updatedAt` is absent or older than the window.
|
|
55
|
+
- `requireSourceRevision: true`: queries fail when the index does not attest a `sourceRevision`.
|
|
56
|
+
|
|
57
|
+
State `empty`/`building` also fail stale; state `failed` fails with `ERR_PRISM_INDEX_FAILED`; unknown states fail closed. The stale-index contract refuses to serve rather than silently degrade. Hosts choose the window; the default is deliberately conservative.
|
|
58
|
+
|
|
59
|
+
## Trust
|
|
60
|
+
|
|
61
|
+
Index output is untrusted:
|
|
62
|
+
|
|
63
|
+
- every hit path is containment-checked under the repository root and the requested path scope (absolute paths, `..` escapes, backslashes, and scope escapes fail with `ERR_PRISM_INDEX_UNTRUSTED`);
|
|
64
|
+
- scores must be finite and in `[0, 1]`, otherwise fail closed;
|
|
65
|
+
- duplicate paths are deduped (first wins);
|
|
66
|
+
- snippets are truncated to the byte cap (default 4096, hard 16384);
|
|
67
|
+
- result count is capped (default 1000, hard 10000);
|
|
68
|
+
- results carry `indexed` provenance (mode/state/sourceRevision/updatedAt) and `untrusted_index: true` — consumers must not treat index text as fact; mutations still require a fresh read/policy.
|
|
69
|
+
|
|
70
|
+
Backend throws are mapped to generic `ERR_PRISM_INDEX_FAILED` without embedded backend error text; query deadline (default 30 s, hard 120 s) and abort map to `ERR_PRISM_INDEX_TIMEOUT`.
|
|
71
|
+
|
|
72
|
+
## Update caps
|
|
73
|
+
|
|
74
|
+
Per update: 1000 changes default / 10000 hard, 16 MiB / 64 MiB total request bytes (paths, old paths, revision, ids). `rename` is routed as remove of the old path plus add of the new; `delete` routes to `remove`. Over-limit updates fail with `ERR_PRISM_INDEX_LIMIT` before any backend call.
|
|
75
|
+
|
|
76
|
+
## Errors
|
|
77
|
+
|
|
78
|
+
`ERR_PRISM_INDEX_UNSUPPORTED`, `ERR_PRISM_INDEX_STALE`, `ERR_PRISM_INDEX_FAILED`, `ERR_PRISM_INDEX_LIMIT`, `ERR_PRISM_INDEX_TIMEOUT`, `ERR_PRISM_INDEX_UNTRUSTED` (`IndexError`).
|
|
79
|
+
|
|
80
|
+
## Scale evidence
|
|
81
|
+
|
|
82
|
+
`scripts/phase26-index-benchmark.test.mjs` runs in the root test chain: a 100000-file metadata fixture, indexed query p95 ≤ 250 ms, 1000-file batch update ≤ 1 s, peak heap ≤ +64 MiB, semantic query bounded, and the literal baseline unchanged.
|
|
@@ -40,6 +40,21 @@ await lang.rename({ file: "src/a.ts", line: 10, character: 4, newName: "renamed"
|
|
|
40
40
|
await lang.dispose();
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
## Document synchronization and diagnostics (0.2.6, plan 026)
|
|
44
|
+
|
|
45
|
+
LSP stays opt-in: `createLanguageIntelligence` is a standalone host-activated factory — nothing spawns on construction and no agent/tool assembly instantiates it.
|
|
46
|
+
|
|
47
|
+
- `syncDocument(file)` — re-syncs a file after an external edit via full-content `textDocument/didChange` (protocol-valid LSP 3.17; no diff engine). Versions are monotonic per document: didOpen stamps 1, each didChange increments.
|
|
48
|
+
- `diagnosticDelta({ files, previous })` — bounded diagnostic refresh for changed files only (never a whole-workspace pull). When the server advertises `diagnosticProvider`, the client uses pull diagnostics (`textDocument/diagnostic` with `previousResultId` reuse — `kind: full|unchanged`; the cached set is reused on `unchanged`); otherwise it reads the push cache (`textDocument/publishDiagnostics` always replaces the full set, `[]` clears). Results are normalized (`NormalizedDiagnostic`), generation-stamped with the document version, and diffed with `diagnosticDelta` into deterministic `added` / `removed` / `unchanged`. Stale views (a previous generation newer than the refresh) are dropped per file — a stale-version response never overwrites newer results. Refresh honors the standard LSP caps (message bytes, diagnostics/file, results/query, timeout, files per request).
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
await lang.syncDocument("src/app.ts");
|
|
52
|
+
const delta = await lang.diagnosticDelta({
|
|
53
|
+
files: ["src/app.ts"],
|
|
54
|
+
previous: { "src/app.ts": { generation: 3, diagnostics: priorDiags } },
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
43
58
|
## Inputs / request
|
|
44
59
|
|
|
45
60
|
`createLanguageIntelligence` options:
|
package/docs/migration.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Migration guide
|
|
2
2
|
|
|
3
|
+
## 0.2.5 → 0.2.6 durable recovery, workspaces, and coding-agent readiness (additive)
|
|
4
|
+
|
|
5
|
+
Release **0.2.6** (plan 026) adds the coding-agent readiness capabilities behind optional host-activated seams: host-selected PTY backends, the indexed/semantic repository-search seam, the ownership-scoped multi-repository/worktree lifecycle, durable process/ACP recovery, and the patch-review/diagnostics workflow. **Additive-only: no exported declaration removed or changed, no persisted 0.2.5 shape repurposed.**
|
|
6
|
+
|
|
7
|
+
New durable records use **separate versioned checkpoint namespaces**, never the 0.2.5 shapes:
|
|
8
|
+
|
|
9
|
+
- `prism.coding-agent.process.v1` (schemaVersion 1) — managed-process recovery records. Readers reject unknown schema versions and corrupt/foreign records fail closed (dropped, never recovered).
|
|
10
|
+
- `prism.coding-agent.workspace.v1` (schemaVersion 1) — coding workspace lifecycle records.
|
|
11
|
+
- `prism.coding-agent.cancel.v1` (schemaVersion 1) — durable ACP run-cancel markers.
|
|
12
|
+
|
|
13
|
+
`CodingCheckpointMetadata` (schemaVersion 1, `prism.coding-agent`) is **never silently repurposed**; 0.2.5 readers reject unknown schema versions as before.
|
|
14
|
+
|
|
15
|
+
**ACP active-run references (Task 5 decision: additive optional field).** `PersistedAcpSession` gains an optional bounded `activeRun` ref (frozen 512-byte cap) recorded while a durable run is live. The decision recorded here: an additive optional field, not a separate recovery namespace, because the ref is advisory metadata — the authoritative run status is always re-queried from `AgentRunLifecycle.status` at restore time, and 0.2.5 hosts safely ignore the field. `PersistedAcpSession.activeRun` stays optional; 0.2.5 records remain readable and a 0.2.5 host reading a 0.2.6 record does not lose required recovery state (the run state itself lives in the existing `prism.agent-run` records, which are untouched).
|
|
16
|
+
|
|
17
|
+
**Downgrade to 0.2.5** is safe only after stopping 0.2.6 workers/replicas and marking any live 0.2.6 process/workspace records `unknown` (their leases expire within TTL); durable state never serializes a PTY fd, browser context, process object, controller, pending promise, raw terminal output, env, token, or credential, and no exact-process-survival claim is made (attach-if-attested, otherwise unknown).
|
|
18
|
+
|
|
3
19
|
## 0.2.4 → 0.2.5 maintainability and bounded performance (no migration)
|
|
4
20
|
|
|
5
21
|
Release **0.2.5** (plan 025) is the maintainability-and-bounded-performance cut: the six remaining implementation god-modules split into cohesive internal family files behind preserved barrels (compat-preserving, no `exports`-map subpath), 21 pure persistence helpers moved into the dependency-free `session-store-codecs` package (ownership scope/assertion, checkpoint stale/encode/decode, branch cursors, lifecycle quota/reason/page-limit, search metadata/clipping, deepFreeze/string-array/throwIfAborted, feedback row mapping), the quadratic per-push `Buffer.concat` loops in language framing and tar parsing became chunk-array readers (linear; caps and fail-closed overflow byte-identical), two internal dead type aliases removed (`PostgresPersistenceCloseOptions`, `SqlitePersistenceCloseOptions` — never re-exported from their adapter indexes), and 76 behavior-backed coverage regressions closed the low-coverage core areas (core 91.43/84.80/91.60 lines/branches/functions). **No runtime contract change and no migration**: no exported declaration was removed or changed (the plain reviewed compat gate at 0.2.5 shows the version literal plus 105 additive internal-helper exports), no persisted shape/schema/default/behavior changed, no new runtime dependency. Store compatibility with 0.2.4: **compatible in both directions** — no migration step; rollback = restore the 0.2.4 manifests/tag (stores never change; the added exports disappear on downgrade). The 20 dead-but-compat-tracked exports deferred from Task 4 are the 0.3.0 breaking-cut removal list (see `docs/_evidence/phase25-dead-exports-triage.md`).
|
package/docs/process-sessions.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
## When to use it
|
|
19
19
|
|
|
20
|
-
Use when a host needs attachable long-running processes (watch modes, language servers, interactive CLIs) that one-shot `shell` cannot model. Do not use as a job-control language
|
|
20
|
+
Use when a host needs attachable long-running processes (watch modes, language servers, interactive CLIs) that one-shot `shell` cannot model. Do not use as a job-control language. `pty: true` (host-selected PTY) requires a `ptyBackend` passed to `createProcessSessions`; without one it fails closed before spawn with `ERR_PRISM_PROCESS_PTY_UNSUPPORTED`. Pass a sandbox with `startProcess` for contained long-running work; omit `sandbox` for native spawn.
|
|
21
21
|
|
|
22
22
|
```ts
|
|
23
23
|
import { createProcessSessions } from "@arnilo/prism-coding-agent";
|
|
@@ -43,6 +43,7 @@ await sessions.dispose();
|
|
|
43
43
|
| `ownership?` | `OwnershipScope` | Default owner key for sessions. |
|
|
44
44
|
| `identity?` | `AgentIdentity` | When ownership omitted, owner key projects from identity. |
|
|
45
45
|
| `sandbox?` | `ProcessSandboxBackend` | When set: require `startProcess` or fail closed; `status` loss → all running → `unknown`. |
|
|
46
|
+
| `ptyBackend?` | `ProcessPtyBackend` | Host-selected interactive-terminal backend. `pty: true` delegates only here; absent backend → `ERR_PRISM_PROCESS_PTY_UNSUPPORTED` before spawn. Host supplies the PTY engine (e.g. node-pty); Prism never depends on one. |
|
|
46
47
|
|
|
47
48
|
`ProcessStartRequest`:
|
|
48
49
|
|
|
@@ -51,7 +52,8 @@ await sessions.dispose();
|
|
|
51
52
|
| `command` / `args?` | Executable + argv (not a shell string). |
|
|
52
53
|
| `cwd?` | Relative/absolute path contained under registry `cwd`. |
|
|
53
54
|
| `env?` | Extra env merged onto `process.env` (never in fingerprint). |
|
|
54
|
-
| `pty?` | Default false; unsupported → `ERR_PRISM_PROCESS_PTY_UNSUPPORTED
|
|
55
|
+
| `pty?` | Default false. `true` requires the `ptyBackend` host option (delegated only to it); unsupported host → `ERR_PRISM_PROCESS_PTY_UNSUPPORTED` before spawn. |
|
|
56
|
+
| `terminal?` | `{ columns, rows, term? }` for `pty: true`; defaults `120 × 40`, `xterm-256color`. Bounds: columns 1–120 (hard 500), rows 1–40 (hard 200), TERM ≤ 64 bytes (hard 256). |
|
|
55
57
|
| `lifetimeMs?` | Bounded by `maxLifetimeMs`. |
|
|
56
58
|
| `owner?` | Override owner string. |
|
|
57
59
|
| `releaseOnCancel?` | If true, `cancelOwned` releases instead of killing. |
|
|
@@ -68,10 +70,23 @@ await sessions.dispose();
|
|
|
68
70
|
| `cancelOwned(owner)` | Kill (default) or release owned running sessions. |
|
|
69
71
|
| `markUnknown` | Backend-loss terminal state; never fabricates `exitCode`. |
|
|
70
72
|
| `reconcile()` | Host resume: mark every running/starting session `unknown` (O(sessions)). |
|
|
73
|
+
| `resize?` | Only when `ptyBackend.capabilities.resize` is true: bounded `{ columns, rows }` routed to the live terminal. |
|
|
71
74
|
|
|
72
75
|
Events: `process_started`, `process_exited`, `process_killed`, `process_released`, `process_expired`, `process_unknown`.
|
|
73
76
|
|
|
74
|
-
Errors: `ERR_PRISM_PROCESS_POLICY`, `ERR_PRISM_PROCESS_OWNERSHIP`, `ERR_PRISM_PROCESS_STATE`, `ERR_PRISM_PROCESS_LIMIT`, `ERR_PRISM_PROCESS_PTY_UNSUPPORTED`, `ERR_PRISM_PROCESS_UNSUPPORTED`.
|
|
77
|
+
Errors: `ERR_PRISM_PROCESS_POLICY`, `ERR_PRISM_PROCESS_OWNERSHIP`, `ERR_PRISM_PROCESS_STATE`, `ERR_PRISM_PROCESS_LIMIT`, `ERR_PRISM_PROCESS_PTY_UNSUPPORTED`, `ERR_PRISM_PROCESS_PTY_BACKEND`, `ERR_PRISM_PROCESS_PTY_LIMIT`, `ERR_PRISM_PROCESS_UNSUPPORTED`.
|
|
78
|
+
|
|
79
|
+
## Host-selected PTY backend contract
|
|
80
|
+
|
|
81
|
+
`ptyBackend` is the host's interactive-terminal capability (plan 026 Task 1):
|
|
82
|
+
|
|
83
|
+
- **Delegation only.** `pty: true` starts a session exclusively through `ptyBackend.startPty`; the native spawn path never allocates a terminal. A missing backend or one without `startPty` fails closed before any spawn (`ERR_PRISM_PROCESS_PTY_UNSUPPORTED`). The non-PTY path is byte-compatible with the 0.2.5 baseline.
|
|
84
|
+
- **Contract.** `startPty({ file, args, cwd, env, columns, rows, term, onData })` returns `{ metadata?, write, signal, kill, release, wait, resize? }`. `capabilities.resize` is explicit — `resize` on the session exists only when declared; never duck-typed. `wait()` resolves on process exit (and rejects on backend loss); the host stops delivering `onData` once the session is terminal.
|
|
85
|
+
- **Terminal data is untrusted output.** Control sequences are never parsed or emulated; the host (or an attached terminal client) interprets them. Input is raw terminal bytes; NUL is rejected with a policy error, other control bytes pass through as terminal data.
|
|
86
|
+
- **Bounded attach.** `startPty` must settle within `maxPtyAttachTimeoutMs` (30 s default, 120 s hard); overflow removes the session record and fails with `ERR_PRISM_PROCESS_PTY_LIMIT`. Resize is rate-limited (60/min default, 600 hard) and fails with the same code. Backend `metadata` is bounded (`maxPtyBackendMetadataBytes`, 4 KiB default / 16 KiB hard).
|
|
87
|
+
- **Backend loss.** A throwing `startPty` or a lost `wait()` surfaces as `ERR_PRISM_PROCESS_PTY_BACKEND` with a generic message (backend error text is never embedded); the session becomes `unknown` with `exitCode: null` — never fabricated.
|
|
88
|
+
- **Parity.** Policy, cwd/ownership, cancel/expiry sweeps, input/lifetime/output caps, command fingerprint, events, and redaction behave exactly as non-PTY sessions; PTY sessions count against `maxSessions`.
|
|
89
|
+
- **Recovery caveat (phase 26, task 5).** PTY sessions are not durable across restart: no serialized terminal fd or raw output is ever persisted. Restart recovery reports such sessions `unknown` unless a host `recoveryBackend` re-attaches them; there is no exact-process-survival claim.
|
|
75
90
|
|
|
76
91
|
## Request/response example
|
|
77
92
|
|
|
@@ -123,6 +138,46 @@ await sessions.dispose();
|
|
|
123
138
|
- Command fingerprint is SHA-256 of `[command, ...args]` only (no env).
|
|
124
139
|
- Docker reference adapter does not implement `startProcess` yet — fail closed until a capable runtime is wired.
|
|
125
140
|
|
|
141
|
+
## Durable process recovery (plan 026 Task 5)
|
|
142
|
+
|
|
143
|
+
Optional, host-activated: pass `checkpoints` + `leases` + `ownerId` (all three
|
|
144
|
+
together; a partial recovery configuration fails closed at construction) and
|
|
145
|
+
optionally `recoveryBackend` + `recoveryLimits`. With durability configured:
|
|
146
|
+
|
|
147
|
+
- Intent is persisted BEFORE spawn into the versioned namespace
|
|
148
|
+
`prism.coding-agent.process.v1` (schemaVersion 1, category `coding-process`),
|
|
149
|
+
and every lifecycle transition (running, exited, killed, released, expired,
|
|
150
|
+
unknown) is a CheckpointStore CAS write under a monotonic LeaseStore fencing
|
|
151
|
+
token. Transition writes are serialized per record so CAS order never inverts
|
|
152
|
+
on slow stores.
|
|
153
|
+
- `recover()` reconciles durable records against the live registry:
|
|
154
|
+
- records already live here report `attached` without mutation;
|
|
155
|
+
- terminal records report `terminal` with their exit code;
|
|
156
|
+
- `starting|running` records attach-if-attested: a `backendRef` (opaque
|
|
157
|
+
non-secret ref surfaced by a PTY/sandbox handle's optional `ref`) plus a
|
|
158
|
+
host `recoveryBackend.attach(ref)` (bounded attach timeout 30 s default /
|
|
159
|
+
120 s hard) may reattach; otherwise the record atomically becomes `unknown`
|
|
160
|
+
with exit code null — no PID probing, no fabricated exit, no duplicate
|
|
161
|
+
spawn. `recover()` without durability configured throws
|
|
162
|
+
`ERR_PRISM_RECOVERY_UNSUPPORTED`.
|
|
163
|
+
- Recovered sessions are live registry sessions: input/signal/kill/release/
|
|
164
|
+
resize/wait reach the attached backend; output streaming is not re-established
|
|
165
|
+
after a restart (the host backend owns any buffered output behind its ref).
|
|
166
|
+
- Replica coordination: every mutation takes a per-record lease (30 s default /
|
|
167
|
+
300 s hard); a crashed replica's lease lapses within TTL, a live one renews
|
|
168
|
+
on transitions and releases on terminal transitions. A held lease makes the
|
|
169
|
+
second replica report `unknown` without touching the record; CAS/fence
|
|
170
|
+
conflicts fail closed with `ERR_PRISM_RECOVERY_FENCE`.
|
|
171
|
+
- `cancelOwned` after recovery either reaches the attached backend or records
|
|
172
|
+
the durable record unknown — never a fabricated exit.
|
|
173
|
+
- Durable records are metadata only: no child/PTY handle, controller, promise,
|
|
174
|
+
raw output, env, token, or credential is ever serialized; forbidden fields,
|
|
175
|
+
corrupt, oversized, or cross-tenant records fail closed (dropped, never
|
|
176
|
+
recovered). Records are capped (32 default / 128 hard; oldest terminal
|
|
177
|
+
records evict beyond the cap; running records are never evicted).
|
|
178
|
+
- Errors: `ERR_PRISM_RECOVERY_UNSUPPORTED` / `_LIMIT` / `_OWNERSHIP` / `_FENCE`
|
|
179
|
+
/ `_UNKNOWN` / `_UNTRUSTED` / `_TIMEOUT` (`ProcessRecoveryError`).
|
|
180
|
+
|
|
126
181
|
## Security and performance notes
|
|
127
182
|
|
|
128
183
|
| Cap | Default | Hard |
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Prism is published as **50 publishable manifests**: the root `@arnilo/prism` core package plus **49 workspace packages** — 14 provider adapters, 9 `prism-*` family/profile packages, and 26 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 50th manifest is the 0.1.6 plan 018 optional `@arnilo/prism-document-reader` package (bounded PDF/Office literal-text extraction for the coding read tool; ships only because its `doc-reader` closeout is demanded — a deferred closeout keeps the graph at 49). 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.6` peer; profiles are pure manifests. Installation activates no provider, listener, database, browser, credential, or tool capability.
|
|
8
8
|
|
|
9
9
|
Current **50** publishable manifests (root + 49 workspace packages):
|
|
10
10
|
|
|
@@ -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.6.tgz`; first-party packages produce `arnilo-prism-provider-<name>-0.2.6.tgz` / `arnilo-prism-compaction-<name>-0.2.6.tgz` / `arnilo-prism-coding-agent-0.2.6.tgz`; family/profile packages produce `arnilo-prism-{providers,compaction,base,code,sdk,all}-0.2.6.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
|
|
|
@@ -303,6 +303,51 @@ npm run release:publish -- --version 0.2.4 --dry-run --allow-dirty --allow-untag
|
|
|
303
303
|
|
|
304
304
|
Protected evidence (never a passing skip): the durable state-concurrency legs (Postgres `prism_phase24_*` schemas — `npm run test:postgres` under `PRISM_TEST_POSTGRES_URL`; absent credentials record **blocked** per the release skip manifest), the phase24 package-truth conformance over built dist + packed tarballs, 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.4 as **blocked**, never a passing skip.
|
|
305
305
|
|
|
306
|
+
## Protected coding journey (0.2.6, plan 026 Task 7)
|
|
307
|
+
|
|
308
|
+
The 0.2.6 protected release profile requires real end-to-end coding-agent evidence, never a passing skip. `scripts/phase26-coding-journey.test.mjs` packs the published packages into a fresh consumer, installs the pinned host browser, and runs `scripts/fixtures/phase26-coding-journey.mjs` against real host services: a real LLM provider call through the Prism `AIProvider` contract (host adapter module), a digest-pinned Docker sandbox (`PRISM_TEST_DOCKER_IMAGE` must be `name@sha256:...`), the durable worktree lifecycle over real Postgres checkpoints/leases, a provider-driven edit through ACP with policy approval, a named check with a host parser and `diagnosticDelta`, a patch review composed over the server `ArtifactService` (accepted then superseded), process recovery across replicas (attach-if-attested, never re-spawn), durable ACP cancellation (terminal-idempotent, never replays tools), real GitHub push + lookup-before-create PR + reconcile + cleanup, host Playwright browser inspection, and the host PTY adapter when in the frozen profile.
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
# Full protected profile (all legs real):
|
|
312
|
+
PRISM_CODING_JOURNEY=1 \
|
|
313
|
+
PRISM_TEST_POSTGRES_URL=postgres://... \
|
|
314
|
+
PRISM_TEST_DOCKER_BIN=/usr/bin/docker \
|
|
315
|
+
PRISM_TEST_DOCKER_IMAGE=name@sha256:... \
|
|
316
|
+
PRISM_LIVE_PLAYWRIGHT=1 \
|
|
317
|
+
PRISM_CODING_FORGE_REPOSITORY=owner/repo \
|
|
318
|
+
PRISM_CODING_FORGE_TOKEN=ghp_... \
|
|
319
|
+
PRISM_CODING_PROVIDER=/abs/path/provider-adapter.mjs \
|
|
320
|
+
PRISM_TEST_PTY_BACKEND=/abs/path/pty-host.mjs \
|
|
321
|
+
node --test scripts/phase26-coding-journey.test.mjs
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Every side effect carries the run suffix and is cleaned up idempotently (PR closed, branch deleted, worktree removed, containers/children/browser context closed); unknown cleanup blocks the journey. The retained report `scripts/phase26-coding-journey-report.json` is regenerated on every real run — `journey.state` pass records the surface `pass` in release evidence; blocked/partial reports record `blocked` (fail closed); `not_run`/missing records are documented protected gaps (`requiredEnv PRISM_CODING_JOURNEY`). The CI profile runs in `.github/workflows/coding-journey.yml` (scheduled + dispatch; Postgres service, host Docker, pinned Chromium, provider + forge secrets from the `coding-journey` environment) and uploads the report artifact. Leak scans cover journey stdout/stderr and the report against every credential-looking env value — the report records env names only. Frozen ceilings: journey wall 20 min (hard 40 min), cleanup 5 min (hard 15 min).
|
|
325
|
+
|
|
326
|
+
### 0.2.6 publish handoff (plan 026 Task 8)
|
|
327
|
+
|
|
328
|
+
**Decision: GO when the operator prerequisites below are recorded.** Release **0.2.6** (plan 026) is the fully-featured coding-agent-readiness cut on the 0.2.x review-remediation line. API surface **additive-only** (plain reviewed compat gate at 0.2.6: expected deltas are the version literal plus the Task 1–6 additive exports — PTY backend/handle types, indexed-search seam, workspace lifecycle, process/ACP recovery, review manifest + diagnostics; zero removals; baselines regenerated with `--update-baseline`, no `--allow-break`; freeze manifest `scripts/phase26-freeze-manifest.json` records per-task evidence tokens, state machines, caps, the demand registry, and deviations D-T3-1/D-T5-1/D-T7-1). Seven roadmap items, **no runtime contract change, additive migration**: (1) **host-selected PTY/interactive terminal backend** (`pty-backend`) — `createProcessSessions` gains an optional `ptyBackend` with explicit `capabilities.resize`; `pty: true` without a backend fails byte-compatibly with `ERR_PRISM_PROCESS_PTY_UNSUPPORTED` before spawn; bounded geometry/TERM/attach/resize-rate/metadata caps, generic backend errors that never leak backend text, NUL rejected as a policy error, backend loss surfaces as `unknown` with `exitCode: null`; the protected PTY leg (`scripts/phase26-pty-protected.test.mjs`, gated by `PRISM_TEST_PTY_BACKEND`) passed 4/4 against a real PTY host (python3 `pty.fork` + `TIOCSWINSZ`). (2) **scalable indexed code-search seam** (`indexed-search`) — `createIndexedRepositoryOperations` composes a host-owned incremental index with bounded literal search as the unchanged default; `indexed_literal`/`semantic` fail closed on stale/failed/unsupported/untrusted indexes (`ERR_PRISM_INDEX_*`), no silent semantic-to-literal downgrade, results labeled `untrusted_index`; 100k-entry benchmark p95 ≤ 250 ms, 1k-file update ≤ 1 s, heap ≤ 64 MiB. (3) **ownership-scoped multi-repository/worktree lifecycle** (`workspace-lifecycle`) — `createCodingWorkspaceLifecycle` over CheckpointStore CAS + LeaseStore fencing (`prism.coding-agent.workspace.v1`), locked worktrees with `prism-workspace:` reasons, credential-free remote fingerprints, idempotent create, verify revalidation, cleanup refusal matrix, `ERR_PRISM_WORKSPACE_*`; `GitOperations` gains worktree `lock`/`unlock` + `fingerprint()`. (4) **forge breadth demand-gated** (`forge-breadth`) — GitLab/Bitbucket stay **deferred** in the demand registry with no named consumer; no adapter source ships; activation requires a recorded named consumer/date/use case. (5) **durable ACP/live-task and managed-process recovery** (`durable-recovery`) — bounded process intent/metadata persisted before spawn (`prism.coding-agent.process.v1`), serialized per-record CAS transition writes, attach-if-attested `recover()` reporting `attached|terminal|unknown` with no fabricated exit code and no PID probing; per-record leases fence replicas (memory + real Postgres two-replica conformance 8/8); ACP `activeRun` refs (additive optional, 0.2.5 records stay readable) + `createAcpRunRecovery` status re-resolution and durable fence-checked cancellation (`prism.coding-agent.cancel.v1`) that never replays a pending/dispatched tool. (6) **bounded patch review and incremental diagnostics** (`review-diagnostics`) — `createCodingPatchReviewManifest` + `assertCodingPatchAccepted` (pending/accepted/rejected/superseded bound to digest + revision + identity, stale acceptance refused, never auto-applies/commits/pushes/merges) composed over the server `ArtifactService`; `normalizeDiagnostics`/`diagnosticDelta` with deterministic added/removed/unchanged deltas and host-supplied check parsers; opt-in LSP `syncDocument`/`diagnosticDelta` (monotonic versions, resultId reuse) — LSP stays strictly opt-in, nothing spawns from tool factories or agent assembly. (7) **protected real coding journey** (`coding-journey`) — `scripts/phase26-coding-journey.test.mjs` packs 10 packages into a fresh consumer and drives real host services (provider call, digest-pinned Docker sandbox, Postgres worktree lifecycle, provider-driven ACP edit with policy approval, named check + `diagnosticDelta`, patch review over the server artifact store, cross-replica process recovery, durable cancellation, real GitHub push/lookup-before-create PR/reconcile/cleanup, host Playwright inspection, host PTY adapter in the frozen profile) under frozen wall/cleanup ceilings with run-suffix side effects and per-step idempotent cleanup; missing credentials/services or skipped substeps record **blocked**, never a passing skip; the retained `scripts/phase26-coding-journey-report.json` (timings/states/ids only) gates release evidence (pass/blocked/protected). Release graph stays **50** publishable manifests at exact **0.2.6**; zero new runtime dependency names (core remains dependency-free); 43 code packages + 6 pure-manifest family/profile.
|
|
329
|
+
|
|
330
|
+
**Measured reductions and deltas (recorded in `scripts/phase26-baseline.json` `exitGate`).** New error families: `ERR_PRISM_PROCESS_PTY_*`, `ERR_PRISM_INDEX_*`, `ERR_PRISM_WORKSPACE_*`, `ERR_PRISM_RECOVERY_*`, `ERR_PRISM_REVIEW_*` — all additive, all fail closed. New protected env names (never values): `PRISM_TEST_PTY_BACKEND`, `PRISM_TEST_POSTGRES_URL`, `PRISM_TEST_DOCKER_BIN`, `PRISM_TEST_DOCKER_IMAGE`, `PRISM_LIVE_PLAYWRIGHT`, `PRISM_CODING_FORGE_REPOSITORY`, `PRISM_CODING_FORGE_TOKEN`, `PRISM_CODING_PROVIDER`, `PRISM_CODING_JOURNEY`. Coverage stayed above the recorded 0.2.5 floors. **Rollback notes.** Rollback = restore the 0.2.5 manifests/tag. Three new versioned checkpoint namespaces exist (`prism.coding-agent.process.v1`, `prism.coding-agent.workspace.v1`, `prism.coding-agent.cancel.v1`) plus the additive optional ACP `activeRun` ref; before downgrading, stop all 0.2.6 workers and mark active PTY/process/recovery records unknown (a crashed 0.2.6 replica that resumes on 0.2.5 fails closed — recovery never fabricates an exit code or re-spawns without host attestation). No 0.2.5 persisted shape changed, so an ordinary downgrade is store-safe; the added exports simply disappear.
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
# Operator prerequisites recorded: clean tree at the v0.2.6 tag candidate, GPG key, npm OIDC publisher.
|
|
334
|
+
node scripts/release.mjs bump --from 0.2.5 --to 0.2.6 # already applied by Task 8; idempotent
|
|
335
|
+
npm test # core + workspace suites + all script gates (incl. phase26-freeze + phase26-index-benchmark)
|
|
336
|
+
npm run security:threat-suites # phase8-11 + phase20-25 public-entry conformance
|
|
337
|
+
PRISM_TEST_POSTGRES_URL=postgres://postgres:prism@127.0.0.1:54329/prism_test npm run test:postgres
|
|
338
|
+
node --test scripts/phase26-recovery-conformance.test.mjs # protected: memory + real Postgres two-replica recovery/workspace conformance
|
|
339
|
+
node --test scripts/phase26-pty-protected.test.mjs # protected: real PTY host (PRISM_TEST_PTY_BACKEND)
|
|
340
|
+
PRISM_TEST_POSTGRES_URL=postgres://postgres:prism@127.0.0.1:54329/prism_test npm run sdk:ready
|
|
341
|
+
node scripts/release.mjs gate --version 0.2.6 # plain reviewed gate at 0.2.6: version literal + additive exports only, 0 breaking deltas
|
|
342
|
+
npm run pack:dry-run # twice; diff reports — deterministic
|
|
343
|
+
npm audit --audit-level=moderate
|
|
344
|
+
npm run release:check -- --version 0.2.6 --report /tmp/prism-0.2.6-preflight.json
|
|
345
|
+
npm run release:publish -- --version 0.2.6 --dry-run --allow-dirty --allow-untagged --report /tmp/prism-0.2.6-dry-run.json
|
|
346
|
+
# run the dry-run twice and diff the reports: deterministic, byte-identical
|
|
347
|
+
```
|
|
348
|
+
|
|
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
|
+
|
|
306
351
|
### 0.2.5 publish handoff (plan 025 Task 6)
|
|
307
352
|
|
|
308
353
|
**Decision: GO when the operator prerequisites below are recorded.** Release **0.2.5** (plan 025) is the maintainability-and-bounded-performance cut on the 0.2.x review-remediation line. API surface **additive-only** (plain reviewed compat gate at 0.2.5: expected deltas are the version literal plus 105 additive internal-helper exports from the splits/dedup — 84 Task 1 cross-family helpers across `@arnilo/prism`/`prism-coding-agent`/`prism-workflows`/`prism-server`/`prism-ag-ui` and 21 `prism-session-store-codecs` helpers — zero removals; baselines regenerated with `--update-baseline`, no `--allow-break`; freeze manifest `scripts/phase25-freeze-manifest.json`). Internal-only refactoring and bounded-performance work, **no runtime contract change and no migration**: (1) **god-module splits, compat-preserving** — the six remaining implementation god-modules split along cohesive boundaries into internal family files behind preserved barrels (0.1.4 precedent): `src/contracts-core.ts` (1,719 L → 10 families, max 403 L), `src/agent-session.ts` (2,049 L → 4 modules; the 1,686-L `RuntimeAgentSession` class is kept intact — a single TS class cannot span files without exporting private methods, recorded reason), `packages/workflows/src/run.ts` (1,227 L → 6 families, max 417 L), `packages/server/src/handler.ts` (1,005 L → 8 families, max 444 L), `packages/coding-agent/src/repository.ts` (974 L → 7 families, max 299 L), `packages/ag-ui/src/acp/agent.ts` (836 L → 8 families, max 386 L). No `exports` map gained a subpath; `ponytail:` comments preserved verbatim; every package verified with **zero breaking compat deltas** (`scripts/phase25-compat-diff.mjs`). (2) **persistence-mechanics dedup** — 21 pure helpers (ownership scope/assertion, checkpoint stale/encode/decode, branch cursors, lifecycle quota/reason/page-limit, search metadata/clipping, deepFreeze/string-array/throwIfAborted, feedback row mapping) moved into the dependency-free `packages/session-store-codecs` (426 → 624 L, stdlib only, no SQL dialect leakage); the adapters shrank 273 lines total (postgres 1,104 → 1,041, sqlite 1,088 → 1,010); SQL fragments, DDL templates, and query execution stay per-adapter; no persisted-shape change; cross-store conformance proves identical semantics before/after. (3) **quadratic accumulation removed** — the per-push `Buffer.concat` loops in language framing (`LspFrameReader`) and tar parsing (`summarizeTarStream`) became chunk-array readers (two-phase header parse + offset-advance `drop`, `take`/`drop` sliding window); caps and fail-closed overflow behavior byte-identical; framing measured **~100–200× faster at N=4000 chunks** (1,298.4 ms → 11.2 ms) and tar linear at 8 MiB; CLI capture (`collectOutput`) audited — already linear since plan 020; the near-limit probe `scripts/phase25-bounded-accumulation.test.mjs` (10 tests) is wired into the npm test gate segment and asserts linear copying by byte-count instrumentation. (4) **dead-code cleanup, internal-only** — the 62 `dead-exports.mjs` candidates triaged into 2 removals (`PostgresPersistenceCloseOptions`, `SqlitePersistenceCloseOptions` — internal type aliases never re-exported from their adapter indexes) + 60 allow-listed with reasons in `docs/_evidence/phase25-dead-exports-triage.md` (37 test-used false positives, 20 dead-but-compat-tracked deferred to the 0.3.0 breaking cut, 3 public type aliases deferred); the named-internal audit (`agent-session`/`cache-telemetry`/`skill-load`) recorded as clean at 0.2.4; no public export removed, so **no `docs/migration.md` removal note is required** (this section records the no-runtime-contract-delta statement instead). (5) **coverage close, behavior-backed** — 76 focused regressions (approval 43 — the untested `agent-approval.ts` resolve/validate/pending paths; conversations 22 — cursor codec + thread projection; artifacts 6 — approval-state/checkpoint-key/error codes; tool-effect-store conformance 5 — violation throws + option defaults; compaction relies on its 17 existing package suites); core coverage rose **90.53/84.20/90.54 → 91.43/84.80/91.60** lines/branches/functions (gate 60/70/75; all 39 non-protected packages above their evidence thresholds). Release graph stays **50** publishable manifests at exact **0.2.5**; zero new runtime dependency names (core remains dependency-free); 43 code packages + 6 pure-manifest family/profile.
|
|
@@ -746,7 +791,7 @@ Audit fixes, dependency updates, and security patches land only for the supporte
|
|
|
746
791
|
|
|
747
792
|
## Extension and configuration notes
|
|
748
793
|
|
|
749
|
-
- **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional **exact** `@arnilo/prism@0.2.
|
|
794
|
+
- **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional **exact** `@arnilo/prism@0.2.6` 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.6` installed with a package peering `@arnilo/prism@0.2.7`) 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.
|
|
750
795
|
- **Public access.** All 50 manifests (root + 49 workspace packages: 43 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.
|
|
751
796
|
- **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).
|
|
752
797
|
- **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`.
|
|
@@ -91,6 +91,10 @@ export const handler = createArtifactHandler({ service: artifacts, authorize: ho
|
|
|
91
91
|
- Frozen caps (default / hard): artifacts per thread 64/256; revisions per artifact 32/128; record 8/64 KiB; preview 16/64 KiB; citations 32/128 and 2/8 KiB each; MIME 128/512 B; hash 256/1 KiB; compare exactly 2 revisions; delivery TTL 5 min/24 h; delivery token 4/16 KiB. Raising the revision cap may require raising `recordBytes` (aggregate backstop).
|
|
92
92
|
- Compare is hash+metadata-bounded (hosts render content); no file bodies are persisted or transferred. With a wired body store, bodies live in the host's object store and are streamed through the adapter (bounded by `maxBodyBytes` 64 MiB/512 MiB, concurrent transfers 4/16, presign TTL 10 min/24 h); object-store outages surface typed `ERR_PRISM_S3_*` / `ERR_PRISM_ARTIFACT_BODY_*` errors, never silent success.
|
|
93
93
|
|
|
94
|
+
## Coding patch review composition (0.2.6, plan 026)
|
|
95
|
+
|
|
96
|
+
`@arnilo/prism-coding-agent` composes over this service for the coding patch review workflow: `createCodingPatchReviewManifest` builds a bounded manifest (repository/worktree identity, base/head, patch digest, changed paths, diffstat, check and diagnostic summaries) and returns a structural `ArtifactAttachInput` whose `preview.review` embeds the manifest and whose `hash` is the patch SHA-256; `assertCodingPatchAccepted` derives `pending|accepted|rejected|superseded` from the returned `ArtifactRecord` by binding to the exact artifact revision, digest, and identity — any patch/repository/worktree/base/head change supersedes a prior acceptance (a newer revision attached after approval makes the old acceptance stale and refused). Decisions never apply/commit/push/merge; the manifest never embeds a raw patch body. Full contract: [Coding review and diagnostics](coding-review-and-diagnostics.md).
|
|
97
|
+
|
|
94
98
|
## Related APIs
|
|
95
99
|
|
|
96
100
|
- [Server](server.md): `createArtifactService` / `createArtifactHandler` mount alongside the Prism handler; ownership only from `authorize`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arnilo/prism",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Agent harness for AI providers, agents, sessions, and tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"build": "npm run build:core && npm run build --workspaces --if-present",
|
|
148
148
|
"typecheck": "npm run build && npm run typecheck --workspaces --if-present && tsc -p examples --noEmit",
|
|
149
149
|
"sweep:unused": "node scripts/sweep-unused.mjs --json",
|
|
150
|
-
"test": "npm run build && node scripts/with-build-lock.mjs node --test dist/__tests__/*.test.js && node scripts/with-build-lock.mjs node --test scripts/release-gate.test.mjs scripts/tooling-gate.test.mjs scripts/budget-gate.test.mjs scripts/phase8-conformance.test.mjs scripts/phase9-conformance.test.mjs scripts/phase10-conformance.test.mjs scripts/phase11-conformance.test.mjs scripts/phase11-freeze.test.mjs scripts/phase12-freeze.test.mjs scripts/phase13-freeze.test.mjs scripts/phase14-freeze.test.mjs scripts/phase15-freeze.test.mjs scripts/phase16-freeze.test.mjs scripts/phase17-freeze.test.mjs scripts/phase18-freeze.test.mjs scripts/phase19-freeze.test.mjs scripts/phase20-freeze.test.mjs scripts/phase21-freeze.test.mjs scripts/benchmark-0.1.0.test.mjs scripts/sweep-unused.test.mjs scripts/e2e-enterprise-journey.test.mjs scripts/e2e-coding-journey.test.mjs scripts/phase23-quality-gates.test.mjs scripts/phase24-truth.test.mjs scripts/phase25-bounded-accumulation.test.mjs && node --test scripts/phase23-build-race.test.mjs && npm run test --workspaces --if-present",
|
|
150
|
+
"test": "npm run build && node scripts/with-build-lock.mjs node --test dist/__tests__/*.test.js && node scripts/with-build-lock.mjs node --test scripts/release-gate.test.mjs scripts/tooling-gate.test.mjs scripts/budget-gate.test.mjs scripts/phase8-conformance.test.mjs scripts/phase9-conformance.test.mjs scripts/phase10-conformance.test.mjs scripts/phase11-conformance.test.mjs scripts/phase11-freeze.test.mjs scripts/phase12-freeze.test.mjs scripts/phase13-freeze.test.mjs scripts/phase14-freeze.test.mjs scripts/phase15-freeze.test.mjs scripts/phase16-freeze.test.mjs scripts/phase17-freeze.test.mjs scripts/phase18-freeze.test.mjs scripts/phase19-freeze.test.mjs scripts/phase20-freeze.test.mjs scripts/phase21-freeze.test.mjs scripts/benchmark-0.1.0.test.mjs scripts/sweep-unused.test.mjs scripts/e2e-enterprise-journey.test.mjs scripts/e2e-coding-journey.test.mjs scripts/phase23-quality-gates.test.mjs scripts/phase24-truth.test.mjs scripts/phase25-bounded-accumulation.test.mjs scripts/phase26-freeze.test.mjs scripts/phase26-index-benchmark.test.mjs && node --test scripts/phase23-build-race.test.mjs && npm run test --workspaces --if-present",
|
|
151
151
|
"test:coverage": "node scripts/with-build-lock.mjs node --test --experimental-test-coverage --test-coverage-lines=60 --test-coverage-functions=70 --test-coverage-branches=75 --test-coverage-exclude='**/__tests__/**' --test-coverage-exclude='**/node_modules/**' --test-coverage-exclude='**/scripts/**' --test-coverage-exclude='**/packages/**' --test-coverage-exclude='**/examples/**' dist/__tests__/*.test.js && node scripts/with-build-lock.mjs node scripts/coverage-summary.mjs && node --test scripts/phase23-coverage.test.mjs && node --test scripts/phase23-skip-manifest.test.mjs",
|
|
152
152
|
"coverage:summary": "node scripts/with-build-lock.mjs node scripts/coverage-summary.mjs",
|
|
153
153
|
"lint": "biome lint . --reporter=sarif --reporter-file=scripts/lint-report.sarif",
|