@arnilo/prism-tools 0.0.0-stage → 0.13.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 (331) hide show
  1. package/CHANGELOG.md +418 -0
  2. package/LICENSE +21 -0
  3. package/README.md +44 -2
  4. package/browser/README.md +98 -0
  5. package/browser/skills/browser-automation/SKILL.md +14 -0
  6. package/browser/src/actions/cdp.ts +131 -0
  7. package/browser/src/actions/dialog.ts +25 -0
  8. package/browser/src/actions/download_release.ts +22 -0
  9. package/browser/src/actions/index.ts +37 -0
  10. package/browser/src/actions/locator.ts +70 -0
  11. package/browser/src/actions/navigate.ts +23 -0
  12. package/browser/src/actions/screenshot.ts +27 -0
  13. package/browser/src/actions/scroll.ts +24 -0
  14. package/browser/src/actions/select_page.ts +21 -0
  15. package/browser/src/actions/types.ts +114 -0
  16. package/browser/src/actions/upload.ts +44 -0
  17. package/browser/src/actions/wait.ts +45 -0
  18. package/browser/src/browser.ts +197 -0
  19. package/browser/src/cdp.ts +178 -0
  20. package/browser/src/checkpoint.ts +189 -0
  21. package/browser/src/config.ts +72 -0
  22. package/browser/src/downloads.ts +253 -0
  23. package/browser/src/errors.ts +31 -0
  24. package/browser/src/evaluate.ts +101 -0
  25. package/browser/src/index.ts +129 -0
  26. package/browser/src/limits.ts +271 -0
  27. package/browser/src/manager.ts +730 -0
  28. package/browser/src/network.ts +263 -0
  29. package/browser/src/observe.ts +178 -0
  30. package/browser/src/policy.ts +133 -0
  31. package/browser/src/screenshot.ts +107 -0
  32. package/browser/src/shared-sandbox.ts +53 -0
  33. package/browser/src/snapshot.ts +99 -0
  34. package/browser/src/targets.ts +165 -0
  35. package/browser/src/tools.ts +480 -0
  36. package/browser/src/types.ts +316 -0
  37. package/browser/src/uploads.ts +174 -0
  38. package/coding/README.md +337 -0
  39. package/coding/src/agent/acp-operations.ts +146 -0
  40. package/coding/src/agent/artifacts.ts +38 -0
  41. package/coding/src/agent/ask-user-decision.ts +623 -0
  42. package/coding/src/agent/atomic-write.ts +23 -0
  43. package/coding/src/agent/bounded-file.ts +21 -0
  44. package/coding/src/agent/checks.ts +301 -0
  45. package/coding/src/agent/coding-checkpoint.ts +828 -0
  46. package/coding/src/agent/delete.ts +237 -0
  47. package/coding/src/agent/diagnostics-tool.ts +163 -0
  48. package/coding/src/agent/diagnostics.ts +262 -0
  49. package/coding/src/agent/edit-diff.ts +382 -0
  50. package/coding/src/agent/edit.ts +322 -0
  51. package/coding/src/agent/effects.ts +102 -0
  52. package/coding/src/agent/env.ts +28 -0
  53. package/coding/src/agent/execution-policy.ts +89 -0
  54. package/coding/src/agent/file-mutation-queue.ts +72 -0
  55. package/coding/src/agent/forge/github.ts +603 -0
  56. package/coding/src/agent/forge/index.ts +16 -0
  57. package/coding/src/agent/forge/types.ts +185 -0
  58. package/coding/src/agent/git-aware-repository.ts +305 -0
  59. package/coding/src/agent/git-exec.ts +326 -0
  60. package/coding/src/agent/git-status.ts +177 -0
  61. package/coding/src/agent/git-tools.ts +360 -0
  62. package/coding/src/agent/git.ts +1074 -0
  63. package/coding/src/agent/glob-match.ts +146 -0
  64. package/coding/src/agent/glob.ts +182 -0
  65. package/coding/src/agent/goal-verify.ts +385 -0
  66. package/coding/src/agent/grep-worker.ts +74 -0
  67. package/coding/src/agent/grep.ts +459 -0
  68. package/coding/src/agent/identity.ts +48 -0
  69. package/coding/src/agent/index.ts +676 -0
  70. package/coding/src/agent/language/client.ts +429 -0
  71. package/coding/src/agent/language/framing.ts +190 -0
  72. package/coding/src/agent/language/index.ts +23 -0
  73. package/coding/src/agent/language/intelligence.ts +682 -0
  74. package/coding/src/agent/language/types.ts +187 -0
  75. package/coding/src/agent/lifecycle-events.ts +121 -0
  76. package/coding/src/agent/lifecycle.ts +166 -0
  77. package/coding/src/agent/limits.ts +214 -0
  78. package/coding/src/agent/move.ts +178 -0
  79. package/coding/src/agent/mutation-path.ts +52 -0
  80. package/coding/src/agent/output-accumulator.ts +304 -0
  81. package/coding/src/agent/path-utils.ts +120 -0
  82. package/coding/src/agent/process/index.ts +58 -0
  83. package/coding/src/agent/process/recovery.ts +508 -0
  84. package/coding/src/agent/process/sessions-host.ts +222 -0
  85. package/coding/src/agent/process/sessions-monitor.ts +141 -0
  86. package/coding/src/agent/process/sessions-recovery.ts +241 -0
  87. package/coding/src/agent/process/sessions-spawn.ts +613 -0
  88. package/coding/src/agent/process/sessions-teardown.ts +281 -0
  89. package/coding/src/agent/process/sessions.ts +30 -0
  90. package/coding/src/agent/process/types.ts +358 -0
  91. package/coding/src/agent/read-path-set.ts +126 -0
  92. package/coding/src/agent/read.ts +713 -0
  93. package/coding/src/agent/repository/glob.ts +147 -0
  94. package/coding/src/agent/repository/indexed-search.ts +480 -0
  95. package/coding/src/agent/repository/list.ts +131 -0
  96. package/coding/src/agent/repository/operations.ts +22 -0
  97. package/coding/src/agent/repository/path.ts +98 -0
  98. package/coding/src/agent/repository/search.ts +277 -0
  99. package/coding/src/agent/repository/types.ts +243 -0
  100. package/coding/src/agent/repository/walk.ts +126 -0
  101. package/coding/src/agent/repository.ts +12 -0
  102. package/coding/src/agent/shell-background.ts +264 -0
  103. package/coding/src/agent/shell-output.ts +75 -0
  104. package/coding/src/agent/shell.ts +526 -0
  105. package/coding/src/agent/todo.ts +399 -0
  106. package/coding/src/agent/truncate.ts +251 -0
  107. package/coding/src/agent/write.ts +170 -0
  108. package/coding/src/ask-user-host.ts +48 -0
  109. package/coding/src/config.ts +278 -0
  110. package/coding/src/hard-deny.ts +97 -0
  111. package/coding/src/index.ts +318 -0
  112. package/coding/src/principal-guard.ts +84 -0
  113. package/coding/src/sandbox.ts +92 -0
  114. package/coding/src/security/approval.ts +144 -0
  115. package/coding/src/security/command-rules.ts +57 -0
  116. package/coding/src/security/docker-cli.ts +222 -0
  117. package/coding/src/security/docker-sandbox.ts +1205 -0
  118. package/coding/src/security/e2b-sandbox.ts +853 -0
  119. package/coding/src/security/egress/dns-pin.ts +88 -0
  120. package/coding/src/security/egress/index.ts +32 -0
  121. package/coding/src/security/egress/limits.ts +57 -0
  122. package/coding/src/security/egress/policy.ts +137 -0
  123. package/coding/src/security/egress/proxy.ts +599 -0
  124. package/coding/src/security/egress/types.ts +30 -0
  125. package/coding/src/security/index.ts +199 -0
  126. package/coding/src/security/native-sandbox.ts +639 -0
  127. package/coding/src/security/path-containment.ts +55 -0
  128. package/coding/src/security/sandbox-coding-operations.ts +392 -0
  129. package/coding/src/security/sandbox-fs-operations.ts +644 -0
  130. package/coding/src/security/sandbox-limits.ts +147 -0
  131. package/coding/src/security/sandbox-tar.ts +308 -0
  132. package/coding/src/security/sandbox.ts +179 -0
  133. package/coding/src/security/semaphore.ts +55 -0
  134. package/coding/src/targets.ts +110 -0
  135. package/coding/src/workspace/diff.ts +78 -0
  136. package/coding/src/workspace/errors.ts +29 -0
  137. package/coding/src/workspace/index.ts +5 -0
  138. package/coding/src/workspace/merge.ts +151 -0
  139. package/coding/src/workspace/routing.ts +76 -0
  140. package/coding/src/workspace/service.ts +584 -0
  141. package/computer-use/README.md +105 -0
  142. package/computer-use/skills/computer-use-linux/SKILL.md +17 -0
  143. package/computer-use/src/classify.ts +53 -0
  144. package/computer-use/src/command.ts +29 -0
  145. package/computer-use/src/config.ts +69 -0
  146. package/computer-use/src/create.ts +170 -0
  147. package/computer-use/src/devices.ts +247 -0
  148. package/computer-use/src/env.ts +28 -0
  149. package/computer-use/src/index.ts +157 -0
  150. package/computer-use/src/skill.ts +21 -0
  151. package/openapi/README.md +97 -0
  152. package/openapi/src/compile.ts +386 -0
  153. package/openapi/src/config.ts +73 -0
  154. package/openapi/src/errors.ts +16 -0
  155. package/openapi/src/execute.ts +241 -0
  156. package/openapi/src/index.ts +124 -0
  157. package/openapi/src/limits.ts +63 -0
  158. package/openapi/src/specs.ts +86 -0
  159. package/openapi/src/tools.ts +60 -0
  160. package/openapi/src/types.ts +61 -0
  161. package/package.json +171 -4
  162. package/research/README.md +72 -0
  163. package/research/skills/research-report/SKILL.md +12 -0
  164. package/research/skills/research-scoping/SKILL.md +10 -0
  165. package/research/skills/research-synthesis/SKILL.md +10 -0
  166. package/research/skills/source-triage/SKILL.md +10 -0
  167. package/research/src/check.ts +71 -0
  168. package/research/src/config.ts +41 -0
  169. package/research/src/index.ts +147 -0
  170. package/research/src/ledger.ts +157 -0
  171. package/research/src/urls.ts +21 -0
  172. package/web/README.md +287 -0
  173. package/web/skills/web-research/SKILL.md +17 -0
  174. package/web/src/brave.ts +66 -0
  175. package/web/src/cache.ts +151 -0
  176. package/web/src/config.ts +210 -0
  177. package/web/src/controls.ts +128 -0
  178. package/web/src/domains.ts +114 -0
  179. package/web/src/exa.ts +60 -0
  180. package/web/src/firecrawl.ts +160 -0
  181. package/web/src/index.ts +387 -0
  182. package/web/src/kagi.ts +73 -0
  183. package/web/src/limits.ts +55 -0
  184. package/web/src/native/convert.ts +84 -0
  185. package/web/src/native/fetch.ts +226 -0
  186. package/web/src/normalize.ts +131 -0
  187. package/web/src/obscura/cdp.ts +201 -0
  188. package/web/src/obscura/classify.ts +35 -0
  189. package/web/src/obscura/cli.ts +254 -0
  190. package/web/src/obscura/errors.ts +8 -0
  191. package/web/src/obscura/index.ts +59 -0
  192. package/web/src/obscura/limits.ts +92 -0
  193. package/web/src/obscura/mcp.ts +104 -0
  194. package/web/src/obscura/playwright.ts +133 -0
  195. package/web/src/obscura/process.ts +235 -0
  196. package/web/src/obscura/search-profile.ts +73 -0
  197. package/web/src/obscura/types.ts +48 -0
  198. package/web/src/obscura/web.ts +419 -0
  199. package/web/src/prompt-fetch.ts +88 -0
  200. package/web/src/render.ts +75 -0
  201. package/web/src/searxng.ts +78 -0
  202. package/web/src/slice.ts +139 -0
  203. package/web/src/tavily.ts +77 -0
  204. package/web/src/tools.ts +182 -0
  205. package/web/src/transport.ts +310 -0
  206. package/web/src/types.ts +199 -0
  207. package/work/README.md +77 -0
  208. package/work/THIRD_PARTY_NOTICES +24 -0
  209. package/work/docling/ocr.py +150 -0
  210. package/work/sandbox/Dockerfile +36 -0
  211. package/work/sandbox/soffice.sh +12 -0
  212. package/work/src/command.ts +29 -0
  213. package/work/src/config.ts +138 -0
  214. package/work/src/connectors/cli.ts +343 -0
  215. package/work/src/connectors/drafts.ts +647 -0
  216. package/work/src/connectors/errors.ts +8 -0
  217. package/work/src/connectors/file-bytes.ts +149 -0
  218. package/work/src/connectors/google-workspace-http.ts +399 -0
  219. package/work/src/connectors/google-workspace.ts +465 -0
  220. package/work/src/connectors/gws-tools.ts +378 -0
  221. package/work/src/connectors/http.ts +123 -0
  222. package/work/src/connectors/idempotency.ts +235 -0
  223. package/work/src/connectors/identity.ts +34 -0
  224. package/work/src/connectors/index.ts +93 -0
  225. package/work/src/connectors/limits.ts +50 -0
  226. package/work/src/connectors/m365-tools.ts +308 -0
  227. package/work/src/connectors/microsoft365-http.ts +428 -0
  228. package/work/src/connectors/microsoft365.ts +402 -0
  229. package/work/src/connectors/normalize.ts +120 -0
  230. package/work/src/connectors/tool-helpers.ts +398 -0
  231. package/work/src/connectors/tools.ts +42 -0
  232. package/work/src/connectors/types.ts +399 -0
  233. package/work/src/diagrams/canonicalize.ts +166 -0
  234. package/work/src/diagrams/caps.ts +52 -0
  235. package/work/src/diagrams/embed.ts +340 -0
  236. package/work/src/diagrams/errors.ts +76 -0
  237. package/work/src/diagrams/index.ts +81 -0
  238. package/work/src/diagrams/messages.ts +150 -0
  239. package/work/src/diagrams/origin.ts +52 -0
  240. package/work/src/diagrams/telemetry.ts +34 -0
  241. package/work/src/diagrams/xml.ts +267 -0
  242. package/work/src/document-extraction/index.ts +563 -0
  243. package/work/src/document-reader/ambient.d.ts +2 -0
  244. package/work/src/document-reader/errors.ts +7 -0
  245. package/work/src/document-reader/index.ts +181 -0
  246. package/work/src/document-reader/mistral-ocr.ts +318 -0
  247. package/work/src/document-reader/parsers.ts +171 -0
  248. package/work/src/documents/caps.ts +135 -0
  249. package/work/src/documents/diff.ts +269 -0
  250. package/work/src/documents/errors.ts +44 -0
  251. package/work/src/documents/fidelity.ts +98 -0
  252. package/work/src/documents/generate.ts +96 -0
  253. package/work/src/documents/hash.ts +8 -0
  254. package/work/src/documents/index.ts +14 -0
  255. package/work/src/documents/model-schema.ts +639 -0
  256. package/work/src/documents/parse.ts +252 -0
  257. package/work/src/documents/patch-history.ts +81 -0
  258. package/work/src/documents/patch.ts +476 -0
  259. package/work/src/documents/preview-html.ts +245 -0
  260. package/work/src/documents/preview.ts +227 -0
  261. package/work/src/documents/telemetry.ts +36 -0
  262. package/work/src/documents/translate/docx.ts +292 -0
  263. package/work/src/documents/translate/pptx.ts +304 -0
  264. package/work/src/documents/translate/xlsx.ts +137 -0
  265. package/work/src/documents/types.ts +188 -0
  266. package/work/src/index.ts +311 -0
  267. package/work/src/runtime/worker-pool-entry.ts +102 -0
  268. package/work/src/runtime/worker-pool.ts +267 -0
  269. package/work/src/sandbox/composition.ts +212 -0
  270. package/work/src/sandbox/image.ts +6 -0
  271. package/work/src/sandbox/index.ts +14 -0
  272. package/work/src/sheets/caps.ts +68 -0
  273. package/work/src/sheets/csv.ts +351 -0
  274. package/work/src/sheets/decimal.ts +388 -0
  275. package/work/src/sheets/errors.ts +52 -0
  276. package/work/src/sheets/index.ts +62 -0
  277. package/work/src/sheets/inference.ts +378 -0
  278. package/work/src/sheets/telemetry.ts +34 -0
  279. package/work/src/sheets/types.ts +162 -0
  280. package/work/src/sheets/xlsx.ts +388 -0
  281. package/work/src/skills/index.ts +8 -0
  282. package/work/src/skills/load.ts +43 -0
  283. package/work/src/tools/filesystem.ts +21 -0
  284. package/work/src/tools/index.ts +1 -0
  285. package/work/src/tools/local-filesystem.ts +33 -0
  286. package/work/src/tools/office.ts +311 -0
  287. package/work/vendor/hermes-agent/LICENSE +21 -0
  288. package/work/vendor/hermes-agent/skills/productivity/docx/LICENSE +21 -0
  289. package/work/vendor/hermes-agent/skills/productivity/docx/SKILL.md +196 -0
  290. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_comments.py +289 -0
  291. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_common.py +94 -0
  292. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_create.py +177 -0
  293. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_edit.py +250 -0
  294. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_read.py +149 -0
  295. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_revisions.py +147 -0
  296. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_template.py +70 -0
  297. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_validate.py +156 -0
  298. package/work/vendor/hermes-agent/skills/productivity/pdf/LICENSE +21 -0
  299. package/work/vendor/hermes-agent/skills/productivity/pdf/SKILL.md +125 -0
  300. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/_raster.py +76 -0
  301. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_marker.py +88 -0
  302. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_pymupdf.py +101 -0
  303. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_create.py +130 -0
  304. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_fill_form.py +97 -0
  305. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_form_layout.py +168 -0
  306. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_make_form.py +145 -0
  307. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_merge.py +50 -0
  308. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_meta.py +115 -0
  309. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_page_image.py +99 -0
  310. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_read.py +153 -0
  311. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_secure.py +71 -0
  312. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_split.py +84 -0
  313. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_stamp.py +143 -0
  314. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_watermark.py +51 -0
  315. package/work/vendor/hermes-agent/skills/productivity/powerpoint/LICENSE +21 -0
  316. package/work/vendor/hermes-agent/skills/productivity/powerpoint/SKILL.md +220 -0
  317. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_create.py +214 -0
  318. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_edit.py +436 -0
  319. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_from_template.py +88 -0
  320. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_read.py +131 -0
  321. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_render.py +93 -0
  322. package/work/vendor/hermes-agent/skills/productivity/xlsx/LICENSE +21 -0
  323. package/work/vendor/hermes-agent/skills/productivity/xlsx/SKILL.md +196 -0
  324. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/csv_to_xlsx.py +104 -0
  325. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_create.py +259 -0
  326. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_edit.py +263 -0
  327. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_read.py +160 -0
  328. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_recalc.py +111 -0
  329. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_restructure.py +337 -0
  330. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_to_csv.py +69 -0
  331. package/work/vendor-lock.json +11 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,418 @@
1
+ # Changelog
2
+
3
+ ## [0.13.1] - 2026-11-12
4
+
5
+ ### ./coding
6
+
7
+ #### Fixed
8
+
9
+ - **`workspace.worktree.create({ sessionId })` keeps one live worktree per session.** Creating a worktree for a session that already holds one now fails with the new code `workspace_session_bound` and creates nothing. Previously a second record was created, and `bound(sessionId)` and tool routing became ambiguous. Two concurrent creates for one session bind once. `worktree.rebind` refuses a session that already holds a different worktree with the same code. Reuse the held worktree through `bound(sessionId)`, or dispose it first.
10
+
11
+ ## [Unreleased]
12
+
13
+ Consolidated seven tool packages into `@arnilo/prism-tools` with explicit subpaths.
14
+
15
+ ### ./coding
16
+
17
+ # Changelog
18
+
19
+ ## [Unreleased]
20
+
21
+ ### Added
22
+
23
+ - **Background waits are event-driven (plan 189 follow-up).** `ProcessSession` gains `onOutput(listener)`, which publishes a chunk or the terminal transition (unsubscribe with the returned function; a throwing listener never fails the process). `shell({ background: true })` therefore returns as soon as the first output arrives instead of always paying the one-second window, and `shell_output({ waitSeconds })` wakes on the publication instead of polling every 50 ms. One `shell_output` call now drains everything that accumulated, in whole chunks up to twice the tail cap, instead of returning only the first chunk. The internal session record carries the listener set; no cap changed.
24
+
25
+ - **The `workspace` service emits `worktree_changed` (plan 189 follow-up).** `createWorkspaceService({ onEvent })` emits `{ type: "worktree_changed", action: "add", path }` after `worktree.create` succeeds and `{ action: "remove", path }` after `dispose()` removed one, through `createCodingLifecycleEmitter` (so the frozen kind's path/byte caps apply). No new export: `WorkspaceServiceOptions` gains the field. Worktree movement keeps a producer at the layer that still owns worktrees, after the `git_worktree` tool was removed, and nothing is emitted when no sink is passed. `createCodingLifecycleEmitter` also swallows a listener that throws now, so a failing consumer cannot fail the operation that produced the event.
26
+
27
+ - **`diagnostics` tool (plan 189 Task 10).** `language.servers` (name → `{ command, args?, languages, env? }`) registers `diagnostics({ path })`, which refreshes the document and returns the language server's diagnostics as `path:line:col severity message` (1-based, capped at 500 per file / 4 KiB per message). No servers means no tool and no process; a configured server starts on the first call for its language and stops when the last open session closes. A server that cannot start is a tool error naming it plus one `status.note("warn", …)`. New exports: `createDiagnosticsTool`, `DiagnosticsToolHandle`, `DiagnosticsToolOptions`, `ToolsCodingLanguageConfig`, `ToolsCodingLanguageServerConfig`, `languageIdForPath`.
28
+
29
+ - **Background processes (plan 189 Task 9).** `shell` takes `background: true` and returns `Started process <id>` plus the output that arrived within one second; `shell_output({ id, waitSeconds?, kill? })` reads the increment since the last call, waits for new output or exit (max 60 s), or kills the process. Processes run on the process-session library, are owned by the calling session (another session's id is an error), are capped by `shell.background.maxProcesses` (default 4, hard 16), and are killed on `session.closed` and `agent.dispose()`. The hard-deny `commandRules` and built-in denies, the environment allow-list, `commandPrefix`, and `spawnHook` all apply to background commands. A backend without `startProcess` (the native `unshare` sandbox) offers no `background` argument and no `shell_output`. New exports: `createBackgroundProcesses`, `createShellOutputTool`, `BackgroundProcesses`, `BackgroundProcessOptions`, `BackgroundRead`, `BackgroundReadInput`, `BackgroundStarted`, `BackgroundStartInput`, `ShellOutputToolOptions`, `DEFAULT_MAX_BACKGROUND_PROCESSES`, `HARD_MAX_BACKGROUND_PROCESSES`, `HARD_MAX_BACKGROUND_WAIT_SECONDS`, `BACKGROUND_FIRST_OUTPUT_MS`.
30
+
31
+ - **`examples/serve-tools.ts`: serving a run's tools to an external agent (plan 165 Task 8).** The Phase 14 end-to-end example, offline. Inside one `session.run` an inline `defineDriver` driver binds a `workspace` worktree for the run's session, writes the vendor's MCP configuration into it with `excludeOverlay`, and serves `read`, `edit`, and the permission tool with `serveTools` over `loopback-http` and then `stdio-relay`, driving the scripted `jsonl-stream` CLI as the vendor binary through `--mcp-endpoint`. It shows an allowed `edit` that changes the file in the worktree, a declined `edit` under `@arnilo/prism-extensions/approval` in `ask` mode, the permission tool's deny and allow, an unserved name as a JSON-RPC error, a call after `close()` refused, the served calls' `origin` on the run's events, the worktree diff with the overlay excluded, and a conflicting `merge` reported unresolved.
32
+ - **Per-session worktree routing (plan 165 Task 7).** A worktree bound with `worktree.create({ sessionId })` now routes that session's tool calls into the worktree: one wrapper at registration (`createWorktreeRouting({ base, create, workspace })`) wraps each registered tool's `execute` with a lookup of the calling session's worktree and dispatches to the tool instance built for it, so relative paths resolve inside the worktree and the tools' own containment follows it. Tool names, descriptions, and schemas are unchanged. The binding lives only in the service's memory, keyed by session id, and is never read from tool arguments or model output. Instances are built once per bound worktree, dropped on `dispose`, and dropped on the `session.closed` hook. A session rebound to another worktree rebuilds. New exports: `createWorktreeRouting`, `WorktreeRouting`, `WorktreeRoutingOptions`, `CodingTargetsContext`.
33
+
34
+ ### Removed
35
+
36
+ - The unregistered patch-review module and its four limits constants: `createCodingPatchReviewManifest`, `assertCodingPatchAccepted`, `CodingPatchReviewError`, `resolveCodingReviewLimits`, `CodingPatchReview*`/`CodingReview*`/`ReviewDiagnosticSeverity` types, `DEFAULT_/HARD_MAX_REVIEW_REVISIONS`, `DEFAULT_/HARD_MAX_REVIEW_MANIFEST_BYTES`. `lifecycle.ts` and `coding-checkpoint.ts` stay (shipped tools type-import the former; `goal-verify.ts` uses the latter).
37
+
38
+ - **Fewer tools, one job each (plan 189 Task 8, breaking).** `repo_list`, `git_branch`, `git_worktree`, and `git_apply` are gone, with `createRepoListTool`, `ListToolOptions`, `createGitBranchTool`, `createGitWorktreeTool`, `createGitApplyTool`, and `classifyGitBranchEffect`/`classifyGitWorktreeEffect`/`classifyGitApplyEffect`. `createGitTools` returns `git_status`, `git_diff`, `git_commit`, `git_pr_handoff`. Directory listing is the `glob` walk (the repository `list` operation stays), worktrees are the `workspace` service's job, and `git branch`/`git apply`/`git worktree` are `shell` calls. `delete`/`move` are registered only when `shell` is absent — `createCodingTools(cwd, { shellAvailable: false })` — so the default set is 12 tools instead of 18 and the read subset is `read`/`grep`/`glob`. `GitToolsOptions.onEvent` is removed; the `workspace` service emits `worktree_changed` instead (see Added).
39
+
40
+ ### Changed
41
+
42
+ - **Skill directories are read-only extra roots for read-effect coding tools (plan 190 Task 6).** `coding.targets` now reports `dirname(skill.path)` for every effective skill with an absolute path, but only for `read`, `glob`, `grep`, and `diagnostics`. A skill's `references/` file outside the workspace is therefore readable without adding a root that would also grant writes, and approval sees the same read-only roots. `write`, `edit`, `delete`, `move`, and `shell` keep the coding roots, so a write under a skill directory is still hard-denied. The skill list is read lazily after setup and the derived directories are cached until its paths change. `createCodingTargets` gains an optional fourth argument, `() => readonly Skill[]`; without it behavior is unchanged. No kernel change; `CodingTargets.roots` and the function's signature are the additive package surface.
43
+
44
+ - **Schemas describe only what the model controls (plan 189 Task 7).** `write` and `edit` include `force` — and the read-before-write sentence in their description — only when the tool was created with `requireReadBeforeWrite`; without the guard `force` is an unknown key and the description is silent about it. `ask_user_decision`'s `pros`/`cons` are optional 0–3 string arrays (were exactly 3), `AskUserDecisionOption.pros`/`.cons` are `readonly string[]`, and a 4-bullet list still fails. Guard behavior when enabled is unchanged.
45
+
46
+
47
+ - **`edit` is exact-match only and reports what it changed (plan 189 Task 6).** Breaking: `normalizeForFuzzyMatch`, `fuzzyFindText`, `applyReplacementsPreservingUnchangedLines`, `AppliedEditsResult.usedFuzzyMatch` and `EditToolDetails.fuzzy` are gone, and a near-miss oldText (trailing whitespace, smart quotes, en-dash, NBSP) now fails loudly with the `Nearby` hint instead of editing a nearby region. A success returns `Edited <path>: N block(s).` followed by the changed lines with their new numbers, two lines of context and a 40-line cap with a `(… N more changed lines)` note. New export `generateChangedLinePreview` shares `read`'s `N\ttext` format; CRLF/BOM preservation is unchanged.
48
+
49
+
50
+ - **`glob` accepts brace patterns by default (plan 189 Task 5).** `braceExpansion` defaults to `true`: `src/**/*.{ts,tsx}` expands on the first call, with the same bounds (128 alternatives / 4096 expanded bytes; unbalanced, nested, or empty braces are errors). The argument is removed from the schema; `createGlobTool(cwd, { braceExpansion: false })` rejects braces and the error says so. `compileGlob(pattern, braceExpansion?)` is exported and used by the `grep` `glob` filter and both sandbox paths, so brace filters expand everywhere.
51
+
52
+
53
+ - **`repo_search` is now `grep`: regex by default, in a terminate-able worker (plan 189 Task 4).** Breaking: the renamed tool takes `pattern` (not `query`), `caseSensitive` defaults to `true`, `context` defaults to `0`, and content output is ripgrep-style (`path:line:text`, `path-line-text` for context, `--` between groups, each line once). A pattern is a JavaScript regex unless `literal: true`; regex matching runs in `agent/grep-worker.ts` and the main thread terminates it at the search time limit, so a catastrophic pattern returns the matches so far with `truncatedBy: "time"` instead of blocking the loop. New `glob` argument filters files by path, `mode` is only in the schema when `modes` enables an index, and `createRepoSearchTool`/`SearchToolOptions` become `createGrepTool`/`GrepToolOptions` (`ToolsOptions.search` stays as a deprecated alias). `RepositorySearchRequest` gains `matcher` and `glob`; `matchLines`/`literalMatcher` are exported and shared by the worker, literal mode, and the sandbox backend.
54
+
55
+
56
+ - `coding.targets`' `CodingTargetsFn` takes an optional third argument, `{ sessionId? }`, and `createCodingTargets(root, roots, workspace?)` takes the `workspace` service. For a bound session it reports the worktree as the session's root and resolves relative paths against it, so approval and the hard-deny hook follow the session; unbound sessions are unchanged. The hard-deny hook now passes the `tool.call` payload's `sessionId`.
57
+ - `worktree.create({ sessionId })` fails with the new `workspace_sandbox_unsupported` when `sandbox` is other than `none`, because a routed worktree path is outside the sandbox root. `create` without `sessionId` still works.
58
+
59
+ - `@arnilo/prism-tools/coding` (plan 154 Task 3): the coding tools moved out of
60
+ `@arnilo/prism-coding-tools/{agent,security}` into a source-exported package on the lockstep line.
61
+ The default export is a `definePackage` definition whose `setup` registers `read`, `write`, `edit`,
62
+ `delete`, `move`, `repo_list`, `repo_search`, `glob`, and `shell` (or the read subset with
63
+ `readOnly: true`, and without `shell` when `shell.enabled` is `false`). Every registered tool now
64
+ declares `effects` — `["read"]` for the read/search tools, `["write"]` for the mutating tools, and
65
+ `["execute"]` for `shell` — so `tool.call` hooks and approval can decide from the capability
66
+ annotation. Config is `{ root, roots?, readOnly?, shell?, repository? }`; a missing `root` fails the
67
+ load with a field-naming error rather than a schema `required` (plan 152 Task 6's config rule).
68
+ - The moved module surface keeps its names as named exports behind the single entry point: the tool
69
+ factories and aggregators, the opt-in Git/check/todo/`ask_user` tools, the workspace/process/
70
+ language/review helpers, and the sandbox, command-rule, path-containment, and egress modules.
71
+ - `tool.call` hard denies (plan 154 Task 4): one decide handler, id
72
+ `@arnilo/prism-tools/coding:tool.call#hard-deny`, denies a path outside `root` ∪ `roots` after
73
+ realpath/symlink resolution, a `shell` command matching a deny `commandRules` entry or a built-in
74
+ deny pattern, and any mutating tool under `readOnly`. The kernel's any-deny-wins rule makes it hold
75
+ under every approval mode and from a custom loop.
76
+ - The `coding.targets` service, `targets(name, args) → { paths, roots } | undefined`, reporting the
77
+ absolute paths and roots of each own-tool call.
78
+ - Config gains `sandbox` (`"none"` default, `"native"`, `"docker"`, `"e2b"`), `commandRules`, and
79
+ `egress`. `e2b` is reached only through a dynamic import of the optional peer; a backend that
80
+ cannot be built notes a warning and withholds `shell` instead of throwing or running unsandboxed.
81
+ - Git, checks, todo, and `ask_user` (plan 154 Task 5). `git` (default on) registers the seven git
82
+ tools with declared effects. `checks` registers `coding_check` only when at least one check is
83
+ configured. `todo` registers `todo_write` and a `run.end` handler, id
84
+ `@arnilo/prism-tools/coding:run.end#todo`, unless `continueOnOpen` is `false`. `ask_user_decision`
85
+ asks through `host.ui`; `unavailable` suspends and `session.resume(decisions)` delivers the answer.
86
+ Durable-effect tools fail closed with `ERR_PRISM_CODING_PRINCIPAL_REQUIRED` or
87
+ `ERR_PRISM_CODING_PRINCIPAL_MISMATCH`. `forge.token` is an `env:`/`keyring:` ref resolved through
88
+ `prism.credentials.resolve`.
89
+
90
+ ### Added
91
+
92
+ - The `workspace` service (plan 165 Task 6): disposable git worktrees for delegated coding work,
93
+ provided whenever `root` is inside a git repository (otherwise a `status.note` and no service).
94
+ `worktree.create({ branch?, base?, sessionId? })` adds and locks a linked worktree under
95
+ `worktreeRoot` and returns `{ path, branch, base, sessionId?, dispose }`; `worktree.bound(sessionId)`
96
+ is the plan 165 Task 7 routing lookup. `diff(path, bounds?)` returns `{ changes, truncated }` — the
97
+ driver kit's `FileActivity` shape — for tracked changes against the worktree's base plus untracked
98
+ files, with overlay files excluded and per-file byte bounds. `merge(path, { into, commit? })`
99
+ probes with `git merge-tree --write-tree` so a conflict reports `{ status: "conflict", files }` with
100
+ both trees, the index, and every ref unchanged; a clean merge returns `{ status: "merged", commit }`.
101
+ `excludeOverlay(path, files)` writes overlay files and excludes them through one marked block in the
102
+ git common directory's `info/exclude` (the driver kit's `writeExcludeBlock`). `dispose({ force? })`
103
+ removes the overlays, unlocks and removes the worktree, drops the session binding, and deletes the
104
+ branch only when git considers it fully merged. New config: `workspace`
105
+ (`enabled`, `worktreeRoot`, `maxWorktrees`, `diff.maxFiles`, `diff.maxBytes`) with defaults and
106
+ schema bounds. `GitOperations` gains `mergeTree`, `commitTree`, `updateRef`, `fastForward`,
107
+ `stageAll`, `writeTree`, `readTree`, and `untrackedFiles`, plus a `base` option on `diff`/`worktree`
108
+ and a `delete` action on `branch` — all through the existing bounded runner.
109
+
110
+ ### Removed
111
+
112
+ - The durable multi-repository workspace lifecycle (plan 165 Task 6): `createCodingWorkspaceLifecycle`,
113
+ `resolveWorkspaceLimits`, `WorkspaceError`, the `WORKSPACE_NAMESPACE`/`WORKSPACE_SCHEMA_VERSION`/
114
+ `WORKSPACE_LOCK_REASON_PREFIX`/`WORKSPACE_STATES` constants, the `CodingWorkspaceRecord`/`Workspace*`
115
+ record and limit types, and the ten `*_MAX_WORKSPACE_*` caps, with no alias. Durable records, leases,
116
+ checkpoints, and multi-repository identity move to `@arnilo/prism-extensions/workflows` in Phase 16; the
117
+ in-process `workspace` service replaces the rest. The phase26 coding-journey fixture's workspace leg
118
+ now uses the service and drops its Postgres verify/cleanup calls.
119
+ - `createCodingApprovalPolicy` and its `CodingApprovalFn`, `CodingApprovalRequest`,
120
+ `CodingApprovalPolicyOptions`, and `ApprovalCacheScope` types (plan 154 Task 4). Prompting moves to
121
+ `@arnilo/prism-extensions/approval` (plan 154 Task 6); containment and command-rule evaluation stay as
122
+ internal checks behind the `tool.call` hard deny.
123
+ - The host ask callback, the workflow suspend/resume validators, and the mutable todo-continuation
124
+ handler (plan 154 Task 5). Asking goes through `host.ui` and `session.resume`. Continuation is
125
+ `createTodoContinuationDecision` on `run.end`.
126
+
127
+ ### Changed
128
+
129
+ - `@arnilo/prism-coding-tools` no longer exports `./agent` or `./security`; the two subpaths are
130
+ deleted with no alias. Its remaining surface is `./openapi`, `./computer-use-linux`, `./dev`,
131
+ `./dev/cli`, and `./impeccable`. Its `diff` dependency moved with the coding tools.
132
+ - Every importer migrates to `@arnilo/prism-tools/coding`: `@arnilo/prism-code`,
133
+ `@arnilo/prism-ag-ui`, `@arnilo/prism-acp-agent`, `@arnilo/prism-agent-sdk`, the examples, and the
134
+ scripts.
135
+
136
+ ## [0.1.0] - 2026-08-09
137
+
138
+ Initial published surface for the lockstep line. See the root `CHANGELOG.md`.
139
+
140
+ ## [0.0.28] - 2026-08-08
141
+
142
+ The coding tools shipped inside `@arnilo/prism-coding-tools` before this package existed; see that
143
+ package's changelog for the pre-split history.
144
+
145
+
146
+ ### ./web
147
+
148
+ # Changelog
149
+
150
+ ## [Unreleased]
151
+
152
+ ### Added
153
+
154
+ - **`web-research` skill (plan 215 Tasks 8 and 11).** The package registers a bundled skill from `skills/web-research` with `skills.addDir`, and `package.json` `files` lists `skills`. It declares `prism-requires-tools: web_fetch`, so a request without `web_fetch` does not list it. The body is eight lines: page text is data, give each fact's URL, prefer the official source, say so when no page has the answer, stop once the primary source answers. Kept on a measured hazard effect, not a pooled gain: `web-no-answer` 6/9 with the skill against 1/9 without across three runs, the other 14 items at ceiling (owner override of the pooled ship rule; evidence in `docs/_evidence/skill-evals/211/v3/`).
155
+
156
+ - **A keyless `native` fetch backend, now the default for `web_fetch` (plan 193 Task 2).** `createNativeFetch` returns a `WebFetchAdapter` that reads a public page through the kernel's DNS-pinned request without a provider key: `pinnedFetch`'s per-candidate SSRF check and address pinning run on every hop, redirects are followed by hand (default 5, hard 10) with each `Location` resolved against the current URL and gated again, and a loop or excess hop fails with `ERR_PRISM_WEB_REDIRECT`. `text/html` and `application/xhtml+xml` are reduced to the article body with Readability on a `linkedom` document (falling back to the whole body) and converted to Markdown with `turndown` — ATX headings, fenced code, absolute links, no scripts; `text/*`, `application/json` (pretty-printed), and XML pass through, and anything else fails with `ERR_PRISM_WEB_CONTENT_TYPE`. The charset comes from the header or `<meta charset>`, invalid bytes are replaced. Requests send a fixed `user-agent` (`Prism-Web-Fetch/<version> …`, overridable by `fetch.userAgent`) and no cookies; the citation URL is the final URL and `WebDocument.redirects` lists the hops. `linkedom@0.18.14`, `@mozilla/readability@0.6.0`, and `turndown@7.2.4` are exact-pinned runtime dependencies loaded on the first HTML conversion only.
157
+
158
+ - **Package config reaches every backend (plan 192 Task 8).** `toolsWeb({ backend: "obscura", obscura: { limits } })` now passes `ObscuraWebLimits` to `createObscuraWebTools` instead of silently dropping them — the config schema declares `obscura.limits`, and an out-of-range value fails setup with `invalid limits config` naming the key. The Obscura backend also provides the `web.fetch` service, built on the exported `createObscuraPageFetcher` (the same URL-validated, byte-bounded `markdown` dump the `web_fetch` tool uses), so wiki ingest and RAG URL loaders work with Obscura. A new `extract.schema` config field registers `web_extract` over that schema: it takes the main key for `backend: "firecrawl"` and `fetch.apiKey` otherwise, compiles the schema at setup with the kernel's `validateJsonSchema` (so an unsupported subset or a remote `$ref` fails setup naming `extract.schema`), and a missing Firecrawl key writes a `warn` note and skips only `web_extract`. `ToolsWebObscuraConfig.limits` and `ToolsWebExtractConfig` are new public config types.
159
+
160
+ - **`web_search` takes structured search controls, per backend (plan 192 Task 6).** `freshness` (`day`/`week`/`month`/`year`), `include_domains` and `exclude_domains` (each up to 10 bare hostnames), `country` (ISO 3166-1 alpha-2), `language` (ISO 639-1) and `offset` are typed on `WebSearchAdapter.search` (`WebSearchCall`) instead of leaving models to write query operators. Brave maps them to `freshness`/`country`/`search_lang`/`offset` (0-based, capped at 9 — a larger value fails with `ERR_PRISM_WEB_INPUT` naming `offset`) and appends `site:`/`-site:` operators for domains; Exa maps them to `startPublishedDate`/`includeDomains`/`excludeDomains`/`userLocation` and refuses `offset` and `language` by name; Obscura's DuckDuckGo profile maps them to `df` and a `kl` region-language pair plus `site:` operators, and refuses `offset` because continuing the list needs a `vqd` token from a second request. Each adapter declares `supports: ReadonlySet<SearchControl>` — one table read by the tool builder and by the adapter's own validation — so the schema and description list only what the configured backend honors, and an unsupported or malformed control fails with `ERR_PRISM_WEB_INPUT` naming the argument. Domain values are validated as bare hostnames (no scheme, path, wildcard, or `site:` syntax) and the operators they generate count toward the provider's query length and word limits. `ObscuraSearchProfile.searchUrl` gained an optional `controls` argument.
161
+
162
+ - **SearXNG, Tavily, and Kagi search backends (plan 193 Task 8).** `backend` now also accepts `"searxng"`, `"tavily"`, and `"kagi"`, and `WebProvider`/`WebSearchAdapter.provider` widened to match.
163
+ - **SearXNG** (`searxng: { origin, allowLoopback? }`) is a self-hosted meta-search instance: no key, `GET /search?format=json`, `pageno` from `offset`, `time_range` with `day`/`month`/`year` only (there is no `week`), `language`, and domains as `site:` operators; no `country`. An optional key is sent as `Authorization: Bearer <key>` for an instance behind an authenticating proxy.
164
+ - **Tavily** (`apiKey` ref) is `POST https://api.tavily.com/search` with `time_range`, `include_domains`/`exclude_domains`, `language`, and `country` converted from Prism's two-letter code to the English name Tavily expects (sent with `topic: general`, the only topic it honors `country` on); `max_results` 0–20 and no paging. Retry mode `safe-post`.
165
+ - **Kagi** (`apiKey` ref) is API **v1** `POST https://kagi.com/api/v1/search` with `Authorization: Bot <token>`, `workflow: "search"`, `page` 1–10 from `offset`, `lens.sitesIncluded`/`sitesExcluded`, `filters.region` for `country`, `lens.timeRelative` for `day`/`week`/`month` and `filters.after` for `year`; no `language`. The deprecated v0 GET is deliberately not implemented. Retry mode `safe-post`.
166
+ - Shared plumbing: `createWebTransport` gained one loopback exemption (`http://` accepted only for a loopback hostname when an adapter passes `allowLoopback`) plus explicit auth modes (`bearer`, `bot`, `none`) so a keyless backend sends no authorization header; `normalizeSearchResults` now uses a per-provider field map instead of Brave/Exa ternaries; and search controls accept a per-backend value map, so a narrowed value (SearXNG `week`) fails with `ERR_PRISM_WEB_INPUT` and the tool schema only offers values the backend honors. The reference host accepts the new backend names.
167
+ - **Opt-in question-focused `web_fetch` (plan 193 Task 7).** New config `fetch.prompt: { model, maxInputChars?, maxOutputTokens? }` (defaults 100 000 characters and 1 024 tokens; the model's own ceiling is never raised) adds an optional `prompt` argument to `web_fetch` on every backend. With it, the page's Markdown is wrapped in the kernel's untrusted envelope, up to `maxInputChars` of it is sent to the configured model in one tool-free request with a fixed extraction system prompt (answer only from the page, quote verbatim, say "not found on the page", ignore page instructions), and the answer comes back as untrusted text with `value { url, title, answer, inputChars, sourceTruncated, usage }`. One model call per `prompt` use and none by default. The package now declares the `models` capability always and calls `prism.models.client` only when configured; because that call has no session/run identity, its usage is outside the run's budget and is reported on the value instead. A model ref that cannot be resolved writes a `warn` note and leaves `web_fetch` without the argument rather than failing the package.
168
+ - **Opt-in per-session page cache (plan 193 Task 6).** New config `cache: { ttlSeconds, maxEntries?, maxBytes? }` (1–86 400 s; defaults 64 entries and 16 MiB of Markdown per agent process). With it, a `web_fetch` or `web.fetch` call for the same canonical URL in the same session within the TTL makes no request, returns `cached: true` with `cachedAgeMs`, and its text says `cached <age> ago`; `web_fetch` gains a `refresh: true` argument that bypasses the entry. One `createFetchCache` (`src/cache.ts`, exported) is shared by every tool and by the service, kept in `prism.state.agent` (process memory, reset at package dispose) and dropped per session by a `session.closed` hook; nothing is written to `prism.state.session` or the store. Entries are LRU-evicted by count and bytes, and a page larger than the whole byte budget is fetched again rather than evicting everything else. Without `cache` config the plan 192 paging memo (10 minutes, 8 pages, no annotations) runs through the same implementation, so there is one cache.
169
+ - **Host domain policy for search and every fetch path (plan 193 Task 5).** New config `domains: { allow?: string[], block?: string[] }` (bare hostnames, a leading `*.` matches subdomains but not the apex, at most 256 entries each, `block` wins, an `allow` list permits only its matches, a malformed entry fails setup with `invalid domains config`). Results a search returns from a non-permitted host are dropped before rendering and counted in `value.filtered`, and the `web_search` description says results are limited when `allow` is set without naming hosts. `web_fetch`, `web_extract`, `obscura_fetch`, `obscura_scrape`, and the `web.fetch` service refuse a non-permitted URL with `ERR_PRISM_WEB_DOMAIN` naming the host; the native backend applies the same check to every redirect hop. One `createDomainPolicy` (`src/domains.ts`, exported) is shared by every tool and adapter; matching normalizes case, a trailing dot, and IDN/punycode, and costs one set lookup plus a suffix walk with no network.
170
+
171
+ ### Security
172
+
173
+ - **Obscura fetches are gated on a binary that guards its own redirects (plan 193 Task 4).** Setup probes `obscura --version` once (one bounded child process, 5 s wall clock) and parses the leading semver. Versions below **0.1.8** — the first release whose HTTP client re-validates each `Location` and resolves through `SsrfGuardResolver` — plus source builds (`0.2.5-dev+<sha>`), unparseable output, and probe failures all fail closed: the package registers `web_search` only, provides `web_fetch` and the `web.fetch` service from the keyless native backend (which re-checks every hop), and writes one `warn` status note naming the probed version. A blocked or hanging `--version` is a fallback, not a package failure. No new config field, no new dependency, and no `@arnilo/prism-tools/browser` attestation type. Tests also pin that the runner never passes `--allow-private-network` and never inherits `OBSCURA_ALLOW_PRIVATE_NETWORK`/`OBSCURA_PROXY` from the host env.
174
+
175
+ ### Changed
176
+
177
+ - **`toolsWeb({ backend: "brave", apiKey })` now registers `web_fetch`.** `fetch.backend` defaults to `"native"` (keyless) instead of `"firecrawl"`, so a search-only key no longer leaves the agent unable to read its results; `fetch: { backend: "firecrawl", apiKey }` keeps the previous behavior. `backend` also accepts `"native"` for a fetch-only package with no key at all. `WebProvider` gains `"native"`, `WebFetchAdapter.provider` is `"firecrawl" | "native"`, and `WebDocument` gains `redirects`.
178
+
179
+ - **Search results and tool descriptions are written for the model (plan 192 Task 5).** `web_search` (Brave, Exa, and Obscura) now returns text content as a numbered list — `1. <title>`, the URL, then `<published> — <snippet>` clipped to 300 characters, plus at most two provider highlights of 200 characters each — via a shared `src/render.ts`; `value` keeps its structure and `WebSearchResult` no longer carries per-result `metadata` (provider/rate facts stay once on the response). Titles, snippets, and highlights are collapsed to one line so untrusted text cannot fake a numbered entry, and nothing in a snippet becomes a link. `obscura_scrape` renders one numbered entry per URL, and `obscura_fetch` returns one 32 000-character slice of its dump as text while `value` keeps the full output. Descriptions now say what each tool is for, when to use `web_search` versus `web_fetch`, the `count` default and maximum (the schema's `count.maximum` comes from the resolved `maxResults`), the fetch slice defaults, and that content is untrusted — with no backend vendor names. Recorded: a ten-result search renders 3.1 kB of text against 6.3 kB of the previous payload.
180
+
181
+ - **`web_fetch` pages a document instead of returning all of it (plan 192 Task 4).** `web_fetch` (Firecrawl and Obscura) takes `max_chars` (integer, default 32 000, maximum 100 000) and `offset`, and returns one slice: its text content is `Showing characters <offset>–<end> of <total>.` plus, while more remains, `Call web_fetch again with offset=<nextOffset> for more.`, then the slice; its `value` is the citation fields plus `{ markdown, offset, totalChars, truncated, nextOffset?, sourceTruncated }`. Slices end on a paragraph break, else a line break, when one falls in the last tenth of the window, and never split a surrogate pair. A page is memoized per tool instance by `(sessionId, url)` — at most 8 pages, at most 10 minutes — so paging makes no new request and one session never reads another's page. `WebDocument` gains `sourceTruncated`, `WebFetchAdapter.fetch` takes `maxBytes`, and the `web.fetch` service honors it (clamped to `maxMarkdownBytes`); the RAG web loader passes its `maxBytes` through.
182
+
183
+ ### Fixed
184
+
185
+ - **Obscura search returns real result URLs and fails loudly when empty (plan 193 Task 3).** The default profile now requests `https://html.duckduckgo.com/html/?q=…` directly (`duckduckgo.com/html/` only 302-redirects) and its constant extraction expression decodes DuckDuckGo's `/l/?uddg=…` redirect wrappers back to the target URL, returns the raw `href` for non-wrapper links, and skips `.result--ad`/`.result--sponsored` rows. A blocked page or a result list that ends up empty — including after host-side `citation()` drops unsafe or duplicate rows — fails `web_search` with `ERR_OBSCURA_SEARCH_EMPTY` ("search returned no results; the search page may have blocked automated access") instead of returning an empty list. A `searchProfile` that returns a bare row array is still accepted. Obscura `web_fetch` documents that its citation is the requested URL because `obscura fetch --dump` cannot report the final URL.
186
+
187
+ - **Parallel web calls wait for a slot instead of failing (plan 192 Task 7).** The transport refused any call past `maxConcurrency`; it now queues up to a new `maxQueued` limit (default 64, hard 256, in `WebLimits`, `DEFAULT_WEB_LIMITS`, `HARD_WEB_LIMITS` and the package config schema) and only fails beyond it with `ERR_PRISM_WEB_LIMIT` `Web operation queue is full`. A waiter is still bounded by `timeoutMs` and the caller's signal, and an aborted waiter leaves the queue without consuming a slot or issuing a request.
188
+ - **Retries follow what a provider bills (plan 192 Task 7).** `transport.json` takes a per-request retry mode: `idempotent` (GETs; connection failures and 429/5xx), `safe-post` (Exa `/search` and Firecrawl `/v2/scrape`; connection failures and 429 only, since a 5xx may mean the work already happened) and `never` (Firecrawl `/v2/extract` job creation, so a paid job is never submitted twice). Extract polling GETs stay `idempotent`.
189
+ - **`Retry-After` given as an HTTP date is honored (plan 192 Task 7).** Only delay-seconds were parsed before; an HTTP-date value now becomes a delay from now, a date in the past means 0, and the result still caps at `maxRateLimitDelayMs`.
190
+
191
+ - **An over-limit document is truncated, not an error (plan 192 Task 4).** Firecrawl Markdown over `maxMarkdownBytes` and Obscura output over `maxOutputBytes` keep their first limit and set `sourceTruncated: true`, so the caller can read and page the head; only a response over the transport's `maxResponseBytes` read cap still fails. New helpers in `src/slice.ts` (`sliceMarkdown`, `fetchSliceText`, `fetchSliceValue`, `truncateUtf8Bytes`, `createFetchMemo`, `fetchSliceArgs`, `fetchParameters`, `DEFAULT_FETCH_CHARS`, `HARD_FETCH_CHARS`) are used by every fetch backend and are not re-exported from the package entry; they appear in `scripts/compat-baseline/arnilo__prism-tools-web.txt` because the declaration emit covers the whole source tree.
192
+
193
+ ### Added
194
+
195
+ - `@arnilo/prism-tools/web` (plan 155 Task 2): the web search and fetch code moved out of
196
+ `@arnilo/prism-web-tools` (root, `./brave`, `./exa`, `./firecrawl`, `./obscura`) into a
197
+ source-exported package on the lockstep line. The default export is a `definePackage` definition
198
+ whose `setup` registers `web_search` and `web_fetch` (plus the native Obscura tools when
199
+ `backend: "obscura"`), each declaring `effects: ["network"]`. Config selects the backend:
200
+ `{ backend, apiKey?, fetch?, obscura?, limits? }`. `apiKey` and `fetch.apiKey` are ref-only
201
+ (`env:`/`keyring:`) and resolve once through `prism.credentials.resolve`; a literal fails
202
+ validation. A missing key ref or a missing Obscura binary writes a `warn` status note and registers
203
+ nothing instead of throwing.
204
+ - The `web.fetch` service, provided when a Firecrawl fetch backend is configured:
205
+ `fetch(url, { signal?, maxBytes? }) → Promise<WebDocument>`.
206
+ - The moved module surface keeps its names as named exports behind the single entry point: the
207
+ Brave/Exa/Firecrawl adapters, `createWebTools`, the limits/normalize/transport helpers, and the
208
+ full Obscura surface (`createObscuraWebTools`, `connectObscuraCdp`, `createObscuraMcpTools`,
209
+ `resolveObscuraCommand`, `validateObscuraCommand`, `spawnObscuraProcess`).
210
+ - `resolveObscuraCommand(name)`: resolves a configured Obscura command to an absolute executable
211
+ path against absolute `PATH` entries only.
212
+
213
+ - **SearXNG, Tavily, and Kagi search backends (plan 193 Task 8).** `backend` now also accepts `"searxng"`, `"tavily"`, and `"kagi"`, and `WebProvider`/`WebSearchAdapter.provider` widened to match.
214
+ - **SearXNG** (`searxng: { origin, allowLoopback? }`) is a self-hosted meta-search instance: no key, `GET /search?format=json`, `pageno` from `offset`, `time_range` with `day`/`month`/`year` only (there is no `week`), `language`, and domains as `site:` operators; no `country`. An optional key is sent as `Authorization: Bearer <key>` for an instance behind an authenticating proxy.
215
+ - **Tavily** (`apiKey` ref) is `POST https://api.tavily.com/search` with `time_range`, `include_domains`/`exclude_domains`, `language`, and `country` converted from Prism's two-letter code to the English name Tavily expects (sent with `topic: general`, the only topic it honors `country` on); `max_results` 0–20 and no paging. Retry mode `safe-post`.
216
+ - **Kagi** (`apiKey` ref) is API **v1** `POST https://kagi.com/api/v1/search` with `Authorization: Bot <token>`, `workflow: "search"`, `page` 1–10 from `offset`, `lens.sitesIncluded`/`sitesExcluded`, `filters.region` for `country`, `lens.timeRelative` for `day`/`week`/`month` and `filters.after` for `year`; no `language`. The deprecated v0 GET is deliberately not implemented. Retry mode `safe-post`.
217
+ - Shared plumbing: `createWebTransport` gained one loopback exemption (`http://` accepted only for a loopback hostname when an adapter passes `allowLoopback`) plus explicit auth modes (`bearer`, `bot`, `none`) so a keyless backend sends no authorization header; `normalizeSearchResults` now uses a per-provider field map instead of Brave/Exa ternaries; and search controls accept a per-backend value map, so a narrowed value (SearXNG `week`) fails with `ERR_PRISM_WEB_INPUT` and the tool schema only offers values the backend honors. The reference host accepts the new backend names.
218
+ - **Opt-in question-focused `web_fetch` (plan 193 Task 7).** New config `fetch.prompt: { model, maxInputChars?, maxOutputTokens? }` (defaults 100 000 characters and 1 024 tokens; the model's own ceiling is never raised) adds an optional `prompt` argument to `web_fetch` on every backend. With it, the page's Markdown is wrapped in the kernel's untrusted envelope, up to `maxInputChars` of it is sent to the configured model in one tool-free request with a fixed extraction system prompt (answer only from the page, quote verbatim, say "not found on the page", ignore page instructions), and the answer comes back as untrusted text with `value { url, title, answer, inputChars, sourceTruncated, usage }`. One model call per `prompt` use and none by default. The package now declares the `models` capability always and calls `prism.models.client` only when configured; because that call has no session/run identity, its usage is outside the run's budget and is reported on the value instead. A model ref that cannot be resolved writes a `warn` note and leaves `web_fetch` without the argument rather than failing the package.
219
+ - **Opt-in per-session page cache (plan 193 Task 6).** New config `cache: { ttlSeconds, maxEntries?, maxBytes? }` (1–86 400 s; defaults 64 entries and 16 MiB of Markdown per agent process). With it, a `web_fetch` or `web.fetch` call for the same canonical URL in the same session within the TTL makes no request, returns `cached: true` with `cachedAgeMs`, and its text says `cached <age> ago`; `web_fetch` gains a `refresh: true` argument that bypasses the entry. One `createFetchCache` (`src/cache.ts`, exported) is shared by every tool and by the service, kept in `prism.state.agent` (process memory, reset at package dispose) and dropped per session by a `session.closed` hook; nothing is written to `prism.state.session` or the store. Entries are LRU-evicted by count and bytes, and a page larger than the whole byte budget is fetched again rather than evicting everything else. Without `cache` config the plan 192 paging memo (10 minutes, 8 pages, no annotations) runs through the same implementation, so there is one cache.
220
+ - **Host domain policy for search and every fetch path (plan 193 Task 5).** New config `domains: { allow?: string[], block?: string[] }` (bare hostnames, a leading `*.` matches subdomains but not the apex, at most 256 entries each, `block` wins, an `allow` list permits only its matches, a malformed entry fails setup with `invalid domains config`). Results a search returns from a non-permitted host are dropped before rendering and counted in `value.filtered`, and the `web_search` description says results are limited when `allow` is set without naming hosts. `web_fetch`, `web_extract`, `obscura_fetch`, `obscura_scrape`, and the `web.fetch` service refuse a non-permitted URL with `ERR_PRISM_WEB_DOMAIN` naming the host; the native backend applies the same check to every redirect hop. One `createDomainPolicy` (`src/domains.ts`, exported) is shared by every tool and adapter; matching normalizes case, a trailing dot, and IDN/punycode, and costs one set lookup plus a suffix walk with no network.
221
+
222
+ ### Security
223
+
224
+ - **Obscura fetches are gated on a binary that guards its own redirects (plan 193 Task 4).** Setup probes `obscura --version` once (one bounded child process, 5 s wall clock) and parses the leading semver. Versions below **0.1.8** — the first release whose HTTP client re-validates each `Location` and resolves through `SsrfGuardResolver` — plus source builds (`0.2.5-dev+<sha>`), unparseable output, and probe failures all fail closed: the package registers `web_search` only, provides `web_fetch` and the `web.fetch` service from the keyless native backend (which re-checks every hop), and writes one `warn` status note naming the probed version. A blocked or hanging `--version` is a fallback, not a package failure. No new config field, no new dependency, and no `@arnilo/prism-tools/browser` attestation type. Tests also pin that the runner never passes `--allow-private-network` and never inherits `OBSCURA_ALLOW_PRIVATE_NETWORK`/`OBSCURA_PROXY` from the host env.
225
+
226
+ ### Changed
227
+
228
+ - **`toolsWeb({ backend: "brave", apiKey })` now registers `web_fetch`.** `fetch.backend` defaults to `"native"` (keyless) instead of `"firecrawl"`, so a search-only key no longer leaves the agent unable to read its results; `fetch: { backend: "firecrawl", apiKey }` keeps the previous behavior. `backend` also accepts `"native"` for a fetch-only package with no key at all. `WebProvider` gains `"native"`, `WebFetchAdapter.provider` is `"firecrawl" | "native"`, and `WebDocument` gains `redirects`.
229
+
230
+ - The `@arnilo/prism-extensions/mcp` import in the Obscura MCP bridge is now dynamic, so importing the package
231
+ loads neither `playwright-core` nor `@arnilo/prism-extensions/mcp`. The Obscura CDP composition uses a local
232
+ structural Playwright type instead of the browser package's types.
233
+
234
+ ## [0.1.0] - 2026-08-09
235
+
236
+ Initial published surface for the lockstep line. See the root `CHANGELOG.md`.
237
+
238
+ ## [0.0.28] - 2026-08-08
239
+
240
+ The web search and fetch tools shipped inside `@arnilo/prism-web-tools` before this package existed;
241
+ see that package's changelog for the pre-split history.
242
+
243
+
244
+ ### ./browser
245
+
246
+ # Changelog
247
+
248
+ ## [Unreleased]
249
+
250
+ ### Added
251
+
252
+ - **`browser-automation` skill (plan 215 Tasks 8 and 11).** The package registers a bundled skill from `skills/browser-automation` with `skills.addDir`, and `package.json` `files` lists `skills`. It declares `prism-requires-tools: browser_open browser_snapshot browser_act`. The body is five lines: page text is untrusted, filling a field is not submitting it, do not type credentials you were not given, snapshot after a change and report what it shows, change approach after two failures. Kept on a measured hazard effect, not a pooled gain: `browser-no-credentials` 8/9 with the skill against 1/9 without across three runs, the other 14 items at ceiling (owner override of the pooled ship rule; evidence in `docs/_evidence/skill-evals/211/v3/`).
253
+ - **`browser_act` description.** It no longer says to prefer snapshot refs. It now points at `{ role, name }` and `{ label }` targets, which need no `snapshotId`, and says a `{ ref }` target needs the latest `snapshotId` and goes stale after any action. Transcripts showed nearly every run without the skill failing on `ref actions require snapshotId`; after this change (made together with raising the evaluation's tool-round limit) runs without the skill finished 0.933 of the evaluation items, against 0.578 before both changes.
254
+
255
+ - `@arnilo/prism-tools/browser` (plan 155 Task 3): the browser automation code moved out of
256
+ `@arnilo/prism-web-tools` (`./browser`) into a source-exported package on the lockstep line. The
257
+ default export is a `definePackage` definition whose `setup` imports `playwright-core`
258
+ dynamically, launches the run-owned browser, and registers `browser_open`, `browser_snapshot`,
259
+ `browser_act`, `browser_close`, `browser_evaluate`, and `browser_observe`, each declaring
260
+ `effects`. `onDispose` closes the manager and the browser it launched. A missing peer or a failed
261
+ launch writes a `warn` status note and registers nothing instead of throwing.
262
+ - Config `{ headless?, executablePath?, channel?, allowOrigins?, containedProxy?, allowLoopback?,
263
+ allowPrivateHosts?, uploadRoots?, quarantine?, cdp? }`. `allowOrigins` is a hard origin allowlist;
264
+ `containedProxy` is the fail-closed egress attestation; `uploadRoots` confines uploads.
265
+ - The moved module surface keeps its names as named exports behind the single entry point: the six
266
+ tools, `createBrowserManager`, the limits/policy/network/upload/download/screenshot helpers, and
267
+ the structural Playwright types.
268
+
269
+ ### Changed
270
+
271
+ - The in-tool `ExecutionPolicy` option is removed from `createBrowserTools`. Approval now runs over
272
+ the declared `effects` through the host `tool.call` gate; blocked schemes, loopback/private hosts,
273
+ the origin allowlist, and upload-root containment stay as internal hard denies. The
274
+ `buildBrowserExecutionAction` helper and the kernel `ExecutionPolicy` import are gone.
275
+
276
+ ## [0.1.0] - 2026-08-09
277
+
278
+ Initial published surface for the lockstep line. See the root `CHANGELOG.md`.
279
+
280
+ ## [0.0.28] - 2026-08-08
281
+
282
+ The browser tools shipped inside `@arnilo/prism-web-tools` before this package existed; see that
283
+ package's changelog for the pre-split history.
284
+
285
+
286
+ ### ./research
287
+
288
+ # Changelog
289
+
290
+ ## [Unreleased]
291
+
292
+ ### Changed
293
+
294
+ - Plan 215 Task 7: the four skills' descriptions now open with when to use them (at most 300 bytes), and `research-report` declares `metadata.prism-requires-tools: cite`, so a request that does not advertise `cite` hides it instead of offering a procedure the model cannot follow. Checked by `scripts/package-skills-conformance.mjs`.
295
+
296
+ ### Added
297
+
298
+ - `@arnilo/prism-tools/research`: four bundled research skills, a per-session source ledger on `tool.result`, a read-only `cite` tool, and a `run.end` citation check. Unresolved markers continue once by default (`maxRepairs`), then the verdict is `failed` and the package emits `research.citations.unresolved`. `research.verify` returns `{ verdict, unresolved, ledgerSize }`.
299
+
300
+
301
+ ### ./openapi
302
+
303
+ # Changelog
304
+
305
+ All notable changes to `@arnilo/prism-tools/openapi` will be documented in this file.
306
+
307
+ ## [Unreleased]
308
+
309
+ ### Added
310
+
311
+ - Package created in plan 155 Task 4: the OpenAPI 3.1 compiler moved out of
312
+ `@arnilo/prism-coding-tools/./openapi` with the factory names unchanged (`createOpenApiTools`,
313
+ `resolveOpenApiLimits`, `DEFAULT_OPENAPI_LIMITS`, `HARD_OPENAPI_LIMITS`, `OpenApiToolError`).
314
+ - `toolsOpenapi({ specs, limits })` `definePackage` entry: reads each spec once (local JSON or an
315
+ `https:` URL through pinned fetch), compiles the allow-listed operations, and registers
316
+ `<id>_<operationId>` tools.
317
+ - Per-method `effects`: `GET`/`HEAD` declare `["network"]`; every other method declares
318
+ `["network", "external"]`.
319
+ - Ref-only `specs[].headers` resolved per request through `prism.credentials.resolve`, so resolved
320
+ values join the agent secret set and are redacted from events, notes, logs, errors, and results.
321
+ - A spec that fails to load or compile notes why and is skipped; cross-spec tool-name collisions
322
+ fail closed through the kernel's tool-conflict rule.
323
+ - New `namePrefix` option on `createOpenApiTools`, plus the `loadSpecDocument`, `firstServerUrl`,
324
+ `listOperationIds`, and `selectOperations` helpers.
325
+
326
+ ## [0.1.0] - 2026-08-09
327
+
328
+ ### Changed
329
+
330
+ - Finalized 0.1.0 package baseline.
331
+
332
+ ## [0.0.28] - 2026-08-08
333
+
334
+ ### Added
335
+
336
+ - Initial OpenAPI 3.1 compile/execute surface (as the `openapi` subpath of `@arnilo/prism-coding-tools`).
337
+
338
+
339
+ ### ./computer-use
340
+
341
+ # Changelog
342
+
343
+ ## [Unreleased]
344
+
345
+ ### Changed
346
+
347
+ - Plan 215 Task 7: the `computer-use-linux` skill description opens with when to use it, and its body no longer names the host-only `setup_accessibility` / `setup_window_targeting` tools (it says the host runs setup). Checked by `scripts/package-skills-conformance.mjs`.
348
+
349
+ ### Added
350
+
351
+ - `@arnilo/prism-tools/computer-use` (plan 155 Task 4): the `computer-use-linux` MCP bridge moved out
352
+ of `@arnilo/prism-coding-tools/./computer-use-linux` into a source-exported package on the lockstep
353
+ line, with the factory names unchanged (`createComputerUseLinuxTools`, `loadComputerUseLinuxSkill`,
354
+ `classifyComputerUseLinuxTool`, the `COMPUTER_USE_LINUX_*` lists). The bundled skill moved with it
355
+ to `skills/computer-use-linux/SKILL.md`.
356
+ - `toolsComputerUse({ command?, args?, display?, includeSetupTools?, approved?, device?, runLimits? })`
357
+ `definePackage` entry: resolves the host-owned executable on `PATH`, connects over stdio, registers
358
+ the upstream tools, registers the bundled skill, and closes the bridge in `onDispose`. A missing
359
+ binary, a missing `device.sandbox`, or a refused device admission writes a `warn` status note and
360
+ registers nothing instead of throwing.
361
+ - Every tool declares `effects: ["external"]`, so the host approval hook sees it.
362
+ - The optional `computer-use.mcp` service lets a host or test supply its own MCP bridge;
363
+ `@arnilo/prism-extensions/mcp` is now imported dynamically only during `setup`, so the package root is inert.
364
+ - New `resolveCommand` helper for absolute-path/PATH executable resolution.
365
+
366
+ ### Changed
367
+
368
+ - The kernel `ExecutionPolicy` import and the `executionPolicy` option are removed. Approval runs over
369
+ the declared `effects` through the host `tool.call` gate; mutating calls still pass in-tool device
370
+ admission (`assertDeviceAdmit`), so a deny holds under `approval` `auto`.
371
+
372
+ ## [0.1.0] - 2026-08-09
373
+
374
+ Initial published surface for the lockstep line. See the root `CHANGELOG.md`.
375
+
376
+ ## [0.0.28] - 2026-08-08
377
+
378
+ The Linux desktop bridge shipped inside `@arnilo/prism-coding-tools` before this package existed; see
379
+ that package's changelog for the pre-split history.
380
+
381
+
382
+ ### ./work
383
+
384
+ # Changelog
385
+
386
+ ## [Unreleased]
387
+
388
+ ### Changed
389
+
390
+ - Plan 215 Task 7: the vendored office skills register through `loadWorkSkills()` with `requiresTools: ["work_exec"]` instead of a bare `prism.skills.addDir`, so they are hidden on a request that does not advertise `work_exec`. The vendored `SKILL.md` bytes are untouched. `WORK_SKILL_TOOLS` (the `office_*` names, a `toolNames` declaration nothing used and that would have failed a narrowed run) is replaced by `WORK_SKILL_REQUIRES_TOOLS`, with no alias.
391
+
392
+ ### Added
393
+
394
+ - Plan 155 Task 5: `@arnilo/prism-tools/work` — the work family as one package with a `definePackage` root entry. `setup` registers the `office_*` document group (read/write effects), `work_exec` inside the host-injected `work.sandbox` service (`execute`), and the `m365_*` / `gws_*` connector tools (`network`, plus `external` for drafts/writes) when a ref-only credential resolves. The bounded document reader is provided as the `work.reader` service and the vendored Hermes productivity skills are added from `vendor/`. A missing optional parser, an unresolved connector credential, or a missing sandbox writes a `warn` status note and registers less; the worker pool closes in `onDispose`.
395
+ - Config: `root` (required, absolute), `office`, `work`, `reader` (formats, caps, optional Mistral OCR), `connectors` (M365/Google CLI adapters with ref-only credentials), `env`.
396
+ - New tests: `tools_work_package_conformance`, `tools_work_groups_by_config`, `tools_work_missing_parser_drops_formats_with_note`, `tools_work_connector_without_credential_notes`, `tools_work_secrets_never_echoed`, `office_parsers_not_loaded_unless_tools_work_listed`, `tools_work_disposes_worker_pool`.
397
+
398
+ ### Changed
399
+
400
+ - Moved `packages/prism-work/**` (source, tests, `fixtures/`, `golden/`, `docling/`, `sandbox/`, `vendor/`, `vendor-lock.json`, `THIRD_PARTY_NOTICES`) to `packages/tools/work/**`. The `@arnilo/prism-work` workspace is deleted with no alias; every importer now uses `@arnilo/prism-tools/work` (and its subpaths).
401
+ - `createOfficeTools` now declares per-tool `effects` (`read` for parse/import/diff/preview, `write` for generate/patch); `createWorkTools` declares `network` for reads and `network` + `external` for drafts/writes; `work_exec` declares `execute`.
402
+ - The package publishes TypeScript source (`exports` targets `./src/...`) instead of `dist/`, so its `build` script is gone and CI builds the workspace with `build:core` only.
403
+
404
+ ## [0.4.0] - 2026-09-01
405
+
406
+ - Plan 054: family ships as part of the 11-package 0.4 lockstep; peer `@arnilo/prism@^0.4.0`.
407
+
408
+ ## [0.3.0] - 2026-09-01
409
+
410
+ Initial family cut: `/documents`, `/sheets`, `/diagrams` subpaths (plans 051–053 landed as one package per plan 054 Task 8).
411
+
412
+ ## [0.1.0] - 2026-08-09
413
+
414
+ Placeholder for the shared 0.1.0 changelog contract (office family first shipped at 0.3.0).
415
+
416
+ ## [0.0.28] - 2026-08-08
417
+
418
+ - Released with exact 0.0.28 graph.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Prism contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,45 @@
1
- # Temporary Holding Version
1
+ # @arnilo/prism-tools
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Install once, then import and list only the capabilities your host needs. The bundle has no root export; importing one subpath does not activate another.
4
+
5
+ ```sh
6
+ bun add @arnilo/prism @arnilo/prism-tools @arnilo/prism-extensions
7
+ ```
8
+
9
+ | Subpath | Capability | Manifest install |
10
+ | --- | --- | --- |
11
+ | [coding](coding/README.md) | Files, search, shell and sandbox adapters | `prism add @arnilo/prism-tools/coding` |
12
+ | [web](web/README.md) | Search, fetch, extraction and web research skill | `prism add @arnilo/prism-tools/web` |
13
+ | [browser](browser/README.md) | Playwright browser tools and automation skill | `prism add @arnilo/prism-tools/browser` |
14
+ | [research](research/README.md) | Source ledger, citations and research skills | `prism add @arnilo/prism-tools/research` |
15
+ | [openapi](openapi/README.md) | Bounded tools from allow-listed operations | `prism add @arnilo/prism-tools/openapi` |
16
+ | [computer-use](computer-use/README.md) | Host-owned Linux desktop MCP tools | `prism add @arnilo/prism-tools/computer-use` |
17
+ | [work](work/README.md) | Office, connectors, reader and productivity skills | `prism add @arnilo/prism-tools/work` |
18
+
19
+ Work libraries are exported at `./work/{connectors,connectors/drafts,connectors/google-workspace,connectors/microsoft365,diagrams,document-extraction,document-reader,documents,sandbox,sheets,skills,tools}`.
20
+
21
+ ## Dependencies and footprint
22
+
23
+ Sizes are the Task 1 installed-package measurements; transitive dependencies and filesystem blocks can change the total. The initial bundle measures 589,902 packed bytes, 2,372,441 unpacked bytes and 331 files. A clean `--ignore-scripts` install without optional peers occupies 21,416,317 bytes in `node_modules`, including the kernel, extensions and required transitive dependencies. The release gate enforces the bundle size budget.
24
+
25
+ | Dependency | Size on disk | Decision |
26
+ | --- | --- | --- |
27
+ | `diff` | 968 K | Required by coding |
28
+ | `@mozilla/readability` | 188 K | Required by native web fetch |
29
+ | `linkedom` | 2.7 M | Required by native web fetch |
30
+ | `turndown` | 208 K | Required by native web fetch |
31
+ | `@office-open/{docx,pptx,xlsx,xml}` | 5.5 M total | Optional peers for work Office libraries |
32
+ | `ajv` | 2.4 M | Optional peer for document validation |
33
+ | `fast-xml-parser` | 1.4 M | Optional peer for work diagrams |
34
+
35
+ `@arnilo/prism-extensions` is required because coding imports workflows. Web and computer-use retain lazy MCP loading; they do not load MCP simply by being imported. A peer's optional flag applies to the whole npm bundle, so extensions cannot also be marked optional for these two subpaths.
36
+
37
+ To enable the work Office libraries and OOXML reader:
38
+
39
+ ```sh
40
+ bun add @office-open/docx@0.14.5 @office-open/pptx@0.14.5 @office-open/xlsx@0.14.5 @office-open/xml@0.14.5 ajv fast-xml-parser@5.11.1
41
+ ```
42
+
43
+ When any of those peers is absent, the work package reports the missing names and registers no Office tools. Connectors, productivity skills and supported PDF/DOCX readers remain available. Direct document, sheets and diagram library imports need their corresponding peers; validation is never bypassed.
44
+
45
+ `playwright-core@1.63.0`, `e2b@2.49.1`, `@firecrawl/anydoc@0.2.4`, `mammoth` and `pdf-parse` remain optional peers. See the subpath pages and [peer dependencies](../../docs/peer-dependencies.md) for activation and host requirements. Vendored skills ship in the tarball; installation needs no postinstall script.