@arnilo/prism 0.2.4 → 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.
Files changed (51) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/agent-session/create-agent.d.ts +6 -0
  3. package/dist/agent-session/create-agent.js +13 -0
  4. package/dist/agent-session/event-subscriber.d.ts +17 -0
  5. package/dist/agent-session/event-subscriber.js +68 -0
  6. package/dist/agent-session/helpers.d.ts +44 -0
  7. package/dist/agent-session/helpers.js +194 -0
  8. package/dist/agent-session/session.d.ts +101 -0
  9. package/dist/agent-session/session.js +1568 -0
  10. package/dist/agent-session.d.ts +6 -105
  11. package/dist/agent-session.js +6 -1833
  12. package/dist/contracts-core/agent.d.ts +257 -0
  13. package/dist/contracts-core/agent.js +5 -0
  14. package/dist/contracts-core/compaction.d.ts +72 -0
  15. package/dist/contracts-core/compaction.js +2 -0
  16. package/dist/contracts-core/content.d.ts +116 -0
  17. package/dist/contracts-core/content.js +2 -0
  18. package/dist/contracts-core/extensions.d.ts +163 -0
  19. package/dist/contracts-core/extensions.js +2 -0
  20. package/dist/contracts-core/loop.d.ts +98 -0
  21. package/dist/contracts-core/loop.js +2 -0
  22. package/dist/contracts-core/persistence.d.ts +366 -0
  23. package/dist/contracts-core/persistence.js +9 -0
  24. package/dist/contracts-core/provider.d.ts +97 -0
  25. package/dist/contracts-core/provider.js +2 -0
  26. package/dist/contracts-core/resources.d.ts +44 -0
  27. package/dist/contracts-core/resources.js +7 -0
  28. package/dist/contracts-core/run-limits.d.ts +81 -0
  29. package/dist/contracts-core/run-limits.js +2 -0
  30. package/dist/contracts-core/session.d.ts +187 -0
  31. package/dist/contracts-core/session.js +131 -0
  32. package/dist/contracts-core.d.ts +13 -1425
  33. package/dist/contracts-core.js +10 -138
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.js +1 -1
  36. package/docs/0.1.0-readiness.md +10 -10
  37. package/docs/acp.md +2 -0
  38. package/docs/browser-automation.md +1 -1
  39. package/docs/coding-agent-tools.md +5 -4
  40. package/docs/coding-review-and-diagnostics.md +76 -0
  41. package/docs/coding-security.md +2 -0
  42. package/docs/coding-workspaces.md +69 -0
  43. package/docs/forge-integration.md +6 -0
  44. package/docs/index.md +5 -5
  45. package/docs/indexed-code-search.md +82 -0
  46. package/docs/language-intelligence.md +15 -0
  47. package/docs/migration.md +20 -0
  48. package/docs/process-sessions.md +58 -3
  49. package/docs/release-and-install.md +71 -3
  50. package/docs/work-artifacts-and-review.md +4 -0
  51. package/package.json +2 -2
@@ -1,139 +1,11 @@
1
- export const SESSION_ENTRY_KINDS = [
2
- "message",
3
- "event",
4
- "summary",
5
- "metadata",
6
- "model_change",
7
- "label",
8
- "custom",
9
- "compaction",
10
- ];
11
- const SESSION_ENTRY_KIND_SET = new Set(SESSION_ENTRY_KINDS);
12
- export const SESSION_ENTRY_SCHEMA_VERSION = 1;
13
- export function isSessionEntryKind(value) {
14
- return typeof value === "string" && SESSION_ENTRY_KIND_SET.has(value);
15
- }
16
- /** Host-written `SessionRecord.metadata` / session metadata key for workspace filtering. */
17
- export const SESSION_SEARCH_WORKSPACE_METADATA_KEY = "workspaceRoot";
18
- export const DEFAULT_SESSION_SEARCH_LIMIT = 20;
19
- export const HARD_MAX_SESSION_SEARCH_LIMIT = 100;
20
- export const DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES = 4 * 1024;
21
- export const HARD_MAX_SESSION_SEARCH_QUERY_BYTES = 16 * 1024;
22
- export const DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES = 512;
23
- export const HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES = 4 * 1024;
24
- export const DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES = 1 * 1024;
25
- export const HARD_MAX_SESSION_SEARCH_CURSOR_BYTES = 4 * 1024;
26
- export const DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS = 1_000;
27
- export const HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS = 5_000;
28
- export const DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES = 10_000;
29
- export const HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES = 50_000;
30
- export const DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES = 8 * 1024 * 1024;
31
- export const HARD_MAX_SESSION_SEARCH_LINEAR_BYTES = 64 * 1024 * 1024;
32
- export const DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES = 1_000;
33
- export const HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES = 5_000;
34
- /**
35
- * O(1) validation before any scan/query. Applies default page limit; rejects NaN,
36
- * non-positive limits, oversize query/cursor/filter strings, and invalid order.
37
- */
38
- export function resolveSessionSearchQuery(query) {
39
- const limit = query.limit === undefined ? DEFAULT_SESSION_SEARCH_LIMIT : query.limit;
40
- if (!Number.isSafeInteger(limit) || limit < 1 || limit > HARD_MAX_SESSION_SEARCH_LIMIT) {
41
- throw new TypeError(`SessionSearchQuery.limit must be a safe integer from 1 to ${HARD_MAX_SESSION_SEARCH_LIMIT}`);
42
- }
43
- const order = query.order ?? "desc";
44
- if (order !== "asc" && order !== "desc") {
45
- throw new TypeError('SessionSearchQuery.order must be "asc" or "desc"');
46
- }
47
- assertSearchStringBytes(query.query, "query", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
48
- assertSearchStringBytes(query.cursor, "cursor", HARD_MAX_SESSION_SEARCH_CURSOR_BYTES);
49
- assertSearchStringBytes(query.workspaceRoot, "workspaceRoot", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
50
- assertSearchStringBytes(query.provider, "provider", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
51
- assertSearchStringBytes(query.model, "model", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
52
- assertSearchStringBytes(query.label, "label", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
53
- assertSearchStringBytes(query.summary, "summary", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
54
- assertSearchStringBytes(query.tenantId, "tenantId", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
55
- assertSearchStringBytes(query.accountId, "accountId", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
56
- assertSearchStringBytes(query.userId, "userId", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
57
- assertSearchStringBytes(query.fromUpdatedAt, "fromUpdatedAt", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
58
- assertSearchStringBytes(query.toUpdatedAt, "toUpdatedAt", HARD_MAX_SESSION_SEARCH_QUERY_BYTES);
59
- return { ...query, limit, order };
60
- }
61
- function assertSearchStringBytes(value, name, hardMax) {
62
- if (value === undefined)
63
- return;
64
- if (typeof value !== "string") {
65
- throw new TypeError(`SessionSearchQuery.${name} must be a string`);
66
- }
67
- // ponytail: UTF-8 byte length via TextEncoder; upgrade only if a non-Unicode host appears.
68
- const bytes = new TextEncoder().encode(value).byteLength;
69
- if (bytes > hardMax) {
70
- throw new TypeError(`SessionSearchQuery.${name} exceeds ${hardMax} bytes`);
71
- }
72
- }
73
- export const SESSION_SEARCH_UNSUPPORTED_CODE = "session_search_unsupported";
74
- /** Thrown when a store opts out of `searchSessions` (memory `unsupported`, JSONL). */
75
- export class SessionSearchUnsupportedError extends Error {
76
- code = SESSION_SEARCH_UNSUPPORTED_CODE;
77
- constructor(message = "session search is unsupported by this store") {
78
- super(message);
79
- this.name = "SessionSearchUnsupportedError";
80
- }
81
- }
82
- export function isSessionSearchUnsupported(error) {
83
- return error instanceof Error && error.code === SESSION_SEARCH_UNSUPPORTED_CODE;
84
- }
85
- /** Stable error code carried by `SessionAppendConflictError`. */
86
- export const SESSION_APPEND_CONFLICT_CODE = "session_append_conflict";
87
- /** CAS conflict code for `appendSession` metadata writes. Stable and message-independent. */
88
- export const SESSION_METADATA_CONFLICT_CODE = "metadata_conflict";
89
- /**
90
- * Thrown when `appendSession` is called with an `expectedVersion` CAS guard and the
91
- * stored session's version no longer matches (concurrent create/branch/archive, or a
92
- * delete raced the write). Recognize via the stable `code` or `isSessionMetadataConflict`.
93
- */
94
- export class SessionMetadataConflictError extends Error {
95
- conflict;
96
- code = SESSION_METADATA_CONFLICT_CODE;
97
- constructor(conflict) {
98
- super(`session metadata conflict: expected version ${conflict.expectedVersion}, current ${conflict.currentVersion}`);
99
- this.conflict = conflict;
100
- this.name = "SessionMetadataConflictError";
101
- }
102
- }
103
- /** Type guard keyed off the stable `code` (works across bundles; not message text). */
104
- export function isSessionMetadataConflict(error) {
105
- return error instanceof Error && error.code === SESSION_METADATA_CONFLICT_CODE;
106
- }
107
- /**
108
- * Thrown when `SessionStore.append` rejects an entry under `SessionAppendOptions`
109
- * (dangling/stale `expectedParentId`, stricter adapter CAS failure, or duplicate
110
- * idempotency key for the same parent). Recognize via the stable `code` and
111
- * `isSessionAppendConflict`, not message text.
112
- */
113
- export class SessionAppendConflictError extends Error {
114
- conflict;
115
- code = SESSION_APPEND_CONFLICT_CODE;
116
- constructor(conflict) {
117
- const detail = conflict.idempotencyDuplicate
118
- ? `idempotency key already used`
119
- : conflict.currentLeafId !== undefined
120
- ? `expected parent ${conflict.expectedParentId ?? "<none>"} does not match current leaf ${conflict.currentLeafId}`
121
- : `expected parent ${conflict.expectedParentId ?? "<none>"} is unavailable`;
122
- super(`session append conflict: ${detail}`);
123
- this.conflict = conflict;
124
- this.name = "SessionAppendConflictError";
125
- }
126
- }
127
- /** Type guard keyed off the stable `code` (works across bundles; not message text). */
128
- export function isSessionAppendConflict(error) {
129
- return error instanceof Error && error.code === SESSION_APPEND_CONFLICT_CODE;
130
- }
131
- const SESSION_METADATA_KEY_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
132
- /** Validate a top-level `SessionRecord.metadata` key used by `SessionQuery.metadataKey` filters. */
133
- export function assertSessionMetadataKey(key) {
134
- if (typeof key !== "string" || !SESSION_METADATA_KEY_PATTERN.test(key)) {
135
- throw new RangeError("metadataKey must match /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/");
136
- }
137
- return key;
138
- }
1
+ export * from "./contracts-core/content.js";
2
+ export * from "./contracts-core/run-limits.js";
3
+ export * from "./contracts-core/provider.js";
4
+ export * from "./contracts-core/agent.js";
5
+ export * from "./contracts-core/extensions.js";
6
+ export * from "./contracts-core/session.js";
7
+ export * from "./contracts-core/persistence.js";
8
+ export * from "./contracts-core/compaction.js";
9
+ export * from "./contracts-core/resources.js";
10
+ export * from "./contracts-core/loop.js";
139
11
  //# sourceMappingURL=contracts-core.js.map
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.4";
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.4";
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
@@ -1,6 +1,6 @@
1
1
  # 0.1.0 / 1.0 Readiness Gates
2
2
 
3
- Status: **0.2.4** 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); **0.1.7** was the terminal 0.1.x baseline; **1.0** readiness remains operator-gated, not automatic.
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.4
21
- snapshot (plan 024) with the 0.1.x tables below as the historical record.
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.4)
23
+ ## Current line (0.2.6)
24
24
 
25
25
  | Item | Status |
26
26
  |---|---|
27
- | Published graph | **50** publishable manifests at exact **0.2.4** (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) |
29
- | Upgrade path | `docs/migration.md` `0.2.3 → 0.2.4` (version literal + peer-version policy only, no migration); 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.4 (version-literal delta only), zero breaking deltas |
31
- | Security policy | `npm audit --audit-level=moderate` 0 at 0.2.4; threat-suites legs (phase8–11 + phase20–24) green; protected Postgres/NATS/live-canary legs operator-gated |
32
- | Docs freeze | tripwires green including the canonical manifest-count tripwire (50/49/14/9/26) and 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 (PTY still unsupported — see [Process sessions](process-sessions.md)).
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 (default). `regex` removed in 0.0.18. |
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.
@@ -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.4** 50-package graph (root + 49 workspace packages) — 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.
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.4** 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.
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
+ - [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