@arnilo/prism-tools 0.0.0-stage → 0.13.0

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 +410 -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 +28 -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 +545 -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
@@ -0,0 +1,157 @@
1
+ /**
2
+ * `@arnilo/prism-tools/computer-use` — the `computer-use-linux` MCP binary as Prism tools.
3
+ *
4
+ * `setup` resolves the host-owned executable on `PATH` and connects over stdio. A missing binary or
5
+ * a refused device admission notes why and registers nothing. Every tool declares
6
+ * `effects: ["external"]`, so the host approval hook sees it; mutating calls additionally pass
7
+ * in-tool device admission (`assertDeviceAdmit`), which denies unless the host explicitly enabled
8
+ * the device, supplied a sandbox, and approved. The bundled desktop procedure registers as the
9
+ * `computer-use-linux` skill.
10
+ *
11
+ * ```ts
12
+ * import toolsComputerUse from "@arnilo/prism-tools/computer-use";
13
+ * import { createAgent } from "@arnilo/prism";
14
+ *
15
+ * const agent = await createAgent({
16
+ * model,
17
+ * packages: [
18
+ * toolsComputerUse({
19
+ * display: ":0",
20
+ * device: { enabled: true, requireApproval: true, sandbox: "linux-desktop" },
21
+ * runLimits: { maxTurns: 32, maxToolCalls: 200 },
22
+ * }),
23
+ * ],
24
+ * });
25
+ * ```
26
+ */
27
+
28
+ import { definePackage, type PackageInstance } from "@arnilo/prism";
29
+ import manifest from "../../package.json" with { type: "json" };
30
+
31
+ import { resolveCommand } from "./command.js";
32
+ import { type ToolsComputerUseConfig, toolsComputerUseConfigSchema } from "./config.js";
33
+ import {
34
+ type ComputerUseLinuxConnect,
35
+ type ComputerUseLinuxTools,
36
+ type ComputerUseLinuxToolsOptions,
37
+ createComputerUseLinuxTools,
38
+ } from "./create.js";
39
+ import { buildChildEnv, DEFAULT_CHILD_ENV_INHERIT } from "./env.js";
40
+ import { loadComputerUseLinuxSkill } from "./skill.js";
41
+
42
+ export {
43
+ COMPUTER_USE_LINUX_MUTATING_TOOLS,
44
+ COMPUTER_USE_LINUX_READ_TOOLS,
45
+ COMPUTER_USE_LINUX_SETUP_TOOLS,
46
+ COMPUTER_USE_LINUX_TOOL_NAMES,
47
+ classifyComputerUseLinuxTool,
48
+ isComputerUseLinuxTool,
49
+ } from "./classify.js";
50
+ export { resolveCommand } from "./command.js";
51
+ export type { ToolsComputerUseConfig, ToolsComputerUseDeviceConfig } from "./config.js";
52
+ export { toolsComputerUseConfigSchema } from "./config.js";
53
+ export {
54
+ type ComputerUseLinuxConnect,
55
+ type ComputerUseLinuxTools,
56
+ type ComputerUseLinuxToolsOptions,
57
+ createComputerUseLinuxTools,
58
+ } from "./create.js";
59
+ export type {
60
+ DeviceAdapter,
61
+ DeviceAdmitRequest,
62
+ DeviceChunkResult,
63
+ DeviceConformanceResult,
64
+ DeviceKind,
65
+ DevicePolicyErrorCode,
66
+ DevicePolicyOptions,
67
+ DeviceStreamLimits,
68
+ ResolvedDevicePolicy,
69
+ } from "./devices.js";
70
+ export {
71
+ acceptDeviceChunk,
72
+ assertDeviceAdmit,
73
+ DEFAULT_DEVICE_MAX_CHUNK_BYTES,
74
+ DEFAULT_DEVICE_MAX_CONCURRENT_SESSIONS,
75
+ DevicePolicyError,
76
+ HARD_DEVICE_MAX_CHUNK_BYTES,
77
+ HARD_DEVICE_MAX_CONCURRENT_SESSIONS,
78
+ redactDeviceTelemetry,
79
+ resolveDevicePolicy,
80
+ runDevicePolicyConformance,
81
+ } from "./devices.js";
82
+ export { buildChildEnv, DEFAULT_CHILD_ENV_INHERIT } from "./env.js";
83
+ export { COMPUTER_USE_LINUX_SKILL_NAME, loadComputerUseLinuxSkill, MAX_SKILL_FILE_BYTES } from "./skill.js";
84
+
85
+ function errorMessage(error: unknown): string {
86
+ return error instanceof Error ? error.message : String(error);
87
+ }
88
+
89
+ const definition = definePackage<ToolsComputerUseConfig>({
90
+ name: "@arnilo/prism-tools/computer-use",
91
+ version: manifest.version,
92
+ description:
93
+ "Linux desktop observation and targeting over the host-owned computer-use-linux MCP binary, with in-tool device admission and declared external effects.",
94
+ requires: { api: 1 },
95
+ config: toolsComputerUseConfigSchema,
96
+ setup: async (prism) => {
97
+ const device = prism.config.device;
98
+ if (device === undefined || typeof device.sandbox !== "string" || device.sandbox.length === 0) {
99
+ prism.status.note("warn", "computer-use: config needs device.sandbox (a host-owned sandbox id)");
100
+ return;
101
+ }
102
+ const runLimits = prism.config.runLimits;
103
+ if (runLimits === undefined) {
104
+ prism.status.note("warn", "computer-use: config needs runLimits (shared run accounting)");
105
+ return;
106
+ }
107
+ const command = prism.config.command ?? "computer-use-linux";
108
+ const resolved = resolveCommand(command);
109
+ if (resolved === undefined) {
110
+ prism.status.note("warn", `computer-use: ${command} not found on PATH; install the host binary`);
111
+ return;
112
+ }
113
+
114
+ // Host/test override: a package may provide its own MCP bridge instead of the bundled one.
115
+ const connect = prism.services.consume<ComputerUseLinuxConnect>("computer-use.mcp");
116
+ const options: ComputerUseLinuxToolsOptions = {
117
+ command: resolved,
118
+ device: {
119
+ kind: "desktop-control",
120
+ enabled: device.enabled === true,
121
+ requireApproval: device.requireApproval !== false,
122
+ sandbox: device.sandbox,
123
+ ...(device.network !== undefined ? { network: device.network } : {}),
124
+ ...(device.limits !== undefined ? { limits: device.limits } : {}),
125
+ },
126
+ runLimits,
127
+ approved: prism.config.approved === true,
128
+ includeSetupTools: prism.config.includeSetupTools === true,
129
+ ...(prism.config.args !== undefined ? { args: prism.config.args } : {}),
130
+ ...(prism.config.display !== undefined
131
+ ? { env: buildChildEnv({ inherit: DEFAULT_CHILD_ENV_INHERIT, set: { DISPLAY: prism.config.display } }) }
132
+ : {}),
133
+ ...(connect !== undefined ? { connect } : {}),
134
+ };
135
+
136
+ let desktop: ComputerUseLinuxTools;
137
+ try {
138
+ desktop = await createComputerUseLinuxTools(options);
139
+ } catch (error) {
140
+ prism.status.note("warn", `computer-use: ${errorMessage(error)}`);
141
+ return;
142
+ }
143
+ for (const tool of desktop.tools) prism.tools.register(tool);
144
+ prism.skills.register(loadComputerUseLinuxSkill());
145
+ prism.onDispose(() => desktop.close());
146
+ },
147
+ });
148
+
149
+ /** The package definition. Pass config through `packageConfig` or use {@link toolsComputerUse}. */
150
+ export default definition;
151
+
152
+ /** The package with config baked in, for `packages: [toolsComputerUse({ device, runLimits })]`. */
153
+ export function toolsComputerUse(config: ToolsComputerUseConfig): PackageInstance {
154
+ return definition(config);
155
+ }
156
+
157
+ export const packageName = "@arnilo/prism-tools/computer-use";
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { parseSkillFile, type Skill } from "@arnilo/prism";
4
+
5
+ export const COMPUTER_USE_LINUX_SKILL_NAME = "computer-use-linux";
6
+ export const MAX_SKILL_FILE_BYTES = 64 * 1024;
7
+ const SKILL_FILE_URL = new URL("../skills/computer-use-linux/SKILL.md", import.meta.url);
8
+
9
+ /** Load Prism's bundled, host-independent desktop-control procedure. */
10
+ export function loadComputerUseLinuxSkill(): Skill {
11
+ const path = fileURLToPath(SKILL_FILE_URL);
12
+ const data = readFileSync(path);
13
+ if (data.byteLength > MAX_SKILL_FILE_BYTES) {
14
+ throw new Error(`computer-use-linux skill exceeds ${MAX_SKILL_FILE_BYTES} byte cap`);
15
+ }
16
+ const skill = parseSkillFile(data.toString("utf8"), path);
17
+ if (skill.name !== COMPUTER_USE_LINUX_SKILL_NAME) {
18
+ throw new Error(`Expected skill name ${COMPUTER_USE_LINUX_SKILL_NAME}, got ${skill.name}`);
19
+ }
20
+ return skill;
21
+ }
@@ -0,0 +1,97 @@
1
+ # @arnilo/prism-tools/openapi
2
+
3
+ The first-party OpenAPI 3.1 tools as a Prism package. The default export is a `definePackage`
4
+ definition whose `setup` reads each configured spec once, compiles the allow-listed operations into
5
+ bounded tools, and registers them as `<id>_<operationId>`.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ prism add @arnilo/prism-tools/openapi
11
+ ```
12
+
13
+ ```jsonc
14
+ // manifest entry
15
+ {
16
+ "name": "@arnilo/prism-tools/openapi",
17
+ "version": "^0.13.0",
18
+ "integrity": "sha512-…",
19
+ "config": {
20
+ "specs": [
21
+ {
22
+ "id": "petstore",
23
+ "source": "./petstore.json",
24
+ "headers": { "Authorization": "env:PETSTORE_TOKEN" }
25
+ }
26
+ ]
27
+ }
28
+ }
29
+ ```
30
+
31
+ ## Configuration
32
+
33
+ | Field | Type | Default | Description |
34
+ | --- | --- | --- | --- |
35
+ | `specs[].id` | `string` | none (required per spec) | Tool-name prefix: every operation compiles as `<id>_<operationId>`. |
36
+ | `specs[].source` | `string` | none (required per spec) | Path to a local **JSON** document, or an `https:` URL fetched once at setup through the kernel's pinned fetch. |
37
+ | `specs[].baseUrl` | `string` | document `servers[0].url` | Pinned origin for the compiled operations. Required when the document declares no absolute server URL. |
38
+ | `specs[].headers` | `Record<string, env:NAME \| keyring:NAME>` | none | Ref-only request headers. A literal fails config validation. Resolved per request through `prism.credentials.resolve`. |
39
+ | `specs[].include` | `string[]` | every operation | Allow-listed operationIds. |
40
+ | `specs[].exclude` | `string[]` | none | operationIds to skip when `include` is absent. |
41
+ | `limits` | `object` | tool defaults | Byte, schema-depth, pagination, retry, and body bounds shared by every spec. |
42
+
43
+ An unknown key fails `createAgent` with the kernel's config error naming the key. A spec that fails
44
+ to load or compile writes a `warn` status note and is skipped; the other specs still register. A
45
+ tool-name collision between specs fails closed through the kernel's tool-conflict rule — there is no
46
+ silent renaming.
47
+
48
+ ## What it changes
49
+
50
+ Registers tools with a declared `effects` annotation:
51
+
52
+ | method | effects | notes |
53
+ | --- | --- | --- |
54
+ | `GET`, `HEAD` | `["network"]` | read-only network reach |
55
+ | anything else | `["network", "external"]` | mutates an external system; the effect declaration also carries `{ kind: "external_mutation", idempotency: "required" }` |
56
+
57
+ Compile-time guarantees (from the moved compiler):
58
+
59
+ - **Allow-list only**: operations not listed never compile; unknown ids throw `ERR_PRISM_OPENAPI_OPERATION_UNKNOWN`.
60
+ - **Origin pinned**: every document/path/operation `servers` entry must resolve to the pinned origin, else `ERR_PRISM_OPENAPI_SERVER_DRIFT`. https only; http allowed only for loopback hosts.
61
+ - **Bounded schemas**: internal `$ref`s resolve into self-contained argument schemas with `additionalProperties: false`; cycles, depth, ref count, external refs, cookie parameters, and duplicate argument names fail closed.
62
+ - **Bounded I/O**: body, response, retry, and pagination caps apply to every call. URL specs are fetched once at setup, never per turn.
63
+
64
+ ## Capabilities
65
+
66
+ `credentials` — header refs resolve per request through the credentials slot. A resolved secret joins
67
+ the agent's secret set, so it is redacted from events, notes, logs, errors, and tool results.
68
+
69
+ ## Cost, security, and failure behavior
70
+
71
+ - Header values never enter tool schemas, results, prompts, telemetry, URLs, or errors. Only refs are accepted in config; a literal fails validation.
72
+ - URL specs go through the kernel's DNS-pinned, redirect-free, byte-bounded fetch; the API calls themselves use the pinned `server` origin.
73
+ - Registration performs one read per spec and no API call.
74
+
75
+ ## Example
76
+
77
+ ```ts
78
+ import toolsOpenapi from "@arnilo/prism-tools/openapi";
79
+ import { createAgent } from "@arnilo/prism";
80
+
81
+ const agent = await createAgent({
82
+ model,
83
+ packages: [
84
+ toolsOpenapi({
85
+ specs: [{ id: "petstore", source: "./petstore.json", headers: { Authorization: "env:PETSTORE_TOKEN" } }],
86
+ }),
87
+ ],
88
+ });
89
+ ```
90
+
91
+ The 0.12 factory names stay exported: `createOpenApiTools`, `resolveOpenApiLimits`,
92
+ `DEFAULT_OPENAPI_LIMITS`, `HARD_OPENAPI_LIMITS`, `OpenApiToolError`, and the spec helpers
93
+ (`loadSpecDocument`, `firstServerUrl`, `listOperationIds`, `selectOperations`).
94
+
95
+ ## License
96
+
97
+ MIT
@@ -0,0 +1,386 @@
1
+ import type { JsonObject, ToolEffectDeclaration } from "@arnilo/prism";
2
+ import { OpenApiToolError } from "./errors.js";
3
+ import type { ResolvedOpenApiLimits } from "./limits.js";
4
+ import type { OpenApiPagination } from "./types.js";
5
+
6
+ export interface CompiledParameter {
7
+ readonly name: string;
8
+ readonly required: boolean;
9
+ readonly schema: JsonObject;
10
+ }
11
+
12
+ export interface CompiledOperation {
13
+ readonly toolName: string;
14
+ readonly operationId: string;
15
+ readonly method: string;
16
+ readonly path: string;
17
+ readonly pathParams: readonly string[];
18
+ readonly queryParams: readonly CompiledParameter[];
19
+ readonly headerParams: readonly CompiledParameter[];
20
+ readonly bodyParam?: { readonly name: string; readonly schema: JsonObject; readonly required: boolean };
21
+ /** Full argument schema: path + query + header + body, no $refs, bounded. */
22
+ readonly parameters: JsonObject;
23
+ readonly description: string;
24
+ readonly effect: ToolEffectDeclaration;
25
+ readonly paginatable: boolean;
26
+ }
27
+
28
+ const HTTP_METHODS = ["get", "put", "post", "delete", "options", "head", "patch", "trace"] as const;
29
+ const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS", "TRACE"]);
30
+ const MUTATING_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
31
+ const MAX_DESCRIPTION_BYTES = 16 * 1024;
32
+
33
+ /** Single-schema keywords (object values). */
34
+ const SCHEMA_OBJECT_KEYWORDS = new Set([
35
+ "items",
36
+ "additionalProperties",
37
+ "not",
38
+ "if",
39
+ "then",
40
+ "else",
41
+ "contains",
42
+ "propertyNames",
43
+ "unevaluatedProperties",
44
+ "unevaluatedItems",
45
+ ]);
46
+ /** Schema-map keywords (object of schemas). */
47
+ const SCHEMA_MAP_KEYWORDS = new Set(["properties", "patternProperties", "dependentSchemas"]);
48
+ /** Schema-array keywords. */
49
+ const SCHEMA_ARRAY_KEYWORDS = new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
50
+
51
+ function isLoopbackHostname(hostname: string): boolean {
52
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
53
+ }
54
+
55
+ function validateServerUrl(value: string, label: string): URL {
56
+ let url: URL;
57
+ try {
58
+ url = new URL(value);
59
+ } catch {
60
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", `${label} is not an absolute URL`);
61
+ }
62
+ if (url.username || url.password) throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", `${label} must not embed credentials`);
63
+ if (url.hash) throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", `${label} must not contain a fragment`);
64
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopbackHostname(url.hostname))) {
65
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", `${label} must be https (http allowed only for loopback hosts)`);
66
+ }
67
+ return url;
68
+ }
69
+
70
+ /** Every declared server URL must resolve to the pinned origin (relative URLs resolve against the pinned base). */
71
+ function assertServerOrigin(pinned: URL, serverUrl: unknown, label: string): void {
72
+ if (typeof serverUrl !== "string" || !serverUrl) {
73
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SERVER_DRIFT", `${label} has an invalid server url`);
74
+ }
75
+ const resolved = new URL(serverUrl, pinned);
76
+ if (resolved.origin !== pinned.origin) {
77
+ throw new OpenApiToolError(
78
+ "ERR_PRISM_OPENAPI_SERVER_DRIFT",
79
+ `${label} origin ${resolved.origin} differs from pinned server origin ${pinned.origin}`,
80
+ );
81
+ }
82
+ }
83
+
84
+ function assertServers(servers: unknown, pinned: URL, label: string): void {
85
+ if (servers === undefined) return;
86
+ if (!Array.isArray(servers) || servers.length === 0) {
87
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SERVER_DRIFT", `${label} must be a non-empty array`);
88
+ }
89
+ for (const entry of servers) {
90
+ const url = typeof entry === "object" && entry !== null ? (entry as Record<string, unknown>).url : undefined;
91
+ assertServerOrigin(pinned, url, label);
92
+ }
93
+ }
94
+
95
+ function parseDocument(document: string | JsonObject, maxDocumentBytes: number): Record<string, unknown> {
96
+ let text: string;
97
+ let parsed: unknown;
98
+ if (typeof document === "string") {
99
+ text = document;
100
+ if (Buffer.byteLength(text, "utf8") > maxDocumentBytes) {
101
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", "document exceeds maxDocumentBytes");
102
+ }
103
+ try {
104
+ parsed = JSON.parse(text);
105
+ } catch {
106
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", "document is not valid JSON");
107
+ }
108
+ } else {
109
+ try {
110
+ text = JSON.stringify(document);
111
+ } catch {
112
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", "document is not JSON-serializable");
113
+ }
114
+ if (Buffer.byteLength(text, "utf8") > maxDocumentBytes) {
115
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", "document exceeds maxDocumentBytes");
116
+ }
117
+ parsed = document;
118
+ }
119
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
120
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", "document must be a JSON object");
121
+ }
122
+ return parsed as Record<string, unknown>;
123
+ }
124
+
125
+ function resolvePointer(doc: unknown, pointer: string): unknown {
126
+ if (!pointer.startsWith("#/")) return undefined;
127
+ const parts = pointer
128
+ .slice(2)
129
+ .split("/")
130
+ .map((part) => part.replace(/~1/g, "/").replace(/~0/g, "~"));
131
+ let current: unknown = doc;
132
+ for (const part of parts) {
133
+ if (typeof current !== "object" || current === null) return undefined;
134
+ current = (current as Record<string, unknown>)[part];
135
+ }
136
+ return current;
137
+ }
138
+
139
+ /** Resolve internal $refs into a self-contained schema; cycles/depth/ref-count bounded, external refs rejected. */
140
+ function resolveSchema(
141
+ value: unknown,
142
+ doc: Record<string, unknown>,
143
+ limits: ResolvedOpenApiLimits,
144
+ state: { readonly refs: number; readonly depth: number },
145
+ ): JsonObject {
146
+ if (state.depth > limits.maxSchemaDepth) {
147
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", "schema exceeds maxSchemaDepth (cycle or nesting)");
148
+ }
149
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return {};
150
+ const schema = value as Record<string, unknown>;
151
+ if (typeof schema.$ref === "string") {
152
+ const refs = state.refs + 1;
153
+ if (refs > limits.maxRefs) throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", "schema exceeds maxRefs");
154
+ if (!schema.$ref.startsWith("#/")) {
155
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", `external $ref is unsupported: ${schema.$ref}`);
156
+ }
157
+ const target = resolvePointer(doc, schema.$ref);
158
+ if (target === undefined) throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", `unresolvable $ref: ${schema.$ref}`);
159
+ return resolveSchema(target, doc, limits, { refs, depth: state.depth + 1 });
160
+ }
161
+ const out: Record<string, unknown> = {};
162
+ for (const [key, val] of Object.entries(schema)) {
163
+ if (key === "$ref") continue;
164
+ if (SCHEMA_ARRAY_KEYWORDS.has(key) && Array.isArray(val)) {
165
+ out[key] = val.map((item) => resolveSchema(item, doc, limits, { refs: state.refs, depth: state.depth + 1 }));
166
+ } else if (SCHEMA_MAP_KEYWORDS.has(key) && typeof val === "object" && val !== null && !Array.isArray(val)) {
167
+ const mapped: Record<string, unknown> = {};
168
+ for (const [name, child] of Object.entries(val as Record<string, unknown>)) {
169
+ mapped[name] = resolveSchema(child, doc, limits, { refs: state.refs, depth: state.depth + 1 });
170
+ }
171
+ out[key] = mapped as JsonObject;
172
+ } else if (SCHEMA_OBJECT_KEYWORDS.has(key) && typeof val === "object" && val !== null && !Array.isArray(val)) {
173
+ out[key] = resolveSchema(val, doc, limits, { refs: state.refs, depth: state.depth + 1 });
174
+ } else {
175
+ out[key] = val;
176
+ }
177
+ }
178
+ return out as JsonObject;
179
+ }
180
+
181
+ function sanitizeToolName(operationId: string): string {
182
+ const name = operationId.replace(/[^A-Za-z0-9._:-]/g, "_");
183
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(name) || name.length > 128) {
184
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", `operationId cannot be represented as a tool name: ${operationId}`);
185
+ }
186
+ return name;
187
+ }
188
+
189
+ function effectForMethod(method: string): ToolEffectDeclaration {
190
+ if (SAFE_METHODS.has(method)) return { kind: "none", idempotency: "none" };
191
+ if (MUTATING_METHODS.has(method)) return { kind: "external_mutation", idempotency: "required" };
192
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", `unsupported HTTP method: ${method}`);
193
+ }
194
+
195
+ function pickJsonMedia(content: Record<string, unknown>): string | undefined {
196
+ if (content["application/json"] !== undefined) return "application/json";
197
+ return Object.keys(content).find((key) => /^application\/.*\+json$/i.test(key));
198
+ }
199
+
200
+ interface OperationRef {
201
+ readonly method: string;
202
+ readonly path: string;
203
+ readonly operation: Record<string, unknown>;
204
+ readonly pathItem: Record<string, unknown>;
205
+ }
206
+
207
+ function compileOperation(
208
+ ref: OperationRef,
209
+ doc: Record<string, unknown>,
210
+ pinned: URL,
211
+ limits: ResolvedOpenApiLimits,
212
+ pagination: OpenApiPagination | undefined,
213
+ ): CompiledOperation {
214
+ const { method, path, operation, pathItem } = ref;
215
+ const operationId = operation.operationId as string;
216
+ assertServers(operation.servers, pinned, `servers for operation ${operationId}`);
217
+ const toolName = sanitizeToolName(operationId);
218
+
219
+ const merged = new Map<string, Record<string, unknown>>();
220
+ for (const list of [pathItem.parameters, operation.parameters]) {
221
+ if (!Array.isArray(list)) continue;
222
+ for (const param of list) {
223
+ if (typeof param !== "object" || param === null) continue;
224
+ const p = param as Record<string, unknown>;
225
+ if (typeof p.name !== "string" || typeof p.in !== "string") {
226
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", `invalid parameter in operation ${operationId}`);
227
+ }
228
+ merged.set(`${p.in}:${p.name}`, p);
229
+ }
230
+ }
231
+
232
+ const pathParams: string[] = [];
233
+ for (const match of path.matchAll(/\{([^}]+)\}/g)) pathParams.push(match[1]);
234
+ for (const name of pathParams) {
235
+ if (!merged.has(`path:${name}`)) {
236
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", `path parameter ${name} of ${operationId} has no declaration`);
237
+ }
238
+ }
239
+
240
+ const queryParams: CompiledParameter[] = [];
241
+ const headerParams: CompiledParameter[] = [];
242
+ const properties: Record<string, unknown> = {};
243
+ const required: string[] = [];
244
+ const argNames = new Set<string>();
245
+ const addArg = (name: string, schema: JsonObject, isRequired: boolean): void => {
246
+ if (argNames.has(name)) {
247
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", `duplicate argument name in ${operationId}: ${name}`);
248
+ }
249
+ argNames.add(name);
250
+ properties[name] = schema;
251
+ if (isRequired) required.push(name);
252
+ };
253
+
254
+ for (const [key, p] of merged) {
255
+ const location = key.slice(0, key.indexOf(":"));
256
+ const name = p.name as string;
257
+ if (location === "cookie") {
258
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", `cookie parameters are unsupported: ${name}`);
259
+ }
260
+ if (p.content !== undefined) {
261
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", `parameter content is unsupported: ${name}`);
262
+ }
263
+ const schema = resolveSchema(p.schema ?? {}, doc, limits, { refs: 0, depth: 0 });
264
+ const isRequired = p.required === true;
265
+ if (location === "path") {
266
+ addArg(name, schema, true);
267
+ } else if (location === "query") {
268
+ queryParams.push({ name, required: isRequired, schema });
269
+ addArg(name, schema, isRequired);
270
+ } else if (location === "header") {
271
+ headerParams.push({ name, required: isRequired, schema });
272
+ addArg(name, schema, isRequired);
273
+ }
274
+ }
275
+
276
+ let bodyParam: CompiledOperation["bodyParam"];
277
+ const requestBody = operation.requestBody;
278
+ if (requestBody !== undefined) {
279
+ if (typeof requestBody !== "object" || requestBody === null) {
280
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", `requestBody of ${operationId} is invalid`);
281
+ }
282
+ const rb = requestBody as Record<string, unknown>;
283
+ const content = rb.content;
284
+ if (typeof content !== "object" || content === null) {
285
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", `requestBody of ${operationId} has no content`);
286
+ }
287
+ const mediaName = pickJsonMedia(content as Record<string, unknown>);
288
+ if (!mediaName) {
289
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_SCHEMA_BOUNDS", `requestBody of ${operationId} has no application/json content`);
290
+ }
291
+ const media = (content as Record<string, unknown>)[mediaName];
292
+ const schema = resolveSchema(
293
+ typeof media === "object" && media !== null ? ((media as Record<string, unknown>).schema ?? {}) : {},
294
+ doc,
295
+ limits,
296
+ { refs: 0, depth: 0 },
297
+ );
298
+ const name = "body";
299
+ addArg(name, schema, rb.required === true);
300
+ bodyParam = { name, schema, required: rb.required === true };
301
+ }
302
+
303
+ const parameters: JsonObject = {
304
+ type: "object",
305
+ properties: properties as JsonObject,
306
+ ...(required.length > 0 ? { required } : {}),
307
+ additionalProperties: false,
308
+ };
309
+
310
+ const summary = typeof operation.summary === "string" ? operation.summary : "";
311
+ const description = typeof operation.description === "string" ? operation.description : "";
312
+ const full = [summary, description].filter(Boolean).join("\n\n");
313
+
314
+ const paginatable = pagination !== undefined && queryParams.some((p) => p.name === pagination.pageParam);
315
+
316
+ return {
317
+ toolName,
318
+ operationId,
319
+ method,
320
+ path,
321
+ pathParams,
322
+ queryParams,
323
+ headerParams,
324
+ bodyParam,
325
+ parameters,
326
+ description: full.slice(0, MAX_DESCRIPTION_BYTES),
327
+ effect: effectForMethod(method),
328
+ paginatable,
329
+ };
330
+ }
331
+
332
+ export function compileOpenApiDocument(input: {
333
+ readonly document: string | JsonObject;
334
+ readonly operations: readonly string[];
335
+ readonly server: string;
336
+ readonly limits: ResolvedOpenApiLimits;
337
+ readonly pagination?: OpenApiPagination;
338
+ }): readonly CompiledOperation[] {
339
+ const { limits } = input;
340
+ const doc = parseDocument(input.document, limits.maxDocumentBytes);
341
+ const version = doc.openapi;
342
+ if (typeof version !== "string" || !version.startsWith("3.1")) {
343
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", "only OpenAPI 3.1 documents are supported");
344
+ }
345
+ const pinned = validateServerUrl(input.server, "server");
346
+ assertServers(doc.servers, pinned, "document servers");
347
+ const paths = doc.paths;
348
+ if (typeof paths !== "object" || paths === null || Array.isArray(paths)) {
349
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", "document paths must be an object");
350
+ }
351
+ if (input.operations.length > limits.maxOperations) {
352
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", "operations allow-list exceeds maxOperations");
353
+ }
354
+
355
+ const byId = new Map<string, OperationRef>();
356
+ for (const [path, pathItemValue] of Object.entries(paths)) {
357
+ if (typeof pathItemValue !== "object" || pathItemValue === null) continue;
358
+ const pathItem = pathItemValue as Record<string, unknown>;
359
+ assertServers(pathItem.servers, pinned, `servers for path ${path}`);
360
+ for (const method of HTTP_METHODS) {
361
+ const operation = pathItem[method];
362
+ if (typeof operation !== "object" || operation === null) continue;
363
+ const op = operation as Record<string, unknown>;
364
+ const operationId = op.operationId;
365
+ if (typeof operationId !== "string" || !operationId) continue;
366
+ if (byId.has(operationId)) {
367
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", `duplicate operationId: ${operationId}`);
368
+ }
369
+ byId.set(operationId, { method: method.toUpperCase(), path, operation: op, pathItem });
370
+ }
371
+ }
372
+
373
+ const compiled: CompiledOperation[] = [];
374
+ const usedNames = new Set<string>();
375
+ for (const operationId of input.operations) {
376
+ const ref = byId.get(operationId);
377
+ if (!ref) throw new OpenApiToolError("ERR_PRISM_OPENAPI_OPERATION_UNKNOWN", `operationId not present in document: ${operationId}`);
378
+ const operation = compileOperation(ref, doc, pinned, limits, input.pagination);
379
+ if (usedNames.has(operation.toolName)) {
380
+ throw new OpenApiToolError("ERR_PRISM_OPENAPI_DOCUMENT_BOUNDS", `tool name collision after sanitization: ${operation.toolName}`);
381
+ }
382
+ usedNames.add(operation.toolName);
383
+ compiled.push(operation);
384
+ }
385
+ return compiled;
386
+ }