@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
@@ -1,12 +1,14 @@
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';
6
7
  import { unsupportedKvCacheDeletion } from './capabilities/kv-cache.js';
8
+ import { readModelSourcePreview } from './capabilities/preview-model-source.js';
7
9
  import { hlog } from './log.js';
8
10
  import { assertEmbedDevice } from './models/embed-config.js';
9
- import { errorCode, errorMessage } from './error-fields.js';
11
+ import { errorCode, errorMessage, isRecord } from './guards.js';
10
12
  import { pushQueue } from './queue.js';
11
13
  import { stageFile, sweepStaging } from './staging.js';
12
14
  import { readInstall, readSetup } from './tools/skills/manifest-parse.js';
@@ -18,9 +20,10 @@ export function connectHarness(transport) {
18
20
  return new RemoteHarness(transport);
19
21
  }
20
22
  export function readEmbeddingDeviceResponse(response) {
21
- const device = typeof response === 'object' && response !== null ? Reflect.get(response, 'device') : undefined;
22
- if (typeof device !== 'string')
23
+ const device = isRecord(response) ? response.device : undefined;
24
+ if (typeof device !== 'string') {
23
25
  throw new Error('invalid embedding device response');
26
+ }
24
27
  assertEmbedDevice(device);
25
28
  return device;
26
29
  }
@@ -35,8 +38,9 @@ export class RemoteHarness extends EventEmitter {
35
38
  this._transport = transport;
36
39
  }
37
40
  _ensure() {
38
- if (this._closed)
41
+ if (this._closed) {
39
42
  return Promise.reject(died('harness closed'));
43
+ }
40
44
  if (this._session) {
41
45
  return this._session.dead
42
46
  ? Promise.reject(died('harness is dead'))
@@ -71,8 +75,9 @@ export class RemoteHarness extends EventEmitter {
71
75
  });
72
76
  stream.on('close', () => {
73
77
  session.dead = true;
74
- if (this._closed)
78
+ if (this._closed) {
75
79
  return;
80
+ }
76
81
  this.emit('status', streamError ? { state: 'died', ...errorDetails(streamError) } : { state: 'died' });
77
82
  });
78
83
  this._session = session;
@@ -137,8 +142,9 @@ export class RemoteHarness extends EventEmitter {
137
142
  let closed = false;
138
143
  let received = 0;
139
144
  const reclaim = () => {
140
- while (staged.length)
141
- 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
+ }
142
148
  };
143
149
  const onAbort = () => duplex?.destroy();
144
150
  try {
@@ -156,18 +162,19 @@ export class RemoteHarness extends EventEmitter {
156
162
  if (event.type !== 'content' && event.type !== 'thinking') {
157
163
  hlog('client frame', { type: event.type, n: received });
158
164
  }
159
- if (event.type === 'done')
165
+ if (event.type === 'done') {
160
166
  break;
167
+ }
161
168
  if (event.type === 'approval-ask') {
162
169
  const callId = event.callId;
163
170
  void Promise.resolve()
164
171
  .then(() => context.approve?.({
165
172
  name: event.name ?? '',
166
- arguments: (event.arguments ?? {}),
173
+ arguments: isRecord(event.arguments) ? event.arguments : {},
167
174
  resource: event.resource ?? null,
168
175
  effect: event.effect === 'read' ? 'read' : 'write',
169
176
  intent: event.intent || null,
170
- display: event.display ?? null
177
+ display: isToolCallDisplay(event.display) ? event.display : null
171
178
  }))
172
179
  .then((approved) => {
173
180
  if (!closed) {
@@ -185,8 +192,9 @@ export class RemoteHarness extends EventEmitter {
185
192
  const callId = event.callId;
186
193
  void bridgeAttach(event, context.attach)
187
194
  .then((frame) => {
188
- if (!closed)
195
+ if (!closed) {
189
196
  duplex.write({ ...frame, callId });
197
+ }
190
198
  }, (err) => {
191
199
  if (!closed) {
192
200
  duplex.write({
@@ -203,12 +211,14 @@ export class RemoteHarness extends EventEmitter {
203
211
  const callId = event.callId;
204
212
  void bridgeFetch(event, context, staged)
205
213
  .then((frame) => {
206
- if (closed)
214
+ if (closed) {
207
215
  return reclaim();
216
+ }
208
217
  duplex.write({ ...frame, callId });
209
218
  }, (err) => {
210
- if (closed)
219
+ if (closed) {
211
220
  return reclaim();
221
+ }
212
222
  duplex.write({ type: 'fetch-result', callId, message: errorMessage(err) });
213
223
  })
214
224
  .catch((err) => hlog('fetch reply failed', { callId, message: errorMessage(err) }));
@@ -218,11 +228,13 @@ export class RemoteHarness extends EventEmitter {
218
228
  const callId = event.callId;
219
229
  void bridgeDrive(event, context)
220
230
  .then((frame) => {
221
- if (!closed)
231
+ if (!closed) {
222
232
  return duplex.write({ ...frame, callId });
233
+ }
223
234
  const stagedPath = frame.payload?.path;
224
- if (stagedPath)
235
+ if (stagedPath) {
225
236
  void fs.promises.unlink(stagedPath).catch(noop);
237
+ }
226
238
  }, (err) => {
227
239
  if (!closed) {
228
240
  duplex.write({
@@ -239,8 +251,9 @@ export class RemoteHarness extends EventEmitter {
239
251
  const callId = event.callId;
240
252
  void bridgeRag(event, context)
241
253
  .then((frame) => {
242
- if (!closed)
254
+ if (!closed) {
243
255
  duplex.write({ ...frame, callId });
256
+ }
244
257
  }, (err) => {
245
258
  if (!closed) {
246
259
  duplex.write({
@@ -254,39 +267,14 @@ export class RemoteHarness extends EventEmitter {
254
267
  continue;
255
268
  }
256
269
  if (event.type === 'question-ask') {
257
- const callId = event.callId;
258
- const askUser = context.askUser;
259
- if (!askUser) {
260
- if (!closed) {
261
- duplex.write({
262
- type: 'question-result',
263
- callId,
264
- message: 'the host cannot ask questions'
265
- });
266
- }
267
- continue;
268
- }
269
- void Promise.resolve()
270
- .then(() => askUser({
271
- question: event.question ?? '',
272
- ...(Array.isArray(event.options)
273
- ? { options: event.options.map(String) }
274
- : {})
275
- }))
276
- .then((answer) => {
277
- if (!closed) {
278
- duplex.write({ type: 'question-result', callId, answer: answer ?? '' });
279
- }
280
- }, (err) => {
281
- if (!closed) {
282
- duplex.write({
283
- type: 'question-result',
284
- callId,
285
- message: errorMessage(err)
286
- });
270
+ answerQuestion(event, {
271
+ askUser: context.askUser,
272
+ reply: (frame) => {
273
+ if (!closed) {
274
+ duplex.write(frame);
275
+ }
287
276
  }
288
- })
289
- .catch((err) => hlog('question reply failed', { callId, message: errorMessage(err) }));
277
+ });
290
278
  continue;
291
279
  }
292
280
  if (event.type === 'progress') {
@@ -294,19 +282,21 @@ export class RemoteHarness extends EventEmitter {
294
282
  continue;
295
283
  }
296
284
  const wire = fromWire(event);
297
- if (wire.type === 'aborted')
285
+ if (wire.type === 'aborted') {
298
286
  emittedAborted = true;
287
+ }
299
288
  yield wire;
300
289
  }
301
290
  hlog('client wire ended', { received });
302
291
  }
303
292
  catch (err) {
304
293
  if (context.signal?.aborted) {
305
- if (!emittedAborted)
294
+ if (!emittedAborted) {
306
295
  yield { type: 'aborted' };
296
+ }
307
297
  return;
308
298
  }
309
- throw died(`harness died mid-run: ${err.message}`);
299
+ throw died(`harness died mid-run: ${errorMessage(err)}`);
310
300
  }
311
301
  finally {
312
302
  hlog('client run end', { received, aborted: Boolean(context.signal?.aborted) });
@@ -315,8 +305,9 @@ export class RemoteHarness extends EventEmitter {
315
305
  closed = true;
316
306
  reclaim();
317
307
  }
318
- if (context.signal?.aborted && !emittedAborted)
308
+ if (context.signal?.aborted && !emittedAborted) {
319
309
  yield { type: 'aborted' };
310
+ }
320
311
  }
321
312
  async *download(context) {
322
313
  const { client } = await this._ensure();
@@ -337,25 +328,28 @@ export class RemoteHarness extends EventEmitter {
337
328
  context.signal?.addEventListener?.('abort', onAbort, { once: true });
338
329
  for await (const event of downloadFrames(stream)) {
339
330
  const wire = fromDownloadWire(event);
340
- if (wire.type === 'aborted')
331
+ if (wire.type === 'aborted') {
341
332
  emittedAborted = true;
333
+ }
342
334
  yield wire;
343
335
  }
344
336
  }
345
337
  catch (err) {
346
338
  if (context.signal?.aborted) {
347
- if (!emittedAborted)
339
+ if (!emittedAborted) {
348
340
  yield { type: 'aborted' };
341
+ }
349
342
  return;
350
343
  }
351
- throw died(`harness died mid-download: ${err.message}`);
344
+ throw died(`harness died mid-download: ${errorMessage(err)}`);
352
345
  }
353
346
  finally {
354
347
  context.signal?.removeEventListener?.('abort', onAbort);
355
348
  stream?.destroy();
356
349
  }
357
- if (context.signal?.aborted && !emittedAborted)
350
+ if (context.signal?.aborted && !emittedAborted) {
358
351
  yield { type: 'aborted' };
352
+ }
359
353
  }
360
354
  async embed(context) {
361
355
  const { client } = await this._ensure();
@@ -389,8 +383,9 @@ export class RemoteHarness extends EventEmitter {
389
383
  const duplex = client.speakStream();
390
384
  const pump = (async () => {
391
385
  try {
392
- for await (const fragment of context.text)
386
+ for await (const fragment of context.text) {
393
387
  duplex.write({ text: fragment });
388
+ }
394
389
  duplex.end();
395
390
  }
396
391
  catch {
@@ -399,12 +394,11 @@ export class RemoteHarness extends EventEmitter {
399
394
  })();
400
395
  try {
401
396
  for await (const frame of frames(duplex)) {
402
- const chunk = frame;
403
- yield { audio: chunk.audio, sampleRate: chunk.sampleRate };
397
+ yield { audio: frame.audio, sampleRate: frame.sampleRate };
404
398
  }
405
399
  }
406
400
  finally {
407
- await pump.catch(() => { });
401
+ await pump.catch(noop);
408
402
  }
409
403
  }
410
404
  async getEmbeddingDevice() {
@@ -423,6 +417,10 @@ export class RemoteHarness extends EventEmitter {
423
417
  const { client } = await this._ensure();
424
418
  await client.setSkillSettings({ entries });
425
419
  }
420
+ async setModelIdleTimeout(ms) {
421
+ const { client } = await this._ensure();
422
+ await client.setModelIdleTimeout({ ms });
423
+ }
426
424
  async listManagedSkills() {
427
425
  const { client } = await this._ensure();
428
426
  const response = await client.listManagedSkills({});
@@ -462,6 +460,14 @@ export class RemoteHarness extends EventEmitter {
462
460
  const { client } = await this._ensure();
463
461
  await client.setCustomModels({ models: entries });
464
462
  }
463
+ async previewModelSource(context) {
464
+ const { client } = await this._ensure();
465
+ const response = await client.previewModelSource({
466
+ src: context.src,
467
+ mmprojSrc: context.mmprojSrc
468
+ });
469
+ return readModelSourcePreview(response);
470
+ }
465
471
  async skills(agentId, modelName, options) {
466
472
  const { client } = await this._ensure();
467
473
  const response = await client.skills({
@@ -482,7 +488,7 @@ export class RemoteHarness extends EventEmitter {
482
488
  binary.bin,
483
489
  {
484
490
  min: minVersion,
485
- ...(binary.command ? { command: binary.command } : {})
491
+ command: binary.command || undefined
486
492
  }
487
493
  ]
488
494
  ]
@@ -496,22 +502,20 @@ export class RemoteHarness extends EventEmitter {
496
502
  allowList: skill.allowList ?? [],
497
503
  platform: skill.platforms ?? [],
498
504
  status: parseSkillState(skill.status),
499
- ...(skill.requirements
505
+ requires: skill.requirements
500
506
  ? {
501
- requires: {
502
- bins: binaries.map((binary) => binary.bin),
503
- ...(skill.requirements?.credentials?.length
504
- ? { credentials: skill.requirements.credentials }
505
- : {}),
506
- ...(Object.keys(binMinVersions).length > 0 ? { binMinVersions } : {})
507
- }
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
508
512
  }
509
- : {}),
510
- ...(install ? { install } : {}),
511
- ...(setup ? { setup } : {}),
512
- ...(skill.stub === true ? { stub: true } : {}),
513
- ...(skill.minCapabilityProfile ? { minCapabilityProfile: skill.minCapabilityProfile } : {}),
514
- ...(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
515
519
  };
516
520
  });
517
521
  }
@@ -533,24 +537,25 @@ export class RemoteHarness extends EventEmitter {
533
537
  const { client } = await this._ensure();
534
538
  await abortable(client.warmModel({
535
539
  model: context.model,
536
- ...(context.agent ? { agent: knobs(context.agent) } : {})
540
+ agent: context.agent ? knobs(context.agent) : undefined
537
541
  }), context.signal);
538
542
  }
539
543
  async list(options = {}) {
540
544
  const { client } = await this._ensure();
541
- const response = (await client.listModels({
545
+ const response = await client.listModels({
542
546
  type: options.type,
543
547
  cachedOnly: options.cachedOnly,
544
548
  fullRegistry: options.fullRegistry
545
- }));
546
- return (response.models ?? []).map(({ actualSize, ...row }) => ({
549
+ });
550
+ return (response.models ?? []).map(({ actualSize, label, ...row }) => ({
547
551
  ...row,
548
- ...(actualSize ? { actualSize } : {})
552
+ actualSize: actualSize || undefined,
553
+ label: label || undefined
549
554
  }));
550
555
  }
551
556
  async deleteModel(name) {
552
557
  const { client } = await this._ensure();
553
- const response = (await client.deleteModel({ name }));
558
+ const response = await client.deleteModel({ name });
554
559
  return { name: response.name, removed: response.removed, freedBytes: response.freedBytes };
555
560
  }
556
561
  async clearKvCache() {
@@ -563,11 +568,13 @@ export class RemoteHarness extends EventEmitter {
563
568
  try {
564
569
  const { client } = await this._ensure();
565
570
  const info = await client.describe({});
566
- if (info.supportsKvCacheDeletion !== true)
571
+ if (info.supportsKvCacheDeletion !== true) {
567
572
  return unsupportedKvCacheDeletion();
573
+ }
568
574
  const response = await call(client);
569
- if (response.status === 'success')
575
+ if (response.status === 'success') {
570
576
  return { status: 'success' };
577
+ }
571
578
  if (response.status === 'failure') {
572
579
  return { status: 'failure', error: response.error ?? 'Harness cache deletion failed' };
573
580
  }
@@ -576,13 +583,13 @@ export class RemoteHarness extends EventEmitter {
576
583
  catch (error) {
577
584
  return {
578
585
  status: 'failure',
579
- error: error instanceof Error ? error.message : String(error)
586
+ error: errorMessage(error)
580
587
  };
581
588
  }
582
589
  }
583
590
  async infraModels(profile) {
584
591
  const { client } = await this._ensure();
585
- const response = (await client.infraModels({ profile }));
592
+ const response = await client.infraModels({ profile });
586
593
  return response.models ?? [];
587
594
  }
588
595
  async capability() {
@@ -592,16 +599,14 @@ export class RemoteHarness extends EventEmitter {
592
599
  return {
593
600
  capabilityProfile: response.capabilityProfile,
594
601
  recommendedModelName: response.recommendedModelName,
595
- ...(memoryBudget
602
+ memoryBudget: memoryBudget
596
603
  ? {
597
- memoryBudget: {
598
- totalBytes: memoryBudget.totalBytes,
599
- availableBytes: memoryBudget.availableBytes,
600
- source: memoryBudget.source,
601
- ...(memoryBudget.gpuClass ? { gpuClass: memoryBudget.gpuClass } : {})
602
- }
604
+ totalBytes: memoryBudget.totalBytes,
605
+ availableBytes: memoryBudget.availableBytes,
606
+ source: memoryBudget.source,
607
+ gpuClass: memoryBudget.gpuClass || undefined
603
608
  }
604
- : {})
609
+ : undefined
605
610
  };
606
611
  }
607
612
  close() {
@@ -611,8 +616,9 @@ export class RemoteHarness extends EventEmitter {
611
616
  async describe() {
612
617
  const { client } = await this._ensure();
613
618
  const describe = client.describe;
614
- if (!describe)
619
+ if (!describe) {
615
620
  return null;
621
+ }
616
622
  const info = await describe.call(client, {});
617
623
  return {
618
624
  toolNames: info?.toolNames ?? [],
@@ -623,12 +629,14 @@ export class RemoteHarness extends EventEmitter {
623
629
  }
624
630
  async fetchDiagnostics() {
625
631
  const session = this._session;
626
- if (!session || session.dead)
632
+ if (!session || session.dead) {
627
633
  return null;
634
+ }
628
635
  const client = session.client;
629
636
  const diagnostics = client.diagnostics;
630
- if (!diagnostics)
637
+ if (!diagnostics) {
631
638
  return null;
639
+ }
632
640
  const snapshot = await diagnostics.call(client, {});
633
641
  const device = snapshot?.device;
634
642
  return {
@@ -637,7 +645,7 @@ export class RemoteHarness extends EventEmitter {
637
645
  sdkLoaded: snapshot?.sdkLoaded ?? false,
638
646
  toolNames: snapshot?.toolNames ?? [],
639
647
  skills: snapshot?.skills ?? [],
640
- ...(device ? { device } : {})
648
+ device: device || undefined
641
649
  };
642
650
  }
643
651
  }
@@ -647,10 +655,11 @@ function parseSkillState(value) {
647
655
  function errorDetails(err) {
648
656
  return { error: errorMessage(err), code: errorCode(err) };
649
657
  }
658
+ function isToolCallDisplay(value) {
659
+ return isRecord(value) && typeof value.label === 'string';
660
+ }
650
661
  function died(message) {
651
- const err = new Error(message);
652
- err.code = HARNESS_DIED;
653
- return err;
662
+ return Object.assign(new Error(message), { code: HARNESS_DIED });
654
663
  }
655
664
  const noop = () => { };
656
665
  function abortable(promise, signal) {
@@ -691,8 +700,9 @@ function knobs(agent) {
691
700
  }
692
701
  async function bridgeAttach(event, attach) {
693
702
  try {
694
- if (!attach)
703
+ if (!attach) {
695
704
  return { type: 'attach-result', message: 'the host cannot persist attachments' };
705
+ }
696
706
  const bytes = await fs.promises.readFile(event.path ?? '');
697
707
  const record = await attach({
698
708
  fileName: event.fileName ?? '',
@@ -711,11 +721,12 @@ async function bridgeAttach(event, attach) {
711
721
  };
712
722
  }
713
723
  catch (err) {
714
- return { type: 'attach-result', message: err.message };
724
+ return { type: 'attach-result', message: errorMessage(err) };
715
725
  }
716
726
  finally {
717
- if (event.path)
727
+ if (event.path) {
718
728
  void fs.promises.unlink(event.path).catch(noop);
729
+ }
719
730
  }
720
731
  }
721
732
  async function bridgeFetch(event, context, staged) {
@@ -726,11 +737,12 @@ async function bridgeFetch(event, context, staged) {
726
737
  const metaOnly = event.meta === true;
727
738
  const found = await context.fetchAttachment({
728
739
  attachmentId: event.attachmentId ?? undefined,
729
- ...(metaOnly ? { meta: true } : {}),
740
+ meta: metaOnly ? true : undefined,
730
741
  signal: context.signal
731
742
  });
732
- if (!found)
743
+ if (!found) {
733
744
  return { type: 'fetch-result' };
745
+ }
734
746
  const size = 'bytes' in found && found.bytes !== null ? found.bytes.byteLength : found.byteLength;
735
747
  if (metaOnly) {
736
748
  return {
@@ -757,20 +769,55 @@ async function bridgeFetch(event, context, staged) {
757
769
  };
758
770
  }
759
771
  catch (err) {
760
- return { type: 'fetch-result', message: err.message };
772
+ return { type: 'fetch-result', message: errorMessage(err) };
761
773
  }
762
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
+ ]);
763
795
  async function bridgeDrive(event, context) {
764
- if (!context.driveOp)
796
+ if (!context.driveOp) {
765
797
  return { type: 'drive-result', message: 'the host cannot proxy drives' };
798
+ }
766
799
  try {
767
- const payload = await context.driveOp(event.payload);
800
+ const payload = await context.driveOp(DriveRequestSchema.parse(event.payload));
768
801
  return { type: 'drive-result', payload };
769
802
  }
770
803
  catch (err) {
771
- return { type: 'drive-result', message: err instanceof Error ? err.message : String(err) };
804
+ return { type: 'drive-result', message: errorMessage(err) };
772
805
  }
773
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
+ }
774
821
  async function bridgeRag(event, context) {
775
822
  if (!context.ragSearch) {
776
823
  return {
@@ -786,7 +833,7 @@ async function bridgeRag(event, context) {
786
833
  return { type: 'rag-result', result };
787
834
  }
788
835
  catch (err) {
789
- return { type: 'rag-result', message: err instanceof Error ? err.message : String(err) };
836
+ return { type: 'rag-result', message: errorMessage(err) };
790
837
  }
791
838
  }
792
839
  async function* frames(stream) {
@@ -807,8 +854,9 @@ async function* frames(stream) {
807
854
  queue.end();
808
855
  });
809
856
  yield* queue;
810
- if (error)
857
+ if (error) {
811
858
  throw error;
859
+ }
812
860
  }
813
861
  async function* downloadFrames(stream) {
814
862
  const queue = pushQueue();
@@ -821,6 +869,7 @@ async function* downloadFrames(stream) {
821
869
  queue.end();
822
870
  });
823
871
  yield* queue;
824
- if (error)
872
+ if (error) {
825
873
  throw error;
874
+ }
826
875
  }