@qvac/harness 0.4.0 → 0.6.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 +740 -141
  2. package/README.md +75 -7
  3. package/dist/harness.bundle +72 -50
  4. package/dist-lib/index.d.ts +49 -39
  5. package/dist-lib/index.js +448 -161
  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 +13 -32
  29. package/dist-lib/lib/connect.js +170 -112
  30. package/dist-lib/lib/context-window.js +25 -21
  31. package/dist-lib/lib/contract-types.d.ts +35 -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 +15 -20
  84. package/dist-lib/lib/models/model-catalog.js +66 -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 +48 -10
  90. package/dist-lib/lib/models/model-pool.js +106 -45
  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 +122 -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 +39 -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 +9 -0
  288. package/dist-lib/spec/calls.js +3 -0
  289. package/dist-lib/spec/hrpc/hrpc.json +39 -0
  290. package/dist-lib/spec/hrpc/index.js +39 -3
  291. package/dist-lib/spec/hrpc/messages.js +105 -8
  292. package/dist-lib/spec/hyperschema/index.js +105 -8
  293. package/dist-lib/spec/hyperschema/schema.json +77 -1
  294. package/dist-lib/spec/hyperschema/types.d.ts +25 -1
  295. package/package.json +14 -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
@@ -1,5 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import fs from 'fs';
3
+ import { z } from 'zod';
3
4
  import { settleOnClose } from './rpc-session.js';
4
5
  import { harnessClient } from '../spec/calls.js';
5
6
  import HRPC from '../spec/hrpc/index.js';
@@ -7,7 +8,7 @@ import { unsupportedKvCacheDeletion } from './capabilities/kv-cache.js';
7
8
  import { readModelSourcePreview } from './capabilities/preview-model-source.js';
8
9
  import { hlog } from './log.js';
9
10
  import { assertEmbedDevice } from './models/embed-config.js';
10
- import { errorCode, errorMessage } from './error-fields.js';
11
+ import { errorCode, errorMessage, isRecord } from './guards.js';
11
12
  import { pushQueue } from './queue.js';
12
13
  import { stageFile, sweepStaging } from './staging.js';
13
14
  import { readInstall, readSetup } from './tools/skills/manifest-parse.js';
@@ -19,9 +20,10 @@ export function connectHarness(transport) {
19
20
  return new RemoteHarness(transport);
20
21
  }
21
22
  export function readEmbeddingDeviceResponse(response) {
22
- const device = typeof response === 'object' && response !== null ? Reflect.get(response, 'device') : undefined;
23
- if (typeof device !== 'string')
23
+ const device = isRecord(response) ? response.device : undefined;
24
+ if (typeof device !== 'string') {
24
25
  throw new Error('invalid embedding device response');
26
+ }
25
27
  assertEmbedDevice(device);
26
28
  return device;
27
29
  }
@@ -36,8 +38,9 @@ export class RemoteHarness extends EventEmitter {
36
38
  this._transport = transport;
37
39
  }
38
40
  _ensure() {
39
- if (this._closed)
41
+ if (this._closed) {
40
42
  return Promise.reject(died('harness closed'));
43
+ }
41
44
  if (this._session) {
42
45
  return this._session.dead
43
46
  ? Promise.reject(died('harness is dead'))
@@ -72,8 +75,9 @@ export class RemoteHarness extends EventEmitter {
72
75
  });
73
76
  stream.on('close', () => {
74
77
  session.dead = true;
75
- if (this._closed)
78
+ if (this._closed) {
76
79
  return;
80
+ }
77
81
  this.emit('status', streamError ? { state: 'died', ...errorDetails(streamError) } : { state: 'died' });
78
82
  });
79
83
  this._session = session;
@@ -138,8 +142,9 @@ export class RemoteHarness extends EventEmitter {
138
142
  let closed = false;
139
143
  let received = 0;
140
144
  const reclaim = () => {
141
- while (staged.length)
142
- void fs.promises.unlink(staged.pop()).catch(noop);
145
+ for (const file of staged.splice(0).reverse()) {
146
+ void fs.promises.unlink(file).catch(noop);
147
+ }
143
148
  };
144
149
  const onAbort = () => duplex?.destroy();
145
150
  try {
@@ -157,18 +162,19 @@ export class RemoteHarness extends EventEmitter {
157
162
  if (event.type !== 'content' && event.type !== 'thinking') {
158
163
  hlog('client frame', { type: event.type, n: received });
159
164
  }
160
- if (event.type === 'done')
165
+ if (event.type === 'done') {
161
166
  break;
167
+ }
162
168
  if (event.type === 'approval-ask') {
163
169
  const callId = event.callId;
164
170
  void Promise.resolve()
165
171
  .then(() => context.approve?.({
166
172
  name: event.name ?? '',
167
- arguments: (event.arguments ?? {}),
173
+ arguments: isRecord(event.arguments) ? event.arguments : {},
168
174
  resource: event.resource ?? null,
169
175
  effect: event.effect === 'read' ? 'read' : 'write',
170
176
  intent: event.intent || null,
171
- display: event.display ?? null
177
+ display: isToolCallDisplay(event.display) ? event.display : null
172
178
  }))
173
179
  .then((approved) => {
174
180
  if (!closed) {
@@ -186,8 +192,9 @@ export class RemoteHarness extends EventEmitter {
186
192
  const callId = event.callId;
187
193
  void bridgeAttach(event, context.attach)
188
194
  .then((frame) => {
189
- if (!closed)
195
+ if (!closed) {
190
196
  duplex.write({ ...frame, callId });
197
+ }
191
198
  }, (err) => {
192
199
  if (!closed) {
193
200
  duplex.write({
@@ -204,12 +211,14 @@ export class RemoteHarness extends EventEmitter {
204
211
  const callId = event.callId;
205
212
  void bridgeFetch(event, context, staged)
206
213
  .then((frame) => {
207
- if (closed)
214
+ if (closed) {
208
215
  return reclaim();
216
+ }
209
217
  duplex.write({ ...frame, callId });
210
218
  }, (err) => {
211
- if (closed)
219
+ if (closed) {
212
220
  return reclaim();
221
+ }
213
222
  duplex.write({ type: 'fetch-result', callId, message: errorMessage(err) });
214
223
  })
215
224
  .catch((err) => hlog('fetch reply failed', { callId, message: errorMessage(err) }));
@@ -219,11 +228,13 @@ export class RemoteHarness extends EventEmitter {
219
228
  const callId = event.callId;
220
229
  void bridgeDrive(event, context)
221
230
  .then((frame) => {
222
- if (!closed)
231
+ if (!closed) {
223
232
  return duplex.write({ ...frame, callId });
233
+ }
224
234
  const stagedPath = frame.payload?.path;
225
- if (stagedPath)
235
+ if (stagedPath) {
226
236
  void fs.promises.unlink(stagedPath).catch(noop);
237
+ }
227
238
  }, (err) => {
228
239
  if (!closed) {
229
240
  duplex.write({
@@ -240,8 +251,9 @@ export class RemoteHarness extends EventEmitter {
240
251
  const callId = event.callId;
241
252
  void bridgeRag(event, context)
242
253
  .then((frame) => {
243
- if (!closed)
254
+ if (!closed) {
244
255
  duplex.write({ ...frame, callId });
256
+ }
245
257
  }, (err) => {
246
258
  if (!closed) {
247
259
  duplex.write({
@@ -255,39 +267,14 @@ export class RemoteHarness extends EventEmitter {
255
267
  continue;
256
268
  }
257
269
  if (event.type === 'question-ask') {
258
- const callId = event.callId;
259
- const askUser = context.askUser;
260
- if (!askUser) {
261
- if (!closed) {
262
- duplex.write({
263
- type: 'question-result',
264
- callId,
265
- message: 'the host cannot ask questions'
266
- });
267
- }
268
- continue;
269
- }
270
- void Promise.resolve()
271
- .then(() => askUser({
272
- question: event.question ?? '',
273
- ...(Array.isArray(event.options)
274
- ? { options: event.options.map(String) }
275
- : {})
276
- }))
277
- .then((answer) => {
278
- if (!closed) {
279
- duplex.write({ type: 'question-result', callId, answer: answer ?? '' });
280
- }
281
- }, (err) => {
282
- if (!closed) {
283
- duplex.write({
284
- type: 'question-result',
285
- callId,
286
- message: errorMessage(err)
287
- });
270
+ answerQuestion(event, {
271
+ askUser: context.askUser,
272
+ reply: (frame) => {
273
+ if (!closed) {
274
+ duplex.write(frame);
275
+ }
288
276
  }
289
- })
290
- .catch((err) => hlog('question reply failed', { callId, message: errorMessage(err) }));
277
+ });
291
278
  continue;
292
279
  }
293
280
  if (event.type === 'progress') {
@@ -295,19 +282,21 @@ export class RemoteHarness extends EventEmitter {
295
282
  continue;
296
283
  }
297
284
  const wire = fromWire(event);
298
- if (wire.type === 'aborted')
285
+ if (wire.type === 'aborted') {
299
286
  emittedAborted = true;
287
+ }
300
288
  yield wire;
301
289
  }
302
290
  hlog('client wire ended', { received });
303
291
  }
304
292
  catch (err) {
305
293
  if (context.signal?.aborted) {
306
- if (!emittedAborted)
294
+ if (!emittedAborted) {
307
295
  yield { type: 'aborted' };
296
+ }
308
297
  return;
309
298
  }
310
- throw died(`harness died mid-run: ${err.message}`);
299
+ throw died(`harness died mid-run: ${errorMessage(err)}`);
311
300
  }
312
301
  finally {
313
302
  hlog('client run end', { received, aborted: Boolean(context.signal?.aborted) });
@@ -316,8 +305,9 @@ export class RemoteHarness extends EventEmitter {
316
305
  closed = true;
317
306
  reclaim();
318
307
  }
319
- if (context.signal?.aborted && !emittedAborted)
308
+ if (context.signal?.aborted && !emittedAborted) {
320
309
  yield { type: 'aborted' };
310
+ }
321
311
  }
322
312
  async *download(context) {
323
313
  const { client } = await this._ensure();
@@ -338,25 +328,28 @@ export class RemoteHarness extends EventEmitter {
338
328
  context.signal?.addEventListener?.('abort', onAbort, { once: true });
339
329
  for await (const event of downloadFrames(stream)) {
340
330
  const wire = fromDownloadWire(event);
341
- if (wire.type === 'aborted')
331
+ if (wire.type === 'aborted') {
342
332
  emittedAborted = true;
333
+ }
343
334
  yield wire;
344
335
  }
345
336
  }
346
337
  catch (err) {
347
338
  if (context.signal?.aborted) {
348
- if (!emittedAborted)
339
+ if (!emittedAborted) {
349
340
  yield { type: 'aborted' };
341
+ }
350
342
  return;
351
343
  }
352
- throw died(`harness died mid-download: ${err.message}`);
344
+ throw died(`harness died mid-download: ${errorMessage(err)}`);
353
345
  }
354
346
  finally {
355
347
  context.signal?.removeEventListener?.('abort', onAbort);
356
348
  stream?.destroy();
357
349
  }
358
- if (context.signal?.aborted && !emittedAborted)
350
+ if (context.signal?.aborted && !emittedAborted) {
359
351
  yield { type: 'aborted' };
352
+ }
360
353
  }
361
354
  async embed(context) {
362
355
  const { client } = await this._ensure();
@@ -390,8 +383,9 @@ export class RemoteHarness extends EventEmitter {
390
383
  const duplex = client.speakStream();
391
384
  const pump = (async () => {
392
385
  try {
393
- for await (const fragment of context.text)
386
+ for await (const fragment of context.text) {
394
387
  duplex.write({ text: fragment });
388
+ }
395
389
  duplex.end();
396
390
  }
397
391
  catch {
@@ -400,12 +394,11 @@ export class RemoteHarness extends EventEmitter {
400
394
  })();
401
395
  try {
402
396
  for await (const frame of frames(duplex)) {
403
- const chunk = frame;
404
- yield { audio: chunk.audio, sampleRate: chunk.sampleRate };
397
+ yield { audio: frame.audio, sampleRate: frame.sampleRate };
405
398
  }
406
399
  }
407
400
  finally {
408
- await pump.catch(() => { });
401
+ await pump.catch(noop);
409
402
  }
410
403
  }
411
404
  async getEmbeddingDevice() {
@@ -424,6 +417,10 @@ export class RemoteHarness extends EventEmitter {
424
417
  const { client } = await this._ensure();
425
418
  await client.setSkillSettings({ entries });
426
419
  }
420
+ async setModelIdleTimeout(ms) {
421
+ const { client } = await this._ensure();
422
+ await client.setModelIdleTimeout({ ms });
423
+ }
427
424
  async listManagedSkills() {
428
425
  const { client } = await this._ensure();
429
426
  const response = await client.listManagedSkills({});
@@ -491,7 +488,7 @@ export class RemoteHarness extends EventEmitter {
491
488
  binary.bin,
492
489
  {
493
490
  min: minVersion,
494
- ...(binary.command ? { command: binary.command } : {})
491
+ command: binary.command || undefined
495
492
  }
496
493
  ]
497
494
  ]
@@ -505,22 +502,20 @@ export class RemoteHarness extends EventEmitter {
505
502
  allowList: skill.allowList ?? [],
506
503
  platform: skill.platforms ?? [],
507
504
  status: parseSkillState(skill.status),
508
- ...(skill.requirements
505
+ requires: skill.requirements
509
506
  ? {
510
- requires: {
511
- bins: binaries.map((binary) => binary.bin),
512
- ...(skill.requirements?.credentials?.length
513
- ? { credentials: skill.requirements.credentials }
514
- : {}),
515
- ...(Object.keys(binMinVersions).length > 0 ? { binMinVersions } : {})
516
- }
507
+ bins: binaries.map((binary) => binary.bin),
508
+ credentials: skill.requirements.credentials?.length
509
+ ? skill.requirements.credentials
510
+ : undefined,
511
+ binMinVersions: Object.keys(binMinVersions).length > 0 ? binMinVersions : undefined
517
512
  }
518
- : {}),
519
- ...(install ? { install } : {}),
520
- ...(setup ? { setup } : {}),
521
- ...(skill.stub === true ? { stub: true } : {}),
522
- ...(skill.minCapabilityProfile ? { minCapabilityProfile: skill.minCapabilityProfile } : {}),
523
- ...(skill.emoji ? { emoji: skill.emoji } : {})
513
+ : undefined,
514
+ install,
515
+ setup,
516
+ stub: skill.stub === true ? true : undefined,
517
+ minCapabilityProfile: skill.minCapabilityProfile || undefined,
518
+ emoji: skill.emoji || undefined
524
519
  };
525
520
  });
526
521
  }
@@ -542,24 +537,43 @@ export class RemoteHarness extends EventEmitter {
542
537
  const { client } = await this._ensure();
543
538
  await abortable(client.warmModel({
544
539
  model: context.model,
545
- ...(context.agent ? { agent: knobs(context.agent) } : {})
540
+ agent: context.agent ? knobs(context.agent) : undefined
546
541
  }), context.signal);
547
542
  }
543
+ async loadModel(context) {
544
+ const { client } = await this._ensure();
545
+ const response = await abortable(client.loadModel({
546
+ model: context.model,
547
+ agent: context.agent ? knobs(context.agent) : undefined
548
+ }), context.signal);
549
+ return {
550
+ loaded: response.loaded === true,
551
+ reason: response.reason === 'memory' ? 'memory' : undefined
552
+ };
553
+ }
554
+ async unloadModel(context) {
555
+ const { client } = await this._ensure();
556
+ const response = await client.unloadModel({
557
+ model: context.model
558
+ });
559
+ return { unloaded: response.unloaded === true };
560
+ }
548
561
  async list(options = {}) {
549
562
  const { client } = await this._ensure();
550
- const response = (await client.listModels({
563
+ const response = await client.listModels({
551
564
  type: options.type,
552
565
  cachedOnly: options.cachedOnly,
553
566
  fullRegistry: options.fullRegistry
554
- }));
555
- return (response.models ?? []).map(({ actualSize, ...row }) => ({
567
+ });
568
+ return (response.models ?? []).map(({ actualSize, label, ...row }) => ({
556
569
  ...row,
557
- ...(actualSize ? { actualSize } : {})
570
+ actualSize: actualSize || undefined,
571
+ label: label || undefined
558
572
  }));
559
573
  }
560
574
  async deleteModel(name) {
561
575
  const { client } = await this._ensure();
562
- const response = (await client.deleteModel({ name }));
576
+ const response = await client.deleteModel({ name });
563
577
  return { name: response.name, removed: response.removed, freedBytes: response.freedBytes };
564
578
  }
565
579
  async clearKvCache() {
@@ -572,11 +586,13 @@ export class RemoteHarness extends EventEmitter {
572
586
  try {
573
587
  const { client } = await this._ensure();
574
588
  const info = await client.describe({});
575
- if (info.supportsKvCacheDeletion !== true)
589
+ if (info.supportsKvCacheDeletion !== true) {
576
590
  return unsupportedKvCacheDeletion();
591
+ }
577
592
  const response = await call(client);
578
- if (response.status === 'success')
593
+ if (response.status === 'success') {
579
594
  return { status: 'success' };
595
+ }
580
596
  if (response.status === 'failure') {
581
597
  return { status: 'failure', error: response.error ?? 'Harness cache deletion failed' };
582
598
  }
@@ -585,13 +601,13 @@ export class RemoteHarness extends EventEmitter {
585
601
  catch (error) {
586
602
  return {
587
603
  status: 'failure',
588
- error: error instanceof Error ? error.message : String(error)
604
+ error: errorMessage(error)
589
605
  };
590
606
  }
591
607
  }
592
608
  async infraModels(profile) {
593
609
  const { client } = await this._ensure();
594
- const response = (await client.infraModels({ profile }));
610
+ const response = await client.infraModels({ profile });
595
611
  return response.models ?? [];
596
612
  }
597
613
  async capability() {
@@ -601,16 +617,14 @@ export class RemoteHarness extends EventEmitter {
601
617
  return {
602
618
  capabilityProfile: response.capabilityProfile,
603
619
  recommendedModelName: response.recommendedModelName,
604
- ...(memoryBudget
620
+ memoryBudget: memoryBudget
605
621
  ? {
606
- memoryBudget: {
607
- totalBytes: memoryBudget.totalBytes,
608
- availableBytes: memoryBudget.availableBytes,
609
- source: memoryBudget.source,
610
- ...(memoryBudget.gpuClass ? { gpuClass: memoryBudget.gpuClass } : {})
611
- }
622
+ totalBytes: memoryBudget.totalBytes,
623
+ availableBytes: memoryBudget.availableBytes,
624
+ source: memoryBudget.source,
625
+ gpuClass: memoryBudget.gpuClass || undefined
612
626
  }
613
- : {})
627
+ : undefined
614
628
  };
615
629
  }
616
630
  close() {
@@ -620,8 +634,9 @@ export class RemoteHarness extends EventEmitter {
620
634
  async describe() {
621
635
  const { client } = await this._ensure();
622
636
  const describe = client.describe;
623
- if (!describe)
637
+ if (!describe) {
624
638
  return null;
639
+ }
625
640
  const info = await describe.call(client, {});
626
641
  return {
627
642
  toolNames: info?.toolNames ?? [],
@@ -632,12 +647,14 @@ export class RemoteHarness extends EventEmitter {
632
647
  }
633
648
  async fetchDiagnostics() {
634
649
  const session = this._session;
635
- if (!session || session.dead)
650
+ if (!session || session.dead) {
636
651
  return null;
652
+ }
637
653
  const client = session.client;
638
654
  const diagnostics = client.diagnostics;
639
- if (!diagnostics)
655
+ if (!diagnostics) {
640
656
  return null;
657
+ }
641
658
  const snapshot = await diagnostics.call(client, {});
642
659
  const device = snapshot?.device;
643
660
  return {
@@ -646,7 +663,7 @@ export class RemoteHarness extends EventEmitter {
646
663
  sdkLoaded: snapshot?.sdkLoaded ?? false,
647
664
  toolNames: snapshot?.toolNames ?? [],
648
665
  skills: snapshot?.skills ?? [],
649
- ...(device ? { device } : {})
666
+ device: device || undefined
650
667
  };
651
668
  }
652
669
  }
@@ -656,10 +673,11 @@ function parseSkillState(value) {
656
673
  function errorDetails(err) {
657
674
  return { error: errorMessage(err), code: errorCode(err) };
658
675
  }
676
+ function isToolCallDisplay(value) {
677
+ return isRecord(value) && typeof value.label === 'string';
678
+ }
659
679
  function died(message) {
660
- const err = new Error(message);
661
- err.code = HARNESS_DIED;
662
- return err;
680
+ return Object.assign(new Error(message), { code: HARNESS_DIED });
663
681
  }
664
682
  const noop = () => { };
665
683
  function abortable(promise, signal) {
@@ -700,8 +718,9 @@ function knobs(agent) {
700
718
  }
701
719
  async function bridgeAttach(event, attach) {
702
720
  try {
703
- if (!attach)
721
+ if (!attach) {
704
722
  return { type: 'attach-result', message: 'the host cannot persist attachments' };
723
+ }
705
724
  const bytes = await fs.promises.readFile(event.path ?? '');
706
725
  const record = await attach({
707
726
  fileName: event.fileName ?? '',
@@ -720,11 +739,12 @@ async function bridgeAttach(event, attach) {
720
739
  };
721
740
  }
722
741
  catch (err) {
723
- return { type: 'attach-result', message: err.message };
742
+ return { type: 'attach-result', message: errorMessage(err) };
724
743
  }
725
744
  finally {
726
- if (event.path)
745
+ if (event.path) {
727
746
  void fs.promises.unlink(event.path).catch(noop);
747
+ }
728
748
  }
729
749
  }
730
750
  async function bridgeFetch(event, context, staged) {
@@ -735,11 +755,12 @@ async function bridgeFetch(event, context, staged) {
735
755
  const metaOnly = event.meta === true;
736
756
  const found = await context.fetchAttachment({
737
757
  attachmentId: event.attachmentId ?? undefined,
738
- ...(metaOnly ? { meta: true } : {}),
758
+ meta: metaOnly ? true : undefined,
739
759
  signal: context.signal
740
760
  });
741
- if (!found)
761
+ if (!found) {
742
762
  return { type: 'fetch-result' };
763
+ }
743
764
  const size = 'bytes' in found && found.bytes !== null ? found.bytes.byteLength : found.byteLength;
744
765
  if (metaOnly) {
745
766
  return {
@@ -766,20 +787,55 @@ async function bridgeFetch(event, context, staged) {
766
787
  };
767
788
  }
768
789
  catch (err) {
769
- return { type: 'fetch-result', message: err.message };
790
+ return { type: 'fetch-result', message: errorMessage(err) };
770
791
  }
771
792
  }
793
+ const DriveRequestSchema = z.discriminatedUnion('op', [
794
+ z.looseObject({ op: z.literal('entry'), driveKey: z.string(), key: z.string() }),
795
+ z.looseObject({ op: z.literal('get'), driveKey: z.string(), key: z.string() }),
796
+ z.looseObject({ op: z.literal('read'), driveKey: z.string(), key: z.string() }),
797
+ z.looseObject({
798
+ op: z.literal('list'),
799
+ driveKey: z.string(),
800
+ folder: z.string(),
801
+ skip: z.array(z.string()),
802
+ limit: z.number(),
803
+ cursor: z.string().nullable()
804
+ }),
805
+ z.looseObject({
806
+ op: z.literal('write'),
807
+ driveKey: z.string(),
808
+ key: z.string(),
809
+ path: z.string(),
810
+ executable: z.boolean().optional()
811
+ })
812
+ ]);
772
813
  async function bridgeDrive(event, context) {
773
- if (!context.driveOp)
814
+ if (!context.driveOp) {
774
815
  return { type: 'drive-result', message: 'the host cannot proxy drives' };
816
+ }
775
817
  try {
776
- const payload = await context.driveOp(event.payload);
818
+ const payload = await context.driveOp(DriveRequestSchema.parse(event.payload));
777
819
  return { type: 'drive-result', payload };
778
820
  }
779
821
  catch (err) {
780
- return { type: 'drive-result', message: err instanceof Error ? err.message : String(err) };
822
+ return { type: 'drive-result', message: errorMessage(err) };
781
823
  }
782
824
  }
825
+ function answerQuestion(event, { askUser, reply }) {
826
+ const callId = event.callId;
827
+ if (!askUser) {
828
+ reply({ type: 'question-result', callId, message: 'the host cannot ask questions' });
829
+ return;
830
+ }
831
+ void Promise.resolve()
832
+ .then(() => askUser({
833
+ question: event.question ?? '',
834
+ options: Array.isArray(event.options) ? event.options.map(String) : undefined
835
+ }))
836
+ .then((answer) => reply({ type: 'question-result', callId, answer: answer ?? '' }), (err) => reply({ type: 'question-result', callId, message: errorMessage(err) }))
837
+ .catch((err) => hlog('question reply failed', { callId, message: errorMessage(err) }));
838
+ }
783
839
  async function bridgeRag(event, context) {
784
840
  if (!context.ragSearch) {
785
841
  return {
@@ -795,7 +851,7 @@ async function bridgeRag(event, context) {
795
851
  return { type: 'rag-result', result };
796
852
  }
797
853
  catch (err) {
798
- return { type: 'rag-result', message: err instanceof Error ? err.message : String(err) };
854
+ return { type: 'rag-result', message: errorMessage(err) };
799
855
  }
800
856
  }
801
857
  async function* frames(stream) {
@@ -816,8 +872,9 @@ async function* frames(stream) {
816
872
  queue.end();
817
873
  });
818
874
  yield* queue;
819
- if (error)
875
+ if (error) {
820
876
  throw error;
877
+ }
821
878
  }
822
879
  async function* downloadFrames(stream) {
823
880
  const queue = pushQueue();
@@ -830,6 +887,7 @@ async function* downloadFrames(stream) {
830
887
  queue.end();
831
888
  });
832
889
  yield* queue;
833
- if (error)
890
+ if (error) {
834
891
  throw error;
892
+ }
835
893
  }