@arnilo/prism 0.0.96 → 0.1.1

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 (203) hide show
  1. package/CHANGELOG.md +290 -2
  2. package/README.md +17 -3
  3. package/dist/agent-definitions.js +2 -3
  4. package/dist/agent-event-source.d.ts +11 -0
  5. package/dist/agent-event-source.js +512 -0
  6. package/dist/agent-loops.d.ts +5 -0
  7. package/dist/agent-loops.js +99 -14
  8. package/dist/agent-run-lifecycle.d.ts +5 -2
  9. package/dist/agent-run-lifecycle.js +18 -2
  10. package/dist/agent-run-state.d.ts +27 -1
  11. package/dist/agent-run-state.js +113 -7
  12. package/dist/agents.d.ts +3 -1
  13. package/dist/agents.js +1255 -129
  14. package/dist/artifacts.d.ts +132 -0
  15. package/dist/artifacts.js +44 -0
  16. package/dist/cache-helpers.js +18 -9
  17. package/dist/checkpoints.d.ts +4 -0
  18. package/dist/checkpoints.js +17 -9
  19. package/dist/cli-init.js +3 -7
  20. package/dist/cli-runner.d.ts +2 -6
  21. package/dist/cli-runner.js +71 -33
  22. package/dist/compaction.js +5 -4
  23. package/dist/config.js +7 -4
  24. package/dist/content.js +26 -24
  25. package/dist/context-budget.d.ts +67 -0
  26. package/dist/context-budget.js +288 -0
  27. package/dist/contracts.d.ts +590 -8
  28. package/dist/contracts.js +142 -1
  29. package/dist/contribution-parsing.js +6 -2
  30. package/dist/contributions.d.ts +2 -0
  31. package/dist/contributions.js +3 -0
  32. package/dist/conversations.d.ts +50 -0
  33. package/dist/conversations.js +98 -0
  34. package/dist/credentials.d.ts +22 -2
  35. package/dist/credentials.js +18 -3
  36. package/dist/devices.d.ts +94 -0
  37. package/dist/devices.js +138 -0
  38. package/dist/event-multiplexer.js +18 -4
  39. package/dist/extensions.d.ts +18 -1
  40. package/dist/extensions.js +79 -6
  41. package/dist/feedback.js +12 -10
  42. package/dist/guardrails.d.ts +1 -1
  43. package/dist/guardrails.js +26 -17
  44. package/dist/identity.d.ts +92 -0
  45. package/dist/identity.js +265 -0
  46. package/dist/index.d.ts +94 -72
  47. package/dist/index.js +48 -36
  48. package/dist/input.d.ts +10 -1
  49. package/dist/input.js +152 -52
  50. package/dist/instruction-injection.d.ts +1 -1
  51. package/dist/middleware.js +9 -1
  52. package/dist/models.d.ts +2 -0
  53. package/dist/models.js +3 -0
  54. package/dist/node/agent-definitions.js +16 -8
  55. package/dist/node/contribution-discovery.d.ts +1 -2
  56. package/dist/node/contribution-discovery.js +3 -3
  57. package/dist/node/session-store-jsonl.js +13 -7
  58. package/dist/node/settings.d.ts +1 -1
  59. package/dist/node/settings.js +1 -1
  60. package/dist/node/system-project-prompts.js +2 -4
  61. package/dist/node/trust.js +1 -1
  62. package/dist/persistence-lifecycle.d.ts +103 -0
  63. package/dist/persistence-lifecycle.js +202 -0
  64. package/dist/provider-events.d.ts +1 -0
  65. package/dist/provider-events.js +6 -1
  66. package/dist/provider-request-policy.js +3 -4
  67. package/dist/providers/media.d.ts +1 -1
  68. package/dist/providers/openai-compatible.d.ts +46 -1
  69. package/dist/providers/openai-compatible.js +123 -53
  70. package/dist/providers/openai-primitives.js +10 -7
  71. package/dist/providers/transport.d.ts +6 -0
  72. package/dist/providers/transport.js +21 -0
  73. package/dist/providers.d.ts +2 -0
  74. package/dist/providers.js +3 -0
  75. package/dist/redaction.d.ts +1 -0
  76. package/dist/redaction.js +26 -9
  77. package/dist/resources.d.ts +2 -2
  78. package/dist/resources.js +2 -2
  79. package/dist/retry.d.ts +5 -0
  80. package/dist/retry.js +8 -1
  81. package/dist/rpc.js +55 -11
  82. package/dist/run-ledger.d.ts +6 -0
  83. package/dist/run-ledger.js +16 -13
  84. package/dist/run-limits.js +49 -10
  85. package/dist/secure-agent.js +8 -2
  86. package/dist/security.js +7 -2
  87. package/dist/session-stores.d.ts +7 -2
  88. package/dist/session-stores.js +195 -21
  89. package/dist/skill-disclosure.d.ts +35 -0
  90. package/dist/skill-disclosure.js +101 -0
  91. package/dist/skill-load.d.ts +25 -0
  92. package/dist/skill-load.js +112 -0
  93. package/dist/structured-output.d.ts +5 -1
  94. package/dist/structured-output.js +20 -2
  95. package/dist/system-prompts.js +7 -2
  96. package/dist/testing/agent-event-source-conformance.d.ts +4 -0
  97. package/dist/testing/agent-event-source-conformance.js +54 -0
  98. package/dist/testing/compaction-conformance.js +5 -1
  99. package/dist/testing/extension-conformance.js +15 -3
  100. package/dist/testing/feedback.d.ts +1 -3
  101. package/dist/testing/feedback.js +1 -1
  102. package/dist/testing/persistence-schema.d.ts +2 -2
  103. package/dist/testing/persistence-schema.js +280 -35
  104. package/dist/testing/provider-conformance.js +3 -3
  105. package/dist/testing/run-ledger-conformance.js +1 -1
  106. package/dist/testing/session-store-conformance.d.ts +6 -0
  107. package/dist/testing/session-store-conformance.js +37 -2
  108. package/dist/testing/tool-conformance.js +30 -5
  109. package/dist/testing/tool-effect-store-conformance.d.ts +9 -0
  110. package/dist/testing/tool-effect-store-conformance.js +85 -0
  111. package/dist/thinking.js +4 -1
  112. package/dist/tool-effects.d.ts +15 -0
  113. package/dist/tool-effects.js +352 -0
  114. package/dist/tool-result-fold.d.ts +40 -0
  115. package/dist/tool-result-fold.js +176 -0
  116. package/dist/tools.d.ts +8 -3
  117. package/dist/tools.js +248 -13
  118. package/docs/0.1.0-readiness.md +215 -0
  119. package/docs/a2a.md +33 -2
  120. package/docs/acp.md +152 -0
  121. package/docs/ag-ui-adoption.md +77 -0
  122. package/docs/ag-ui.md +225 -0
  123. package/docs/agent-events.md +34 -3
  124. package/docs/agent-identity.md +144 -0
  125. package/docs/agent-loops.md +17 -2
  126. package/docs/agent-session-runtime.md +21 -4
  127. package/docs/browser-automation.md +5 -0
  128. package/docs/caveman.md +129 -0
  129. package/docs/cli-rpc.md +3 -6
  130. package/docs/coding-agent-tools.md +229 -25
  131. package/docs/coding-security.md +77 -11
  132. package/docs/compaction-and-retry.md +5 -2
  133. package/docs/compaction-llm.md +20 -1
  134. package/docs/compaction-observational-memory.md +52 -8
  135. package/docs/context-and-skills.md +94 -7
  136. package/docs/contribution-registries.md +1 -0
  137. package/docs/conversations.md +135 -0
  138. package/docs/credential-storage.md +34 -1
  139. package/docs/credentials-and-redaction.md +11 -1
  140. package/docs/database-persistence.md +27 -7
  141. package/docs/device-adapters.md +97 -0
  142. package/docs/enterprise-postgres-state.md +178 -0
  143. package/docs/evaluations.md +14 -1
  144. package/docs/extensions.md +4 -1
  145. package/docs/forge-integration.md +113 -0
  146. package/docs/guardrails.md +16 -2
  147. package/docs/host-security.md +35 -4
  148. package/docs/index.md +69 -37
  149. package/docs/input-and-prompt-assembly.md +8 -7
  150. package/docs/language-intelligence.md +162 -0
  151. package/docs/mcp-tools.md +62 -5
  152. package/docs/middleware-hooks.md +2 -2
  153. package/docs/migration.md +427 -2
  154. package/docs/model-routing.md +111 -0
  155. package/docs/multimodal-content.md +8 -5
  156. package/docs/node-jsonl-session-store.md +1 -1
  157. package/docs/observability.md +2 -0
  158. package/docs/openapi-tools.md +56 -0
  159. package/docs/performance.md +282 -0
  160. package/docs/policy-and-audit.md +171 -0
  161. package/docs/ponytail.md +127 -0
  162. package/docs/postgres-persistence.md +8 -4
  163. package/docs/process-sessions.md +147 -0
  164. package/docs/provider-caching.md +13 -1
  165. package/docs/provider-conformance.md +29 -5
  166. package/docs/provider-packages.md +43 -2
  167. package/docs/provider-request-policies.md +2 -0
  168. package/docs/providers/ai-sdk.md +24 -7
  169. package/docs/providers/alibaba.md +179 -0
  170. package/docs/providers/anthropic.md +93 -0
  171. package/docs/providers/azure.md +74 -0
  172. package/docs/providers/bedrock.md +72 -0
  173. package/docs/providers/google.md +89 -0
  174. package/docs/providers/ollama.md +166 -0
  175. package/docs/providers/openai-compatible.md +31 -2
  176. package/docs/providers/openai.md +24 -5
  177. package/docs/providers/openrouter.md +2 -0
  178. package/docs/providers/vertex.md +71 -0
  179. package/docs/public-contracts.md +68 -4
  180. package/docs/rag.md +41 -12
  181. package/docs/release-and-install.md +362 -208
  182. package/docs/resource-loading.md +3 -0
  183. package/docs/runs-and-usage.md +3 -0
  184. package/docs/server.md +44 -6
  185. package/docs/session-store-conformance.md +2 -0
  186. package/docs/session-stores.md +41 -2
  187. package/docs/sqlite-persistence.md +11 -3
  188. package/docs/structured-output.md +7 -1
  189. package/docs/supervisors.md +8 -0
  190. package/docs/tool-effects.md +95 -0
  191. package/docs/tools.md +5 -0
  192. package/docs/work-artifacts-and-review.md +102 -0
  193. package/docs/work-connectors.md +32 -0
  194. package/docs/work-tools.md +137 -0
  195. package/docs/workflows.md +6 -0
  196. package/docs/working-and-semantic-memory.md +40 -7
  197. package/package.json +30 -7
  198. package/templates/init/providers.json +22 -0
  199. package/docs/review-coverage-2026-07-14.md +0 -260
  200. package/docs/review-coverage-2026-07-15.md +0 -193
  201. package/docs/review-coverage-2026-07-17-provider-validation.md +0 -192
  202. package/docs/review-coverage-2026-07-19-phase-3.md +0 -174
  203. package/docs/review-coverage-2026-07-20-phase-4.md +0 -175
@@ -106,6 +106,9 @@ await browser.close();
106
106
  - Observation (`snapshot`, `wait`, open-without-url, `close`) vs mutation/high-impact (`navigate`, click/form, dialog accept, upload, download release, popup select) is classified for `ExecutionPolicy` / `beforeSideEffect`.
107
107
  - `createSharedSandboxBrowserOptions()` aligns browser uploads/downloads with Task 1 sandbox `/workspace` and `/downloads`. `assertBrowserSandboxNetwork()` in `@arnilo/prism-coding-security` fails closed for custom Docker networks without browser egress attestation.
108
108
  - Raw CSS is absent from production defaults. Ref resolution uses Playwright’s built-in `aria-ref=` selector with a package-owned snapshot ref table for staleness checks.
109
+ - Verified-state checkpoints (0.0.14): `createBrowserCheckpointLedger()` records navigation state — URL, a domain-state hash, and host-owned data refs — never serialized browser internals (cookies/storage/contexts), which are fragile and secret-bearing. Frozen caps: URL 8 KiB/16 KiB, domain-state hash 256 B/1 KiB, host-data ref 2 KiB/8 KiB (refs only, never bodies), 16/64 checkpoints per run (oldest evicted). After any resume/interruption `markResumed(runId)` marks state stale; `assertVerifiedBeforeSideEffect(runId)` fails closed until the host reloads + `verify()`s, so side effects never replay on stale state. Checkpoints are run-scoped: a conversation thread composes through the run it owns, reusing the manager's sandbox/egress/approval/limit policy above.
110
+
111
+ Observation tools declare `kind: none`; mutations are `external_mutation`/`unsupported` and fail closed on stale checkpoint state. See [tool effects](tool-effects.md).
109
112
 
110
113
  ## Security and performance notes
111
114
 
@@ -121,4 +124,6 @@ Default tests use fake Playwright APIs only. Protected live gate: `PRISM_LIVE_PL
121
124
  - [Host security](host-security.md): browser endpoint, approval, egress proxy, and artifact trust boundaries.
122
125
  - [Performance and resource limits](performance.md): browser ceilings and charging points.
123
126
  - [Coding execution approval and sandboxing](coding-security.md): optional shared disposable sandbox for coding+browser.
127
+ - [Conversations](conversations.md): durable threads that own the runs browser checkpoints scope to.
128
+ - [Device adapters](device-adapters.md): deny-by-default voice/desktop-control contracts (no vendor package in 0.0.14).
124
129
  - [Migration](migration.md): additive optional package activation.
@@ -0,0 +1,129 @@
1
+ # Caveman behavior integration
2
+
3
+ ## What it does
4
+
5
+ `@arnilo/prism-caveman` is an optional package that wires [juliusbrussee/caveman](https://github.com/juliusbrussee/caveman) into Prism contribution contracts.
6
+
7
+ It registers upstream skills and commands, injects active level prompt slices via `InstructionInjector`, and persists level as session custom `caveman-level` entries. Import and extension `setup` without a resolvable upstream path fail closed with a bounded redacted error and register zero contributions.
8
+
9
+ Upstream prompt fragments, skill bodies, and rules load from the host-supplied upstream checkout — Prism does not reimplement or vendor Caveman content.
10
+
11
+ ## When to use it
12
+
13
+ Use it when a host wants terse token-efficient communication modes (`lite`, `full`, `ultra`, wenyan variants, `micro`) with upstream Caveman skills (`caveman-commit`, `caveman-review`, `caveman-stats`, `caveman-compress`, `caveman-help`, `cavecrew`) in a Prism extension kernel.
14
+
15
+ Skip it when you do not have a local Caveman checkout (Caveman is not published on npm) or when you only need progressive skill catalog without mode injection.
16
+
17
+ Pair with Phase 3 progressive disclosure: register `createLoadSkillTool` and keep `skillsDisclosure: "progressive"` so full `SKILL.md` bodies stay catalog-only; mode slices come from the `caveman-mode` injector, not eager skill bodies.
18
+
19
+ ## Inputs / request
20
+
21
+ `createCavemanExtension(options)`:
22
+
23
+ | Field | Type | Required | Purpose |
24
+ | --- | --- | --- | --- |
25
+ | `upstreamPath` | `string` | yes | Absolute path to a Caveman checkout containing `skills/`. |
26
+ | `defaultLevel` | `CavemanLevel` | no | Initial level when no session entry exists (default upstream: `full`). |
27
+ | `showStatus` | `boolean` | no | Emit `caveman:status` extension events on level changes. |
28
+ | `appendEntry` | `(entry, opts?) => Promise<void>` | yes | Host session append (OM `attach` pattern). |
29
+ | `getEntries` | `() => readonly SessionEntry[] \| Promise<...>` | yes | Current branch entries for level restore. |
30
+ | `configPath` | `string` | no | Bounded local config file for `defaultLevel` / `showStatus`. |
31
+
32
+ `CavemanLevel`: `off` \| `lite` \| `full` \| `ultra` \| `wenyan-lite` \| `wenyan` \| `wenyan-ultra` \| `micro`.
33
+
34
+ Session custom entry shape:
35
+
36
+ ```json
37
+ { "kind": "custom", "data": { "type": "caveman-level", "level": "full" } }
38
+ ```
39
+
40
+ Registered skills: `caveman`, `caveman-commit`, `caveman-review`, `caveman-stats`, `caveman-compress`, `caveman-help`, `cavecrew`.
41
+
42
+ Registered commands: `caveman`, `caveman-init`, `caveman-commit`, `caveman-review`, `caveman-stats`, `caveman-compress`.
43
+
44
+ ## Outputs / response / events
45
+
46
+ | Export | Purpose |
47
+ | --- | --- |
48
+ | `createCavemanExtension(options)` | Returns an inert `Extension` until `kernel.load([...])`. |
49
+ | `caveman-mode` injector | `InstructionInjector` — upstream filtered `skills/caveman/SKILL.md` slice when level ≠ `off`. |
50
+ | `caveman` command | Set level (`/caveman lite\|full\|ultra\|wenyan\|micro\|off`) or toggle `off`↔`full`. |
51
+ | Alias commands | Dispatch `{ skill, dispatch: "load_skill" }` metadata for companion skills. |
52
+ | `caveman:status` event | Optional metadata when `showStatus: true`. |
53
+
54
+ Deactivation phrases `stop caveman` and `normal mode` clear active injection without erasing session history.
55
+
56
+ ## Request/response example
57
+
58
+ ```json
59
+ { "command": "caveman", "args": { "level": "ultra" }, "sessionId": "s1" }
60
+ ```
61
+
62
+ ```json
63
+ { "kind": "custom", "data": { "type": "caveman-level", "level": "ultra" } }
64
+ ```
65
+
66
+ ## Implementation example
67
+
68
+ ```ts
69
+ import { createCavemanExtension } from "@arnilo/prism-caveman";
70
+ import {
71
+ createExtensionKernel,
72
+ createLoadSkillTool,
73
+ createLoadedSkillSet,
74
+ createMemorySessionStore,
75
+ createSkillRegistry,
76
+ createSessionEntry,
77
+ } from "@arnilo/prism";
78
+
79
+ const store = createMemorySessionStore();
80
+ const callbacks = {
81
+ appendEntry: async (entry, options) => store.append(entry, options),
82
+ getEntries: async () => store.list("s1"),
83
+ };
84
+
85
+ const kernel = createExtensionKernel({ errorPolicy: "throw" });
86
+ await kernel.load([
87
+ createCavemanExtension({
88
+ upstreamPath: "/path/to/juliusbrussee-caveman",
89
+ defaultLevel: "full",
90
+ ...callbacks,
91
+ }),
92
+ ]);
93
+
94
+ const registry = createSkillRegistry(kernel.registries.skills.list());
95
+ const loaded = createLoadedSkillSet();
96
+ const loadSkill = createLoadSkillTool({ registry, loaded });
97
+
98
+ await kernel.registries.commands.get("caveman")!.execute({ level: "lite" }, { sessionId: "s1" });
99
+ // Select instructionInjectors: ["caveman-mode"] on runs that should receive level slices.
100
+ ```
101
+
102
+ See `examples/caveman-ponytail.ts` for progressive catalog + `load_skill` wiring with fixture upstream trees (network-free).
103
+
104
+ ## Extension and configuration notes
105
+
106
+ - Import alone registers nothing and starts no timers, watchers, or network I/O (`sideEffects: false`).
107
+ - `kernel.load` calls `setup`, which resolves upstream first; failure throws before any `register*`.
108
+ - Level restore scans `getEntries()` for the latest `data.type === "caveman-level"` — same OM attach pattern; core does not auto-emit `session_start`.
109
+ - Host must register `createLoadSkillTool` and pass `skillsDisclosure: "progressive"` for catalog-only skill bodies.
110
+ - `caveman-stats` dispatches skill metadata only; full stats need host session-log integration.
111
+ - `caveman-init` returns upstream guidance text; it does not write files in the host repo.
112
+ - No TUI status bar; optional `caveman:status` events for host UI.
113
+
114
+ ## Security and performance notes
115
+
116
+ - Upstream `SKILL.md` and injected text are untrusted host-supplied content; reads are size-bounded (`MAX_SKILL_FILE_BYTES` 256 KiB, `MAX_INJECTED_INSTRUCTION_BYTES` 32 KiB).
117
+ - Config read/write is bounded (`MAX_CONFIG_FILE_BYTES` 16 KiB) at host-owned `configPath` only.
118
+ - Errors redact home directories and absolute paths.
119
+ - Setup is O(skills) directory scan; mode read/write is O(1) per change; injection is O(1) upstream lookup per turn.
120
+ - Session custom entries respect host session ownership and redaction policies.
121
+
122
+ ## Related APIs
123
+
124
+ - [Ponytail behavior integration](ponytail.md): complementary lazy-minimalism mode package.
125
+ - [Extension kernel and event bus](extensions.md): `kernel.load` and contribution registration.
126
+ - [Context and skills](context-and-skills.md): progressive disclosure + `createLoadSkillTool`.
127
+ - [Instruction injection](instruction-injection.md): `caveman-mode` injector selection.
128
+ - [Observational memory compaction package](compaction-observational-memory.md): `appendEntry` / `getEntries` attach precedent.
129
+ - [Migration guide](migration.md): `0.0.21 → 0.0.22` install and opt-in notes.
package/docs/cli-rpc.md CHANGED
@@ -45,10 +45,6 @@ Default generation installs only `@arnilo/prism` (mock provider). Selecting a re
45
45
  | `--provider <name>` | Explicit provider id. The built-in `mock` id is only a smoke-test provider. |
46
46
  | `--model <name>` | Explicit model name. |
47
47
  | `--session <id>` | Session id. |
48
- | `--config <path>` | Explicit config path recorded by the adapter; not auto-loaded. |
49
- | `--resource <uri>` | Explicit resource URI recorded by the adapter; not auto-loaded. |
50
- | `--extension <name>` | Explicit extension name recorded by the adapter; not auto-loaded/imported. |
51
- | `--tool <name>` | Explicit tool name recorded by the adapter; not auto-enabled. |
52
48
  | `--system <text>` | System instructions. |
53
49
  | `--context <text>` | Context text reserved for host adapters. |
54
50
  | `--compact <entries>` | Auto-compaction threshold for the run. |
@@ -106,7 +102,7 @@ Branch-aware session commands return live handle details:
106
102
 
107
103
  `sessionId` identifies the durable session. `leafId` is the selected branch tip. `handleId` is the RPC map key used by `switchSession`; forks that share the same `sessionId` get stable ids like `session-1#2` so the parent handle is not overwritten.
108
104
 
109
- Invalid CLI flags return exit code `2`. Invalid JSON, missing ids, unknown RPC commands, unsupported `steer`, unknown command contributions, and runtime failures return `ok: false` response envelopes without executing unknown tools or commands.
105
+ Invalid CLI flags return exit code `2`. Invalid JSON, missing ids, unknown RPC commands, unknown command contributions, and runtime failures return `ok: false` response envelopes without executing unknown tools or commands. `steer` with no active run (or overflow) returns `ok: false`.
110
106
 
111
107
  ## Request/response example
112
108
 
@@ -128,6 +124,7 @@ Invalid CLI flags return exit code `2`. Invalid JSON, missing ids, unknown RPC c
128
124
  - `state`, `messages`, `setModel`, `switchSession`, `forkSession`, `cloneSession`, `checkout`, and registered `command` requests are processed immediately.
129
125
  - `compact` is fail-closed: if the current session has an active run, it returns `ok: false` because the session rejects compaction during a run.
130
126
  - A second `prompt` or `followUp` for the same session while it already has an active run returns `ok: false` immediately instead of blocking the input loop.
127
+ - `steer` enqueues mid-run user text for the active session (`params.input`, optional `params.softInterrupt`). Fails closed when no active run or when the pending steer queue overflows (8 messages / 64 KiB). Soft interrupt aborts the current provider stream only; the run continues.
131
128
 
132
129
  Events streamed during a run keep the original prompt request id, even when an `abort` with a different request id cancels the run. The completion or error response for the prompt also uses the original prompt request id.
133
130
 
@@ -201,6 +198,6 @@ Suspended workflow resume parameters are `{ workflowId, runId, decision: "approv
201
198
  - [Observational memory compaction package](compaction-observational-memory.md): optional `om:status` and `om:view` command factories for explicitly wired hosts.
202
199
  - [Workflows](workflows.md): optional `createWorkflowCommands()` for direct/background/replay/status/cancel/resume and selected schedule control over the same RPC `command` seam.
203
200
 
204
- The CLI records flags but does not auto-load project-local resources, extensions, tools, or config. The two system/project prompt files are the exception: in print/json modes the CLI auto-loads `<workspaceRoot>/AGENTS.md` (trust-gated) and an app-supplied `SYSTEM.md` layer as `AgentConfig.systemPrompt` layers composed with `--system` (base); `--no-agents-md` / `--no-system-md` skip them and `--agents-md-file` / `--system-md-file` override the paths. The CLI does not default `globalRoot` to the user's home directory — pass it from a host adapter or use `--agents-config <path>` for the app-config bundle layout. RPC mode does not auto-read these files (the host owns the session factory). Hosts must make explicit trust and permission decisions before wiring any other local loading.
201
+ The CLI records flags but does not auto-load project-local resources, extensions, tools, or config. `--config`, `--resource`, `--extension`, and `--tool` were parsed-and-recorded in earlier builds without any effect; they are now rejected loudly (`<flag> is not supported in this build`) until a CLI-harness plan wires them. The two system/project prompt files are the exception: in print/json modes the CLI auto-loads `<workspaceRoot>/AGENTS.md` (trust-gated) and an app-supplied `SYSTEM.md` layer as `AgentConfig.systemPrompt` layers composed with `--system` (base); `--no-agents-md` / `--no-system-md` skip them and `--agents-md-file` / `--system-md-file` override the paths. The CLI does not default `globalRoot` to the user's home directory — pass it from a host adapter or use `--agents-config <path>` for the app-config bundle layout. RPC mode does not auto-read these files (the host owns the session factory). Hosts must make explicit trust and permission decisions before wiring any other local loading.
205
202
 
206
203
  For app-controlled agent bundles under `<configRoot>/agents/<name>/AGENT.md` (including the three-layer `SYSTEM.md` → `AGENT.md` body → repo `AGENTS.md` prompt append and the union skill/tool scopes), see [Agent definitions](agent-definitions.md).
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- `@arnilo/prism-coding-agent` is an optional first-party package that provides host shell/filesystem/repository tools as Prism `ToolDefinition` objects. It ships six default coding tools — `shell`, `read`, `write`, `edit`, `repo_list`, `repo_search` — plus an opt-in structured Git/check set (`createGitTools`) for status/diff/branch/worktree/apply/commit/PR-handoff and named checks. Bounded coding-plan/checkpoint helpers compose ordinary workspace Markdown with workflow checkpoint state (references/hashes/summaries/fingerprints only). The tools are **inert** until a host imports them and registers them into a `ToolRegistry`. Behavior for shell/read/write/edit is a behavioral port of the pi coding agent's tools, adapted to Prism's `ToolDefinition` / `ToolResult` contracts (no `@earendil-works/*` or `typebox` dependencies; only `diff` plus the Node standard library). List/search/Git are native Prism tools with no glob/ripgrep/Git-library dependency.
5
+ `@arnilo/prism-coding-agent` is an optional first-party package that provides host shell/filesystem/repository tools as Prism `ToolDefinition` objects. It ships nine default coding tools — `shell`, `read`, `write`, `edit`, `repo_list`, `repo_search`, `glob`, `delete`, `move` — plus opt-in structured Git/check set (`createGitTools`), opt-in `createAskUserDecisionTool({ ask })`, and bounded coding-plan/checkpoint helpers. The tools are **inert** until a host imports them and registers them into a `ToolRegistry`. Hosts may register any subset, omit aggregators entirely, or mix first-party tools with host-owned `ToolDefinition`s. Behavior for shell/read/write/edit is a behavioral port of the pi coding agent's tools, adapted to Prism's `ToolDefinition` / `ToolResult` contracts (no `@earendil-works/*` or `typebox` dependencies; only `diff` plus the Node standard library). List/search/glob/Git are native Prism tools with no picomatch/ripgrep/Git-library dependency (hand-rolled `*`/`?`/`**` glob matcher).
6
6
 
7
7
  | Export | Purpose |
8
8
  | --- | --- |
@@ -11,13 +11,22 @@
11
11
  | `createWriteTool(cwd, options?)` | `write` tool: create or overwrite a file, creating parent directories. |
12
12
  | `createEditTool(cwd, options?)` | `edit` tool: precise exact-then-fuzzy text replacement in an existing file. |
13
13
  | `createRepoListTool(cwd, options?)` | `repo_list` tool: bounded deterministic repository listing. |
14
- | `createRepoSearchTool(cwd, options?)` | `repo_search` tool: bounded literal/regex text search. |
15
- | `createCodingTools(cwd, options?)` | Default six tools (`shell`, `read`, `write`, `edit`, `repo_list`, `repo_search`). |
16
- | `createReadOnlyTools(cwd, options?)` | Read-only subset: `read`, `repo_list`, `repo_search`. |
14
+ | `createRepoSearchTool(cwd, options?)` | `repo_search` tool: bounded literal text search (`outputMode`: content / files_with_matches / count). |
15
+ | `createGlobTool(cwd, options?)` | `glob` tool: bounded filename-pattern match (`*` / `?` / `**`; no brace expansion). |
16
+ | `createDeleteTool(cwd, options?)` | `delete` tool: high-risk delete of a file or empty directory (no recursive delete, no trash). |
17
+ | `createMoveTool(cwd, options?)` | `move` tool: high-risk rename/move within the workspace (`overwrite` default false). |
18
+ | `createReadPathSet()` | Session-scoped path set for optional `requireReadBeforeWrite` soft guard. |
19
+ | `createCodingTools(cwd, options?)` | Default nine tools (`shell`, `read`, `write`, `edit`, `repo_list`, `repo_search`, `glob`, `delete`, `move`). |
20
+ | `createReadOnlyTools(cwd, options?)` | Read-only subset: `read`, `repo_list`, `repo_search`, `glob`. |
17
21
  | `createAllTools(cwd, options?)` | Identical to `createCodingTools` (Git tools remain opt-in via `createGitTools`). |
18
22
  | `createGitTools(cwd, options?)` | Opt-in Git tools (`git_status`/`git_diff`/`git_branch`/`git_worktree`/`git_apply`/`git_commit`/`git_pr_handoff`) plus optional `coding_check`. |
19
23
  | `createCodingCheckTool(cwd, options)` | Named host-declared checks; model selects only a name. |
20
- | `createLocalRepositoryOperations(limits?)` | Default streaming Node filesystem backend for list/search. |
24
+ | `createAskUserDecisionTool(options)` | Opt-in user decision tool (`ask_user_decision`); host supplies `ask` callback. Not in default aggregators. |
25
+ | `createLocalRepositoryOperations(limits?)` | Default streaming Node filesystem backend for list/search/glob. |
26
+ | `createGitAwareRepositoryOperations(cwd, options?)` | Optional Git `ls-files` ignore-aware enumeration with native fallback; host-only `includeIgnored`. |
27
+ | `createLanguageIntelligence(options)` | Optional host-activated LSP language intelligence (symbols/definitions/references/diagnostics/hover/rename); see [Language intelligence](language-intelligence.md). |
28
+ | `createProcessSessions(options)` | Optional managed long-running process sessions (start/output/input/wait/signal/kill/release); see [Process sessions](process-sessions.md). |
29
+ | `createGitHubForge(options)` | Optional reference GitHub forge adapter (issue context, push, PR create/update, review comments, checks, handoff reconcile) with `ToolEffectStore` idempotency; see [Forge integration](forge-integration.md). |
21
30
  | `createGitOperations(options)` | Typed Git operations backend (argument arrays, safe config, finite output). |
22
31
  | `buildCodingCheckpointMetadata` / `validateCodingCheckpointMetadata` / `assertCodingResumeAllowed` | Bounded durable coding-task metadata for workflow `state.coding` (no second runtime). |
23
32
  | `writeCodingPlanFile` / `readCodingPlanFile` / `createCodingPlanMarkdown` / `parseCodingPlanTodos` | Workspace plan/todo Markdown helpers with finite byte/todo caps and hash verification. |
@@ -65,13 +74,41 @@ const tools = createCodingTools(workspaceRoot, {
65
74
  | `read` | `read` |
66
75
  | `write` | `write` |
67
76
  | `edit` | `edit` |
68
- | `repo_list` / `repo_search` | _(native; no pi equivalent)_ |
77
+ | `repo_list` / `repo_search` / `glob` | _(native; no pi equivalent)_ |
78
+ | `delete` / `move` | _(native; no pi equivalent)_ |
79
+
80
+ ### Tool selection guide
81
+
82
+ | Need | Prefer | Avoid |
83
+ | --- | --- | --- |
84
+ | Enumerate directories | `repo_list` | `shell` `find`/`ls` |
85
+ | Match filename patterns | `glob` | `shell` `find` |
86
+ | Find text in files | `repo_search` | `shell` `grep`/`rg` |
87
+ | Read one file (paged) | `read` | `shell` `cat` |
88
+ | Create / full overwrite | `write` | — |
89
+ | Targeted replace | `edit` | full `write` rewrite when a small edit works |
90
+ | Remove file / empty dir | `delete` | `shell` `rm` |
91
+ | Rename / relocate | `move` | `shell` `mv` |
92
+ | Arbitrary process | `shell` | dedicated tools above |
93
+
94
+ ### Phase 4 non-goals (0.0.21)
95
+
96
+ These are **out of scope** for the 0.0.21 package baseline (see roadmap Phase 9 / later for LSP and process work):
97
+
98
+ - **No PDF / document reader** — text and supported images only via `read`.
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)).
101
+ - **LSP language-server tools** — not in default aggregators; optional `createLanguageIntelligence` is Phase 9 (see [Language intelligence](language-intelligence.md)).
102
+ - **Managed process sessions** — not in default aggregators; optional `createProcessSessions` is Phase 9 (see [Process sessions](process-sessions.md)).
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.
104
+ - **No recursive directory delete** — `delete` refuses non-empty directories.
105
+ - **No brace-expansion globs** — `glob` supports only `*`, `?`, and `**`.
69
106
 
70
107
  ## Inputs / request
71
108
 
72
109
  ### `shell`
73
110
 
74
- Run a shell command and return combined stdout+stderr.
111
+ Run a shell command and return combined stdout+stderr. Prefer dedicated coding tools (table above) when they fit.
75
112
 
76
113
  **Inputs:**
77
114
 
@@ -140,7 +177,7 @@ const read = createReadTool(cwd, {
140
177
 
141
178
  ### `write`
142
179
 
143
- Create or overwrite a file, creating parent directories as needed.
180
+ Create or **overwrite** a file (full replace), creating parent directories as needed. Prefer `edit` for targeted changes.
144
181
 
145
182
  **Inputs:**
146
183
 
@@ -148,11 +185,27 @@ Create or overwrite a file, creating parent directories as needed.
148
185
  | --- | --- | --- |
149
186
  | `path` | `string` | Path to the file to write (relative or absolute). Required. |
150
187
  | `content` | `string` | Content to write (empty string creates an empty file). Required. |
188
+ | `force` | `boolean` | Bypass optional read-before-write guard when the host enabled `requireReadBeforeWrite`. |
151
189
 
152
190
  **Outputs:** a `TextContent` confirmation naming the **absolute path** with UTF-8 byte and line counts (e.g. `Successfully wrote 42 bytes (3 lines) to /abs/path.txt`). `maxInputBytes` defaults to 8 MiB (64 MiB hard cap); oversized UTF-8 input fails before policy evaluation, directory creation, or write. Write failures and abort are error results. Empty `content` is valid.
153
191
 
192
+ Default local `writeFile` uses same-directory temp + `rename` so a crash mid-write cannot truncate the target; custom `WriteOperations` should provide equivalent durability.
193
+
154
194
  `write` result `metadata`: `{ bytes, lines, path }` (absolute path). Concurrent writes to the same path serialize through `withFileMutationQueue`; writes to different paths run in parallel.
155
195
 
196
+ ### Optional read-before-write guard
197
+
198
+ Hosts may opt in to a session-scoped soft guard: share one `createReadPathSet()` across `read` / `write` / `edit` and set `requireReadBeforeWrite: true` on write/edit options. Successful `read` marks the path; unread existing-file writes/edits fail with a clear error unless `force: true`. Default is **off** (no behavior change for hosts that ignore it).
199
+
200
+ ```ts
201
+ import { createReadPathSet, createReadTool, createWriteTool, createEditTool } from "@arnilo/prism-coding-agent";
202
+
203
+ const readPaths = createReadPathSet();
204
+ const read = createReadTool(cwd, { readPathSet: readPaths });
205
+ const write = createWriteTool(cwd, { requireReadBeforeWrite: true, readPathSet: readPaths });
206
+ const edit = createEditTool(cwd, { requireReadBeforeWrite: true, readPathSet: readPaths });
207
+ ```
208
+
156
209
  ### `edit`
157
210
 
158
211
  Precise text replacement in an existing file via exact-then-fuzzy matching.
@@ -163,8 +216,13 @@ Precise text replacement in an existing file via exact-then-fuzzy matching.
163
216
  | --- | --- | --- |
164
217
  | `path` | `string` | Path to the file to edit. Required. |
165
218
  | `edits` | `Array<{ oldText: string, newText: string }>` | Targeted replacements, each matched against the **original** file (not incrementally). No overlapping/nested edits. Required, non-empty. |
219
+ | `force` | `boolean` | Bypass optional read-before-write guard when enabled. |
220
+
221
+ Each `edits[].oldText` must match a unique, non-overlapping region of the original file. Matching is exact first, then fuzzy (unicode normalization / whitespace collapse).
166
222
 
167
- Each `edits[].oldText` must match a unique, non-overlapping region of the original file. Matching is exact first, then fuzzy (unicode normalization / whitespace collapse). A BOM is stripped before matching and re-prepended on write; original line endings are restored. Defaults reject targets over 8 MiB, aggregate old/new UTF-8 input over 2 MiB, or more than 100 edits (hard caps: 64 MiB, 16 MiB, and 1,000). Stat and bounded read checks run before matching or mutation.
223
+ **Fuzzy silent-success tradeoff (loud):** when exact match fails, fuzzy may still apply a replacement **without warning the model**. That can edit the wrong region if `oldText` is slightly off (extra/missing whitespace, unicode lookalikes). Prefer exact `oldText` copied from a fresh `read`. Duplicate / non-unique matches already **fail closed** and leave the file unchanged — ambiguity is not silently resolved by picking the first hit.
224
+
225
+ A BOM is stripped before matching and re-prepended on write; original line endings are restored. Defaults reject targets over 8 MiB, aggregate old/new UTF-8 input over 2 MiB, or more than 100 edits (hard caps: 64 MiB, 16 MiB, and 1,000). Stat and bounded read checks run before matching or mutation. Default local `writeFile` uses same-directory temp + `rename` (crash-safe replace).
168
226
 
169
227
  **Outputs:** a `TextContent` confirmation (`Successfully replaced N block(s) in {path}.`) plus `metadata`. Any failure — missing/unreadable file, no match, duplicate (non-unique) match, overlap, empty `oldText`, no-op edit, or abort — is an error result, and the file is left **unchanged** (the match runs before the write).
170
228
 
@@ -172,7 +230,24 @@ Each `edits[].oldText` must match a unique, non-overlapping region of the origin
172
230
 
173
231
  ### `repo_list`
174
232
 
175
- List repository entries with deterministic relative paths. Uses Node `opendir`/`lstat` only — no glob dependency. Does not follow symlinks; rejects path escapes outside the workspace root. Hidden names and excluded basenames (default `.git`, `node_modules`, `dist`) are skipped unless `includeHidden` is set / host `exclude` is overridden.
233
+ List repository entries with deterministic relative paths. Uses Node `opendir`/`lstat` only — no glob dependency. Prefer `glob` when you already know a filename pattern. Prefer `repo_search` to find text inside files. Does not follow symlinks; rejects path escapes outside the workspace root. Hidden names and excluded basenames (default `.git`, `node_modules`, `dist`) are skipped unless `includeHidden` is set / host `exclude` is overridden.
234
+
235
+ #### Git-aware enumeration
236
+
237
+ `createGitAwareRepositoryOperations(cwd, options?)` is an optional `RepositoryOperations` backend that enumerates via fixed `git ls-files --cached --others --exclude-standard -z` (honors nested `.gitignore`, `$GIT_DIR/info/exclude`, and exclude-standard rules). Inject it through `ToolsOptions.repository.operations` (or per-tool `repository.operations`).
238
+
239
+ - **Detection:** cached `git rev-parse --is-inside-work-tree`. Outside a Git work tree, or when detection fails, delegates to `options.fallback` (default: `createLocalRepositoryOperations`).
240
+ - **Fail closed:** after successful detection, `ls-files` errors throw `RepositoryError` — no silent mid-session fallback.
241
+ - **Ignored paths:** stay excluded unless the host sets `includeIgnored: true` (factory option only; never a model-facing tool argument). Tracked-but-ignored files remain visible via `--cached` (Git semantics).
242
+ - **Bounds:** at most two Git invocations per operation; stdout capped by `DEFAULT_MAX_LS_FILES_OUTPUT_BYTES` (8 MiB, hard 64 MiB). Existing repo depth/entry/file/result/time caps still apply. No per-file Git spawn; no hand-rolled ignore parser; argv is never model-supplied.
243
+ - **Security:** `.git` internals never listed; paths re-checked against the workspace root; symlink escapes match native fail-closed behavior.
244
+
245
+ ```ts
246
+ import { createCodingTools, createGitAwareRepositoryOperations } from "@arnilo/prism-coding-agent";
247
+
248
+ const operations = createGitAwareRepositoryOperations(cwd); // native fallback outside Git
249
+ const tools = createCodingTools(cwd, { repository: { operations } });
250
+ ```
176
251
 
177
252
  **Inputs:**
178
253
 
@@ -188,21 +263,70 @@ List repository entries with deterministic relative paths. Uses Node `opendir`/`
188
263
 
189
264
  ### `repo_search`
190
265
 
191
- Search text files under the workspace. Default mode is literal substring match; `mode: "regex"` enables length-bounded regular expressions. Binary files (NUL in a bounded prefix) and oversize files are skipped. Aggregate scanned bytes, matches, line bytes, pattern bytes, and wall time are finite.
266
+ Search text files under the workspace using literal substring match. Binary files (NUL in a bounded prefix) and oversize files are skipped. Aggregate scanned bytes, matches, line bytes, pattern bytes, and wall time are finite.
192
267
 
193
268
  **Inputs:**
194
269
 
195
270
  | Field | Type | Purpose |
196
271
  | --- | --- | --- |
197
- | `query` | `string` | Literal or regex pattern (required). |
272
+ | `query` | `string` | Literal substring (required). |
198
273
  | `path` | `string` | Workspace-relative start path. |
199
- | `mode` | `"literal" \| "regex"` | Default `literal`. |
274
+ | `mode` | `"literal"` | Literal only (default). `regex` removed in 0.0.18. |
200
275
  | `caseSensitive` | `boolean` | Default false. |
201
276
  | `includeHidden` | `boolean` | Default false. |
202
- | `context` | `number` | Context lines before/after each match (default 5, hard 20). |
277
+ | `context` | `number` | Context lines before/after each match (default 5, hard 20). Ignored for non-content `outputMode`. |
203
278
  | `maxMatches` | `number` | Match cap (default 1,000, hard 10,000). |
279
+ | `outputMode` | `"content"` \| `"files_with_matches"` \| `"count"` | Result shape (default `content`). |
280
+
281
+ **Outputs:**
282
+ - `content` (default): ripgrep-like lines `path:line:column:text` with optional `path-` / `path+` context.
283
+ - `files_with_matches`: unique matching paths only.
284
+ - `count`: totals (`N matches in M files`) without line bodies.
285
+
286
+ Metadata includes `matches`, `truncated`, scan/skip counts; non-content modes also expose `fileCount`.
287
+
288
+ ### `glob`
289
+
290
+ Find workspace files by filename pattern without shell `find`. Hand-rolled matcher: `*` (one path segment), `?` (one char), `**` (directories). Brace expansion (`{a,b}`) is **rejected**. Patterns match workspace-relative full paths (e.g. `src/util/a.ts`). Returns **files only** (directories traversed but not listed). Same exclude/hidden/depth/page/time caps as `repo_list`.
291
+
292
+ **Inputs:**
204
293
 
205
- **Outputs:** ripgrep-like lines `path:line:column:text` with optional `path-` / `path+` context, plus metadata (`matches`, `truncated`, scan/skip counts).
294
+ | Field | Type | Purpose |
295
+ | --- | --- | --- |
296
+ | `pattern` | `string` | Glob pattern (required). |
297
+ | `path` | `string` | Workspace-relative start directory (default root). |
298
+ | `includeHidden` | `boolean` | Default false. |
299
+ | `maxDepth` | `number` | Depth cap (default 32, hard 128). |
300
+ | `maxResults` | `number` | Page size (default 1,000, hard 10,000). |
301
+ | `offset` | `number` | Matches to skip (default 0). |
302
+
303
+ **Outputs:** one relative path per line plus metadata (`truncated`, `truncatedBy`, `nextOffset`, scan counts). Continue with `offset=nextOffset` when truncated.
304
+
305
+ ### `delete`
306
+
307
+ High-risk: permanently delete a **single file or empty directory**. Non-empty directories fail closed (no recursive delete). Symlinks are unlinked as links (targets not followed for containment). **No trash daemon** — host undo is not automatic; gate with approval policy.
308
+
309
+ **Inputs:**
310
+
311
+ | Field | Type | Purpose |
312
+ | --- | --- | --- |
313
+ | `path` | `string` | File or empty directory to delete. Required. |
314
+
315
+ **Outputs:** confirmation with absolute path, or error (missing, non-empty dir, escape, abort).
316
+
317
+ ### `move`
318
+
319
+ High-risk: rename or move a file within the workspace. Dual-path mutation queue (lexicographic lock order). `overwrite` defaults **false**; when true, replaces an existing destination **file** only. Does not create parent directories. **No trash** — host undo is not automatic.
320
+
321
+ **Inputs:**
322
+
323
+ | Field | Type | Purpose |
324
+ | --- | --- | --- |
325
+ | `from` | `string` | Source path. Required. |
326
+ | `to` | `string` | Destination path. Required. |
327
+ | `overwrite` | `boolean` | Replace existing destination file (default false). |
328
+
329
+ **Outputs:** confirmation with absolute from/to, or error (missing source, dest exists without overwrite, escape, abort).
206
330
 
207
331
  ### Structured Git tools (`createGitTools`)
208
332
 
@@ -231,6 +355,72 @@ const gitTools = createGitTools(workspaceRoot, {
231
355
  });
232
356
  ```
233
357
 
358
+ ### Ask-user decision (`createAskUserDecisionTool`)
359
+
360
+ Opt-in `ask_user_decision` for ambiguous, high-impact direction choices. Model must pass a question plus 2+ options, each with **exactly 3 pros and 3 cons**. Host supplies `ask` (blocks until the user picks). Not in `createCodingTools` / `createAllTools` / `createReadOnlyTools`.
361
+
362
+ | Mode | How |
363
+ | --- | --- |
364
+ | Single (default) | `selectionMode: "single"` → host returns `{ selectedId }` (or length-1 `selectedIds`) |
365
+ | Multi | `selectionMode: "multiple"` → `{ selectedIds: [...] }` (non-empty, known ids) |
366
+ | Free-text | `allowCustom: true` → host may return `{ customText }` **XOR** selection (never both) |
367
+ | Blocking tool | `createAskUserDecisionTool({ ask })` — in-process UI callback |
368
+ | Durable workflow | `suspendAskUserDecision(request)` + `createAskUserDecisionResumeValidator()` / `validateAskUserDecisionResume` on `resumeWorkflow` |
369
+ | Agent durable adapter | `validateAskUserDecisionAgentResume({ request, answer })` — same validation; **no** new `AgentRunInterruption` kinds in 0.0.11 |
370
+
371
+ Custom-text caps match question defaults (2 KiB / hard 8 KiB). Options default max 6 (hard 16).
372
+
373
+ ```ts
374
+ import { createToolRegistry } from "@arnilo/prism";
375
+ import {
376
+ createAskUserDecisionTool,
377
+ createCodingTools,
378
+ suspendAskUserDecision,
379
+ createAskUserDecisionResumeValidator,
380
+ } from "@arnilo/prism-coding-agent";
381
+
382
+ const tools = createToolRegistry([
383
+ ...createCodingTools(workspaceRoot),
384
+ createAskUserDecisionTool({
385
+ ask: async ({ question, options, selectionMode, allowCustom }) =>
386
+ ui.ask({ question, options, selectionMode, allowCustom }),
387
+ }),
388
+ ]);
389
+
390
+ // Workflow node:
391
+ return suspendAskUserDecision({
392
+ question: "Ship sqlite or postgres?",
393
+ options: [/* ≥2 with 3 pros + 3 cons each */],
394
+ selectionMode: "single",
395
+ allowCustom: false,
396
+ });
397
+ // resumeWorkflow(..., { validateResume: createAskUserDecisionResumeValidator() })
398
+ ```
399
+
400
+ ### Goal → verify helper (`runCodingGoalVerify`)
401
+
402
+ Thin composition over existing plan Markdown, named checks, workflow `suspend`/`resumeWorkflow`, and bounded PR handoff. **No Goal table / second runtime.** Peer `@arnilo/prism-workflows`. Example: `examples/coding-goal-verify.ts`.
403
+
404
+ ```ts
405
+ import { runCodingGoalVerify } from "@arnilo/prism-coding-agent";
406
+
407
+ const result = await runCodingGoalVerify({
408
+ goal: "Fix the flake",
409
+ cwd: process.cwd(),
410
+ taskId: "flake-1",
411
+ baseBranch: "main",
412
+ branch: "fix/flake",
413
+ checkNames: ["test"],
414
+ checkDefinitions: { test: { file: "/usr/bin/npm", args: ["test"] } },
415
+ runCheck: hostRunCheck,
416
+ buildHandoff: hostBuildHandoff,
417
+ approval: { validateResume: hostValidate },
418
+ checkpoints,
419
+ ownership,
420
+ redactor,
421
+ });
422
+ ```
423
+
234
424
  ### Durable coding plans and checkpoints
235
425
 
236
426
  There is no `CodingRun`, todo database, or second approval engine. Persist executable plan/todos as ordinary workspace Markdown (for example `plans/<task>.md`) and store only bounded metadata under workflow `state.coding`:
@@ -282,10 +472,10 @@ Minimal drop-in for any Prism app:
282
472
  import { createToolRegistry } from "@arnilo/prism";
283
473
  import { createCodingTools, createReadOnlyTools } from "@arnilo/prism-coding-agent";
284
474
 
285
- // Full coding set (shell + read + write + edit + repo_list + repo_search) against the project root:
475
+ // Full coding set (shell + read + write + edit + repo_list + repo_search + glob + delete + move):
286
476
  const tools = createToolRegistry(createCodingTools(process.cwd()));
287
477
 
288
- // Or a read-only set for inspection-only agents (read + repo_list + repo_search):
478
+ // Or a read-only set for inspection-only agents (read + repo_list + repo_search + glob):
289
479
  const ro = createToolRegistry(createReadOnlyTools(process.cwd()));
290
480
  ```
291
481
 
@@ -300,6 +490,8 @@ const shell = createShellTool("/repo", {
300
490
  maxLines: 500,
301
491
  timeout: 600,
302
492
  maxTotalOutputBytes: 64 * 1024 * 1024,
493
+ // Optional: scrub the environment the spawn hook and child process see (default: full process.env clone).
494
+ envAllowlist: ["PATH", "HOME", "LANG"],
303
495
  });
304
496
 
305
497
  const remoteWrite = createWriteTool("/repo", {
@@ -310,22 +502,29 @@ const remoteWrite = createWriteTool("/repo", {
310
502
  });
311
503
  ```
312
504
 
505
+ Packed capability demo: `examples/coding-tools-capability-gaps.ts` (search modes, glob, read-before-write, delete/move).
506
+
313
507
  ## Extension and configuration notes
314
508
 
315
- - **Pluggable operation backends.** Every tool accepts an `operations` seam. Custom `ReadOperations` must implement bounded `readText` plus `statFile`; custom `EditOperations` must implement `statFile`; read/write methods receive caps/signals. `BashOperations` must stream through `onData` and honor `signal`/`timeout`. Custom `RepositoryOperations` must honor depth/entry/file/match/scan/time caps and abort. A hostile custom backend can still violate its host-owned contract, so isolate it separately.
316
- - **Per-tool options.** `ShellToolOptions` adds `timeout` and `maxTotalOutputBytes`; `ReadToolOptions` adds `maxScanBytes`; `WriteToolOptions` adds `maxInputBytes`; `EditToolOptions` adds `maxFileBytes`, `maxInputBytes`, and `maxEdits`; list/search accept `repository` limits and shared aggregator `ToolsOptions.repository`.
317
- - **Aggregator options.** `ToolsOptions` (`{ executionPolicy?, shell?, read?, write?, edit?, list?, search?, repository? }`) threads each sub-object to the matching tool. `createCodingTools()`, `createAllTools()`, and `createReadOnlyTools()` apply the shared policy unless that tool has an explicit per-tool override. Read-only membership is deliberately `read` + `repo_list` + `repo_search` (0.0.9 behavior change).
318
- - **Sandbox composition.** Prefer `@arnilo/prism-coding-security` `createSandboxCodingTools(cwd, { sandbox, ... })` to wire shell through a `SandboxAdapter` while sharing repository options. Filesystem tools still use the host `cwd` unless custom operations are supplied; Docker tmpfs workspace mutations stay inside the container until export.
509
+ - **Long coding sessions.** Use `createCodingCompactionStrategy()` from optional `@arnilo/prism-compaction-llm` when history needs a bounded coding handoff. It is selected explicitly through normal `session.compact()` / agent compaction configuration, preserves raw session entries, and prioritizes file paths, patch intent, checks, plan/todo state, blockers, and verification steps. It does not read files, retain full diffs, or create a second coding runtime.
510
+ - **Pluggable operation backends.** Every tool accepts an `operations` seam. Custom `ReadOperations` must implement bounded `readText` plus `statFile`; custom `EditOperations` must implement `statFile`; read/write methods receive caps/signals. `BashOperations` must stream through `onData` and honor `signal`/`timeout`. Custom `RepositoryOperations` must honor depth/entry/file/match/scan/time caps and abort (including `glob`). Custom `DeleteOperations` / `MoveOperations` must honor containment and abort. A hostile custom backend can still violate its host-owned contract, so isolate it separately.
511
+ - **Per-tool options.** `ShellToolOptions` adds `timeout` and `maxTotalOutputBytes`; `ReadToolOptions` adds `maxScanBytes` and optional `readPathSet`; `WriteToolOptions` / `EditToolOptions` add input caps plus optional `requireReadBeforeWrite` / `readPathSet` / `force`; list/search/glob accept `repository` limits and shared aggregator `ToolsOptions.repository`.
512
+ - **Aggregator options.** `ToolsOptions` (`{ executionPolicy?, shell?, read?, write?, edit?, delete?, move?, list?, search?, glob?, repository? }`) threads each sub-object to the matching tool. `createCodingTools()`, `createAllTools()`, and `createReadOnlyTools()` apply the shared policy unless that tool has an explicit per-tool override. Full membership is nine tools; read-only is `read` + `repo_list` + `repo_search` + `glob`.
513
+ - **Sandbox composition.** Prefer `@arnilo/prism-coding-security` `createSandboxCodingComposition(cwd, { workspaceMode, sandbox, ... })` (or tools-only wrappers). `workspaceMode` is required: `"sandbox"` keeps shell/read/write/edit/list/search/glob/delete/move on one disposable tree; `"host"` runs against host cwd and never claims containment. Mixed sandbox-shell + host-FS wiring throws unless `allowMixedWorkspaceWiring: true`. Same-tree Git: `createGitTools(composition.workspaceRoot, { execFile: sandbox.execFile, commitIdentity })`.
319
514
  - **`ToolsOptions`** and the per-tool option types are exported from the package barrel for host configuration.
320
515
  - No auto-discovery or manifest registration: import and register explicitly. This package registers no extensions and owns no globals (the mutation queue is a process-wide per-path map — see `ponytail:` note in the source).
321
516
 
517
+ Read/list/search/glob are observation effects; write/edit/delete/move are optional local mutations; shell/check are unsupported external mutations. `reconcileCodingToolEffect` proves local postconditions or returns `unknown`. See [tool effects](tool-effects.md).
518
+
322
519
  ## Security and performance notes
323
520
 
324
- - **Host shell/filesystem access.** These tools run real commands and read/write/list/search real files. They provide **no sandbox**. Gate them with Prism `PermissionPolicy` / `ToolValidator` / trust policies before registering them for any provider turn. Shared `executionPolicy` applies to both full and read-only aggregators before filesystem/process side effects. See [Host security guide](host-security.md) and [Security/auth/trust](settings-auth-trust-security.md).
521
+ - **Host shell/filesystem access.** These tools run real commands and read/write/list/search/glob/delete/move real files. They provide **no sandbox**. Gate them with Prism `PermissionPolicy` / `ToolValidator` / trust policies before registering them for any provider turn. Shared `executionPolicy` applies to both full and read-only aggregators before filesystem/process side effects. See [Host security guide](host-security.md) and [Security/auth/trust](settings-auth-trust-security.md).
522
+ - **High-risk mutations.** `delete` and `move` are permanent (no trash). Prefer host confirmation via `ExecutionPolicy` before allowing them. Do not instruct models to bypass policy/sandbox.
325
523
  - **Non-zero exit is not an error.** A failing command is a normal `shell` result (exit code in metadata); only timeout/abort/spawn failures are error results. Do not assume `error == undefined` means the command succeeded.
326
- - **Bounded I/O.** `read` streams one page and bounds scan bytes; image/edit reads use stat plus a shared cap-enforcing reader; write/edit inputs are measured before mutation. `repo_list`/`repo_search` stream walks and charge depth/entry/file/match/scan/time before retention. Structured Git tools use argument arrays with finite output/path/ref/message/patch caps, disable hooks/credential prompts/external diff by default, and never push or open PRs. `shell` retains only a rolling display tail and synchronously spills accepted raw chunks so stream backpressure cannot grow heap; wall time and total raw output remain finite.
327
- - **Per-path serialization.** Concurrent mutations to the same file serialize; concurrent mutations to different files do not block each other. The queue is a process-wide map — across sessions in one process, same-path writes still serialize (upgrade path: scope per registry if throughput matters).
524
+ - **Bounded I/O.** `read` streams one page and bounds scan bytes; image/edit reads use stat plus a shared cap-enforcing reader; write/edit inputs are measured before mutation. `repo_list`/`repo_search`/`glob` stream walks and charge depth/entry/file/match/scan/time before retention. Structured Git tools use argument arrays with finite output/path/ref/message/patch caps, disable hooks/credential prompts/external diff by default, and never push or open PRs. `shell` retains only a rolling display tail and synchronously spills accepted raw chunks so stream backpressure cannot grow heap; wall time and total raw output remain finite.
525
+ - **Per-path serialization.** Concurrent mutations to the same file serialize; concurrent mutations to different files do not block each other. `move` locks both paths in lexicographic order. The queue is a process-wide map — across sessions in one process, same-path writes still serialize (upgrade path: scope per registry if throughput matters).
328
526
  - **Bounded image reads.** `read` rejects images over `maxImageBytes` (default 10 MB) by `stat` before read when possible; MIME is detected from magic bytes only. Optional `transformImage` is host-owned — the base package has no image-processing dependency.
527
+ - **Fuzzy edit risk.** Silent fuzzy success can mis-apply edits; duplicate matches fail closed. See the `edit` section above.
329
528
 
330
529
  ### Resource-limit defaults and hard caps
331
530
 
@@ -340,7 +539,7 @@ const remoteWrite = createWriteTool("/repo", {
340
539
  | Shell total stdout+stderr | 64 MiB | 1 GiB | process-tree kill; spill removal |
341
540
  | Repo depth / entries / files / page | 32 / 10,000 / 10,000 / 1,000 | 128 / 100,000 / 100,000 / 10,000 | before descending/retaining next entry |
342
541
  | Search scan / file / matches | 64 MiB / 8 MiB / 1,000 | 1 GiB / 64 MiB / 10,000 | before next file/match retention |
343
- | Search pattern / line / context / time | 512 B / 50 KiB / 5 / 30 s | 4 KiB / 1 MiB / 20 / 300 s | before regex compile / line retain / deadline |
542
+ | Search pattern / line / context / time | 512 B / 50 KiB / 5 / 30 s | 4 KiB / 1 MiB / 20 / 300 s | before pattern compile / line retain / deadline |
344
543
  | Git paths / refs / message | 1,000 / 1 KiB / 64 KiB | 10,000 / 4 KiB / 256 KiB | before process/temp-file creation |
345
544
  | Git output / diff lines / changed files / patch | 4 MiB / 10,000 / 1,000 / 16 MiB | 64 MiB / 100,000 / 10,000 / 64 MiB | stream before retain; artifact spill optional |
346
545
  | Worktrees | 4 | 16 | before add |
@@ -354,7 +553,12 @@ Every configurable value is a positive safe integer (context may be zero); Prism
354
553
 
355
554
  ## Related APIs
356
555
 
556
+ - [Language intelligence](language-intelligence.md): optional host-activated LSP contract (`createLanguageIntelligence`) — symbols/definitions/references/diagnostics/hover/rename.
557
+ - [Process sessions](process-sessions.md): optional managed long-running processes (`createProcessSessions`) — start/output/input/wait/signal/kill/release.
558
+ - [Forge integration](forge-integration.md): optional GitHub adapter (`createGitHubForge`) — issue context, authenticated push, PR create/update, review comments, checks, bounded handoff reconcile; effect-store idempotency, no duplicate PRs/comments on retry, tokens never in argv/logs/events.
357
559
  - [Tools](tools.md): the host-owned tool harness — `createToolRegistry`, `dispatchToolCall`, filtering, and the `ToolDefinition` contract these factories satisfy.
358
560
  - [Public contracts](public-contracts.md): `ToolDefinition`, `ToolResult`, `ToolExecutionContext`, `ContentBlock`, and `JsonObject` shapes.
359
561
  - [Host security guide](host-security.md): fail-closed checklist for permission policies, tool validation, and trust boundaries that must gate these tools.
360
562
  - [Tool conformance](tool-conformance.md): assertions for the tool-dispatch blocked-reason matrix these tools participate in.
563
+ - [ACP coding-host interop](acp.md): host editors drive these tools through stable ACP v1 — client fs/terminal adapters, `CodingLifecycleEvent` emission (`file_changed` etc. via the `onEvent` options), and permission/elicitation through the shared four-outcome decision model.
564
+ - [LLM compaction package](compaction-llm.md): optional `createCodingCompactionStrategy()` retains bounded paths, patch intent, checks, plan/todo state, blockers, and next verification—not complete diffs or raw command output.