@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
@@ -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,25 @@ 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
  }
548
543
  async list(options = {}) {
549
544
  const { client } = await this._ensure();
550
- const response = (await client.listModels({
545
+ const response = await client.listModels({
551
546
  type: options.type,
552
547
  cachedOnly: options.cachedOnly,
553
548
  fullRegistry: options.fullRegistry
554
- }));
555
- return (response.models ?? []).map(({ actualSize, ...row }) => ({
549
+ });
550
+ return (response.models ?? []).map(({ actualSize, label, ...row }) => ({
556
551
  ...row,
557
- ...(actualSize ? { actualSize } : {})
552
+ actualSize: actualSize || undefined,
553
+ label: label || undefined
558
554
  }));
559
555
  }
560
556
  async deleteModel(name) {
561
557
  const { client } = await this._ensure();
562
- const response = (await client.deleteModel({ name }));
558
+ const response = await client.deleteModel({ name });
563
559
  return { name: response.name, removed: response.removed, freedBytes: response.freedBytes };
564
560
  }
565
561
  async clearKvCache() {
@@ -572,11 +568,13 @@ export class RemoteHarness extends EventEmitter {
572
568
  try {
573
569
  const { client } = await this._ensure();
574
570
  const info = await client.describe({});
575
- if (info.supportsKvCacheDeletion !== true)
571
+ if (info.supportsKvCacheDeletion !== true) {
576
572
  return unsupportedKvCacheDeletion();
573
+ }
577
574
  const response = await call(client);
578
- if (response.status === 'success')
575
+ if (response.status === 'success') {
579
576
  return { status: 'success' };
577
+ }
580
578
  if (response.status === 'failure') {
581
579
  return { status: 'failure', error: response.error ?? 'Harness cache deletion failed' };
582
580
  }
@@ -585,13 +583,13 @@ export class RemoteHarness extends EventEmitter {
585
583
  catch (error) {
586
584
  return {
587
585
  status: 'failure',
588
- error: error instanceof Error ? error.message : String(error)
586
+ error: errorMessage(error)
589
587
  };
590
588
  }
591
589
  }
592
590
  async infraModels(profile) {
593
591
  const { client } = await this._ensure();
594
- const response = (await client.infraModels({ profile }));
592
+ const response = await client.infraModels({ profile });
595
593
  return response.models ?? [];
596
594
  }
597
595
  async capability() {
@@ -601,16 +599,14 @@ export class RemoteHarness extends EventEmitter {
601
599
  return {
602
600
  capabilityProfile: response.capabilityProfile,
603
601
  recommendedModelName: response.recommendedModelName,
604
- ...(memoryBudget
602
+ memoryBudget: memoryBudget
605
603
  ? {
606
- memoryBudget: {
607
- totalBytes: memoryBudget.totalBytes,
608
- availableBytes: memoryBudget.availableBytes,
609
- source: memoryBudget.source,
610
- ...(memoryBudget.gpuClass ? { gpuClass: memoryBudget.gpuClass } : {})
611
- }
604
+ totalBytes: memoryBudget.totalBytes,
605
+ availableBytes: memoryBudget.availableBytes,
606
+ source: memoryBudget.source,
607
+ gpuClass: memoryBudget.gpuClass || undefined
612
608
  }
613
- : {})
609
+ : undefined
614
610
  };
615
611
  }
616
612
  close() {
@@ -620,8 +616,9 @@ export class RemoteHarness extends EventEmitter {
620
616
  async describe() {
621
617
  const { client } = await this._ensure();
622
618
  const describe = client.describe;
623
- if (!describe)
619
+ if (!describe) {
624
620
  return null;
621
+ }
625
622
  const info = await describe.call(client, {});
626
623
  return {
627
624
  toolNames: info?.toolNames ?? [],
@@ -632,12 +629,14 @@ export class RemoteHarness extends EventEmitter {
632
629
  }
633
630
  async fetchDiagnostics() {
634
631
  const session = this._session;
635
- if (!session || session.dead)
632
+ if (!session || session.dead) {
636
633
  return null;
634
+ }
637
635
  const client = session.client;
638
636
  const diagnostics = client.diagnostics;
639
- if (!diagnostics)
637
+ if (!diagnostics) {
640
638
  return null;
639
+ }
641
640
  const snapshot = await diagnostics.call(client, {});
642
641
  const device = snapshot?.device;
643
642
  return {
@@ -646,7 +645,7 @@ export class RemoteHarness extends EventEmitter {
646
645
  sdkLoaded: snapshot?.sdkLoaded ?? false,
647
646
  toolNames: snapshot?.toolNames ?? [],
648
647
  skills: snapshot?.skills ?? [],
649
- ...(device ? { device } : {})
648
+ device: device || undefined
650
649
  };
651
650
  }
652
651
  }
@@ -656,10 +655,11 @@ function parseSkillState(value) {
656
655
  function errorDetails(err) {
657
656
  return { error: errorMessage(err), code: errorCode(err) };
658
657
  }
658
+ function isToolCallDisplay(value) {
659
+ return isRecord(value) && typeof value.label === 'string';
660
+ }
659
661
  function died(message) {
660
- const err = new Error(message);
661
- err.code = HARNESS_DIED;
662
- return err;
662
+ return Object.assign(new Error(message), { code: HARNESS_DIED });
663
663
  }
664
664
  const noop = () => { };
665
665
  function abortable(promise, signal) {
@@ -700,8 +700,9 @@ function knobs(agent) {
700
700
  }
701
701
  async function bridgeAttach(event, attach) {
702
702
  try {
703
- if (!attach)
703
+ if (!attach) {
704
704
  return { type: 'attach-result', message: 'the host cannot persist attachments' };
705
+ }
705
706
  const bytes = await fs.promises.readFile(event.path ?? '');
706
707
  const record = await attach({
707
708
  fileName: event.fileName ?? '',
@@ -720,11 +721,12 @@ async function bridgeAttach(event, attach) {
720
721
  };
721
722
  }
722
723
  catch (err) {
723
- return { type: 'attach-result', message: err.message };
724
+ return { type: 'attach-result', message: errorMessage(err) };
724
725
  }
725
726
  finally {
726
- if (event.path)
727
+ if (event.path) {
727
728
  void fs.promises.unlink(event.path).catch(noop);
729
+ }
728
730
  }
729
731
  }
730
732
  async function bridgeFetch(event, context, staged) {
@@ -735,11 +737,12 @@ async function bridgeFetch(event, context, staged) {
735
737
  const metaOnly = event.meta === true;
736
738
  const found = await context.fetchAttachment({
737
739
  attachmentId: event.attachmentId ?? undefined,
738
- ...(metaOnly ? { meta: true } : {}),
740
+ meta: metaOnly ? true : undefined,
739
741
  signal: context.signal
740
742
  });
741
- if (!found)
743
+ if (!found) {
742
744
  return { type: 'fetch-result' };
745
+ }
743
746
  const size = 'bytes' in found && found.bytes !== null ? found.bytes.byteLength : found.byteLength;
744
747
  if (metaOnly) {
745
748
  return {
@@ -766,20 +769,55 @@ async function bridgeFetch(event, context, staged) {
766
769
  };
767
770
  }
768
771
  catch (err) {
769
- return { type: 'fetch-result', message: err.message };
772
+ return { type: 'fetch-result', message: errorMessage(err) };
770
773
  }
771
774
  }
775
+ const DriveRequestSchema = z.discriminatedUnion('op', [
776
+ z.looseObject({ op: z.literal('entry'), driveKey: z.string(), key: z.string() }),
777
+ z.looseObject({ op: z.literal('get'), driveKey: z.string(), key: z.string() }),
778
+ z.looseObject({ op: z.literal('read'), driveKey: z.string(), key: z.string() }),
779
+ z.looseObject({
780
+ op: z.literal('list'),
781
+ driveKey: z.string(),
782
+ folder: z.string(),
783
+ skip: z.array(z.string()),
784
+ limit: z.number(),
785
+ cursor: z.string().nullable()
786
+ }),
787
+ z.looseObject({
788
+ op: z.literal('write'),
789
+ driveKey: z.string(),
790
+ key: z.string(),
791
+ path: z.string(),
792
+ executable: z.boolean().optional()
793
+ })
794
+ ]);
772
795
  async function bridgeDrive(event, context) {
773
- if (!context.driveOp)
796
+ if (!context.driveOp) {
774
797
  return { type: 'drive-result', message: 'the host cannot proxy drives' };
798
+ }
775
799
  try {
776
- const payload = await context.driveOp(event.payload);
800
+ const payload = await context.driveOp(DriveRequestSchema.parse(event.payload));
777
801
  return { type: 'drive-result', payload };
778
802
  }
779
803
  catch (err) {
780
- return { type: 'drive-result', message: err instanceof Error ? err.message : String(err) };
804
+ return { type: 'drive-result', message: errorMessage(err) };
781
805
  }
782
806
  }
807
+ function answerQuestion(event, { askUser, reply }) {
808
+ const callId = event.callId;
809
+ if (!askUser) {
810
+ reply({ type: 'question-result', callId, message: 'the host cannot ask questions' });
811
+ return;
812
+ }
813
+ void Promise.resolve()
814
+ .then(() => askUser({
815
+ question: event.question ?? '',
816
+ options: Array.isArray(event.options) ? event.options.map(String) : undefined
817
+ }))
818
+ .then((answer) => reply({ type: 'question-result', callId, answer: answer ?? '' }), (err) => reply({ type: 'question-result', callId, message: errorMessage(err) }))
819
+ .catch((err) => hlog('question reply failed', { callId, message: errorMessage(err) }));
820
+ }
783
821
  async function bridgeRag(event, context) {
784
822
  if (!context.ragSearch) {
785
823
  return {
@@ -795,7 +833,7 @@ async function bridgeRag(event, context) {
795
833
  return { type: 'rag-result', result };
796
834
  }
797
835
  catch (err) {
798
- return { type: 'rag-result', message: err instanceof Error ? err.message : String(err) };
836
+ return { type: 'rag-result', message: errorMessage(err) };
799
837
  }
800
838
  }
801
839
  async function* frames(stream) {
@@ -816,8 +854,9 @@ async function* frames(stream) {
816
854
  queue.end();
817
855
  });
818
856
  yield* queue;
819
- if (error)
857
+ if (error) {
820
858
  throw error;
859
+ }
821
860
  }
822
861
  async function* downloadFrames(stream) {
823
862
  const queue = pushQueue();
@@ -830,6 +869,7 @@ async function* downloadFrames(stream) {
830
869
  queue.end();
831
870
  });
832
871
  yield* queue;
833
- if (error)
872
+ if (error) {
834
873
  throw error;
874
+ }
835
875
  }