@arnilo/prism 0.0.10 → 0.0.12

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 (60) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/agent-loops.d.ts +1 -0
  3. package/dist/agent-loops.js +37 -2
  4. package/dist/agent-run-lifecycle.d.ts +5 -1
  5. package/dist/agent-run-lifecycle.js +17 -1
  6. package/dist/agents.d.ts +3 -1
  7. package/dist/agents.js +202 -24
  8. package/dist/context-budget.d.ts +63 -0
  9. package/dist/context-budget.js +235 -0
  10. package/dist/contracts.d.ts +109 -0
  11. package/dist/contracts.js +77 -0
  12. package/dist/index.d.ts +8 -6
  13. package/dist/index.js +5 -4
  14. package/dist/input.d.ts +3 -0
  15. package/dist/input.js +71 -28
  16. package/dist/node/session-store-jsonl.js +4 -1
  17. package/dist/rpc.js +13 -2
  18. package/dist/session-stores.d.ts +7 -2
  19. package/dist/session-stores.js +174 -4
  20. package/dist/structured-output.d.ts +5 -1
  21. package/dist/structured-output.js +18 -0
  22. package/dist/testing/persistence-schema.d.ts +1 -1
  23. package/dist/testing/persistence-schema.js +8 -2
  24. package/dist/testing/session-store-conformance.d.ts +6 -0
  25. package/dist/testing/session-store-conformance.js +36 -1
  26. package/docs/a2a.md +1 -0
  27. package/docs/ag-ui.md +123 -0
  28. package/docs/agent-events.md +2 -1
  29. package/docs/agent-loops.md +8 -1
  30. package/docs/agent-session-runtime.md +10 -2
  31. package/docs/cli-rpc.md +2 -1
  32. package/docs/coding-agent-tools.md +70 -1
  33. package/docs/compaction-and-retry.md +2 -1
  34. package/docs/compaction-llm.md +20 -1
  35. package/docs/credential-storage.md +2 -1
  36. package/docs/credentials-and-redaction.md +8 -0
  37. package/docs/evaluations.md +1 -1
  38. package/docs/host-security.md +2 -0
  39. package/docs/index.md +20 -17
  40. package/docs/input-and-prompt-assembly.md +4 -1
  41. package/docs/migration.md +32 -0
  42. package/docs/node-jsonl-session-store.md +1 -1
  43. package/docs/performance.md +36 -0
  44. package/docs/postgres-persistence.md +3 -3
  45. package/docs/provider-packages.md +11 -1
  46. package/docs/providers/anthropic.md +93 -0
  47. package/docs/providers/google.md +88 -0
  48. package/docs/providers/openai.md +2 -2
  49. package/docs/public-contracts.md +6 -0
  50. package/docs/release-and-install.md +232 -65
  51. package/docs/review-coverage-2026-07-22-phase-6.md +209 -0
  52. package/docs/review-coverage-2026-07-22-phase-7.md +173 -0
  53. package/docs/runs-and-usage.md +1 -0
  54. package/docs/server.md +1 -0
  55. package/docs/session-store-conformance.md +2 -0
  56. package/docs/session-stores.md +40 -1
  57. package/docs/sqlite-persistence.md +3 -3
  58. package/docs/structured-output.md +7 -1
  59. package/docs/workflows.md +2 -0
  60. package/package.json +3 -2
@@ -0,0 +1,209 @@
1
+ # Review coverage — 2026-07-22 Phase 6
2
+
3
+ Working evidence for Plan 074 Task 0. Freezes Phase 6 / Release **0.0.11** scope, primitive ownership, finite limits, threats, tests, docs, and release gates before implementation.
4
+
5
+ **Evidence frozen:** 2026-07-22. **Prism source:** `a677113a409b1b60a3361a76c980e7411013916a` (post-0.0.10 / Task 0 freeze). **Release target:** 0.0.11. **Default test rule:** network-free fakes/fixtures; provider live canaries remain gated (`PRISM_LIVE_PROVIDER_TESTS=1` + host keys).
6
+
7
+ ## Status legend
8
+
9
+ | Status | Meaning |
10
+ | --- | --- |
11
+ | `existing` | Current public contract covers the requirement. |
12
+ | `extend` | Owning task extends an existing core/package contract. |
13
+ | `new-package` | New optional workspace package behind existing provider-package seams. |
14
+ | `compose` | Existing public primitives suffice; package-local glue / docs / examples only. |
15
+ | `out-of-scope` | Coding-harness P2+ or later release; must not land in 0.0.11 tasks. |
16
+
17
+ ## Frozen product decision
18
+
19
+ 0.0.11 closes **coding-harness P1 fundamentals** only: bounded session search/index, assembler-time context budgets with omission reports, native Anthropic then Google provider packages, and a thin goal→verify coding helper/example.
20
+
21
+ **Not in 0.0.11** (owned by later phases — do not implement here):
22
+
23
+ | Deferred item | Owner phase |
24
+ | --- | --- |
25
+ | Additional subscription OAuth adapters | 0.0.12 Phase 7 |
26
+ | AG-UI/ACP-facing event adapter | 0.0.12 Phase 7 |
27
+ | Coding-aware compaction preset | 0.0.12 Phase 7 |
28
+ | Enterprise identity / Azure·Bedrock·Vertex / work connectors | 0.0.13+ |
29
+ | Always-on FTS reindex workers / indexer daemons | never in 0.0.11 |
30
+ | Goal database / second agent or workflow runtime | never in 0.0.11 |
31
+ | Shared core Anthropic Messages serializer extraction | defer until ≥2 byte-identical non-test consumers |
32
+ | Soft-raising hard caps; unbounded full-store scans as default search | never in 0.0.11 |
33
+
34
+ ## Frozen external revisions
35
+
36
+ | Surface | Frozen reference | Compatibility decision |
37
+ | --- | --- | --- |
38
+ | Prism | [`a677113a409b1b60a3361a76c980e7411013916a`](../plans/074-release-0-0-11-coding-harness-fundamentals.md) | Caller/limit claims checked against shipped 0.0.10 tree. |
39
+ | Node.js | Local reference `v24.18.0`; release support remains Node 20 and current | Providers use `fetch` + existing `@arnilo/prism/providers/transport` SSE helpers; no vendor SDKs. |
40
+ | Anthropic Messages | ctx7 `/anthropics/anthropic-sdk-typescript` + official Messages docs at impl time | Stream SSE, tools, `cache_control` ephemeral `5m`/`1h`, thinking blocks, usage; package-local wire (OpenCode Go route is pattern only). |
41
+ | Google Gemini | ctx7 `/websites/ai_google_dev_gemini-api` + Gemini `generateContent` / stream docs at impl time | Tools/function calling, multimodal `inlineData`, usage metadata; Vertex enterprise identity stays 0.0.13. |
42
+ | SQLite FTS5 / PostgreSQL FTS | Adapter docs at impl time | Dialect-local FTS; metadata filters shared shape. |
43
+
44
+ ## Frozen API and mode contract
45
+
46
+ | Decision | Frozen choice |
47
+ | --- | --- |
48
+ | Search interface | `SessionIndex` with `search(query: SessionSearchQuery): Promise<PersistencePage<SessionSearchHit>>`. Optional on stores via `searchSessions?` **or** returned companion index — adapters implement one authoritative path; hosts must not need both. |
49
+ | Query / hit types | `SessionSearchQuery`, `SessionSearchHit` in core (next to session/persistence contracts). |
50
+ | Hit fields | Required: `sessionId`. Optional: `leafId` (branch tip for `checkout`), `updatedAt`, `label`, `summary`, `snippet`, safe display metadata. **Never** credentials, raw message bodies wholesale, or secret-looking payloads. |
51
+ | Workspace filter | Host-written `metadata.workspaceRoot` (string path/key). No first-class `prism_sessions.workspace` column in 0.0.11. Query field name: `workspaceRoot`. |
52
+ | Text / filters | Optional `query` (FTS/message+summary); `provider` / `model`; `label`/`summary` substring or FTS per adapter; `fromUpdatedAt`/`toUpdatedAt`; `OwnershipScope` (`tenantId`/`accountId`/`userId`). |
53
+ | Pagination | Reuse `PersistencePage` + `limit`/`cursor`/`order`. |
54
+ | Memory search | `createMemorySessionStore(entries?, options?)` with `sessionSearchMode: "linear" \| "unsupported"`; **default `"linear"`**. `"unsupported"` throws typed error (not empty success). JSONL: document unsupported unless a thin linear path is trivial — prefer explicit unsupported. |
55
+ | Context budget | `contextBudget` on `AssembleProviderInputOptions` (and forward from `AgentConfig`/`RunOptions` only if needed). Shape: `{ maxInputTokens?: number; maxInputBytes?: number; reportOmissions?: boolean }`. At least one max required when budget object present. |
56
+ | Eviction priority | Keep first: **system/AGENTS instructions → skills → context blocks → history/tool results** (drop from the end of that priority stack). Current user `input` and mandatory system prefix fail closed if they cannot fit. |
57
+ | Omission report | Prefer non-breaking: `ProviderRequest.metadata.contextBudgetReport` + typed helper `getContextBudgetReport(request)`. Report ids/kinds/sizes/tokenEstimates — not secret values. Raw session store entries untouched. |
58
+ | Token estimate | Finite heuristic: **UTF-16 code units / 4** (chars÷4) unless model supplies a cheaper local estimator later. Document as estimate, not billing. |
59
+ | Anthropic package | `@arnilo/prism-provider-anthropic`; exports `createAnthropicProviderPackage`, `createAnthropicMessagesProvider`, `listAnthropicModels` (caller-gated). |
60
+ | Google package | `@arnilo/prism-provider-google`; exports `createGoogleProviderPackage`, Gemini provider factory, `listGoogleModels` (caller-gated). |
61
+ | Goal/verify | `runCodingGoalVerify` exported from `@arnilo/prism-coding-agent`; example `examples/coding-goal-verify.ts`. No Goal table. |
62
+ | Package count | 0.0.10 graph **32** publishable packages → **34** after anthropic + google (**confirmed** at Task 13). |
63
+
64
+ ## Capability traceability matrix
65
+
66
+ | Phase 6 roadmap criterion | Current 0.0.10 surface | Minimum 0.0.11 gap | Status / owner | Required proof | Docs | Release gate |
67
+ | --- | --- | --- | --- | --- | --- | --- |
68
+ | Bounded `SessionIndex` lists/filters by workspace, time, model/provider, label/summary, optional message FTS; hits return `sessionId` + optional `leafId` | `ProductionPersistenceStore.querySessions` metadata-only (`SessionQuery`); no text/FTS/workspace filters; `SessionStore` has no search | Core `SessionIndex` + query/hit types + optional store seam | `extend` / Task 1 | type/export smoke; invalid limit/query rejected; conformance empty/pagination/ownership | `public-contracts.md`, `session-stores.md` | offline + conformance |
69
+ | SQLite + PostgreSQL implement search with finite pages | DDL has `metadata`, entry `label`/`summary`; no FTS objects | Migrations + adapter `search`; metadata filters + optional FTS | `extend` / Task 2 | pagination, label/summary/FTS hits, ownership isolation, resume via `leafId`, migration drift | `sqlite-persistence.md`, `postgres-persistence.md` | offline adapter tests |
70
+ | Memory: linear fallback **or** explicit unsupported | `createMemorySessionStore(entries)` only; no options | Both modes; default linear; unsupported throws typed | `extend` / Task 3 | hit/miss/cap; unsupported ≠ empty | `session-stores.md` | offline |
71
+ | Token/context budget + omission report; no raw history delete | `assembleProviderInput` / message groups; no budget | `contextBudget` + deterministic eviction + report | `extend` / Task 4 | eviction order; omission completeness; zero-budget fail; cache-aware prefix when under budget | `input-and-prompt-assembly.md` | offline |
72
+ | Native Anthropic Messages package | OpenCode Go Anthropic **route** only; AI SDK escape hatch | New `@arnilo/prism-provider-anthropic` | `new-package` / Task 5 | offline conformance matrix + gated live | `providers/anthropic.md` | offline + opt-in live |
73
+ | Native Google Gemini package | No first-party Google package | New `@arnilo/prism-provider-google` after Anthropic | `new-package` / Task 6 | same conformance bar; Vertex out of scope | `providers/google.md` | offline + opt-in live |
74
+ | Thin goal→verify helper + example; no Goal DB / second runtime | `createCodingPlanMarkdown`, checks, `git_pr_handoff`, `suspend`/`resumeWorkflow`, `examples/durable-coding-workflow.ts` | `runCodingGoalVerify` + `examples/coding-goal-verify.ts` | `compose` / Task 7 | fail→suspend→approve→resume; bounded handoff; no secrets in plan state | `coding-agent-tools.md`, `agent-loops.md`, `workflows.md` | offline + example |
75
+ | Performance: finite search/budget; network-free benches | `querySessions` default `limit ?? 100`; no search/budget benches | Caps below; `scripts/benchmark-0.0.11.mjs` | `extend` / Tasks 1–4, 9 | schema/bounds; search+budget benches | `performance.md` | benchmark + `sdk:ready` |
76
+ | Code quality: optional seams; optional provider pkgs; helper composition | Provider package pattern; optional `readBranchPath` | Same pattern for search/budget; no core Anthropic extract | `compose` / Tasks 1–7 | ≥2-consumer rule for shared extract; package-local first | provider/session docs | pack/install |
77
+ | Security: ownership on search; no creds in hits/omissions; late-bound provider creds | `OwnershipScope` on persistence queries; provider redaction | Enforce on search; redact snippets/reports | `extend` / Tasks 1–6 | ownership empty/forbidden; secret scan fixtures | session + provider docs | offline + audit |
78
+ | Docs/migration 0.0.10 → 0.0.11 | Phase 5 docs | Update functional pages + two provider pages | `extend` / Task 8 | docs.test assertions | `migration.md`, index | docs tests |
79
+ | Version/release 0.0.11 | Graph at `0.0.10` | Bump to `0.0.11`; umbrella deps; dry-run | `extend` / Task 9 (renumbered Task 13) **done** | `sdk:ready` + release dry-run | `release-and-install.md` | Task 9/13 gates |
80
+
81
+ ## Primitive and caller inventory
82
+
83
+ Frozen at `a677113a409b1b60a3361a76c980e7411013916a` (+ this evidence page).
84
+
85
+ | Primitive / symbol | Existing contract / callers | Phase 6 disposition |
86
+ | --- | --- | --- |
87
+ | `SessionStore` / `readBranchPath?` | Core; memory/JSONL omit path; sqlite/postgres implement | **Extend optional** with search seam (`searchSessions?` or companion `SessionIndex`). Do not require FTS on memory/JSONL. |
88
+ | `ProductionPersistenceStore.querySessions` | Metadata listing; sqlite/postgres | **Keep** for admin listing. Search is separate (`SessionIndex.search`); do not overload `SessionQuery` as sole FTS surface. |
89
+ | `SessionQuery` / `SessionRecord` / `OwnershipScope` / `PersistencePage` | Core contracts | Reuse ownership + page shape; add parallel search types. |
90
+ | `prism_sessions.metadata`, entry `label`/`summary` | sqlite/postgres DDL | Reuse for workspace/label/summary filters; FTS tables adapter-local. |
91
+ | `assembleProviderInput` / `createDefaultInputBuilder` / groups / `legacy`\|`cache_aware` | `src/input.ts` | **Extend** with `contextBudget` after groups built / before final request; no parallel assembler. |
92
+ | `ProviderRequest.metadata` | Core | Host omission report via frozen metadata key + helper. |
93
+ | Provider package setup (`defineProviderPackage`, conformance, transport SSE, media SSRF) | openai/kimi/opencode-go/… | Reuse. New anthropic/google packages follow same layout. |
94
+ | `packages/provider-opencode-go/src/anthropic-messages.ts` | Vendor OpenCode route | **Pattern only.** No shared core extract in 0.0.11. |
95
+ | Kimi Anthropic-compatible route | package-local | Unchanged; not a consumer of new anthropic package serializer. |
96
+ | `createCodingPlanMarkdown` / checks / `git_pr_handoff` / coding-checkpoint limits | coding-agent | Reuse inside `runCodingGoalVerify`. |
97
+ | `suspend` / `resumeWorkflow` | `@arnilo/prism-workflows` | Reuse; helper does not fork workflow engine. |
98
+ | `examples/durable-coding-workflow.ts` | Example pattern | Pattern for `examples/coding-goal-verify.ts`. |
99
+ | AI SDK provider package | Escape hatch | Remains escape hatch; not primary Anthropic/Google path. |
100
+
101
+ ### Primitive decision
102
+
103
+ **Authorized new/extended core seams (minimal):**
104
+
105
+ 1. `SessionIndex` / `SessionSearchQuery` / `SessionSearchHit` + finite search-cap constants.
106
+ 2. Optional `SessionStore` search hook (or documented companion index from adapter factories).
107
+ 3. `contextBudget` on assemble options + `getContextBudgetReport` (+ estimate/apply helpers; may live in `src/context-budget.ts` re-exported).
108
+ 4. Memory store options: `sessionSearchMode`.
109
+
110
+ **Authorized new packages:** `@arnilo/prism-provider-anthropic`, `@arnilo/prism-provider-google`.
111
+
112
+ **Authorized package-local compose:** `runCodingGoalVerify` in coding-agent + example.
113
+
114
+ **Rejected for 0.0.11:** Goal DB; second runtime; always-on indexer; `ToolDefinition.metadata`; core Anthropic shared serializer; Vertex/Bedrock/Azure enterprise adapters; raising hard caps; default unbounded full-store scan.
115
+
116
+ Promote additional shared helpers to core only with ≥2 non-test consumers **and** migration/conformance evidence.
117
+
118
+ ## Frozen capability boundary
119
+
120
+ | Surface | Supported in 0.0.11 | Explicitly unsupported |
121
+ | --- | --- | --- |
122
+ | Session search | Metadata filters + optional FTS; finite pages; memory linear (capped) or unsupported throw | Daemon indexer; silent empty on unsupported; credential fields on hits; first-class workspace column |
123
+ | Context budget | Assembler eviction + omission report; heuristic estimate | Compaction/summarization as budget (0.0.12 preset); deleting store history; provider round-trip for count |
124
+ | Anthropic | Native Messages HTTP package; tools/cache/thinking/media/usage/discovery | Subscription OAuth (0.0.12); official SDK dependency; env scan at import |
125
+ | Google | Native Gemini HTTP package for coding-host semantics | Vertex enterprise identity (0.0.13); `@google/genai` runtime dep unless fetch proven impossible at impl (prefer fetch) |
126
+ | Goal/verify | Thin coding-agent helper + network-free example | Goal table; second workflow/agent engine; auto-push/PR network |
127
+
128
+ ## Frozen finite limits and charging points
129
+
130
+ **Rule:** Search and budget paths must validate caps in O(1) before scan/query. No always-on watchers. Do not raise hard caps in Tasks 1–9 without updating this page + tests + docs.
131
+
132
+ ### Session search (new — freeze defaults)
133
+
134
+ | Resource | Default / hard cap | Charge/check point | Failure/cleanup owner |
135
+ | --- | --- | --- | --- |
136
+ | Page `limit` | 20 / 100 | Before query/scan | Task 1 validation; Task 2–3 adapters |
137
+ | Query string bytes | 4 KiB / 16 KiB | Before parse/FTS | Task 1 |
138
+ | Snippet bytes per hit | 512 / 4 KiB | Before hit assembly | Task 2–3 |
139
+ | Cursor bytes | 1 KiB / 4 KiB | Before decode | Task 1–2 |
140
+ | Memory linear: sessions scanned | 1,000 / 5,000 | During scan; abort on signal | Task 3 |
141
+ | Memory linear: entries scanned | 10,000 / 50,000 | During scan | Task 3 |
142
+ | Memory linear: bytes scanned | 8 MiB / 64 MiB | During scan (serialized entry text) | Task 3 |
143
+ | DB FTS match candidates before page trim | adapter-local ≤ 1,000 / 5,000 | Before materializing snippets | Task 2 |
144
+
145
+ **Forbidden:** default path that scans entire store with no limit; background reindex workers; retaining unbounded snippet corpora.
146
+
147
+ Existing `querySessions` default `limit ?? 100` stays for admin listing; search defaults are tighter (20).
148
+
149
+ ### Context budget (new — freeze defaults)
150
+
151
+ | Resource | Default / hard cap | Charge/check point | Owner |
152
+ | --- | --- | --- | --- |
153
+ | `maxInputTokens` when set | caller-required; hard max 2_000_000 | Before eviction | Task 4 |
154
+ | `maxInputBytes` when set | caller-required; hard max 32 MiB | Before eviction | Task 4 |
155
+ | Estimate cost | O(assembled messages); no network | During estimate | Task 4 |
156
+ | Omission report entries | 256 / 1,024 | When `reportOmissions` | Task 4 |
157
+
158
+ **Forbidden:** provider call for budgeting; mutating/deleting `SessionStore` entries from budget path.
159
+
160
+ ### Providers / coding helper (reuse existing)
161
+
162
+ | Resource | Disposition |
163
+ | --- | --- |
164
+ | Stream/SSE/media/run limits | Reuse existing provider transport + run-limit ceilings (Tasks 5–6) |
165
+ | Coding checkpoint / check / PR handoff bytes | Reuse `packages/coding-agent` `DEFAULT_*` / `HARD_*` (Task 7) |
166
+ | Workflow suspend/resume | Existing workflow limits unchanged |
167
+
168
+ ## Threat and authority matrix
169
+
170
+ | Boundary | Trusted authority | Untrusted input | Mandatory control | Default / unsupported |
171
+ | --- | --- | --- | --- | --- |
172
+ | Search ownership | Host passes `OwnershipScope` | Model-supplied tenant ids | Adapters filter tenant/account/user when present; mismatch → empty or typed forbidden per existing persistence norms | Cross-tenant search unsupported |
173
+ | Search snippets | Adapter redaction-safe projection | Message/tool payloads | Snippet caps; never copy credential fields; prefer label/summary/FTS fragment | Raw full transcript as hit unsupported |
174
+ | Budget omissions | Assembler metadata helper | Dropped block contents | Report kinds/ids/sizes only; redactor still applies to provider messages | Secret values in omission report unsupported |
175
+ | Provider credentials | Host `CredentialValueSource` late-bound | Env/process ambient keys | No import/setup network or keychain scan; redact errors | Env auto-discovery unsupported |
176
+ | Goal/verify approval | Host approval policy | Model “approved” claims | Fail closed without approval; suspend/resume via workflows | Implicit approve unsupported |
177
+ | Provider headers | Provider-owned required headers win | Host override attempts on reserved names | Existing header-ownership rules | Host overwrite of provider auth headers unsupported |
178
+
179
+ ## Validation matrix for Task 0
180
+
181
+ | Check | Frozen assertion |
182
+ | --- | --- |
183
+ | Traceability | Every Phase 6 roadmap criterion maps to exactly one primary owner among Tasks 1–9; 0.0.12+ items listed only under out-of-scope. |
184
+ | Primitive reuse | Only authorized core seams above; Anthropic/Google are new optional packages; goal/verify is compose-only. Shared Anthropic extract deferred (≥2-consumer rule). |
185
+ | Finite resources | Search/budget caps table enforced; no indexer daemons; no unbounded default scans. |
186
+ | Security claims | Ownership on search; hits/omissions credential-free; provider creds late-bound/redacted. |
187
+ | API names | `SessionIndex` / `SessionSearchQuery` / `SessionSearchHit`; `contextBudget` + `getContextBudgetReport`; `sessionSearchMode`; `createAnthropicProviderPackage` / `createGoogleProviderPackage`; `runCodingGoalVerify`; workspace key `metadata.workspaceRoot`. |
188
+ | Eviction order | system/AGENTS → skills → context → history/tool results. |
189
+
190
+ ## Post-freeze extensions (Tasks 8–11)
191
+
192
+ Plan 074 inserted mid-run steer + ask_user_decision multi/free-text/suspend before docs/release (renumbered Tasks 12–13). Still inside 0.0.11; no Goal DB / no new `AgentRunInterruption` kinds.
193
+
194
+ | Extension | Frozen choice | Docs | Proof |
195
+ | --- | --- | --- | --- |
196
+ | Mid-run `steer` | Queue ≤8 / ≤64 KiB; softInterrupt aborts provider stream only; same `runId`; fail closed with no active run | `agent-session-runtime.md`, `cli-rpc.md`, `agent-loops.md` | agent + RPC tests |
197
+ | ask_user multi | `selectionMode: "multiple"` → `selectedIds` | `coding-agent-tools.md` | coding-agent tests |
198
+ | ask_user free-text | `allowCustom` + XOR `customText` | `coding-agent-tools.md` | coding-agent tests |
199
+ | ask_user suspend glue | `suspendAskUserDecision` + resume validators; agent adapter only | `coding-agent-tools.md`, `workflows.md` | workflow suspend→approve test |
200
+
201
+ ## Documentation and release ownership
202
+
203
+ - Task 0 (this page): scope freeze, index link, docs.test evidence assertions.
204
+ - Tasks 1–7: original Phase 6 implementation owners (search/budget/providers/goal-verify).
205
+ - Tasks 8–11: steer + ask_user_decision multi/free-text/suspend glue.
206
+ - Task 12: session/input/provider/coding/workflows/migration/performance docs, READMEs/CHANGELOGs, index summaries.
207
+ - Task 13: version `0.0.11`, benchmarks, `sdk:ready`, pack/install/supply-chain, release dry-run — **done** (2,047 tests / 2,014 pass / 33 skip; 34/34 dry-run; no tag/publish).
208
+
209
+ No public implementation API changes in Task 0. This page, `roadmap.md` Phase 6, and Plan 074 are the authoritative pre-implementation boundary; later tasks may tighten defaults but cannot raise hard caps, add daemons/Goal DB/second runtime, or pull 0.0.12+ items without updating tests, docs, and this evidence.
@@ -0,0 +1,173 @@
1
+ # Review coverage — 2026-07-22 Phase 7
2
+
3
+ Working evidence for Plan 075 Task 0. Freezes Phase 7 / Release **0.0.12** scope, protocol revisions, primitive ownership, finite limits, OAuth eligibility, threats, tests, docs, and release gates before implementation.
4
+
5
+ **Evidence frozen:** 2026-07-22. **Prism source:** `f9630a9bd12f299fdf473640e3869eea050b786f`. **Release target:** 0.0.12. **Default test rule:** network-free fakes and protocol fixtures; provider live canaries remain explicit host/operator gates.
6
+
7
+ ## Status legend
8
+
9
+ | Status | Meaning |
10
+ | --- | --- |
11
+ | `existing` | Current public contract covers the requirement. |
12
+ | `extend` | Owning task adds a generic reusable contract to an existing seam. |
13
+ | `new-package` | New optional workspace package; core remains protocol/UI-free. |
14
+ | `compose` | Existing public primitives suffice; package-local wiring only. |
15
+ | `out-of-scope` | Later phase or deliberately unsupported; must not land in 0.0.12. |
16
+
17
+ ## Frozen product decision
18
+
19
+ 0.0.12 closes **coding-harness P2 interoperability** only: one optional AG-UI package with a stable ACP sibling, one generic streamed durable-resume seam, and a thin coding-focused LLM compaction preset.
20
+
21
+ **Not in 0.0.12** (do not implement here):
22
+
23
+ | Deferred or rejected item | Owner / reason |
24
+ | --- | --- |
25
+ | Conversation storage/service, artifacts API, personal-agent UX, device sync | 0.0.13+ product scope. |
26
+ | Enterprise identity, Azure/Bedrock/Vertex, work connectors, policy ledger | 0.0.13+ enterprise scope. |
27
+ | Prism TUI, desktop app, browser UI framework, server route | Host-owned UI; adapter uses Web `Request`/`Response` only. |
28
+ | ACP terminal, filesystem, editor, process, diff, or MCP implementation | Host/editor responsibility; stable sibling maps only shared message/tool/approval lifecycle. |
29
+ | Protocol types or AG-UI/ACP dependencies in `@arnilo/prism` core | Optional package owns protocol dependencies. |
30
+ | Second agent runtime, event daemon, polling worker, UI state database, replay cache | Reuse session/event/ledger/checkpoint seams; no background process. |
31
+ | Raw event passthrough, client-defined executable tools, client state mutation, raw tool args/results, local paths | Default-deny projection boundary. |
32
+ | Anthropic Claude Code or Google Gemini CLI subscription OAuth/token reuse | Current provider terms prohibit third-party subscription credential routing/piggybacking. |
33
+ | Generic OAuth framework or credential-file/CLI scraping | Provider-local supported flow only; current core OAuth seams already suffice. |
34
+ | Observational-memory coding runtime/profile | LLM preset is sufficient; do not add a second coding memory system. |
35
+
36
+ ## Frozen external revisions
37
+
38
+ | Surface | Frozen reference | Compatibility decision |
39
+ | --- | --- | --- |
40
+ | Prism | [`f9630a9bd12f299fdf473640e3869eea050b786f`](../plans/075-release-0-0-12-coding-harness-interoperability.md) | Existing core/session/server/compaction contracts inventoried below. |
41
+ | Node.js | Release support remains Node 20+ | Optional package uses Web `Request`/`Response`, `ReadableStream`, native abort, and existing bounded SSE patterns; no framework dependency. |
42
+ | AG-UI | `@ag-ui/core` **0.0.57**; [Events](https://docs.ag-ui.com/concepts/events), [Interrupts](https://docs.ag-ui.com/concepts/interrupts), [Serialization](https://docs.ag-ui.com/concepts/serialization), [TypeScript schemas](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/typescript/packages/core) | Pin the official schema package. Validate produced events with `EventSchemas`; close active message/tool sequences before `RUN_FINISHED` or `RUN_ERROR`. |
43
+ | ACP | `@agentclientprotocol/sdk` **1.3.0** stable root; [overview](https://agentclientprotocol.com/protocol/overview), [tool calls](https://agentclientprotocol.com/protocol/tool-calls), [TypeScript SDK](https://agentclientprotocol.com/libraries/typescript) | Use stable `session/update` and `session/request_permission` contracts only. Exclude `./experimental/v2`. |
44
+ | OpenAI Codex OAuth | Existing `packages/provider-openai/src/oauth.ts`; [OpenAI provider docs](providers/openai.md) | Existing RFC 7636 PKCE browser/device-code flow is retained, host-invoked, abortable, bounded, and redacted. |
45
+ | Anthropic Claude Code auth/legal | [Authentication](https://docs.anthropic.com/en/docs/claude-code/authentication), [legal and compliance](https://docs.anthropic.com/en/docs/claude-code/legal-and-compliance) | No third-party Claude.ai subscription OAuth adapter: Anthropic directs third-party products to API keys or cloud providers. |
46
+ | Gemini CLI auth/terms | [Authentication](https://github.com/google-gemini/gemini-cli/blob/main/docs/get-started/authentication.mdx), [FAQ](https://github.com/google-gemini/gemini-cli/blob/main/docs/resources/faq.md), [terms/privacy](https://github.com/google-gemini/gemini-cli/blob/main/docs/resources/tos-privacy.md) | No Gemini CLI OAuth adapter or token import: terms prohibit third-party software use of Gemini CLI OAuth; use API key or Vertex instead. |
47
+
48
+ ## Frozen package and API contract
49
+
50
+ | Decision | Frozen choice |
51
+ | --- | --- |
52
+ | Package | New optional `@arnilo/prism-ag-ui`; it depends on `@arnilo/prism` and pinned protocol packages. `@arnilo/prism` remains dependency-free and protocol-free. |
53
+ | Subpath | `@arnilo/prism-ag-ui/acp` is the only ACP export. It uses the stable ACP SDK root, never the experimental v2 subpath. |
54
+ | Root exports | `createAgUiEventMapper`, `createAgUiHandler`, `createPersistenceAgUiReplay`, and package-local limits/projection types. |
55
+ | ACP exports | `createAcpEventMapper`, `createPrismAcpAgent`. No terminal/filesystem/editor/process/MCP export. |
56
+ | Generic core gap | Add `resumeAgentRunStream()` and `AgentRunLifecycle.resumeStream()`. Both reuse one private resume preparation/CAS path with existing `resumeAgentRun()`; no AG-UI/ACP type enters core. |
57
+ | AG-UI run input | Parse official `RunAgentInput` before host capability resolution. Host rebinds `threadId`/`runId` to authorized session/run ownership; default input selection is final accepted user text only. |
58
+ | AG-UI lifecycle | Map Prism lifecycle/message/tool events to official `RUN_*`, `TEXT_MESSAGE_*`, and `TOOL_CALL_*` schemas. Close open text/tool sequences before terminal events. Map durable suspension to `RUN_FINISHED` with `{ type: "interrupt", interrupts }`; map failure to one `RUN_ERROR`. |
59
+ | Durable approval | Exact checkpoint `runId`, authorized ownership, interruption, and `expectedVersion` are required. AG-UI resume resolves every pending interrupt in one payload; ACP unknown/cancel/reject choices deny. No side effect is replayed after an ambiguous dispatched tool. |
60
+ | Replay | `createPersistenceAgUiReplay` adapts ownership-scoped `ProductionPersistenceStore.queryEvents({ sessionId, runId, ... })`. It pages durable redacted rows, then attaches to a live bounded subscriber. Delivery is at-least-once across page/live boundary; stable event/message/tool IDs permit client deduplication. Terminal replay never invokes a provider or tool. |
61
+ | Projection | Default deny: tool name/status only; no args, result, progress payload, raw event, local path, frontend tool, or arbitrary state. Host must explicitly provide redaction-safe projectors for any exposed value. |
62
+ | ACP parity | Map assistant chunks, safe tool status, usage, errors, and durable permissions over same projection/limits/ownership contracts. ACP `session/update` does not create a second run runtime. |
63
+ | Coding compaction | `createCodingCompactionStrategy()` lives in `@arnilo/prism-compaction-llm`, wraps `createLlmCompactionStrategy()`, enables existing file-operation retention, and adds coding instructions. It produces normal `kind: "compaction"` entries; raw history remains. |
64
+ | Release graph | Task 8 adds one publishable package (34 → 35) and includes it only in `@arnilo/prism-all`, never `@arnilo/prism-code` or `@arnilo/prism-sdk`. |
65
+
66
+ ## Capability traceability matrix
67
+
68
+ | Phase 7 roadmap criterion | Existing surface | Minimum gap | Status / owner | Required proof | Docs | Release gate |
69
+ | --- | --- | --- | --- | --- | --- | --- |
70
+ | Provider-authorized subscription OAuth behind existing seams | `OAuthProvider`, `refreshOAuthCredential`, Node OAuth store, OpenAI Codex PKCE/device flow | Support matrix and eligibility gate; no currently eligible Anthropic/Google flow | `compose` / Task 6 | provider registration absence; existing Codex OAuth tests | credential + provider pages | offline + provider-policy review |
71
+ | AG-UI event mapping without core/UI coupling | Redacted ordered `AgentEvent`, session `stream()`/`subscribe()` | Optional mapper over official schemas and projection policy | `new-package` / Task 2 | schema/lifecycle/redaction/limit fixtures | `ag-ui.md`, agent events | offline package test |
72
+ | Host TUI/desktop run, approval, resume, reconnect | Durable run state, `AgentRunLifecycle`, `AgentEventRecord`, `queryEvents`, server SSE limits | Generic streamed durable resume + handler/replay adapter | `extend` / Task 1; `new-package` / Task 3 | CAS/ownership/reconnect/no-rerun/overflow fixtures | runtime, runs, server, security | offline integration |
73
+ | ACP parity where event/tool/approval contracts overlap | Same agent event, interruption, and lifecycle seams | Stable sibling mapper/agent over shared policy | `new-package` / Task 4 | stable SDK type/permission/subpath packed-consumer tests | AG-UI, A2A, security | offline package test |
74
+ | Coding-aware compaction retaining path/diff/check/plan signals | LLM `CompactionStrategy`, file-operation collection, coding checkpoints/check summaries | Thin LLM preset and fixture | `compose` / Task 5 | prompt/redaction/repeated-compaction/conformance fixtures | compaction + coding docs | offline package test |
75
+ | Finite protocol/reconnect/compaction behavior | Server/SSE, subscriber, persistence, compaction limits | Package-local resolved limits plus benchmark | `new-package` / Tasks 2–3; `compose` / Task 5; Task 8 | hostile-input and benchmark schema/budget tests | performance + AG-UI docs | benchmark + `sdk:ready` |
76
+ | No product/UI/enterprise scope creep | Host-owned UI/product boundaries | Explicit negative tests and release guard | `compose` / Tasks 0, 8 | source/package/export scope assertions | review + migration docs | pack/install + diff review |
77
+
78
+ ## Primitive and caller inventory
79
+
80
+ | Primitive / symbol | Existing contract and callers | Phase 7 disposition |
81
+ | --- | --- | --- |
82
+ | `AgentEvent`, `redactAgentEvent`, `AgentSession.stream()` / `subscribe()` | Core redacted ordered live events; subscriber default queue 1024 and close/drop policies | Reuse. Adapter owns protocol event correlation and projected payload. |
83
+ | `AgentEventRecord`, `RunLedger`, `ProductionPersistenceStore.queryEvents()` | Redacted durable event rows; SQLite/Postgres persistence queries | Reuse for replay only after host ownership binding. Add no event store/cache. |
84
+ | `resumeAgentRun`, `AgentRunLifecycle.resume()` | CAS, exact ownership/ref/session/fingerprint/revision, denial, dispatched-tool ambiguity protection | **Extend once** with streamed resume wrappers used by AG-UI and ACP. |
85
+ | `StoredAgentRunState`, `AgentRunInterruption`, checkpoint store | Redacted versioned pre-side-effect durable state; 256 KiB default / 1 MiB hard | Reuse. Adapter exposes only public interruption/status/version. |
86
+ | `PrismServerLimits` and server SSE handler | Existing Web request/response, abort, bounded stream/event/queue/time controls | Reuse values/pattern; AG-UI remains package-local and does not add server route. |
87
+ | `SecretRedactor`, credential resolver/OAuth store | Exact known-secret redaction; host-owned late-bound credentials/storage | Reuse. Project before transport/persistence; do not scan CLI credentials. |
88
+ | `OAuthProvider`, `refreshOAuthCredential`, `createOAuthCredentialStoreAdapter` | Generic login/refresh/store seam, OpenAI provider implementation | Reuse unchanged. Future provider OAuth stays provider-local and eligibility-gated. |
89
+ | `CompactionStrategy`, LLM preparation/file operations | Standard append semantics; LLM tracks read/modified files and caps summary/provider output | Reuse. Implement preset, not a memory runtime. |
90
+ | Observational-memory strategy | Optional projection/ledger/fold rendering workers | Leave unchanged; no coding profile needed. |
91
+ | Coding checkpoint and named check summaries | Bounded plan/todo/artifact/check references and fixed command checks | Supply coding signals to LLM compaction fixture/instructions; do not expose raw checkpoint/path data by default. |
92
+
93
+ ### Primitive decision
94
+
95
+ **Authorized generic core extension:** `resumeAgentRunStream()` and `AgentRunLifecycle.resumeStream()` only. The implementation must share resume validation/CAS/dispatch preparation with `resumeAgentRun()` and use the existing bounded event subscriber. Both AG-UI and ACP consume it.
96
+
97
+ **Authorized new package:** `@arnilo/prism-ag-ui`, including its `./acp` sibling and package-local mapper, handler, replay, SSE, projection, and limit code.
98
+
99
+ **Authorized package-local composition:** `createCodingCompactionStrategy()` in `@arnilo/prism-compaction-llm`; provider support documentation/negative guards.
100
+
101
+ **Rejected:** protocol types in core; UI framework/server route; new runtime/store/worker; generic OAuth abstraction; observational-memory coding runtime; client-controlled tools/state; raw event passthrough; Claude/Gemini CLI credential reuse.
102
+
103
+ ## Frozen finite limits and charging points
104
+
105
+ **Rule:** adapter validates every incoming selector/body/count before resolving a session, invoking resume, querying replay, or subscribing. It checks every outgoing projected field before enqueue/serialization. Task 2/3 must implement these exact defaults and hard caps; they may tighten a default but must not raise a hard cap without updating this page, tests, and docs.
106
+
107
+ | Resource | Default / hard cap | Charge/check point | Failure/cleanup owner |
108
+ | --- | ---: | --- | --- |
109
+ | Request body | 64 KiB / 1 MiB | Before AG-UI parse | Task 3 returns bounded client error. |
110
+ | Input messages | 128 / 1,024 | Before host input resolver | Task 3 rejects; never silently truncates user intent. |
111
+ | Input text or one content value | 64 KiB / 1 MiB | Before message projection/host resolver | Task 3 rejects. |
112
+ | Frontend tools / frontend state mutation | 0 / 0 | Before session/run lookup | Task 3 rejects unless a later scope revises this freeze. |
113
+ | Replay cursor | 4 KiB / 16 KiB | Before durable query | Task 3 rejects malformed/oversized cursor. |
114
+ | Replay page rows | 100 / 500 | Before `queryEvents` | Task 3 pages; never full-scans. |
115
+ | Outbound projected event | 64 KiB / 1 MiB | Before SSE/ACP enqueue | Tasks 2–4 close/fail safely; do not split JSON. |
116
+ | Outbound text, tool display, state snapshot | 64 KiB / 1 MiB | Before schema serialization | Tasks 2–4 omit/truncate only with explicit bounded marker. |
117
+ | Tool args/results/progress payload | 0 / 0 by default | Projection | Tasks 2–4 omit. Any later host projector remains inside outbound-event cap. |
118
+ | Error detail | 8 KiB / 64 KiB | Before protocol error event | Tasks 2–4 redact and truncate. |
119
+ | Stream events / aggregate bytes | 10,000 / 100,000; 10 MiB / 64 MiB | Before each enqueue | Task 3 closes stream with bounded terminal error. |
120
+ | Subscriber queue | 128 / 4,096 | Existing `SubscribeOptions` passed by adapter | Tasks 1/3 use `overflow: "close"`; no hidden queue. |
121
+ | Request/run wall time | 120 s / 30 min | Adapter-owned abort signal | Task 3 aborts/cleans up; background completion is explicit host policy only. |
122
+ | Coding compaction output | reuse 16,384 / 131,072 tokens; error 1 KiB / 8 KiB | Existing LLM strategy | Task 5 adds no provider call or unbounded parser. |
123
+
124
+ The request/event/aggregate/queue/time values intentionally match existing `@arnilo/prism-server` defaults and hard caps. Replay page and cursor values reuse bounded persistence/MCP conventions. The `0 / 0` tool/state rows are a capability denial, not a zero-length serialization limit.
125
+
126
+ **Forbidden:** unbounded replay, polling, background queue, raw tool payload forwarding, arbitrary client state patch, local path disclosure, auto OAuth refresh timer, CLI credential scan, second compaction call, or hard-cap increase.
127
+
128
+ ## OAuth eligibility matrix
129
+
130
+ | Provider surface | 0.0.12 status | Allowed auth | Explicitly prohibited / absent | Future eligibility evidence |
131
+ | --- | --- | --- | --- | --- |
132
+ | OpenAI Codex | supported existing flow | `createOpenAICodexOAuthProvider`; host-invoked browser/device code, PKCE, abort, refresh/store seam | automatic login, ambient credential discovery | Existing protocol/abort/refresh/redaction/store tests remain required. |
133
+ | Anthropic provider | API key only | Host-supplied `apiKey` | `createAnthropicSubscriptionOAuthProvider`, Claude Code credential file/token import, Claude.ai subscription routing | Provider-published third-party authorization plus exact flow/scopes, terms review, network-free fixtures. |
134
+ | Google provider | API key only | Host-supplied `apiKey`; Vertex remains later enterprise scope | `createGeminiCliOAuthProvider`, Gemini CLI credential/token import | Provider-published third-party authorization plus exact flow/scopes, terms review, network-free fixtures. |
135
+
136
+ A future provider-local subscription adapter requires all of: explicit provider permission for third-party products; exact authorize/token/refresh documentation; abort and expiry behavior; PKCE/state where required; bounded request/response handling; token/code/error redaction; durable store round trip; no import/setup network; protocol conformance; legal review. Until then absence is correct behavior, not a missing stub.
137
+
138
+ ## Threat and authority matrix
139
+
140
+ | Boundary | Trusted authority | Untrusted input | Mandatory control | Default / unsupported |
141
+ | --- | --- | --- | --- | --- |
142
+ | AG-UI thread/run IDs | Host `authorize` and session/run resolver | Client `threadId`, `runId`, cursor | Rebind to exact ownership/session/run; validate checkpoint version | Cross-owner replay/resume unsupported. |
143
+ | Resume/approval | Durable checkpoint + host policy | Resume payload/permission result | Exact run/interruption/version; all open interrupts addressed; CAS before side effect | Partial, stale, unknown, or ambiguous resumes fail. |
144
+ | Event/replay payload | Runtime redactor + host projection | Messages, tool events, ledger rows | Redact then allow-list/project/cap before transport | Raw events/paths/args/results/state unsupported. |
145
+ | Client tools/state | Host tool registry/session state | `RunAgentInput.tools`, state, custom fields | Reject before agent lookup | Client cannot grant capabilities or mutate backend state. |
146
+ | ACP permissions | Host permission policy + checkpoint | ACP option/outcome | Only known allow-once/reject selection maps to exact resume; unknown/cancel denies | Allow-always persistence is not added in 0.0.12. |
147
+ | OAuth | Provider terms + host credential ownership | OAuth code/token/CLI credential files | Explicit authorized flow, bounded calls, redaction, host store | Claude/Gemini CLI subscription credential use unsupported. |
148
+ | Compaction | Existing LLM redactor/limits/provider | Paths, diffs, command/check output, previous summary | Existing serialization/redaction/caps; retain signal summary only | Full diff/transcript retention and second memory runtime unsupported. |
149
+
150
+ ## Validation matrix for Task 0
151
+
152
+ | Check | Frozen assertion |
153
+ | --- | --- |
154
+ | Traceability | Every Phase 7 roadmap criterion has one primary Task 1–8 owner; 0.0.13+ conversations/artifacts/enterprise identity have none. |
155
+ | Protocol revisions | `@ag-ui/core@0.0.57`; `@agentclientprotocol/sdk@1.3.0` stable root; official AG-UI schemas validate mapped events. |
156
+ | Primitive reuse | Only streamed durable resume is a generic core extension with two consumers; all AG-UI/ACP types stay optional-package-local. |
157
+ | Lifecycle | `RUN_FINISHED` closes text/tool lifecycle; durable suspension is interrupt outcome; terminal replay never reruns work. |
158
+ | Finite resources | All request/event/page/queue/text/tool/state/error/time caps above are enforced; no polling/daemon/unbounded scan. |
159
+ | Security | Ownership before data, exact-version approvals, default-deny projection, redaction before transport, and no frontend capability grant. |
160
+ | OAuth policy | OpenAI Codex remains only subscription OAuth; Anthropic/Google register API key only and expose no forbidden factory. |
161
+ | Coding compaction | One LLM preset reuses file operations/standard entries/redaction/caps; raw session history stays intact. |
162
+
163
+ ## Documentation and release ownership
164
+
165
+ - Task 0: this evidence page, `docs/index.md`, and `docs.test.ts` regression guard.
166
+ - Task 1: generic streamed durable resume; runtime/events/public-contract docs.
167
+ - Tasks 2–4: optional AG-UI/ACP mapper, handler/replay, and stable sibling; AG-UI/security/A2A docs.
168
+ - Task 5: coding LLM compaction preset and docs.
169
+ - Task 6: OAuth support-matrix docs and absence guards.
170
+ - Task 7: canonical docs, examples, migration, package metadata/navigation.
171
+ - Task 8: 0.0.12 graph, benchmark, pack/install, supply-chain, dry-run publish, and roadmap completion evidence.
172
+
173
+ No public implementation API changes land in Task 0. This page, `roadmap.md` Phase 7, and Plan 075 are authoritative until implementation; later tasks may tighten defaults but cannot widen scope, raise hard caps, add protocol types to core, add a background worker/store, or enable unsupported subscription OAuth without updating this evidence, tests, docs, and plan.
@@ -315,3 +315,4 @@ Runtime session snapshots cache one leaf/generation for at most one second. Succ
315
315
  - [Provider caching](provider-caching.md): cache hints and `cacheUsageReport()` diagnostics.
316
316
  - [Observability](observability.md): `provider_turn_*` events, tool duration metadata, OpenTelemetry adapter.
317
317
  - [Public contracts](public-contracts.md): full contract inventory.
318
+ - [Frontend interoperability (AG-UI and ACP)](ag-ui.md): `AgentEventRecord` pages become redacted, ownership-scoped, at-least-once replay only through an explicit host adapter.
package/docs/server.md CHANGED
@@ -139,4 +139,5 @@ A2A routes are not added to `createPrismHandler()`. Install `@arnilo/prism-super
139
139
  - [MCP client and server exposure](mcp-tools.md): selected MCP capabilities and web-standard MCP transport.
140
140
  - [Host security guide](host-security.md): remote-boundary checklist.
141
141
  - [A2A interoperability](a2a.md): separately mounted A2A 1.0 handler/client.
142
+ - [Frontend interoperability (AG-UI and ACP)](ag-ui.md): separately installed authorized AG-UI Web handler; it is not a `@arnilo/prism-server` route.
142
143
  - [Release and install](release-and-install.md): optional package installation and profiles.
@@ -25,6 +25,8 @@ Use this helper when implementing a DB-backed `SessionStore` (for example, the r
25
25
  - session ids remain isolated (`assertSessionStoreConforms` always probes a secondary session)
26
26
  - optional concurrent fork children of the same parent succeed when `exerciseConcurrentParentAppend: true`
27
27
  - optional durable reopen/idempotency survival when `runSessionStoreConformance(..., { exerciseReopen: true })`
28
+ - optional `searchSessions` bounds/ownership/empty-page checks when `exerciseSearchSessions: true` (`assertSessionStoreSearchSessions`)
29
+ - optional `searchSessions` bounds/ownership/empty-page checks when `exerciseSearchSessions: true` (`assertSessionStoreSearchSessions`)
28
30
 
29
31
  ## Inputs / request
30
32
 
@@ -24,13 +24,16 @@ import type { SessionStore, SessionEntry } from "@arnilo/prism";
24
24
  | `list(sessionId)` | Return all entries for one session in stored order. Development fallback for branch reads. |
25
25
  | `get?(id)` | Return one entry by id, if present. Optional. |
26
26
  | `readBranchPath?(query)` | Optional DB-friendly branch read. Return one branch's ancestor chain as a `PersistencePage<SessionEntry>` so the runtime can avoid `list(sessionId)`. |
27
+ | `searchSessions?(query)` | Optional bounded session search (`SessionSearchQuery` → `PersistencePage<SessionSearchHit>`). SQLite/Postgres implement FTS + metadata filters; memory default is linear; JSONL throws `SessionSearchUnsupportedError`. |
27
28
 
28
29
  Public helpers:
29
30
 
30
31
  | Helper | Purpose |
31
32
  | --- | --- |
32
33
  | `createSessionEntry(options)` | Build a `SessionEntry` with generated `id`/`timestamp` when omitted. |
33
- | `createMemorySessionStore(initialEntries?)` | Built-in in-memory `SessionStore`. |
34
+ | `createMemorySessionStore(initialEntries?, options?)` | Built-in in-memory `SessionStore`. `options.sessionSearchMode`: `"linear"` (default) or `"unsupported"` (throws `SessionSearchUnsupportedError`). |
35
+ | `resolveSessionSearchQuery(query)` | Validate/clamp search limits (page, query bytes, snippet, cursor, linear/FTS caps). |
36
+ | `SessionIndex` | Narrow search seam (`search(query)`); adapters may expose this instead of `SessionStore.searchSessions`. |
34
37
  | `getSessionBranchEntries(entries, options)` | Return root-to-leaf entries for a leaf id (sync array path). |
35
38
  | `getSessionBranchEntries(reader, query)` | Async overload for a `BranchReader` / `readBranchPath` implementation. |
36
39
  | `listSessionBranches(entries)` | List every branch handle as `{ leafId, entries }`. Pair with `sessionId` for a durable `(sessionId, leafId)` branch handle. |
@@ -107,6 +110,42 @@ Recognize it with `isSessionAppendConflict(error)`, not message text. Built-in s
107
110
  - Branch semantics are parent links plus a leaf id. External UIs should keep branch handles as `(sessionId, leafId)`; RPC exposes an additional `handleId` for active handles.
108
111
  - Development stores can omit `readBranchPath`; the runtime falls back to `list(sessionId)` and the pure in-memory branch walk. Database-backed stores should implement `readBranchPath` so `entries()`, `clone()`, and context rebuild read only the selected ancestor chain.
109
112
 
113
+ ## Session search (0.0.11)
114
+
115
+ Bounded `SessionIndex` / `searchSessions` lists sessions by optional `workspaceRoot` (`metadata.workspaceRoot`), provider/model, label/summary, time range, ownership, and optional text `query` (FTS on SQLite/Postgres; case-sensitive substring on memory linear). Hits require `sessionId` and may include `leafId` for `checkout`; never credentials or whole transcripts.
116
+
117
+ ```ts
118
+ import { createMemorySessionStore, resolveSessionSearchQuery } from "@arnilo/prism";
119
+
120
+ const store = createMemorySessionStore([], { sessionSearchMode: "linear" });
121
+ const page = await store.searchSessions!({
122
+ workspaceRoot: "/repo",
123
+ query: "flake",
124
+ limit: 20,
125
+ });
126
+ // Opt out: createMemorySessionStore([], { sessionSearchMode: "unsupported" })
127
+ ```
128
+
129
+ Finite caps (defaults / hard): page 20/100; query string 4 KiB/16 KiB; snippet 512 B/4 KiB; cursor 1 KiB/4 KiB; memory linear sessions 1000/5000, entries 10000/50000, bytes 8 MiB/64 MiB; DB FTS candidates 1000/5000. Overflow fails closed via `resolveSessionSearchQuery`. See [Phase 6 evidence](review-coverage-2026-07-22-phase-6.md).
130
+
131
+ ## Session search (0.0.11)
132
+
133
+ Bounded `SessionIndex` / `searchSessions` lists sessions by optional `workspaceRoot` (`metadata.workspaceRoot`), provider/model, label/summary, time range, ownership, and optional text `query` (FTS on SQLite/Postgres; case-sensitive substring on memory linear). Hits require `sessionId` and may include `leafId` for `checkout`; never credentials or whole transcripts.
134
+
135
+ ```ts
136
+ import { createMemorySessionStore, resolveSessionSearchQuery } from "@arnilo/prism";
137
+
138
+ const store = createMemorySessionStore([], { sessionSearchMode: "linear" });
139
+ const page = await store.searchSessions!({
140
+ workspaceRoot: "/repo",
141
+ query: "flake",
142
+ limit: 20,
143
+ });
144
+ // Opt out: createMemorySessionStore([], { sessionSearchMode: "unsupported" })
145
+ ```
146
+
147
+ Finite caps (defaults / hard): page 20/100; query string 4 KiB/16 KiB; snippet 512 B/4 KiB; cursor 1 KiB/4 KiB; memory linear sessions 1000/5000, entries 10000/50000, bytes 8 MiB/64 MiB; DB FTS candidates 1000/5000. Overflow fails closed via `resolveSessionSearchQuery`. See [Phase 6 evidence](review-coverage-2026-07-22-phase-6.md).
148
+
110
149
  ## Security and performance notes
111
150
 
112
151
  - Do not store provider credentials, credential resolvers, provider instances, or unredacted secrets in session entries, append options, idempotency keys, or branch records.
@@ -4,7 +4,7 @@
4
4
 
5
5
  The optional `@arnilo/prism-session-store-sqlite` package ships a production-oriented SQLite adapter that implements:
6
6
 
7
- - `SessionStore` — atomic `append` / `list` / `get` / `readBranchPath`
7
+ - `SessionStore` — atomic `append` / `list` / `get` / `readBranchPath` / bounded `searchSessions` / bounded `searchSessions`
8
8
  - `RunLedger` — durable run, event, tool-call, and usage rows
9
9
  - `ProductionPersistenceStore` — cursor-paginated `query*` reads plus generic `checkpoints` and atomic `leases` capabilities
10
10
 
@@ -56,7 +56,7 @@ import { createSqlitePersistence } from "@arnilo/prism-session-store-sqlite";
56
56
  | `leases` | Atomic `LeaseStore` backed by `prism_leases`; database-clock expiry, opaque renew/release token, monotonic takeover fence. |
57
57
  | `close()` | Closes the underlying database when the adapter opened it. |
58
58
 
59
- Migrations run automatically on open and are idempotent across reopen. Under the SQLite migration transaction, startup checks ordered contract name/version/SHA-256 rows plus full schema-v3 PRAGMA/catalog shape (all required tables, columns/types/nullability/defaults, PK/unique/FK keys, and named indexes) before any runtime write. A complete legacy 0.0.5 history with all `checksum` values `NULL` is shape-verified then backfilled transactionally once. Unknown, duplicate, out-of-order, partial-legacy, checksum, or shape drift rejects open; restore or apply reviewed DDL rather than editing migration rows.
59
+ Migrations run automatically on open and are idempotent across reopen. Under the SQLite migration transaction, startup checks ordered contract name/version/SHA-256 rows plus full schema-v4 PRAGMA/catalog shape (all required tables, columns/types/nullability/defaults, PK/unique/FK keys, and named indexes) before any runtime write. A complete legacy 0.0.5 history with all `checksum` values `NULL` is shape-verified then backfilled transactionally once. Unknown, duplicate, out-of-order, partial-legacy, checksum, or shape drift rejects open; restore or apply reviewed DDL rather than editing migration rows.
60
60
 
61
61
  ## Request/response example
62
62
 
@@ -99,7 +99,7 @@ For resume/timeline flows, use `queryRuns`, `queryEvents`, `queryToolCalls`, and
99
99
  - The package is optional and workspace-local; `@arnilo/prism` core has no SQLite dependency.
100
100
  - Hosts choose the database path and own backup, retention enforcement, and filesystem permissions.
101
101
  - `SessionAppendOptions` idempotency rows are durable in `prism_session_append_idempotency` and survive reopen.
102
- - Schema version **3** applies `001_init`, additive `002_usage_scope`, and `003_run_feedback`. Migration 003 adds immutable `prism_run_feedback` rows with run FK/cascade deletion and owner/run/trace cursor indexes. `persistence.feedback` validates exact run ownership, bounds/redacts through optional `feedbackRedactor`, queries bounded pages, and deletes only exact-owned IDs. PostgreSQL shares the same model with dialect-local DDL.
102
+ - Schema version **4** applies `001_init`, `002_usage_scope`, `003_run_feedback`, and `004_session_search`. Migration 003 adds immutable `prism_run_feedback` rows with run FK/cascade deletion and owner/run/trace cursor indexes. Migration 004 adds session search FTS (FTS5 virtual table `prism_session_search_fts` dual-written on append) plus `prism_sessions(updated_at, id)` cursor index; existing entries are backfilled once. `persistence.feedback` validates exact run ownership, bounds/redacts through optional `feedbackRedactor`, queries bounded pages, and deletes only exact-owned IDs. Search hits never include credentials; ownership filters apply when present. PostgreSQL shares the same model with dialect-local DDL.
103
103
  - Pass an existing `better-sqlite3` `Database` via `database` when your host already manages connections.
104
104
 
105
105
  ## Security and performance notes
@@ -49,8 +49,11 @@ await session.run(input, {
49
49
  parser, // optional; default treats assistant text as the value
50
50
  repairer, // optional; default stringifies validation.errors[].message
51
51
  maxRevisions: 3, // optional; default 3
52
+ toolCalls: "bounded", // optional; default "disabled"
52
53
  structuredOutput: { name: "answer", schema, strict: true }, // optional native mode
53
54
  structuredOutputMode: "native", // or "artifact-loop" to skip provider-native schema
55
+ // Opt-in: schema only on artifact/revision turns (tools free on earlier turns).
56
+ structuredOutputTiming: "final-turn-only", // default "every-turn"
54
57
  },
55
58
  providerOptions: {
56
59
  structuredOutput: { name: "answer", schema, strict: true }, // direct native request
@@ -58,6 +61,8 @@ await session.run(input, {
58
61
  });
59
62
  ```
60
63
 
64
+ `structuredOutputTiming: "final-turn-only"` (with `toolCalls: "bounded"` and native schema) sends tool-eligible turns **without** `response_format` so models can call tools; once the model returns a call-free candidate (or tool rounds are exhausted), the next turn withdraws tools and attaches schema. Revision turns stay schema-on / tools-off. Default `"every-turn"` keeps legacy behavior (schema on every provider request).
65
+
61
66
  ## Outputs / response / events
62
67
 
63
68
  `generateValidateReviseLoop.run(ctx)` returns `Promise<Usage | undefined>`. Observable behavior is emitted through `AgentEvent` artifact variants (zero emitted by `singleShotLoop`):
@@ -230,11 +235,12 @@ Key cross-seam points:
230
235
 
231
236
  - `generate-validate-revise` is selected via `AgentConfig.loop` / `RunOptions.loop` (`RunOptions.loop` wins). See [Agent loops](agent-loops.md). `resolveLoop()` maps the options form to the factory; an unknown `strategy` throws before the first turn; a custom `AgentLoopStrategy` instance bypasses the options form.
232
237
  - Native structured output uses provider-neutral `StructuredOutputOptions` on `ProviderRequestOptions` / loop options. Capable OpenAI-family providers map to JSON-schema wire fields; unsupported models fail before fetch unless the host sets `structuredOutputMode: "artifact-loop"` and relies on parser/validator/repairer only.
238
+ - `structuredOutputTiming: "final-turn-only"` (opt-in; default `"every-turn"`) with `toolCalls: "bounded"` omits native schema on tool-eligible turns and attaches schema only on artifact/revision turns (tools withdrawn). Call-free tool-phase output promotes to one schema turn before parse/validate.
233
239
  - `validateStructuredOutputOptions()` enforces JSON-safe schemas, forbidden prototype-pollution keys, and a 64 KiB schema size cap.
234
240
  - The default parser treats non-empty assistant text as the value (`{ ok: true, value: text }`); empty/whitespace-only call-free text is a `parse_error` before the parser. Supply a host parser whenever `T` is not `string`.
235
241
  - The default repairer builds a user message from `validation.errors[].message`; supply a host repairer for schema-specific guidance.
236
242
  - `maxRevisions` (default 3) bounds revision turns; budget exhaustion ends the loop and emits `artifact_failed`. Session runs then fail with `AgentRunError` unless `artifact_finished` occurred (direct `loop.run` still returns usage without throwing).
237
- - Tools are inert in artifact turns unless `loop.toolCalls: "bounded"` is explicit. Bounded mode uses run-global `maxToolRounds`, dispatches calls sequentially through normal runtime guards, skips parser/validator for tool-calling responses, and permits at most `1 + maxRevisions + maxToolRounds` provider turns. An extra tool response yields terminal `artifact_failed` with `result.metadata.reason === "tool_round_limit"` and executes nothing.
243
+ - Tools are inert in artifact turns unless `loop.toolCalls: "bounded"` is explicit. Bounded mode uses run-global `maxToolRounds`, dispatches calls sequentially through normal runtime guards, skips parser/validator for tool-calling responses, and permits at most `1 + maxRevisions + maxToolRounds` provider turns (plus one extra schema turn under `final-turn-only` when a tool-phase call-free draft promotes). An extra tool response yields terminal `artifact_failed` with `result.metadata.reason === "tool_round_limit"` and executes nothing.
238
244
 
239
245
  ## Security and performance notes
240
246
 
package/docs/workflows.md CHANGED
@@ -72,6 +72,8 @@ All workflow limits and runtime `concurrency` reject non-safe integers, zero, ne
72
72
 
73
73
  A function node returns `suspend({ reason, data?, resumeSchema? })` to persist `status: "suspended"`. Its next invocation receives `ctx.resume` only after an approved resume. `resumeWorkflow(workflow, { runId }, options)` validates schema/version/ownership/`definitionHash`, claims the checkpoint before node execution, and continues the suspended node. Denial persists terminal `denied` status without invoking it. Existing failed/aborted checkpoint resume remains available without a human decision.
74
74
 
75
+ Coding-agent ask-user glue (opt-in, no Goal DB): `suspendAskUserDecision(request)` wraps `suspend` with durable question/options/`selectionMode`/`allowCustom` data + resume schema; resume with `createAskUserDecisionResumeValidator()` or `validateAskUserDecisionResume`. Goal→verify: `runCodingGoalVerify` / `createCodingGoalVerifyWorkflow` compose plan Markdown → named checks → approve suspend → bounded handoff over the same primitives (`examples/coding-goal-verify.ts`).
76
+
75
77
  Every node receives bounded `ctx.state`, `ctx.stateVersion`, and async `ctx.updateState(patch, { mode: "merge" | "replace" })`. Updates serialize, validate, redact, and snapshot before checkpoint save. `workflowNode({ workflow })` runs its child with the same ownership, agent/tool registries, execution policy, redactor, signal, checkpoints, and event bus; child state replaces parent state after success.
76
78
 
77
79
  `replayWorkflow(workflow, { sourceRunId, fromNodeId, runId? }, options)` requires a succeeded source/node, creates a new checkpoint, copies terminal evidence outside the selected node's downstream closure, restores selected-node pre-state, and records `{ sourceRunId, fromNodeId, rootRunId, depth }`. Source evidence is untouched. Copying any prior nested/tool approval is rejected; replay from that approval node or earlier so Phase 8 approval executes again.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arnilo/prism",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "Agent harness for AI providers, agents, sessions, and tools.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -111,15 +111,16 @@
111
111
  "packages/credentials-node",
112
112
  "packages/mcp",
113
113
  "packages/evals",
114
+ "packages/workflows",
114
115
  "packages/coding-agent",
115
116
  "packages/coding-security",
116
- "packages/workflows",
117
117
  "packages/memory",
118
118
  "packages/rag",
119
119
  "packages/server",
120
120
  "packages/supervisor",
121
121
  "packages/web-tools",
122
122
  "packages/browser",
123
+ "packages/ag-ui",
123
124
  "packages/prism-*"
124
125
  ],
125
126
  "scripts": {