@qvac/harness 0.4.0 → 0.5.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 (299) hide show
  1. package/CHANGELOG.md +689 -141
  2. package/README.md +29 -7
  3. package/dist/harness.bundle +71 -50
  4. package/dist-lib/index.d.ts +36 -39
  5. package/dist-lib/index.js +279 -114
  6. package/dist-lib/lib/audio-transcode-bare.d.ts +2 -2
  7. package/dist-lib/lib/audio-transcode-bare.js +28 -18
  8. package/dist-lib/lib/audio-transcode-node.d.ts +2 -2
  9. package/dist-lib/lib/capabilities/audio-resample.d.ts +7 -6
  10. package/dist-lib/lib/capabilities/audio-resample.js +30 -18
  11. package/dist-lib/lib/capabilities/completion.d.ts +20 -19
  12. package/dist-lib/lib/capabilities/completion.js +193 -132
  13. package/dist-lib/lib/capabilities/download.d.ts +6 -6
  14. package/dist-lib/lib/capabilities/download.js +15 -8
  15. package/dist-lib/lib/capabilities/embed.js +2 -1
  16. package/dist-lib/lib/capabilities/enhance.d.ts +6 -1
  17. package/dist-lib/lib/capabilities/enhance.js +7 -4
  18. package/dist-lib/lib/capabilities/invoke.js +7 -4
  19. package/dist-lib/lib/capabilities/kv-cache.js +2 -1
  20. package/dist-lib/lib/capabilities/preview-model-source.d.ts +9 -7
  21. package/dist-lib/lib/capabilities/preview-model-source.js +80 -28
  22. package/dist-lib/lib/capabilities/speech-text.js +5 -4
  23. package/dist-lib/lib/capabilities/tool-selection.js +10 -9
  24. package/dist-lib/lib/capabilities/tool-turns.js +30 -16
  25. package/dist-lib/lib/capabilities/usage-breakdown.js +2 -1
  26. package/dist-lib/lib/capabilities/voice.d.ts +5 -1
  27. package/dist-lib/lib/capabilities/voice.js +22 -12
  28. package/dist-lib/lib/connect.d.ts +11 -32
  29. package/dist-lib/lib/connect.js +152 -112
  30. package/dist-lib/lib/context-window.js +25 -21
  31. package/dist-lib/lib/contract-types.d.ts +22 -15
  32. package/dist-lib/lib/diagram-repair.d.ts +4 -4
  33. package/dist-lib/lib/diagram-repair.js +6 -3
  34. package/dist-lib/lib/docx/index.d.ts +3 -2
  35. package/dist-lib/lib/{error-fields.d.ts → guards.d.ts} +1 -0
  36. package/dist-lib/lib/{error-fields.js → guards.js} +3 -0
  37. package/dist-lib/lib/hardware/capability-probe.js +20 -10
  38. package/dist-lib/lib/hardware/gpu-classify.js +6 -3
  39. package/dist-lib/lib/hardware/gpu-probe-addon.js +8 -4
  40. package/dist-lib/lib/hardware/gpu-probe.js +4 -2
  41. package/dist-lib/lib/hardware/gpu-stats.native.cjs +8 -9
  42. package/dist-lib/lib/hardware/parsers.js +8 -4
  43. package/dist-lib/lib/helpers/buffer-burst.js +4 -2
  44. package/dist-lib/lib/helpers/control-tokens.js +4 -2
  45. package/dist-lib/lib/helpers/join-guard.d.ts +2 -2
  46. package/dist-lib/lib/helpers/join-guard.js +4 -2
  47. package/dist-lib/lib/helpers/load-stall.js +4 -2
  48. package/dist-lib/lib/helpers/markup-stream-filter.d.ts +2 -2
  49. package/dist-lib/lib/helpers/markup-stream-filter.js +6 -3
  50. package/dist-lib/lib/helpers/race-abort.js +2 -1
  51. package/dist-lib/lib/helpers/race-timeout.d.ts +5 -0
  52. package/dist-lib/lib/helpers/race-timeout.js +14 -0
  53. package/dist-lib/lib/helpers/run-outcome.d.ts +6 -8
  54. package/dist-lib/lib/helpers/run-outcome.js +36 -20
  55. package/dist-lib/lib/helpers/spawn-path.js +6 -3
  56. package/dist-lib/lib/helpers/text-coalescer.js +6 -3
  57. package/dist-lib/lib/helpers/thinking.js +38 -45
  58. package/dist-lib/lib/helpers/tool-call-markup.d.ts +9 -7
  59. package/dist-lib/lib/helpers/tool-call-markup.js +36 -20
  60. package/dist-lib/lib/helpers/tool-intent.d.ts +3 -2
  61. package/dist-lib/lib/helpers/tool-intent.js +4 -2
  62. package/dist-lib/lib/helpers/tool-label.js +18 -9
  63. package/dist-lib/lib/hooks/built-in/arg-repair.js +23 -13
  64. package/dist-lib/lib/hooks/built-in/egress.js +2 -1
  65. package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
  66. package/dist-lib/lib/hooks/pipeline.d.ts +5 -4
  67. package/dist-lib/lib/hooks/pipeline.js +6 -3
  68. package/dist-lib/lib/hooks/run.js +12 -8
  69. package/dist-lib/lib/model-download-progress.js +10 -8
  70. package/dist-lib/lib/models/context.d.ts +1 -1
  71. package/dist-lib/lib/models/context.js +5 -1
  72. package/dist-lib/lib/models/custom-model-cache.d.ts +10 -1
  73. package/dist-lib/lib/models/custom-model-cache.js +43 -15
  74. package/dist-lib/lib/models/gguf-header.d.ts +3 -2
  75. package/dist-lib/lib/models/gguf-header.js +44 -22
  76. package/dist-lib/lib/models/idle-unload.d.ts +14 -0
  77. package/dist-lib/lib/models/idle-unload.js +51 -0
  78. package/dist-lib/lib/models/kv-cache.d.ts +3 -2
  79. package/dist-lib/lib/models/kv-cache.js +4 -3
  80. package/dist-lib/lib/models/llm-presets.d.ts +2 -2
  81. package/dist-lib/lib/models/llm-presets.js +4 -2
  82. package/dist-lib/lib/models/model-catalog-constants.js +3 -2
  83. package/dist-lib/lib/models/model-catalog.d.ts +13 -20
  84. package/dist-lib/lib/models/model-catalog.js +59 -31
  85. package/dist-lib/lib/models/model-delete.js +18 -8
  86. package/dist-lib/lib/models/model-handle.js +2 -1
  87. package/dist-lib/lib/models/model-list.d.ts +8 -3
  88. package/dist-lib/lib/models/model-list.js +11 -6
  89. package/dist-lib/lib/models/model-pool.d.ts +44 -10
  90. package/dist-lib/lib/models/model-pool.js +47 -42
  91. package/dist-lib/lib/models/model-source.js +10 -5
  92. package/dist-lib/lib/models/music-catalog.d.ts +2 -2
  93. package/dist-lib/lib/models/nmt-catalog.d.ts +4 -3
  94. package/dist-lib/lib/models/nmt-catalog.js +10 -5
  95. package/dist-lib/lib/models/opencl-cache.d.ts +6 -1
  96. package/dist-lib/lib/models/opencl-cache.js +5 -3
  97. package/dist-lib/lib/models/sdk-faults.d.ts +11 -8
  98. package/dist-lib/lib/models/sdk-faults.js +9 -6
  99. package/dist-lib/lib/models/shards.d.ts +11 -0
  100. package/dist-lib/lib/models/shards.js +15 -0
  101. package/dist-lib/lib/models/tool-dialect.js +10 -5
  102. package/dist-lib/lib/no-redirect-fetch-bare.d.ts +3 -3
  103. package/dist-lib/lib/no-redirect-fetch-bare.js +1 -1
  104. package/dist-lib/lib/no-redirect-fetch-impl.js +6 -3
  105. package/dist-lib/lib/no-redirect-fetch-node.d.ts +1 -1
  106. package/dist-lib/lib/no-redirect-fetch-node.js +9 -9
  107. package/dist-lib/lib/office/index.d.ts +3 -2
  108. package/dist-lib/lib/office/python.d.ts +3 -2
  109. package/dist-lib/lib/office/python.js +2 -1
  110. package/dist-lib/lib/prompt.d.ts +5 -4
  111. package/dist-lib/lib/prompt.js +8 -4
  112. package/dist-lib/lib/queue.js +10 -5
  113. package/dist-lib/lib/resolution-detector.js +21 -11
  114. package/dist-lib/lib/rpc-session.js +2 -1
  115. package/dist-lib/lib/sdk-loader.js +2 -1
  116. package/dist-lib/lib/sdk-storage.js +4 -2
  117. package/dist-lib/lib/serve.js +110 -66
  118. package/dist-lib/lib/spawn.js +7 -4
  119. package/dist-lib/lib/tmp-path.d.ts +7 -6
  120. package/dist-lib/lib/tool-call-display.d.ts +2 -2
  121. package/dist-lib/lib/tools/create-documents.js +18 -10
  122. package/dist-lib/lib/tools/edit-documents.js +23 -13
  123. package/dist-lib/lib/tools/edit-image.d.ts +2 -2
  124. package/dist-lib/lib/tools/edit-image.js +60 -36
  125. package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +10 -10
  126. package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +17 -11
  127. package/dist-lib/lib/tools/exec/adapters/python/attachments.js +53 -40
  128. package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +2 -2
  129. package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +60 -32
  130. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts +33 -22
  131. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +58 -29
  132. package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts +2 -2
  133. package/dist-lib/lib/tools/exec/adapters/python/loader.js +23 -12
  134. package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +13 -11
  135. package/dist-lib/lib/tools/exec/adapters/python/memfs.js +10 -5
  136. package/dist-lib/lib/tools/exec/adapters/python/net-shim.js +3 -1
  137. package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +6 -4
  138. package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +20 -10
  139. package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +2 -2
  140. package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +14 -7
  141. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +41 -34
  142. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +316 -166
  143. package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +9 -8
  144. package/dist-lib/lib/tools/exec/adapters/python/run-session.js +56 -28
  145. package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +8 -10
  146. package/dist-lib/lib/tools/exec/adapters/python/wheels.js +22 -11
  147. package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +3 -2
  148. package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +41 -26
  149. package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +27 -18
  150. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -2
  151. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +14 -13
  152. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +4 -4
  153. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +10 -5
  154. package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +4 -4
  155. package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +2 -1
  156. package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +6 -1
  157. package/dist-lib/lib/tools/exec/obsidian/cli-process.js +8 -5
  158. package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +6 -6
  159. package/dist-lib/lib/tools/exec/obsidian/cli.js +4 -4
  160. package/dist-lib/lib/tools/exec/obsidian/command.d.ts +3 -2
  161. package/dist-lib/lib/tools/exec/obsidian/command.js +6 -3
  162. package/dist-lib/lib/tools/exec/obsidian/output.js +2 -1
  163. package/dist-lib/lib/tools/exec/obsidian/route.d.ts +4 -6
  164. package/dist-lib/lib/tools/exec/obsidian/route.js +17 -11
  165. package/dist-lib/lib/tools/exec/obsidian/safe-path.js +2 -1
  166. package/dist-lib/lib/tools/exec/obsidian/vault-create.js +15 -7
  167. package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +2 -2
  168. package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +20 -10
  169. package/dist-lib/lib/tools/exec/obsidian/vault-read.js +20 -13
  170. package/dist-lib/lib/tools/exec/obsidian/vault-write.js +24 -12
  171. package/dist-lib/lib/tools/exec/runner.d.ts +14 -14
  172. package/dist-lib/lib/tools/exec/runner.js +5 -3
  173. package/dist-lib/lib/tools/exec/scope.js +52 -28
  174. package/dist-lib/lib/tools/exec/shell-argv.js +31 -18
  175. package/dist-lib/lib/tools/exec/shell-guidance.d.ts +6 -1
  176. package/dist-lib/lib/tools/exec/shell-guidance.js +35 -20
  177. package/dist-lib/lib/tools/exec/video-tools.js +2 -2
  178. package/dist-lib/lib/tools/exec.d.ts +6 -8
  179. package/dist-lib/lib/tools/exec.js +113 -54
  180. package/dist-lib/lib/tools/file-glob.d.ts +4 -5
  181. package/dist-lib/lib/tools/file-glob.js +16 -11
  182. package/dist-lib/lib/tools/fs/glob.js +13 -18
  183. package/dist-lib/lib/tools/fs/remote-protocol.d.ts +10 -10
  184. package/dist-lib/lib/tools/fs/remote.d.ts +3 -2
  185. package/dist-lib/lib/tools/fs/remote.js +13 -7
  186. package/dist-lib/lib/tools/fs/scope.d.ts +8 -3
  187. package/dist-lib/lib/tools/fs/scope.js +20 -12
  188. package/dist-lib/lib/tools/fs/walk.js +6 -3
  189. package/dist-lib/lib/tools/fs/write.d.ts +3 -2
  190. package/dist-lib/lib/tools/generate-image.d.ts +11 -9
  191. package/dist-lib/lib/tools/generate-image.js +73 -37
  192. package/dist-lib/lib/tools/generate-music.d.ts +22 -11
  193. package/dist-lib/lib/tools/generate-music.js +79 -48
  194. package/dist-lib/lib/tools/grep.js +66 -41
  195. package/dist-lib/lib/tools/http-request/encode-fields.js +18 -9
  196. package/dist-lib/lib/tools/http-request/{utils.js → request.js} +20 -11
  197. package/dist-lib/lib/tools/http-request/truncate-response.js +57 -46
  198. package/dist-lib/lib/tools/http-request.d.ts +1 -1
  199. package/dist-lib/lib/tools/http-request.js +33 -18
  200. package/dist-lib/lib/tools/mcp/protocol.d.ts +53 -0
  201. package/dist-lib/lib/tools/mcp/{utils.js → protocol.js} +45 -23
  202. package/dist-lib/lib/tools/mcp-call.d.ts +1 -1
  203. package/dist-lib/lib/tools/mcp-call.js +37 -16
  204. package/dist-lib/lib/tools/operations/builders.js +10 -6
  205. package/dist-lib/lib/tools/operations/compile.js +20 -10
  206. package/dist-lib/lib/tools/operations/decoders.js +12 -6
  207. package/dist-lib/lib/tools/operations/gmail-message.js +3 -2
  208. package/dist-lib/lib/tools/outbound-guard.d.ts +4 -3
  209. package/dist-lib/lib/tools/outbound-guard.js +14 -8
  210. package/dist-lib/lib/tools/private-ip.js +8 -7
  211. package/dist-lib/lib/tools/rag-search.js +14 -7
  212. package/dist-lib/lib/tools/read-files.js +23 -20
  213. package/dist-lib/lib/tools/skill.js +19 -10
  214. package/dist-lib/lib/tools/skills/auth-resolver.d.ts +11 -18
  215. package/dist-lib/lib/tools/skills/auth-resolver.js +30 -18
  216. package/dist-lib/lib/tools/skills/bin-check.d.ts +3 -2
  217. package/dist-lib/lib/tools/skills/bin-check.js +10 -3
  218. package/dist-lib/lib/tools/skills/bundled-dir.js +20 -10
  219. package/dist-lib/lib/tools/skills/bundled-hash.d.ts +3 -2
  220. package/dist-lib/lib/tools/skills/bundled-hash.js +6 -1
  221. package/dist-lib/lib/tools/skills/catalog.d.ts +3 -2
  222. package/dist-lib/lib/tools/skills/catalog.js +33 -28
  223. package/dist-lib/lib/tools/skills/cli-argv.d.ts +3 -2
  224. package/dist-lib/lib/tools/skills/cli-argv.js +55 -52
  225. package/dist-lib/lib/tools/skills/cli-schema-check.js +21 -10
  226. package/dist-lib/lib/tools/skills/cli-schema-load.js +28 -20
  227. package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +6 -6
  228. package/dist-lib/lib/tools/skills/cli-schema.js +2 -1
  229. package/dist-lib/lib/tools/skills/credential-check.d.ts +6 -3
  230. package/dist-lib/lib/tools/skills/credential-check.js +24 -12
  231. package/dist-lib/lib/tools/skills/credential-grants.js +1 -6
  232. package/dist-lib/lib/tools/skills/credential-health.d.ts +5 -1
  233. package/dist-lib/lib/tools/skills/credential-health.js +5 -3
  234. package/dist-lib/lib/tools/skills/frontmatter-parser.js +10 -5
  235. package/dist-lib/lib/tools/skills/manifest-parse.js +84 -64
  236. package/dist-lib/lib/tools/skills/mention.js +7 -6
  237. package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +2 -2
  238. package/dist-lib/lib/tools/skills/obsidian-readiness.js +11 -3
  239. package/dist-lib/lib/tools/skills/overlay-crud.d.ts +6 -4
  240. package/dist-lib/lib/tools/skills/overlay-crud.js +12 -6
  241. package/dist-lib/lib/tools/skills/overlay-dir.js +4 -2
  242. package/dist-lib/lib/tools/skills/overlay-watch.d.ts +3 -2
  243. package/dist-lib/lib/tools/skills/overlay-watch.js +8 -4
  244. package/dist-lib/lib/tools/skills/setup-routes.js +1 -3
  245. package/dist-lib/lib/tools/skills/skill-settings.js +4 -1
  246. package/dist-lib/lib/tools/skills/skill-state.d.ts +6 -11
  247. package/dist-lib/lib/tools/skills/skill-state.js +48 -29
  248. package/dist-lib/lib/tools/skills/tool-grants.js +6 -3
  249. package/dist-lib/lib/tools/toolsets.js +2 -1
  250. package/dist-lib/lib/tools/translate.d.ts +4 -5
  251. package/dist-lib/lib/tools/translate.js +31 -14
  252. package/dist-lib/lib/tools/url-guard.js +2 -1
  253. package/dist-lib/lib/tools/weather-lookup/place.js +8 -4
  254. package/dist-lib/lib/tools/weather-lookup/report.d.ts +7 -1
  255. package/dist-lib/lib/tools/weather-lookup/report.js +46 -9
  256. package/dist-lib/lib/tools/weather-lookup.js +18 -12
  257. package/dist-lib/lib/tools/web-fetch.js +47 -24
  258. package/dist-lib/lib/tools/web-search.js +8 -3
  259. package/dist-lib/lib/validating-lookup.d.ts +4 -4
  260. package/dist-lib/lib/wav-encode.d.ts +1 -1
  261. package/dist-lib/lib/wav-encode.js +11 -10
  262. package/dist-lib/lib/web-fetch/embedded-state.js +24 -12
  263. package/dist-lib/lib/web-fetch/extract.js +4 -2
  264. package/dist-lib/lib/web-fetch/images.js +8 -4
  265. package/dist-lib/lib/web-fetch/video.d.ts +2 -2
  266. package/dist-lib/lib/web-fetch/video.js +8 -4
  267. package/dist-lib/lib/web-search/engine-registry.d.ts +2 -2
  268. package/dist-lib/lib/web-search/engines/aol.js +1 -1
  269. package/dist-lib/lib/web-search/engines/bing.js +1 -1
  270. package/dist-lib/lib/web-search/engines/brave.js +4 -1
  271. package/dist-lib/lib/web-search/engines/duckduckgo.js +3 -2
  272. package/dist-lib/lib/web-search/engines/google.js +7 -3
  273. package/dist-lib/lib/web-search/engines/wikipedia.js +5 -4
  274. package/dist-lib/lib/web-search/engines/yahoo.js +1 -1
  275. package/dist-lib/lib/web-search/fetch-html.d.ts +5 -4
  276. package/dist-lib/lib/web-search/fetch-html.js +13 -7
  277. package/dist-lib/lib/web-search/orchestrator.d.ts +9 -5
  278. package/dist-lib/lib/web-search/orchestrator.js +3 -3
  279. package/dist-lib/lib/wire.d.ts +3 -2
  280. package/dist-lib/lib/wire.js +23 -15
  281. package/dist-lib/schema/baseline.js +10 -5
  282. package/dist-lib/schema/build.js +2 -1
  283. package/dist-lib/schema/harness.d.ts +15 -2
  284. package/dist-lib/schema/harness.js +14 -2
  285. package/dist-lib/schema/release-tags.d.ts +3 -2
  286. package/dist-lib/schema/release-tags.js +6 -3
  287. package/dist-lib/spec/bind.js +3 -0
  288. package/dist-lib/spec/calls.js +1 -0
  289. package/dist-lib/spec/hrpc/hrpc.json +13 -0
  290. package/dist-lib/spec/hrpc/index.js +15 -3
  291. package/dist-lib/spec/hrpc/messages.js +39 -8
  292. package/dist-lib/spec/hyperschema/index.js +39 -8
  293. package/dist-lib/spec/hyperschema/schema.json +30 -1
  294. package/dist-lib/spec/hyperschema/types.d.ts +9 -1
  295. package/package.json +13 -14
  296. package/worker/harness-sidecar.mjs +15 -22
  297. package/worker/stow-entry.mjs +7 -4
  298. package/dist-lib/lib/tools/mcp/utils.d.ts +0 -47
  299. /package/dist-lib/lib/tools/http-request/{utils.d.ts → request.d.ts} +0 -0
@@ -25,12 +25,13 @@ function importedModules(source) {
25
25
  continue;
26
26
  }
27
27
  const imported = /^\s*import\s+([\w.]+(?:\s*,\s*[\w.]+)*)/.exec(line)?.[1];
28
- if (imported)
28
+ if (imported) {
29
29
  modules.push(...imported.split(',').map((m) => m.trim()));
30
+ }
30
31
  }
31
- return modules.map((m) => m.split('.')[0]);
32
+ return modules.map((m) => m.split('.')[0] ?? '');
32
33
  }
33
- export function videoToolError(command, packages, python, network = false) {
34
+ export function videoToolError(command, { packages, python, network = false }) {
34
35
  const names = [
35
36
  dashModule(command) ?? '',
36
37
  ...(pipInstallTargets(command) ?? []),
@@ -38,14 +39,18 @@ export function videoToolError(command, packages, python, network = false) {
38
39
  ...(python ? importedModules(unwrapPythonSource(command) ?? command) : [commandBin(command)])
39
40
  ];
40
41
  const tools = names.map(videoTool).filter((tool) => tool !== null);
41
- if (tools.length === 0)
42
+ if (tools.length === 0) {
42
43
  return null;
43
- if (!network)
44
+ }
45
+ if (!network) {
44
46
  return VIDEO_TOOLS_ERROR;
45
- if (tools.some(isTranscoder))
47
+ }
48
+ if (tools.some(isTranscoder)) {
46
49
  return TRANSCODE_ERROR;
47
- if (python)
50
+ }
51
+ if (python) {
48
52
  return null;
53
+ }
49
54
  const program = videoTool(commandBin(command)) ?? videoTool(dashModule(command) ?? '');
50
55
  return program ? downloaderShellError(program) : null;
51
56
  }
@@ -59,25 +64,30 @@ function writesFile(words, curl) {
59
64
  const long = curl ? '--output' : '--output-document';
60
65
  let saves = !curl;
61
66
  for (let i = 1; i < words.length; i++) {
62
- const word = words[i];
63
- if (curl && /^--remote-name(-all)?$/.test(word))
67
+ const word = words[i] ?? '';
68
+ if (curl && /^--remote-name(-all)?$/.test(word)) {
64
69
  saves = true;
70
+ }
65
71
  if (word === long || word.startsWith(`${long}=`)) {
66
72
  const value = word === long ? words[++i] : word.slice(long.length + 1);
67
73
  saves = value !== '-';
68
74
  continue;
69
75
  }
70
- if (!/^-[A-Za-z]/.test(word))
76
+ if (!/^-[A-Za-z]/.test(word)) {
71
77
  continue;
78
+ }
72
79
  for (let j = 1; j < word.length; j++) {
73
- const flag = word[j];
74
- if (curl && flag === 'O')
80
+ const flag = word.charAt(j);
81
+ if (curl && flag === 'O') {
75
82
  saves = true;
76
- if (!valued.has(flag))
83
+ }
84
+ if (!valued.has(flag)) {
77
85
  continue;
86
+ }
78
87
  const value = j + 1 < word.length ? word.slice(j + 1) : words[++i];
79
- if (flag === output)
88
+ if (flag === output) {
80
89
  saves = value !== '-';
90
+ }
81
91
  break;
82
92
  }
83
93
  }
@@ -85,14 +95,16 @@ function writesFile(words, curl) {
85
95
  }
86
96
  export function shellDownloadNote(command) {
87
97
  const bin = commandBin(command);
88
- if (bin !== 'curl' && bin !== 'wget')
98
+ if (bin !== 'curl' && bin !== 'wget') {
89
99
  return null;
100
+ }
90
101
  return writesFile(shellArgv(command), bin === 'curl') ? SHELL_DOWNLOAD_NOTE : null;
91
102
  }
92
103
  export function pipError(command, sandbox = false) {
93
- if (!PIP.test(commandBin(command)) && dashModule(command) !== 'pip')
104
+ if (!PIP.test(commandBin(command)) && dashModule(command) !== 'pip') {
94
105
  return null;
95
- return pipMessage(pipInstallTargets(command), sandbox);
106
+ }
107
+ return pipMessage(pipInstallTargets(command), { sandbox });
96
108
  }
97
109
  function firstCodeLine(command) {
98
110
  return (command
@@ -106,18 +118,21 @@ export function startsLikePython(command) {
106
118
  }
107
119
  export function isPythonSourceShaped(command) {
108
120
  const first = firstCodeLine(command);
109
- if (/^from\s+[\w.]+\s+import\b/.test(first))
121
+ if (/^from\s+[\w.]+\s+import\b/.test(first)) {
110
122
  return true;
123
+ }
111
124
  if (/^[A-Za-z_][\w.]*\s*\(/.test(first) && !/^[A-Za-z_]\w*\s*\(\s*\)\s*\{/.test(first)) {
112
125
  return true;
113
126
  }
114
- if (!PYTHON_START.test(first))
127
+ if (!PYTHON_START.test(first)) {
115
128
  return false;
129
+ }
116
130
  return command.trim().includes('\n') || sanitizeCommand(command) !== null;
117
131
  }
118
132
  export function pythonRedirectError(command, sandbox = false) {
119
133
  const pip = pipError(command, sandbox);
120
- if (pip)
134
+ if (pip) {
121
135
  return pip;
136
+ }
122
137
  return INTERPRETER.test(commandBin(command)) ? PYTHON_BIN_ERROR : null;
123
138
  }
@@ -26,10 +26,10 @@ export function downloaderShellError(tool) {
26
26
  'in outputs so it reaches the user.');
27
27
  }
28
28
  export function videoTool(name) {
29
- const bare = name
29
+ const bare = (name
30
30
  .trim()
31
31
  .replace(/^['"]|['"]$/g, '')
32
- .split(/[=<>!~[\s]/)[0]
32
+ .split(/[=<>!~[\s]/)[0] ?? '')
33
33
  .toLowerCase()
34
34
  .replace(/\.exe$/, '')
35
35
  .replace(/[-_.]+/g, '-');
@@ -1,5 +1,7 @@
1
1
  import type { ToolDefinition } from '../capabilities/completion.ts';
2
+ import type { ExecError } from './exec/adapters/python/output-policy.ts';
2
3
  import type { LoadPyodideFn } from './exec/adapters/python/pyodide.ts';
4
+ import { type ShellRuntime } from './exec/adapters/shell/shell-adapter.ts';
3
5
  import { type KillProcess, type SpawnFn } from './exec/runner.ts';
4
6
  import { type Realpath } from './fs/scope.ts';
5
7
  import type { CredentialStoreFn } from './skills/auth-resolver.ts';
@@ -17,14 +19,10 @@ export interface ExecToolOptions {
17
19
  readonly deniedRoots?: readonly string[];
18
20
  readonly skillsDir?: string;
19
21
  }
20
- export declare function bareRuntime(): Promise<{
21
- readonly spawn: SpawnFn;
22
- readonly killProcess: KillProcess;
23
- }>;
24
- export declare function resolveScriptRun(rawArgs: Record<string, unknown>, skillsDir: string | undefined): {
22
+ export declare function bareRuntime(): Promise<Required<ShellRuntime>>;
23
+ export interface ScriptRun {
25
24
  args: Record<string, unknown>;
26
25
  trusted: boolean;
27
- } | {
28
- error: string;
29
- } | null;
26
+ }
27
+ export declare function resolveScriptRun(rawArgs: Record<string, unknown>, skillsDir: string | undefined): ScriptRun | ExecError | null;
30
28
  export declare function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide, pythonNetwork, credentialStore, skillSettings, realpath, deniedRoots, skillsDir }?: ExecToolOptions): ToolDefinition;
@@ -31,18 +31,21 @@ function resolveShellOps(command, toolOps) {
31
31
  }
32
32
  function checkShellScope(command, toolOps) {
33
33
  const unsafe = sanitizeCommand(command);
34
- if (unsafe)
34
+ if (unsafe) {
35
35
  return unsafe;
36
+ }
36
37
  const { ops, argv } = resolveShellOps(command, toolOps);
37
- if (ops.length === 0)
38
+ if (ops.length === 0) {
38
39
  return null;
40
+ }
39
41
  return checkCliOps(argv, ops);
40
42
  }
41
43
  function shellEffect(command, toolOps) {
42
44
  const { ops, argv } = resolveShellOps(command, toolOps);
43
45
  const matched = ops.filter((op) => op.check(argv) === null);
44
- if (matched.length === 0)
46
+ if (matched.length === 0) {
45
47
  return 'write';
48
+ }
46
49
  return matched.every((op) => op.annotation(argv) === 'read') ? 'read' : 'write';
47
50
  }
48
51
  export async function bareRuntime() {
@@ -54,11 +57,12 @@ export async function bareRuntime() {
54
57
  const spawnFn = (command, opts = {}) => rawSpawn(command, { ...opts, env: spawnEnv(process.env, process.platform, opts.env) });
55
58
  return { spawn: spawnFn, killProcess: process.kill.bind(process) };
56
59
  }
57
- function resolveExecution(adapters, fallback, args, ctx) {
60
+ function resolveExecution({ adapters, fallback }, args, ctx) {
58
61
  for (const adapter of adapters) {
59
62
  const plan = adapter.resolve(args, ctx);
60
- if (plan)
63
+ if (plan) {
61
64
  return plan;
65
+ }
62
66
  }
63
67
  return fallback.resolve(args, ctx);
64
68
  }
@@ -70,13 +74,16 @@ function checkFileArgs(args, python) {
70
74
  return FILES_NEED_PYTHON_ERROR;
71
75
  }
72
76
  const inputs = parseInputs(args.inputs);
73
- if (inputs && 'error' in inputs)
77
+ if (inputs && 'error' in inputs) {
74
78
  return inputs.error;
79
+ }
75
80
  const outputs = parseOutputs(args.outputs);
76
- if (outputs && 'error' in outputs)
81
+ if (outputs && 'error' in outputs) {
77
82
  return outputs.error;
78
- if (!python)
83
+ }
84
+ if (!python) {
79
85
  return null;
86
+ }
80
87
  const workdir = parseWorkdir(args.workdir);
81
88
  return workdir && typeof workdir === 'object' ? workdir.error : null;
82
89
  }
@@ -107,8 +114,9 @@ function connectAsker(askUser) {
107
114
  }
108
115
  function networkPrefix(source) {
109
116
  const hosts = namedHosts(source);
110
- if (hosts.length === 0)
117
+ if (hosts.length === 0) {
111
118
  return ' · network: no host named in the code';
119
+ }
112
120
  return ` · network: ${hosts.join(', ')}`;
113
121
  }
114
122
  function packageSuffix(args) {
@@ -143,22 +151,25 @@ function commandRequiredError(args, network) {
143
151
  'python source) or script + scriptArgs exactly as the loaded skill reference shows');
144
152
  }
145
153
  const packages = stringList(args.packages);
146
- const impossible = videoToolError('', packages, true, network);
147
- if (impossible)
154
+ const impossible = videoToolError('', { packages, python: true, network });
155
+ if (impossible) {
148
156
  return impossible;
157
+ }
149
158
  if (packages?.length) {
150
159
  return `command is required: call exec again with language "python", packages: ${JSON.stringify(packages.map(pinned))} and the Python source that uses them in command. Installing and running are the same one call.`;
151
160
  }
152
161
  return 'command is required';
153
162
  }
154
163
  function parseScriptArgs(value) {
155
- if (Array.isArray(value))
164
+ if (Array.isArray(value)) {
156
165
  return value.map(String);
166
+ }
157
167
  if (typeof value === 'string' && value.trim().startsWith('[')) {
158
168
  try {
159
169
  const parsed = JSON.parse(value);
160
- if (Array.isArray(parsed))
170
+ if (Array.isArray(parsed)) {
161
171
  return parsed.map(String);
172
+ }
162
173
  }
163
174
  catch {
164
175
  }
@@ -168,16 +179,19 @@ function parseScriptArgs(value) {
168
179
  function normalizeScriptCall(rawArgs) {
169
180
  let script = typeof rawArgs.script === 'string' ? unquoteArg(rawArgs.script) : '';
170
181
  let scriptArgs = parseScriptArgs(rawArgs.scriptArgs);
171
- if (!script || script.includes('\n'))
182
+ if (!script || script.includes('\n')) {
172
183
  return { script, scriptArgs };
184
+ }
173
185
  if (script.includes(' ')) {
174
186
  const tokens = script.split(/\s+/).filter(Boolean);
175
187
  script = tokens[0] ?? '';
176
- if (!scriptArgs.length)
188
+ if (!scriptArgs.length) {
177
189
  scriptArgs = tokens.slice(1);
190
+ }
178
191
  }
179
- if (!script.startsWith('scripts/') && !script.includes('/'))
192
+ if (!script.startsWith('scripts/') && !script.includes('/')) {
180
193
  script = `scripts/${script}`;
194
+ }
181
195
  return { script, scriptArgs };
182
196
  }
183
197
  export function resolveScriptRun(rawArgs, skillsDir) {
@@ -203,10 +217,12 @@ export function resolveScriptRun(rawArgs, skillsDir) {
203
217
  };
204
218
  }
205
219
  const skill = typeof rawArgs.skill === 'string' ? unquoteArg(rawArgs.skill) : '';
206
- if (!skill)
220
+ if (!skill) {
207
221
  return { error: 'script runs need the owning skill name in `skill`' };
208
- if (!skillsDir)
222
+ }
223
+ if (!skillsDir) {
209
224
  return { error: 'script execution is not available on this device' };
225
+ }
210
226
  if (String(rawArgs.command ?? '').trim()) {
211
227
  return { error: 'pass either command or script, never both' };
212
228
  }
@@ -222,8 +238,9 @@ export function resolveScriptRun(rawArgs, skillsDir) {
222
238
  return { error: `script not found in skill ${skill}: ${script}` };
223
239
  }
224
240
  const staged = stagingGap(rawArgs, scriptArgs);
225
- if (staged)
241
+ if (staged) {
226
242
  return { error: staged };
243
+ }
227
244
  const argv = JSON.stringify([script.slice('scripts/'.length), ...scriptArgs]);
228
245
  const command = `import sys\nsys.argv = ${argv}\n${source}`;
229
246
  return { args: { ...rawArgs, language: 'python', command, scriptArgs }, trusted: true };
@@ -237,8 +254,9 @@ function stagingGap(rawArgs, scriptArgs) {
237
254
  ...(Array.isArray(outputs) ? outputs : [])
238
255
  ]);
239
256
  const missing = scriptArgs.filter((arg) => SCRIPT_FILE_ARG.test(arg) && !known.has(arg));
240
- if (!missing.length)
257
+ if (!missing.length) {
241
258
  return null;
259
+ }
242
260
  return (`scriptArgs name ${missing.map((name) => `"${name}"`).join(', ')} but the working directory ` +
243
261
  'starts empty: stage each existing file in inputs [{ attachmentId, path }] and declare each ' +
244
262
  'produced file in outputs');
@@ -260,24 +278,32 @@ function withInferredLanguage(args, disablePyodide) {
260
278
  ? { ...args, language: 'python' }
261
279
  : args;
262
280
  }
263
- function checkExecutionScope(args, toolOps, disablePyodide, sandbox, network) {
281
+ function checkExecutionScope(args, { toolOps, disablePyodide, sandbox, network }) {
264
282
  const command = String(args.command ?? '').trim();
265
- const video = videoToolError(command, stringList(args.packages), runsThroughPyodide(args.language, disablePyodide), network);
266
- if (video)
283
+ const video = videoToolError(command, {
284
+ packages: stringList(args.packages),
285
+ python: runsThroughPyodide(args.language, disablePyodide),
286
+ network
287
+ });
288
+ if (video) {
267
289
  return video;
268
- if (runsThroughPyodide(args.language, disablePyodide))
290
+ }
291
+ if (runsThroughPyodide(args.language, disablePyodide)) {
269
292
  return null;
293
+ }
270
294
  if (!disablePyodide) {
271
295
  const redirect = pythonRedirectError(command, sandbox);
272
- if (redirect)
296
+ if (redirect) {
273
297
  return redirect;
298
+ }
274
299
  }
275
300
  return checkShellScope(command, toolOps);
276
301
  }
277
302
  async function skillsEnv(skillSettings, ctx) {
278
303
  const skills = ctx?.skills ?? [];
279
- if (skills.length === 0)
304
+ if (skills.length === 0) {
280
305
  return undefined;
306
+ }
281
307
  const env = {};
282
308
  for (const skill of skills) {
283
309
  Object.assign(env, settingsEnv(skill, await skillSettings(skill, ctx?.agentId)));
@@ -404,21 +430,30 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
404
430
  if (typeof args.script === 'string' && args.script.trim() && !args.script.includes('\n')) {
405
431
  return 'read';
406
432
  }
407
- if (runsThroughPyodide(args.language, disablePyodide))
433
+ if (runsThroughPyodide(args.language, disablePyodide)) {
408
434
  return pythonCreateEffect(args);
435
+ }
409
436
  return shellEffect(String(args.command ?? '').trim(), ctx?.toolOps);
410
437
  },
411
438
  validate(args, ctx) {
412
439
  const scriptRun = resolveScriptRun(args, skillsDir);
413
- if (scriptRun && 'error' in scriptRun)
440
+ if (scriptRun && 'error' in scriptRun) {
414
441
  return scriptRun.error;
442
+ }
415
443
  const rawArgs = withInferredLanguage(scriptRun ? scriptRun.args : args, disablePyodide);
416
444
  const command = String(rawArgs.command ?? '').trim();
417
- if (!command)
445
+ if (!command) {
418
446
  return commandRequiredError(rawArgs, pythonNetwork && ctx?.sandboxMode !== true);
419
- const scopeError = checkExecutionScope(rawArgs, ctx?.toolOps, disablePyodide, ctx?.sandboxMode === true, pythonNetwork && ctx?.sandboxMode !== true);
420
- if (scopeError)
447
+ }
448
+ const scopeError = checkExecutionScope(rawArgs, {
449
+ toolOps: ctx?.toolOps,
450
+ disablePyodide,
451
+ sandbox: ctx?.sandboxMode === true,
452
+ network: pythonNetwork && ctx?.sandboxMode !== true
453
+ });
454
+ if (scopeError) {
421
455
  return scopeError;
456
+ }
422
457
  const python = runsThroughPyodide(rawArgs.language, disablePyodide);
423
458
  const workdirGiven = rawArgs.workdir !== undefined && rawArgs.workdir !== null;
424
459
  if (workdirGiven && !python && (ctx?.fsRoots ?? []).length === 0) {
@@ -426,35 +461,44 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
426
461
  }
427
462
  if (python) {
428
463
  const outputs = parseOutputs(rawArgs.outputs);
429
- if (outputs && 'error' in outputs)
464
+ if (outputs && 'error' in outputs) {
430
465
  return outputs.error;
466
+ }
431
467
  const packages = parsePackages(rawArgs.packages);
432
- if (packages && 'error' in packages)
468
+ if (packages && 'error' in packages) {
433
469
  return packages.error;
470
+ }
434
471
  if (!scriptRun || !scriptRun.trusted) {
435
472
  const sourceError = checkPythonSource(command);
436
- if (sourceError)
473
+ if (sourceError) {
437
474
  return sourceError;
475
+ }
438
476
  const pptxError = checkPptxBuildSource(unwrapPythonSource(command) ?? command, outputs, packages);
439
- if (pptxError)
477
+ if (pptxError) {
440
478
  return pptxError;
479
+ }
441
480
  const pdfError = checkPdfBuildSource(unwrapPythonSource(command) ?? command, outputs, packages);
442
- if (pdfError)
481
+ if (pdfError) {
443
482
  return pdfError;
483
+ }
444
484
  const bareLength = checkBareLengthArgs(unwrapPythonSource(command) ?? command, packages);
445
- if (bareLength)
485
+ if (bareLength) {
446
486
  return bareLength;
487
+ }
447
488
  const inputs = parseInputs(rawArgs.inputs);
448
489
  const unstagedRead = checkUnstagedRead(unwrapPythonSource(command) ?? command, Array.isArray(inputs) ? inputs : undefined);
449
- if (unstagedRead)
490
+ if (unstagedRead) {
450
491
  return unstagedRead;
492
+ }
451
493
  }
452
494
  }
453
495
  const parsed = parsePackages(rawArgs.packages);
454
- if (parsed && 'error' in parsed)
496
+ if (parsed && 'error' in parsed) {
455
497
  return parsed.error;
456
- if (parsed && !python)
498
+ }
499
+ if (parsed && !python) {
457
500
  return PACKAGES_NEED_PYTHON_ERROR;
501
+ }
458
502
  return checkFileArgs(rawArgs, python);
459
503
  },
460
504
  resource(rawArgs, ctx) {
@@ -464,8 +508,9 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
464
508
  return { kind: 'command', label: scriptLabel(args) };
465
509
  }
466
510
  const command = String(args.command ?? '').trim() || script.trim();
467
- if (!command)
511
+ if (!command) {
468
512
  return null;
513
+ }
469
514
  const workdir = typeof args.workdir === 'string' && args.workdir.trim() ? args.workdir.trim() : null;
470
515
  const source = unwrapPythonSource(command) ?? command;
471
516
  const online = pythonNetwork && ctx?.sandboxMode !== true;
@@ -479,16 +524,23 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
479
524
  },
480
525
  async execute(args, ctx) {
481
526
  const scriptRun = resolveScriptRun(args, skillsDir);
482
- if (scriptRun && 'error' in scriptRun)
527
+ if (scriptRun && 'error' in scriptRun) {
483
528
  return { error: scriptRun.error };
529
+ }
484
530
  const rawArgs = withInferredLanguage(scriptRun ? scriptRun.args : args, disablePyodide);
485
531
  const command = String(rawArgs.command ?? '').trim();
486
532
  if (!command) {
487
533
  return { error: commandRequiredError(rawArgs, pythonNetwork && ctx?.sandboxMode !== true) };
488
534
  }
489
- const scopeError = checkExecutionScope(rawArgs, ctx?.toolOps, disablePyodide, ctx?.sandboxMode === true, pythonNetwork && ctx?.sandboxMode !== true);
490
- if (scopeError)
535
+ const scopeError = checkExecutionScope(rawArgs, {
536
+ toolOps: ctx?.toolOps,
537
+ disablePyodide,
538
+ sandbox: ctx?.sandboxMode === true,
539
+ network: pythonNetwork && ctx?.sandboxMode !== true
540
+ });
541
+ if (scopeError) {
491
542
  return { error: scopeError };
543
+ }
492
544
  const workdir = rawArgs.workdir === undefined || rawArgs.workdir === null
493
545
  ? undefined
494
546
  : String(rawArgs.workdir);
@@ -496,22 +548,26 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
496
548
  let cwd = workdir;
497
549
  if (workdir !== undefined && onShellPath) {
498
550
  const roots = ctx?.fsRoots ?? [];
499
- if (roots.length === 0)
551
+ if (roots.length === 0) {
500
552
  return { error: WORKDIR_NO_ROOTS_ERROR };
553
+ }
501
554
  const deny = await canonicalDenyRoots(deniedRoots, realpath);
502
- const resolved = await resolveKey(workdir, roots, realpath, deny);
503
- if ('error' in resolved)
555
+ const resolved = await resolveKey(workdir, roots, { realpath, deny });
556
+ if ('error' in resolved) {
504
557
  return { error: resolved.error };
558
+ }
505
559
  if (resolved.root.kind !== 'local') {
506
560
  return { error: 'workdir must be a local directory, not a drive' };
507
561
  }
508
- if (!resolved.exists)
562
+ if (!resolved.exists) {
509
563
  return { error: `workdir does not exist: ${workdir}` };
564
+ }
510
565
  cwd = hostPath(resolved.root, resolved.key);
511
566
  }
512
567
  let timeout = parsePositiveInt(rawArgs.timeoutMs, 'timeoutMs', 600_000);
513
- if (timeout && typeof timeout === 'object')
568
+ if (timeout && typeof timeout === 'object') {
514
569
  return timeout;
570
+ }
515
571
  if (ctx?.runBudget) {
516
572
  const remaining = ctx.runBudget.deadlineMs - Date.now();
517
573
  if (remaining <= 0) {
@@ -520,12 +576,14 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
520
576
  guidance: true
521
577
  };
522
578
  }
523
- if (timeout !== undefined)
579
+ if (timeout !== undefined) {
524
580
  timeout = Math.min(timeout, remaining);
581
+ }
525
582
  }
526
583
  const maxOutputChars = parsePositiveInt(rawArgs.maxOutputChars, 'maxOutputChars', MAX_OUTPUT_CHARS_LIMIT);
527
- if (maxOutputChars && typeof maxOutputChars === 'object')
584
+ if (maxOutputChars && typeof maxOutputChars === 'object') {
528
585
  return maxOutputChars;
586
+ }
529
587
  if (!isPythonLanguage(rawArgs.language)) {
530
588
  const obsidian = await routeObsidianCommand(command, ctx, {
531
589
  credentialStore,
@@ -535,12 +593,13 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
535
593
  timeoutMs: timeout,
536
594
  maxOutputChars
537
595
  });
538
- if (obsidian)
596
+ if (obsidian) {
539
597
  return obsidian;
598
+ }
540
599
  }
541
600
  const skillEnv = skillSettings ? await skillsEnv(skillSettings, ctx) : undefined;
542
601
  try {
543
- const execution = resolveExecution(adapters, fallback, rawArgs, {
602
+ const execution = resolveExecution({ adapters, fallback }, rawArgs, {
544
603
  attach: ctx?.attach,
545
604
  fetchAttachment: ctx?.fetchAttachment,
546
605
  turnImages: ctx?.turnImages,
@@ -555,7 +614,7 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
555
614
  timeoutMs: timeout,
556
615
  maxOutputChars,
557
616
  signal: ctx?.signal,
558
- ...(skillEnv ? { env: skillEnv } : {})
617
+ env: skillEnv
559
618
  });
560
619
  }
561
620
  catch (err) {
@@ -1,15 +1,14 @@
1
1
  import type { ToolDefinition } from '../capabilities/completion.ts';
2
- import { type DriveRoot, type FsToolOptions, type Realpath } from './fs/scope.ts';
2
+ import { type DriveRoot, type FsToolError, type FsToolOptions, type ResolveKeyOptions } from './fs/scope.ts';
3
3
  export declare const DEFAULT_GLOB_MATCHES = 100;
4
4
  export declare const MAX_GLOB_MATCHES = 1000;
5
5
  export interface SearchTarget {
6
6
  root: DriveRoot;
7
7
  folder: string;
8
8
  }
9
- export declare function searchTargets(dir: unknown, roots: readonly DriveRoot[], realpath: Realpath, deny?: readonly string[]): Promise<{
9
+ export interface ResolvedTargets {
10
10
  targets: SearchTarget[];
11
- } | {
12
- error: string;
13
- }>;
11
+ }
12
+ export declare function searchTargets(dir: unknown, roots: readonly DriveRoot[], { realpath, deny }: ResolveKeyOptions): Promise<ResolvedTargets | FsToolError>;
14
13
  export declare function relativeKey(key: string, folder: string): string;
15
14
  export declare function createFileGlobTool({ realpath, deniedRoots }?: FsToolOptions): ToolDefinition;
@@ -3,14 +3,16 @@ import { NO_DIRS_GRANTED, canonicalDenyRoots, defaultRealpath, isDeniedPath, res
3
3
  import { walkFiles } from './fs/walk.js';
4
4
  export const DEFAULT_GLOB_MATCHES = 100;
5
5
  export const MAX_GLOB_MATCHES = 1000;
6
- export async function searchTargets(dir, roots, realpath, deny) {
6
+ export async function searchTargets(dir, roots, { realpath, deny }) {
7
7
  if (typeof dir === 'string' && dir.trim()) {
8
- const resolved = await resolveKey(dir, roots, realpath, deny);
9
- if ('error' in resolved)
8
+ const resolved = await resolveKey(dir, roots, { realpath, deny });
9
+ if ('error' in resolved) {
10
10
  return resolved;
11
+ }
11
12
  if (resolved.root.kind === 'local') {
12
- if (!resolved.exists)
13
+ if (!resolved.exists) {
13
14
  return { error: `no such file: ${dir}` };
15
+ }
14
16
  }
15
17
  else {
16
18
  let hasEntry = false;
@@ -24,8 +26,9 @@ export async function searchTargets(dir, roots, realpath, deny) {
24
26
  }
25
27
  return { targets: [{ root: resolved.root, folder: resolved.key }] };
26
28
  }
27
- if (roots.length === 0)
29
+ if (roots.length === 0) {
28
30
  return { error: NO_DIRS_GRANTED };
31
+ }
29
32
  return { targets: roots.map((root) => ({ root, folder: '/' })) };
30
33
  }
31
34
  export function relativeKey(key, folder) {
@@ -76,13 +79,15 @@ export function createFileGlobTool({ realpath = defaultRealpath, deniedRoots } =
76
79
  async execute(rawArgs, ctx) {
77
80
  const roots = ctx?.fsRoots ?? [];
78
81
  const pattern = String(rawArgs.pattern ?? '').trim();
79
- if (!pattern)
82
+ if (!pattern) {
80
83
  return { error: 'pattern is required' };
84
+ }
81
85
  const limit = rawArgs.limit ? Number(rawArgs.limit) : DEFAULT_GLOB_MATCHES;
82
86
  const deny = await canonicalDenyRoots(deniedRoots, realpath);
83
- const scoped = await searchTargets(rawArgs.dir, roots, realpath, deny);
84
- if ('error' in scoped)
87
+ const scoped = await searchTargets(rawArgs.dir, roots, { realpath, deny });
88
+ if ('error' in scoped) {
85
89
  return scoped;
90
+ }
86
91
  const match = compileGlob(pattern);
87
92
  const matches = [];
88
93
  let truncated = false;
@@ -98,14 +103,14 @@ export function createFileGlobTool({ realpath = defaultRealpath, deniedRoots } =
98
103
  continue;
99
104
  matches.push(file.path);
100
105
  if (matches.length >= limit) {
101
- return { matches, truncated: true, ...(skippedLinks ? { skippedLinks } : {}) };
106
+ return { matches, truncated: true, skippedLinks: skippedLinks || undefined };
102
107
  }
103
108
  }
104
109
  }
105
110
  return {
106
111
  matches,
107
- ...(truncated ? { truncated } : {}),
108
- ...(skippedLinks ? { skippedLinks } : {})
112
+ truncated: truncated || undefined,
113
+ skippedLinks: skippedLinks || undefined
109
114
  };
110
115
  }
111
116
  };