@qvac/harness 0.3.3 → 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 (301) hide show
  1. package/CHANGELOG.md +700 -126
  2. package/README.md +156 -0
  3. package/dist/harness.bundle +71 -103
  4. package/dist/prebuilds-manifest.json +0 -8
  5. package/dist-lib/index.d.ts +42 -40
  6. package/dist-lib/index.js +307 -117
  7. package/dist-lib/lib/audio-transcode-bare.d.ts +2 -2
  8. package/dist-lib/lib/audio-transcode-bare.js +28 -18
  9. package/dist-lib/lib/audio-transcode-node.d.ts +2 -2
  10. package/dist-lib/lib/capabilities/audio-resample.d.ts +7 -6
  11. package/dist-lib/lib/capabilities/audio-resample.js +30 -18
  12. package/dist-lib/lib/capabilities/completion.d.ts +20 -19
  13. package/dist-lib/lib/capabilities/completion.js +194 -135
  14. package/dist-lib/lib/capabilities/download.d.ts +6 -6
  15. package/dist-lib/lib/capabilities/download.js +15 -8
  16. package/dist-lib/lib/capabilities/embed.js +2 -1
  17. package/dist-lib/lib/capabilities/enhance.d.ts +6 -1
  18. package/dist-lib/lib/capabilities/enhance.js +7 -4
  19. package/dist-lib/lib/capabilities/invoke.js +7 -4
  20. package/dist-lib/lib/capabilities/kv-cache.js +2 -1
  21. package/dist-lib/lib/capabilities/preview-model-source.d.ts +69 -0
  22. package/dist-lib/lib/capabilities/preview-model-source.js +439 -0
  23. package/dist-lib/lib/capabilities/speech-text.js +5 -4
  24. package/dist-lib/lib/capabilities/tool-selection.js +10 -9
  25. package/dist-lib/lib/capabilities/tool-turns.js +30 -16
  26. package/dist-lib/lib/capabilities/usage-breakdown.js +2 -1
  27. package/dist-lib/lib/capabilities/voice.d.ts +5 -1
  28. package/dist-lib/lib/capabilities/voice.js +22 -12
  29. package/dist-lib/lib/connect.d.ts +15 -37
  30. package/dist-lib/lib/connect.js +161 -112
  31. package/dist-lib/lib/context-window.js +25 -21
  32. package/dist-lib/lib/contract-types.d.ts +26 -20
  33. package/dist-lib/lib/diagram-repair.d.ts +4 -4
  34. package/dist-lib/lib/diagram-repair.js +6 -3
  35. package/dist-lib/lib/docx/index.d.ts +3 -2
  36. package/dist-lib/lib/{error-fields.d.ts → guards.d.ts} +1 -0
  37. package/dist-lib/lib/{error-fields.js → guards.js} +3 -0
  38. package/dist-lib/lib/hardware/capability-probe.js +20 -10
  39. package/dist-lib/lib/hardware/gpu-classify.js +6 -3
  40. package/dist-lib/lib/hardware/gpu-probe-addon.js +8 -4
  41. package/dist-lib/lib/hardware/gpu-probe.js +4 -2
  42. package/dist-lib/lib/hardware/gpu-stats.native.cjs +8 -9
  43. package/dist-lib/lib/hardware/parsers.js +8 -4
  44. package/dist-lib/lib/helpers/buffer-burst.js +4 -2
  45. package/dist-lib/lib/helpers/control-tokens.js +4 -2
  46. package/dist-lib/lib/helpers/join-guard.d.ts +2 -2
  47. package/dist-lib/lib/helpers/join-guard.js +4 -2
  48. package/dist-lib/lib/helpers/load-stall.js +4 -2
  49. package/dist-lib/lib/helpers/markup-stream-filter.d.ts +2 -2
  50. package/dist-lib/lib/helpers/markup-stream-filter.js +6 -3
  51. package/dist-lib/lib/helpers/race-abort.js +2 -1
  52. package/dist-lib/lib/helpers/race-timeout.d.ts +5 -0
  53. package/dist-lib/lib/helpers/race-timeout.js +14 -0
  54. package/dist-lib/lib/helpers/run-outcome.d.ts +6 -8
  55. package/dist-lib/lib/helpers/run-outcome.js +36 -20
  56. package/dist-lib/lib/helpers/spawn-path.js +6 -3
  57. package/dist-lib/lib/helpers/text-coalescer.js +6 -3
  58. package/dist-lib/lib/helpers/thinking.js +38 -45
  59. package/dist-lib/lib/helpers/tool-call-markup.d.ts +9 -7
  60. package/dist-lib/lib/helpers/tool-call-markup.js +36 -20
  61. package/dist-lib/lib/helpers/tool-intent.d.ts +3 -2
  62. package/dist-lib/lib/helpers/tool-intent.js +4 -2
  63. package/dist-lib/lib/helpers/tool-label.js +18 -9
  64. package/dist-lib/lib/hooks/built-in/arg-repair.js +23 -13
  65. package/dist-lib/lib/hooks/built-in/egress.js +2 -1
  66. package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
  67. package/dist-lib/lib/hooks/pipeline.d.ts +5 -4
  68. package/dist-lib/lib/hooks/pipeline.js +6 -3
  69. package/dist-lib/lib/hooks/run.js +12 -8
  70. package/dist-lib/lib/model-download-progress.js +10 -8
  71. package/dist-lib/lib/models/context.d.ts +1 -1
  72. package/dist-lib/lib/models/context.js +5 -1
  73. package/dist-lib/lib/models/custom-model-cache.d.ts +13 -0
  74. package/dist-lib/lib/models/custom-model-cache.js +70 -0
  75. package/dist-lib/lib/models/gguf-header.d.ts +31 -0
  76. package/dist-lib/lib/models/gguf-header.js +230 -0
  77. package/dist-lib/lib/models/idle-unload.d.ts +14 -0
  78. package/dist-lib/lib/models/idle-unload.js +51 -0
  79. package/dist-lib/lib/models/kv-cache.d.ts +3 -2
  80. package/dist-lib/lib/models/kv-cache.js +4 -3
  81. package/dist-lib/lib/models/llm-presets.d.ts +2 -2
  82. package/dist-lib/lib/models/llm-presets.js +4 -2
  83. package/dist-lib/lib/models/model-catalog-constants.js +3 -2
  84. package/dist-lib/lib/models/model-catalog.d.ts +19 -21
  85. package/dist-lib/lib/models/model-catalog.js +66 -33
  86. package/dist-lib/lib/models/model-delete.d.ts +4 -1
  87. package/dist-lib/lib/models/model-delete.js +30 -15
  88. package/dist-lib/lib/models/model-handle.js +2 -1
  89. package/dist-lib/lib/models/model-list.d.ts +14 -3
  90. package/dist-lib/lib/models/model-list.js +33 -6
  91. package/dist-lib/lib/models/model-pool.d.ts +44 -10
  92. package/dist-lib/lib/models/model-pool.js +47 -42
  93. package/dist-lib/lib/models/model-source.js +11 -6
  94. package/dist-lib/lib/models/music-catalog.d.ts +2 -2
  95. package/dist-lib/lib/models/nmt-catalog.d.ts +4 -3
  96. package/dist-lib/lib/models/nmt-catalog.js +10 -5
  97. package/dist-lib/lib/models/opencl-cache.d.ts +6 -1
  98. package/dist-lib/lib/models/opencl-cache.js +5 -3
  99. package/dist-lib/lib/models/sdk-faults.d.ts +11 -8
  100. package/dist-lib/lib/models/sdk-faults.js +9 -6
  101. package/dist-lib/lib/models/shards.d.ts +11 -0
  102. package/dist-lib/lib/models/shards.js +15 -0
  103. package/dist-lib/lib/models/tool-dialect.js +10 -5
  104. package/dist-lib/lib/no-redirect-fetch-bare.d.ts +3 -3
  105. package/dist-lib/lib/no-redirect-fetch-bare.js +1 -1
  106. package/dist-lib/lib/no-redirect-fetch-impl.js +6 -3
  107. package/dist-lib/lib/no-redirect-fetch-node.d.ts +1 -1
  108. package/dist-lib/lib/no-redirect-fetch-node.js +9 -9
  109. package/dist-lib/lib/office/index.d.ts +3 -2
  110. package/dist-lib/lib/office/python.d.ts +3 -2
  111. package/dist-lib/lib/office/python.js +2 -1
  112. package/dist-lib/lib/prompt.d.ts +5 -4
  113. package/dist-lib/lib/prompt.js +8 -4
  114. package/dist-lib/lib/queue.js +10 -5
  115. package/dist-lib/lib/resolution-detector.js +21 -11
  116. package/dist-lib/lib/rpc-session.js +2 -1
  117. package/dist-lib/lib/sdk-loader.js +2 -1
  118. package/dist-lib/lib/sdk-storage.js +4 -2
  119. package/dist-lib/lib/serve.js +128 -68
  120. package/dist-lib/lib/spawn.js +7 -4
  121. package/dist-lib/lib/tmp-path.d.ts +7 -6
  122. package/dist-lib/lib/tool-call-display.d.ts +2 -2
  123. package/dist-lib/lib/tools/create-documents.js +18 -10
  124. package/dist-lib/lib/tools/edit-documents.js +23 -13
  125. package/dist-lib/lib/tools/edit-image.d.ts +2 -2
  126. package/dist-lib/lib/tools/edit-image.js +60 -36
  127. package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +10 -10
  128. package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +17 -11
  129. package/dist-lib/lib/tools/exec/adapters/python/attachments.js +53 -40
  130. package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +2 -2
  131. package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +60 -32
  132. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts +33 -22
  133. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +58 -29
  134. package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts +2 -2
  135. package/dist-lib/lib/tools/exec/adapters/python/loader.js +23 -12
  136. package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +13 -11
  137. package/dist-lib/lib/tools/exec/adapters/python/memfs.js +10 -5
  138. package/dist-lib/lib/tools/exec/adapters/python/net-shim.js +3 -1
  139. package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +6 -4
  140. package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +20 -10
  141. package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +2 -2
  142. package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +14 -7
  143. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +41 -34
  144. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +316 -166
  145. package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +9 -8
  146. package/dist-lib/lib/tools/exec/adapters/python/run-session.js +56 -28
  147. package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +8 -10
  148. package/dist-lib/lib/tools/exec/adapters/python/wheels.js +22 -11
  149. package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +3 -2
  150. package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +41 -26
  151. package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +27 -18
  152. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -2
  153. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +14 -13
  154. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +4 -4
  155. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +10 -5
  156. package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +4 -4
  157. package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +2 -1
  158. package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +6 -1
  159. package/dist-lib/lib/tools/exec/obsidian/cli-process.js +8 -5
  160. package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +6 -6
  161. package/dist-lib/lib/tools/exec/obsidian/cli.js +4 -4
  162. package/dist-lib/lib/tools/exec/obsidian/command.d.ts +3 -2
  163. package/dist-lib/lib/tools/exec/obsidian/command.js +6 -3
  164. package/dist-lib/lib/tools/exec/obsidian/output.js +2 -1
  165. package/dist-lib/lib/tools/exec/obsidian/route.d.ts +4 -6
  166. package/dist-lib/lib/tools/exec/obsidian/route.js +17 -11
  167. package/dist-lib/lib/tools/exec/obsidian/safe-path.js +2 -1
  168. package/dist-lib/lib/tools/exec/obsidian/vault-create.js +15 -7
  169. package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +2 -2
  170. package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +20 -10
  171. package/dist-lib/lib/tools/exec/obsidian/vault-read.js +20 -13
  172. package/dist-lib/lib/tools/exec/obsidian/vault-write.js +24 -12
  173. package/dist-lib/lib/tools/exec/runner.d.ts +14 -14
  174. package/dist-lib/lib/tools/exec/runner.js +5 -3
  175. package/dist-lib/lib/tools/exec/scope.js +52 -28
  176. package/dist-lib/lib/tools/exec/shell-argv.js +31 -18
  177. package/dist-lib/lib/tools/exec/shell-guidance.d.ts +6 -1
  178. package/dist-lib/lib/tools/exec/shell-guidance.js +35 -20
  179. package/dist-lib/lib/tools/exec/video-tools.js +2 -2
  180. package/dist-lib/lib/tools/exec.d.ts +6 -8
  181. package/dist-lib/lib/tools/exec.js +113 -54
  182. package/dist-lib/lib/tools/file-glob.d.ts +4 -5
  183. package/dist-lib/lib/tools/file-glob.js +16 -11
  184. package/dist-lib/lib/tools/fs/glob.js +13 -18
  185. package/dist-lib/lib/tools/fs/remote-protocol.d.ts +10 -10
  186. package/dist-lib/lib/tools/fs/remote.d.ts +3 -2
  187. package/dist-lib/lib/tools/fs/remote.js +13 -7
  188. package/dist-lib/lib/tools/fs/scope.d.ts +8 -3
  189. package/dist-lib/lib/tools/fs/scope.js +20 -12
  190. package/dist-lib/lib/tools/fs/walk.js +6 -3
  191. package/dist-lib/lib/tools/fs/write.d.ts +3 -2
  192. package/dist-lib/lib/tools/generate-image.d.ts +11 -9
  193. package/dist-lib/lib/tools/generate-image.js +73 -37
  194. package/dist-lib/lib/tools/generate-music.d.ts +22 -11
  195. package/dist-lib/lib/tools/generate-music.js +79 -48
  196. package/dist-lib/lib/tools/grep.js +66 -41
  197. package/dist-lib/lib/tools/http-request/encode-fields.js +18 -9
  198. package/dist-lib/lib/tools/http-request/{utils.js → request.js} +20 -11
  199. package/dist-lib/lib/tools/http-request/truncate-response.js +57 -46
  200. package/dist-lib/lib/tools/http-request.d.ts +1 -1
  201. package/dist-lib/lib/tools/http-request.js +33 -18
  202. package/dist-lib/lib/tools/mcp/protocol.d.ts +53 -0
  203. package/dist-lib/lib/tools/mcp/{utils.js → protocol.js} +45 -23
  204. package/dist-lib/lib/tools/mcp-call.d.ts +1 -1
  205. package/dist-lib/lib/tools/mcp-call.js +37 -16
  206. package/dist-lib/lib/tools/operations/builders.js +10 -6
  207. package/dist-lib/lib/tools/operations/compile.js +20 -10
  208. package/dist-lib/lib/tools/operations/decoders.js +12 -6
  209. package/dist-lib/lib/tools/operations/gmail-message.js +3 -2
  210. package/dist-lib/lib/tools/outbound-guard.d.ts +4 -3
  211. package/dist-lib/lib/tools/outbound-guard.js +14 -8
  212. package/dist-lib/lib/tools/private-ip.js +8 -7
  213. package/dist-lib/lib/tools/rag-search.js +14 -7
  214. package/dist-lib/lib/tools/read-files.js +23 -20
  215. package/dist-lib/lib/tools/skill.js +19 -10
  216. package/dist-lib/lib/tools/skills/auth-resolver.d.ts +11 -18
  217. package/dist-lib/lib/tools/skills/auth-resolver.js +30 -18
  218. package/dist-lib/lib/tools/skills/bin-check.d.ts +3 -2
  219. package/dist-lib/lib/tools/skills/bin-check.js +10 -3
  220. package/dist-lib/lib/tools/skills/bundled-dir.js +20 -10
  221. package/dist-lib/lib/tools/skills/bundled-hash.d.ts +3 -2
  222. package/dist-lib/lib/tools/skills/bundled-hash.js +6 -1
  223. package/dist-lib/lib/tools/skills/catalog.d.ts +3 -2
  224. package/dist-lib/lib/tools/skills/catalog.js +33 -28
  225. package/dist-lib/lib/tools/skills/cli-argv.d.ts +3 -2
  226. package/dist-lib/lib/tools/skills/cli-argv.js +55 -52
  227. package/dist-lib/lib/tools/skills/cli-schema-check.js +21 -10
  228. package/dist-lib/lib/tools/skills/cli-schema-load.js +28 -20
  229. package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +6 -6
  230. package/dist-lib/lib/tools/skills/cli-schema.js +2 -1
  231. package/dist-lib/lib/tools/skills/credential-check.d.ts +6 -3
  232. package/dist-lib/lib/tools/skills/credential-check.js +24 -12
  233. package/dist-lib/lib/tools/skills/credential-grants.js +1 -6
  234. package/dist-lib/lib/tools/skills/credential-health.d.ts +5 -1
  235. package/dist-lib/lib/tools/skills/credential-health.js +5 -3
  236. package/dist-lib/lib/tools/skills/frontmatter-parser.js +10 -5
  237. package/dist-lib/lib/tools/skills/manifest-parse.js +84 -64
  238. package/dist-lib/lib/tools/skills/mention.js +7 -6
  239. package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +2 -2
  240. package/dist-lib/lib/tools/skills/obsidian-readiness.js +11 -3
  241. package/dist-lib/lib/tools/skills/overlay-crud.d.ts +6 -4
  242. package/dist-lib/lib/tools/skills/overlay-crud.js +12 -6
  243. package/dist-lib/lib/tools/skills/overlay-dir.js +4 -2
  244. package/dist-lib/lib/tools/skills/overlay-watch.d.ts +3 -2
  245. package/dist-lib/lib/tools/skills/overlay-watch.js +8 -4
  246. package/dist-lib/lib/tools/skills/setup-routes.js +1 -3
  247. package/dist-lib/lib/tools/skills/skill-settings.js +4 -1
  248. package/dist-lib/lib/tools/skills/skill-state.d.ts +6 -11
  249. package/dist-lib/lib/tools/skills/skill-state.js +48 -29
  250. package/dist-lib/lib/tools/skills/tool-grants.js +6 -3
  251. package/dist-lib/lib/tools/toolsets.js +2 -1
  252. package/dist-lib/lib/tools/translate.d.ts +4 -5
  253. package/dist-lib/lib/tools/translate.js +31 -14
  254. package/dist-lib/lib/tools/url-guard.js +2 -1
  255. package/dist-lib/lib/tools/weather-lookup/place.js +8 -4
  256. package/dist-lib/lib/tools/weather-lookup/report.d.ts +7 -1
  257. package/dist-lib/lib/tools/weather-lookup/report.js +46 -9
  258. package/dist-lib/lib/tools/weather-lookup.js +18 -12
  259. package/dist-lib/lib/tools/web-fetch.js +47 -24
  260. package/dist-lib/lib/tools/web-search.js +8 -3
  261. package/dist-lib/lib/validating-lookup.d.ts +4 -4
  262. package/dist-lib/lib/wav-encode.d.ts +1 -1
  263. package/dist-lib/lib/wav-encode.js +11 -10
  264. package/dist-lib/lib/web-fetch/embedded-state.js +24 -12
  265. package/dist-lib/lib/web-fetch/extract.js +4 -2
  266. package/dist-lib/lib/web-fetch/images.js +8 -4
  267. package/dist-lib/lib/web-fetch/video.d.ts +2 -2
  268. package/dist-lib/lib/web-fetch/video.js +8 -4
  269. package/dist-lib/lib/web-search/engine-registry.d.ts +2 -2
  270. package/dist-lib/lib/web-search/engines/aol.js +1 -1
  271. package/dist-lib/lib/web-search/engines/bing.js +1 -1
  272. package/dist-lib/lib/web-search/engines/brave.js +4 -1
  273. package/dist-lib/lib/web-search/engines/duckduckgo.js +3 -2
  274. package/dist-lib/lib/web-search/engines/google.js +7 -3
  275. package/dist-lib/lib/web-search/engines/wikipedia.js +5 -4
  276. package/dist-lib/lib/web-search/engines/yahoo.js +1 -1
  277. package/dist-lib/lib/web-search/fetch-html.d.ts +5 -4
  278. package/dist-lib/lib/web-search/fetch-html.js +13 -7
  279. package/dist-lib/lib/web-search/orchestrator.d.ts +9 -5
  280. package/dist-lib/lib/web-search/orchestrator.js +3 -3
  281. package/dist-lib/lib/wire.d.ts +3 -2
  282. package/dist-lib/lib/wire.js +23 -15
  283. package/dist-lib/schema/baseline.js +10 -5
  284. package/dist-lib/schema/build.js +2 -1
  285. package/dist-lib/schema/harness.d.ts +15 -2
  286. package/dist-lib/schema/harness.js +55 -2
  287. package/dist-lib/schema/release-tags.d.ts +3 -2
  288. package/dist-lib/schema/release-tags.js +6 -3
  289. package/dist-lib/spec/bind.js +6 -0
  290. package/dist-lib/spec/calls.js +2 -0
  291. package/dist-lib/spec/hrpc/hrpc.json +26 -0
  292. package/dist-lib/spec/hrpc/index.js +27 -3
  293. package/dist-lib/spec/hrpc/messages.js +207 -8
  294. package/dist-lib/spec/hyperschema/index.js +207 -8
  295. package/dist-lib/spec/hyperschema/schema.json +165 -1
  296. package/dist-lib/spec/hyperschema/types.d.ts +42 -1
  297. package/package.json +13 -14
  298. package/worker/harness-sidecar.mjs +16 -22
  299. package/worker/stow-entry.mjs +7 -4
  300. package/dist-lib/lib/tools/mcp/utils.d.ts +0 -47
  301. /package/dist-lib/lib/tools/http-request/{utils.d.ts → request.d.ts} +0 -0
@@ -19,18 +19,36 @@ export function supportsPlatform(entry, platform) {
19
19
  }
20
20
  export function isModelCompatible(entry, activeProfile) {
21
21
  const required = entry.minCapabilityProfile;
22
- if (!required || activeProfile === undefined || !isCapabilityProfile(required))
22
+ if (!required || activeProfile === undefined || !isCapabilityProfile(required)) {
23
23
  return true;
24
+ }
24
25
  return meetsMinCapabilityProfile(activeProfile, required);
25
26
  }
27
+ function slotHealth(slot, { records, isRejected }) {
28
+ let bad = false;
29
+ for (const key of slot) {
30
+ const record = records.get(key);
31
+ const expired = !!record && isCredentialExpired(record);
32
+ if (record && !isRejected(key) && !expired) {
33
+ return 'healthy';
34
+ }
35
+ if (isRejected(key) || expired) {
36
+ bad = true;
37
+ }
38
+ }
39
+ return bad ? 'bad' : 'missing';
40
+ }
26
41
  export async function resolveSkillState(entry, inputs = {}) {
27
42
  try {
28
- if (!supportsPlatform(entry, inputs.platform))
43
+ if (!supportsPlatform(entry, inputs.platform)) {
29
44
  return SKILL_STATE.unsupported;
30
- if (isStub(entry))
45
+ }
46
+ if (isStub(entry)) {
31
47
  return SKILL_STATE.disabled;
32
- if (!isModelCompatible(entry, inputs.activeProfile))
48
+ }
49
+ if (!isModelCompatible(entry, inputs.activeProfile)) {
33
50
  return SKILL_STATE.incompatibleModel;
51
+ }
34
52
  const bins = entry.requires?.bins ?? [];
35
53
  if (entry.name === OBSIDIAN_SKILL_NAME) {
36
54
  const vault = inputs.credentialStore
@@ -39,10 +57,12 @@ export async function resolveSkillState(entry, inputs = {}) {
39
57
  skills: [entry.name]
40
58
  }))
41
59
  : null;
42
- if (vault && isObsidianVaultPath(vault.token))
60
+ if (vault && isObsidianVaultPath(vault.token)) {
43
61
  return SKILL_STATE.ready;
44
- if (isSandboxedWorkbenchInstall())
62
+ }
63
+ if (isSandboxedWorkbenchInstall()) {
45
64
  return SKILL_STATE.needsSetup;
65
+ }
46
66
  }
47
67
  let missing = bins.length > 0 ? missingBins(bins, inputs.platform, inputs.binOnPath) : [];
48
68
  if (entry.name === OBSIDIAN_SKILL_NAME) {
@@ -52,8 +72,9 @@ export async function resolveSkillState(entry, inputs = {}) {
52
72
  obsidianInstallState(inputs.platform, inputs.obsidianProbe) === 'installed') {
53
73
  missing = missing.filter((bin) => bin !== 'obsidian');
54
74
  }
55
- if (missing.length === 0 && cli === 'disabled')
75
+ if (missing.length === 0 && cli === 'disabled') {
56
76
  return SKILL_STATE.needsSetup;
77
+ }
57
78
  }
58
79
  if (missing.length > 0) {
59
80
  return SKILL_STATE.needsSetup;
@@ -67,18 +88,22 @@ export async function resolveSkillState(entry, inputs = {}) {
67
88
  const settings = inputs.settingsStore
68
89
  ? await inputs.settingsStore(entry.name, inputs.agentId)
69
90
  : {};
70
- if (required.some((key) => !settings[key]))
91
+ if (required.some((key) => !settings[key])) {
71
92
  return SKILL_STATE.needsSetup;
93
+ }
72
94
  }
73
95
  const slots = credentialSlots(entry);
74
- if (slots.length === 0)
96
+ if (slots.length === 0) {
75
97
  return SKILL_STATE.ready;
76
- const isRejected = inputs.rejected
77
- ? (key) => inputs.rejected.has(key)
98
+ }
99
+ const { rejected } = inputs;
100
+ const isRejected = rejected
101
+ ? (key) => rejected.has(key)
78
102
  : (key) => isCredentialRejected(key, inputs.agentId, entry.name);
79
103
  const store = inputs.credentialStore;
80
- if (!store)
104
+ if (!store) {
81
105
  return SKILL_STATE.needsSetup;
106
+ }
82
107
  const records = new Map(await Promise.all([...new Set(slots.flat())].map(async (key) => [
83
108
  key,
84
109
  credentialRecord(await store(key, { agentId: inputs.agentId, skills: [entry.name] }))
@@ -86,29 +111,23 @@ export async function resolveSkillState(entry, inputs = {}) {
86
111
  let disconnected = false;
87
112
  let needsSetup = false;
88
113
  for (const slot of slots) {
89
- let healthy = false;
90
- let bad = false;
91
- for (const key of slot) {
92
- const record = records.get(key);
93
- const expired = !!record && isCredentialExpired(record);
94
- if (record && !isRejected(key) && !expired) {
95
- healthy = true;
96
- break;
97
- }
98
- if (isRejected(key) || expired)
99
- bad = true;
100
- }
101
- if (healthy)
114
+ const health = slotHealth(slot, { records, isRejected });
115
+ if (health === 'healthy') {
102
116
  continue;
103
- if (bad)
117
+ }
118
+ if (health === 'bad') {
104
119
  disconnected = true;
105
- else
120
+ }
121
+ else {
106
122
  needsSetup = true;
123
+ }
107
124
  }
108
- if (disconnected)
125
+ if (disconnected) {
109
126
  return SKILL_STATE.disconnected;
110
- if (needsSetup)
127
+ }
128
+ if (needsSetup) {
111
129
  return SKILL_STATE.needsSetup;
130
+ }
112
131
  return SKILL_STATE.ready;
113
132
  }
114
133
  catch {
@@ -2,13 +2,16 @@ const GRANT_RE = /^([\w-]+)\((.*)\)$/;
2
2
  export function parseToolGrant(entry) {
3
3
  const trimmed = entry.trim();
4
4
  const match = GRANT_RE.exec(trimmed);
5
- if (!match)
5
+ if (!match) {
6
6
  return { name: trimmed, scope: null };
7
+ }
7
8
  const name = match[1] ?? '';
8
9
  const scope = (match[2] ?? '').trim();
9
- if (!name)
10
+ if (!name) {
10
11
  return { name: trimmed, scope: null };
11
- if (!scope)
12
+ }
13
+ if (!scope) {
12
14
  return { name, scope: null };
15
+ }
13
16
  return { name, scope };
14
17
  }
@@ -11,8 +11,9 @@ export function asToolset(value) {
11
11
  }
12
12
  export const NETWORK_TOOLSETS = ['web-search', 'http-request', 'mcp-call'];
13
13
  export function permittedTools(tools, disabled) {
14
- if (!disabled?.length)
14
+ if (!disabled?.length) {
15
15
  return [...tools];
16
+ }
16
17
  const off = new Set(disabled);
17
18
  return tools.filter((tool) => !tool.toolset || !off.has(tool.toolset));
18
19
  }
@@ -65,12 +65,11 @@ export interface TranslateArgs {
65
65
  to: string;
66
66
  from?: string;
67
67
  }
68
- export declare function parseTranslateArgs(args: Record<string, unknown>): TranslateArgs | {
68
+ export interface TranslateError {
69
69
  error: string;
70
- };
71
- export declare function sourceLanguage(text: string, from?: string): string | {
72
- error: string;
73
- };
70
+ }
71
+ export declare function parseTranslateArgs(args: Record<string, unknown>): TranslateArgs | TranslateError;
72
+ export declare function sourceLanguage(text: string, from?: string): string | TranslateError;
74
73
  export interface TranslateToolOptions {
75
74
  slm?: SlmTranslateFn;
76
75
  }
@@ -10,17 +10,20 @@ export const TRANSLATION_LOAD_STALL_MS = 15 * 60_000;
10
10
  export const TRANSLATION_LOAD_STALL_ERROR = 'translation model download stalled';
11
11
  function packName(plan) {
12
12
  const pair = plan.model.match(/model\.([a-z]+)\.intgemm/)?.[1];
13
- if (pair)
13
+ if (pair) {
14
14
  return `Bergamot ${pair}`;
15
+ }
15
16
  return plan.model.includes('/en-xx/') ? 'EuroNano en-xx' : 'EuroNano xx-en';
16
17
  }
17
18
  const fingerprintOf = (plan) => `${plan.model}|${plan.pivot ?? ''}`;
18
19
  function bergamotConfig(plan) {
19
20
  const config = { engine: 'Bergamot', from: plan.from, to: plan.to };
20
- if (plan.srcVocab)
21
+ if (plan.srcVocab) {
21
22
  config.srcVocabSrc = plan.srcVocab;
22
- if (plan.dstVocab)
23
+ }
24
+ if (plan.dstVocab) {
23
25
  config.dstVocabSrc = plan.dstVocab;
26
+ }
24
27
  return config;
25
28
  }
26
29
  export function createTranslationRuntime({ loadSdk, loads = createLoadTracker(), onProgress, stallMs = TRANSLATION_LOAD_STALL_MS }) {
@@ -54,7 +57,7 @@ export function createTranslationRuntime({ loadSdk, loads = createLoadTracker(),
54
57
  if (err instanceof Error &&
55
58
  err.message === TRANSLATION_LOAD_STALL_ERROR &&
56
59
  pending.requestId) {
57
- void sdk.cancel?.({ requestId: pending.requestId }).catch(() => { });
60
+ void sdk.cancel?.({ requestId: pending.requestId }).catch(noop);
58
61
  }
59
62
  throw err;
60
63
  }
@@ -94,8 +97,9 @@ export function createTranslationRuntime({ loadSdk, loads = createLoadTracker(),
94
97
  const fingerprint = fingerprintOf(plan);
95
98
  await lock.lock();
96
99
  try {
97
- if (loading && residentFingerprint !== fingerprint)
100
+ if (loading && residentFingerprint !== fingerprint) {
98
101
  await doUnload();
102
+ }
99
103
  residentFingerprint = fingerprint;
100
104
  loading ??= loadPack(sdk, plan).catch((err) => {
101
105
  loading = null;
@@ -124,19 +128,22 @@ export function createTranslationRuntime({ loadSdk, loads = createLoadTracker(),
124
128
  }
125
129
  export function stripLanguageLabel(text, ...codes) {
126
130
  const at = text.indexOf(':');
127
- if (at === -1)
131
+ if (at === -1) {
128
132
  return text;
133
+ }
129
134
  const head = text.slice(0, at).trim();
130
135
  const labels = codes.map((code) => languageLabel(code) ?? getLangName(code));
131
136
  return labels.includes(head) ? text.slice(at + 1).trim() : text;
132
137
  }
133
138
  export function parseTranslateArgs(args) {
134
139
  const text = typeof args.text === 'string' ? args.text.trim() : '';
135
- if (!text)
140
+ if (!text) {
136
141
  return { error: 'text must be a non-empty string' };
142
+ }
137
143
  const to = typeof args.to === 'string' ? args.to.trim() : '';
138
- if (!to)
144
+ if (!to) {
139
145
  return { error: 'to must be a language code' };
146
+ }
140
147
  const from = typeof args.from === 'string' ? args.from.trim() : undefined;
141
148
  return { text, to, from };
142
149
  }
@@ -144,8 +151,9 @@ export function sourceLanguage(text, from) {
144
151
  if (from)
145
152
  return from;
146
153
  const [{ code }] = detectLanguage({ text });
147
- if (code === UNDETERMINED)
154
+ if (code === UNDETERMINED) {
148
155
  return { error: NEEDS_SOURCE };
156
+ }
149
157
  return code;
150
158
  }
151
159
  const NEEDS_SOURCE = 'could not detect the source language; pass `from` explicitly';
@@ -180,17 +188,20 @@ export function createTranslateTool(runtime, { slm } = {}) {
180
188
  resource: (args) => ({ kind: 'translation', label: String(args.to ?? '') }),
181
189
  execute: async (args, ctx) => {
182
190
  const parsed = parseTranslateArgs(args);
183
- if ('error' in parsed)
191
+ if ('error' in parsed) {
184
192
  return parsed;
193
+ }
185
194
  const from = sourceLanguage(parsed.text, parsed.from);
186
- if (typeof from === 'object')
195
+ if (typeof from === 'object') {
187
196
  return from;
197
+ }
188
198
  if (sameLanguage(from, parsed.to)) {
189
199
  return { text: parsed.text, from, to: parsed.to, engine: 'none' };
190
200
  }
191
201
  const plan = translationPlanFor(from, parsed.to);
192
- if ('error' in plan)
202
+ if ('error' in plan) {
193
203
  return plan;
204
+ }
194
205
  if (plan.route === 'slm' && !slm) {
195
206
  return { error: `this device cannot translate ${from} to ${parsed.to}` };
196
207
  }
@@ -199,6 +210,9 @@ export function createTranslateTool(runtime, { slm } = {}) {
199
210
  await runtime.enter(ctx?.signal);
200
211
  entered = true;
201
212
  if (plan.route === 'slm') {
213
+ if (!slm) {
214
+ throw new Error('slm translation is unavailable');
215
+ }
202
216
  const text = await slm(plan.model, { text: parsed.text, from: plan.from, to: plan.to }, ctx?.signal);
203
217
  return {
204
218
  text: stripLanguageLabel(text, plan.from, plan.to),
@@ -215,14 +229,17 @@ export function createTranslateTool(runtime, { slm } = {}) {
215
229
  return { text, from, to: parsed.to, engine: 'nmt', pivoted: Boolean(plan.pivot) };
216
230
  }
217
231
  catch (err) {
218
- if (err instanceof AbortedError)
232
+ if (err instanceof AbortedError) {
219
233
  return { error: 'translation canceled' };
234
+ }
220
235
  throw err;
221
236
  }
222
237
  finally {
223
- if (entered)
238
+ if (entered) {
224
239
  runtime.exit();
240
+ }
225
241
  }
226
242
  }
227
243
  };
228
244
  }
245
+ function noop() { }
@@ -24,8 +24,9 @@ export async function resolveOutboundUrl(url, resolveHost, signal) {
24
24
  addresses = await raceAbort(resolveHost(host), signal);
25
25
  }
26
26
  catch (err) {
27
- if (err instanceof AbortedError)
27
+ if (err instanceof AbortedError) {
28
28
  throw err;
29
+ }
29
30
  return { blocked: `could not resolve host: ${host}` };
30
31
  }
31
32
  if (addresses.length === 0) {
@@ -107,16 +107,18 @@ export function describePlace(place) {
107
107
  }
108
108
  export function qualifierOf(location) {
109
109
  const comma = location.indexOf(',');
110
- if (comma === -1)
110
+ if (comma === -1) {
111
111
  return null;
112
+ }
112
113
  const qualifier = fold(location.slice(comma + 1));
113
114
  return qualifier.length > 0 ? qualifier : null;
114
115
  }
115
116
  function expand(qualifier) {
116
117
  const code = qualifier.replace(/ /g, '');
117
118
  const subdivision = US_STATES[code] ?? OTHER_SUBDIVISIONS[code];
118
- if (subdivision !== undefined)
119
+ if (subdivision !== undefined) {
119
120
  return subdivision;
121
+ }
120
122
  for (const [alias, full] of QUALIFIER_ALIASES) {
121
123
  if (qualifier === fold(alias))
122
124
  return full;
@@ -143,10 +145,12 @@ export function checkQualifier(qualifier, place) {
143
145
  continue;
144
146
  if (initialsOf(candidate) === code)
145
147
  return 'match';
146
- if (opaque)
148
+ if (opaque) {
147
149
  continue;
148
- if (candidate.includes(wanted) || wanted.includes(candidate))
150
+ }
151
+ if (candidate.includes(wanted) || wanted.includes(candidate)) {
149
152
  return 'match';
153
+ }
150
154
  }
151
155
  return opaque ? 'unverifiable' : 'contradiction';
152
156
  }
@@ -1,3 +1,4 @@
1
+ import { z } from 'zod';
1
2
  import { type ResolvedPlace } from './place.ts';
2
3
  interface Translated {
3
4
  readonly value: string;
@@ -32,6 +33,11 @@ export interface WeatherJson {
32
33
  readonly current_condition?: ReadonlyArray<CurrentCondition>;
33
34
  readonly weather?: ReadonlyArray<DailyForecast>;
34
35
  }
36
+ export declare const WeatherJsonSchema: z.ZodType<WeatherJson>;
35
37
  export declare function placeOf(json: WeatherJson): ResolvedPlace | null;
36
- export declare function renderReport(json: WeatherJson, place: ResolvedPlace, asked: string, verified: boolean): string;
38
+ export interface RenderReportOptions {
39
+ readonly asked: string;
40
+ readonly verified: boolean;
41
+ }
42
+ export declare function renderReport(json: WeatherJson, place: ResolvedPlace, { asked, verified }: RenderReportOptions): string;
37
43
  export {};
@@ -1,4 +1,33 @@
1
+ import { z } from 'zod';
1
2
  import { describePlace } from './place.js';
3
+ const TranslatedSchema = z.array(z.object({ value: z.string() })).optional();
4
+ export const WeatherJsonSchema = z.object({
5
+ nearest_area: z
6
+ .array(z.object({ areaName: TranslatedSchema, region: TranslatedSchema, country: TranslatedSchema }))
7
+ .optional(),
8
+ current_condition: z
9
+ .array(z.object({
10
+ temp_C: z.string().optional(),
11
+ temp_F: z.string().optional(),
12
+ FeelsLikeC: z.string().optional(),
13
+ humidity: z.string().optional(),
14
+ windspeedKmph: z.string().optional(),
15
+ weatherDesc: TranslatedSchema
16
+ }))
17
+ .optional(),
18
+ weather: z
19
+ .array(z.object({
20
+ date: z.string().optional(),
21
+ maxtempC: z.string().optional(),
22
+ mintempC: z.string().optional(),
23
+ maxtempF: z.string().optional(),
24
+ mintempF: z.string().optional(),
25
+ hourly: z
26
+ .array(z.object({ time: z.string().optional(), weatherDesc: TranslatedSchema }))
27
+ .optional()
28
+ }))
29
+ .optional()
30
+ });
2
31
  const MAX_DAYS = 3;
3
32
  const DAY_LABELS = ['today', 'tomorrow'];
4
33
  const RANGE = '\u2013';
@@ -8,8 +37,9 @@ function firstValue(values) {
8
37
  }
9
38
  export function placeOf(json) {
10
39
  const area = json.nearest_area?.[0];
11
- if (area === undefined)
40
+ if (area === undefined) {
12
41
  return null;
42
+ }
13
43
  const place = {
14
44
  area: firstValue(area.areaName),
15
45
  region: firstValue(area.region),
@@ -17,33 +47,40 @@ export function placeOf(json) {
17
47
  };
18
48
  return place.area || place.region || place.country ? place : null;
19
49
  }
20
- export function renderReport(json, place, asked, verified) {
50
+ export function renderReport(json, place, { asked, verified }) {
21
51
  const lines = [headline(place, asked, verified)];
22
52
  const now = json.current_condition?.[0];
23
- if (now !== undefined)
53
+ if (now !== undefined) {
24
54
  lines.push(currentLine(now));
55
+ }
25
56
  const days = json.weather?.slice(0, MAX_DAYS) ?? [];
26
- for (const [index, day] of days.entries())
57
+ for (const [index, day] of days.entries()) {
27
58
  lines.push(dailyLine(day, index));
59
+ }
28
60
  return lines.join('\n');
29
61
  }
30
62
  function headline(place, asked, verified) {
31
63
  const resolved = describePlace(place);
32
- if (verified)
64
+ if (verified) {
33
65
  return resolved;
66
+ }
34
67
  return `${resolved} (not "${asked}" — this is where it resolved)`;
35
68
  }
36
69
  function currentLine(now) {
37
70
  const parts = [`Now: ${now.temp_C ?? '?'}°C / ${now.temp_F ?? '?'}°F`];
38
71
  const desc = firstValue(now.weatherDesc);
39
- if (desc)
72
+ if (desc) {
40
73
  parts.push(desc);
41
- if (now.FeelsLikeC !== undefined)
74
+ }
75
+ if (now.FeelsLikeC !== undefined) {
42
76
  parts.push(`feels like ${now.FeelsLikeC}°C`);
43
- if (now.humidity !== undefined)
77
+ }
78
+ if (now.humidity !== undefined) {
44
79
  parts.push(`humidity ${now.humidity}%`);
45
- if (now.windspeedKmph !== undefined)
80
+ }
81
+ if (now.windspeedKmph !== undefined) {
46
82
  parts.push(`wind ${now.windspeedKmph} km/h`);
83
+ }
47
84
  return parts.join(', ');
48
85
  }
49
86
  function dailyLine(day, index) {
@@ -1,10 +1,11 @@
1
- import defaultFetch from '#no-redirect-fetch';
1
+ import { noRedirectFetch } from '#no-redirect-fetch';
2
2
  import { AbortController } from 'bare-abort-controller';
3
3
  import { lookupHost } from '../dns-lookup.js';
4
- import { isRedirectStatus } from './http-request/utils.js';
4
+ import { errorMessage } from '../guards.js';
5
+ import { isRedirectStatus } from './http-request/request.js';
5
6
  import { resolveOutboundUrl } from './url-guard.js';
6
7
  import { checkQualifier, cityMatches, describePlace, qualifierOf } from './weather-lookup/place.js';
7
- import { placeOf, renderReport } from './weather-lookup/report.js';
8
+ import { placeOf, renderReport, WeatherJsonSchema } from './weather-lookup/report.js';
8
9
  const WEATHER_HOST = 'wttr.in';
9
10
  const LOOKUP_TIMEOUT_MS = 15_000;
10
11
  const MAX_BODY_BYTES = 400_000;
@@ -36,18 +37,20 @@ export function createWeatherLookupTool({ fetch: fetchImpl, resolveHost = lookup
36
37
  return { error: 'location must be a non-empty string' };
37
38
  }
38
39
  const location = raw.trim();
39
- if (ctx?.signal?.aborted)
40
+ if (ctx?.signal?.aborted) {
40
41
  return { error: 'weather_lookup cancelled' };
42
+ }
41
43
  const url = new URL(`https://${WEATHER_HOST}/${encodeURIComponent(location)}?format=j1`);
42
- const fetch = fetchImpl ?? defaultFetch;
44
+ const fetch = fetchImpl ?? noRedirectFetch;
43
45
  const controller = new AbortController();
44
46
  const timeout = setTimeout(() => controller.abort(undefined), LOOKUP_TIMEOUT_MS);
45
47
  const onCallerAbort = () => controller.abort(undefined);
46
48
  ctx?.signal?.addEventListener('abort', onCallerAbort);
47
49
  try {
48
50
  const check = await resolveOutboundUrl(url, resolveHost, ctx?.signal);
49
- if (check.blocked)
51
+ if (check.blocked) {
50
52
  return { error: check.blocked };
53
+ }
51
54
  const response = await fetch(url, {
52
55
  method: 'GET',
53
56
  headers: { Accept: 'application/json' },
@@ -60,21 +63,23 @@ export function createWeatherLookupTool({ fetch: fetchImpl, resolveHost = lookup
60
63
  return { error: `${WEATHER_HOST} redirected the lookup for "${location}"` };
61
64
  }
62
65
  const body = await response.text();
63
- if (/location not found/i.test(body))
66
+ if (/location not found/i.test(body)) {
64
67
  return { content: notFound(location) };
68
+ }
65
69
  if (response.status !== 200) {
66
70
  return { error: unavailable(`it answered HTTP ${response.status}`), code: 'transport' };
67
71
  }
68
72
  let json;
69
73
  try {
70
- json = JSON.parse(body);
74
+ json = WeatherJsonSchema.parse(JSON.parse(body));
71
75
  }
72
76
  catch {
73
77
  return { error: unavailable('it did not return a forecast') };
74
78
  }
75
79
  const place = placeOf(json);
76
- if (place === null)
80
+ if (place === null) {
77
81
  return { content: notFound(location) };
82
+ }
78
83
  const qualifier = qualifierOf(location);
79
84
  const verdict = qualifier === null ? null : checkQualifier(qualifier, place);
80
85
  const named = cityMatches(location, place);
@@ -85,11 +90,12 @@ export function createWeatherLookupTool({ fetch: fetchImpl, resolveHost = lookup
85
90
  };
86
91
  }
87
92
  const verified = qualifier === null ? named : verdict === 'match';
88
- return { content: renderReport(json, place, location, verified) };
93
+ return { content: renderReport(json, place, { asked: location, verified }) };
89
94
  }
90
95
  catch (err) {
91
- if (ctx?.signal?.aborted)
96
+ if (ctx?.signal?.aborted) {
92
97
  return { error: 'weather_lookup cancelled' };
98
+ }
93
99
  if (controller.signal.aborted) {
94
100
  return {
95
101
  error: `weather_lookup timed out after ${LOOKUP_TIMEOUT_MS} ms`,
@@ -97,7 +103,7 @@ export function createWeatherLookupTool({ fetch: fetchImpl, resolveHost = lookup
97
103
  };
98
104
  }
99
105
  return {
100
- error: unavailable(err instanceof Error ? err.message : String(err)),
106
+ error: unavailable(errorMessage(err)),
101
107
  code: 'transport'
102
108
  };
103
109
  }