@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,4 +1,5 @@
1
1
  import { jsonrepair } from 'jsonrepair';
2
+ import { isRecord } from '../../guards.js';
2
3
  import { hlog } from '../../log.js';
3
4
  const STRUCTURED_TYPES = new Set(['object', 'array']);
4
5
  export class ArgRepairHook {
@@ -9,11 +10,13 @@ export class ArgRepairHook {
9
10
  let args = ctx.args;
10
11
  for (const [key, schema] of Object.entries(ctx.parameters.properties)) {
11
12
  const value = args[key];
12
- if (typeof value !== 'string')
13
+ if (typeof value !== 'string') {
13
14
  continue;
15
+ }
14
16
  const types = structuredTypes(schema);
15
- if (types.size === 0)
17
+ if (types.size === 0) {
16
18
  continue;
19
+ }
17
20
  const parsed = parseStructured(value, types);
18
21
  if (parsed === null) {
19
22
  hlog('arg repair declined', { tool: ctx.toolName, param: key, length: value.length });
@@ -21,8 +24,9 @@ export class ArgRepairHook {
21
24
  }
22
25
  args = { ...args, [key]: parsed };
23
26
  }
24
- if (args !== ctx.args)
27
+ if (args !== ctx.args) {
25
28
  return { args };
29
+ }
26
30
  }
27
31
  }
28
32
  function parseStructured(value, types) {
@@ -36,16 +40,18 @@ function parseStructured(value, types) {
36
40
  return null;
37
41
  }
38
42
  const accepted = accept(parsed, types);
39
- if (accepted !== null)
43
+ if (accepted !== null) {
40
44
  return accepted;
45
+ }
41
46
  if (Array.isArray(parsed) &&
42
47
  types.has('object') &&
43
- isRecord(parsed[0]) &&
48
+ isPlainObject(parsed[0]) &&
44
49
  typeof parsed.at(-1) === 'string') {
45
50
  return parsed[0];
46
51
  }
47
- if (text[0] === '"' && types.has('object'))
52
+ if (text[0] === '"' && types.has('object')) {
48
53
  return accept(read(`{${text}}`), types);
54
+ }
49
55
  return null;
50
56
  }
51
57
  function read(text) {
@@ -57,7 +63,7 @@ function read(text) {
57
63
  }
58
64
  }
59
65
  function accept(value, types) {
60
- if (isRecord(value) && types.has('object'))
66
+ if (isPlainObject(value) && types.has('object'))
61
67
  return value;
62
68
  if (Array.isArray(value) && types.has('array'))
63
69
  return value;
@@ -65,24 +71,28 @@ function accept(value, types) {
65
71
  }
66
72
  function structuredTypes(schema) {
67
73
  const found = new Set();
68
- if (!isRecord(schema))
74
+ if (!isPlainObject(schema)) {
69
75
  return found;
76
+ }
70
77
  const declared = Array.isArray(schema.type) ? schema.type : [schema.type];
71
78
  for (const type of declared) {
72
- if (typeof type === 'string' && STRUCTURED_TYPES.has(type))
79
+ if (typeof type === 'string' && STRUCTURED_TYPES.has(type)) {
73
80
  found.add(type);
81
+ }
74
82
  }
75
83
  for (const key of ['oneOf', 'anyOf']) {
76
84
  const branches = schema[key];
77
- if (!Array.isArray(branches))
85
+ if (!Array.isArray(branches)) {
78
86
  continue;
87
+ }
79
88
  for (const branch of branches) {
80
- for (const type of structuredTypes(branch))
89
+ for (const type of structuredTypes(branch)) {
81
90
  found.add(type);
91
+ }
82
92
  }
83
93
  }
84
94
  return found;
85
95
  }
86
- function isRecord(value) {
87
- return typeof value === 'object' && value !== null && !Array.isArray(value);
96
+ function isPlainObject(value) {
97
+ return isRecord(value) && !Array.isArray(value);
88
98
  }
@@ -23,7 +23,8 @@ export class EgressGuardHook {
23
23
  return;
24
24
  }
25
25
  const blocked = checkOutboundHost(url);
26
- if (blocked)
26
+ if (blocked) {
27
27
  return { error: blocked, guidance: true };
28
+ }
28
29
  }
29
30
  }
@@ -11,8 +11,9 @@ export class SkillCatalogGuardHook {
11
11
  const known = new Set(entries.map((entry) => (typeof entry === 'string' ? entry : entry.name)));
12
12
  const raw = ctx.args.name ?? ctx.args.skill_name ?? ctx.args.skill;
13
13
  const name = typeof raw === 'string' ? raw : '';
14
- if (known.has(name))
14
+ if (known.has(name)) {
15
15
  return;
16
+ }
16
17
  const listed = [...known].join(', ');
17
18
  const rest = listed
18
19
  ? `The only skills are: ${listed}. Nothing else needs a skill load — answer or act with the tools you already have.`
@@ -7,14 +7,15 @@ export type PreToolFold = {
7
7
  ok: false;
8
8
  rejection: HookRejection;
9
9
  };
10
+ export interface PostToolFold {
11
+ result: unknown;
12
+ outcome?: ToolExecutionOutcome;
13
+ }
10
14
  export declare class HookPipeline {
11
15
  private readonly _behaviorHooks;
12
16
  constructor(hooks?: readonly HarnessHook[]);
13
17
  onRunStart(payload: Omit<RunStartPayload, 'phase'>): Promise<void>;
14
18
  onRunEnd(payload: Omit<RunEndPayload, 'phase'>): Promise<void>;
15
19
  runPreToolFold(payload: Omit<PreToolCallPayload, 'phase'>): Promise<PreToolFold>;
16
- runPostToolFold(payload: Omit<PostToolCallPayload, 'phase'>): Promise<{
17
- result: unknown;
18
- outcome?: ToolExecutionOutcome;
19
- }>;
20
+ runPostToolFold(payload: Omit<PostToolCallPayload, 'phase'>): Promise<PostToolFold>;
20
21
  }
@@ -9,13 +9,15 @@ export class HookPipeline {
9
9
  }
10
10
  async onRunStart(payload) {
11
11
  const folded = await runHooks(this._behaviorHooks, { phase: 'runStart', ...payload });
12
- if ('guidance' in folded)
12
+ if ('guidance' in folded) {
13
13
  hlog('run hook failed', { stage: 'runStart', error: folded.error });
14
+ }
14
15
  }
15
16
  async onRunEnd(payload) {
16
17
  const folded = await runHooks(this._behaviorHooks, { phase: 'runEnd', ...payload });
17
- if ('guidance' in folded)
18
+ if ('guidance' in folded) {
18
19
  hlog('run hook failed', { stage: 'runEnd', error: folded.error });
20
+ }
19
21
  }
20
22
  async runPreToolFold(payload) {
21
23
  const folded = await runHooks(this._behaviorHooks, { phase: 'preToolCall', ...payload });
@@ -42,8 +44,9 @@ export class HookPipeline {
42
44
  result = folded.result;
43
45
  }
44
46
  }
45
- if (outcome !== undefined)
47
+ if (outcome !== undefined) {
46
48
  outcome = normalizeToolExecutionOutcome(result, outcome);
49
+ }
47
50
  return { result, outcome };
48
51
  }
49
52
  }
@@ -1,3 +1,4 @@
1
+ import { errorMessage, isRecord } from '../guards.js';
1
2
  import { hlog } from '../log.js';
2
3
  function cloneJson(value) {
3
4
  try {
@@ -8,19 +9,20 @@ function cloneJson(value) {
8
9
  return value;
9
10
  }
10
11
  }
11
- function isRecord(value) {
12
- return typeof value === 'object' && value !== null && !Array.isArray(value);
12
+ function isPlainObject(value) {
13
+ return isRecord(value) && !Array.isArray(value);
13
14
  }
14
15
  function cloneArgs(args) {
15
16
  const cloned = cloneJson(args);
16
- return isRecord(cloned) ? cloned : args;
17
+ return isPlainObject(cloned) ? cloned : args;
17
18
  }
18
19
  function isMessage(value) {
19
- return isRecord(value) && typeof value.role === 'string' && typeof value.content === 'string';
20
+ return isPlainObject(value) && typeof value.role === 'string' && typeof value.content === 'string';
20
21
  }
21
22
  function freshContext(ctx) {
22
- if (ctx.phase === 'preToolCall')
23
+ if (ctx.phase === 'preToolCall') {
23
24
  return { ...ctx, args: cloneArgs(ctx.args) };
25
+ }
24
26
  if (ctx.phase === 'postToolCall') {
25
27
  return { ...ctx, args: cloneArgs(ctx.args), result: cloneJson(ctx.result) };
26
28
  }
@@ -52,7 +54,7 @@ function merge(current, returned) {
52
54
  return current;
53
55
  }
54
56
  function rejectionOf(current, err) {
55
- const message = err instanceof Error ? err.message : String(err);
57
+ const message = errorMessage(err);
56
58
  hlog('hook failed', { stage: current.phase, error: message });
57
59
  if (current.phase === 'postToolCall') {
58
60
  return {
@@ -69,10 +71,12 @@ export async function runHooks(hooks, ctx) {
69
71
  continue;
70
72
  try {
71
73
  const returned = await hook.run(freshContext(current));
72
- if (!returned || typeof returned !== 'object')
74
+ if (!returned || typeof returned !== 'object') {
73
75
  continue;
74
- if (isRejection(returned))
76
+ }
77
+ if (isRejection(returned)) {
75
78
  return rejection(returned);
79
+ }
76
80
  current = merge(current, returned);
77
81
  }
78
82
  catch (err) {
@@ -1,20 +1,24 @@
1
+ import { isRecord } from './guards.js';
1
2
  const EMA_WEIGHT = 0.25;
2
3
  const STALL_MS = 2_000;
3
4
  const NO_BYTES = { downloaded: 0, total: 0, aggregate: false };
4
5
  export function transferProgress(event) {
5
6
  const aggregate = aggregateTransfer(event.fileSetInfo) ?? aggregateTransfer(event.shardInfo);
6
- if (aggregate)
7
+ if (aggregate) {
7
8
  return aggregate;
9
+ }
8
10
  const bytes = { downloaded: uint(event.downloaded), total: uint(event.total), aggregate: false };
9
11
  const reported = reportedPercentage(event.percentage);
10
- if (reported === null && bytes.downloaded === 0 && bytes.total === 0)
12
+ if (reported === null && bytes.downloaded === 0 && bytes.total === 0) {
11
13
  return null;
14
+ }
12
15
  return { ...bytes, percentage: resolvePercentage(reported, bytes) };
13
16
  }
14
17
  export function progressBytes(event) {
15
18
  const transfer = transferProgress(event);
16
- if (!transfer)
19
+ if (!transfer) {
17
20
  return NO_BYTES;
21
+ }
18
22
  return {
19
23
  downloaded: transfer.downloaded,
20
24
  total: transfer.total,
@@ -34,8 +38,9 @@ export class DownloadSpeedSampler {
34
38
  return 0;
35
39
  }
36
40
  const elapsedMs = at - this._at;
37
- if (elapsedMs <= 0)
41
+ if (elapsedMs <= 0) {
38
42
  return Math.round(this._bytesPerSecond);
43
+ }
39
44
  const delta = bytes.downloaded - this._downloaded;
40
45
  if (delta === 0) {
41
46
  if (elapsedMs >= STALL_MS) {
@@ -62,7 +67,7 @@ export class DownloadSpeedSampler {
62
67
  }
63
68
  }
64
69
  function aggregateTransfer(value) {
65
- if (!record(value))
70
+ if (!isRecord(value))
66
71
  return null;
67
72
  const bytes = {
68
73
  downloaded: uint(value.overallDownloaded),
@@ -84,9 +89,6 @@ function reportedPercentage(value) {
84
89
  function bytesPercentage(bytes) {
85
90
  return bytes.total > 0 ? percentage((bytes.downloaded / bytes.total) * 100) : 0;
86
91
  }
87
- function record(value) {
88
- return typeof value === 'object' && value !== null;
89
- }
90
92
  function uint(value) {
91
93
  return typeof value === 'number' && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
92
94
  }
@@ -2,4 +2,4 @@ import type { ModelRegistryEntry } from '@qvac/sdk';
2
2
  export type ModelSrc = string | (ModelRegistryEntry & {
3
3
  src: string;
4
4
  });
5
- export declare function defaultContextSize(modelSrc: ModelSrc): number;
5
+ export declare function defaultContextSize(modelSrc: ModelSrc, trainedContext?: number): number;
@@ -1,7 +1,11 @@
1
1
  const TINY_MODEL_PARAMS_BILLIONS = 0.8;
2
2
  const TINY_MODEL_CONTEXT_SIZE = 8_192;
3
3
  const DESKTOP_DEFAULT_CONTEXT_SIZE = 51_200;
4
- export function defaultContextSize(modelSrc) {
4
+ export function defaultContextSize(modelSrc, trainedContext) {
5
+ const size = tieredContextSize(modelSrc);
6
+ return trainedContext ? Math.min(size, trainedContext) : size;
7
+ }
8
+ function tieredContextSize(modelSrc) {
5
9
  if (typeof modelSrc === 'string')
6
10
  return DESKTOP_DEFAULT_CONTEXT_SIZE;
7
11
  const params = Number.parseFloat(modelSrc.params);
@@ -1,4 +1,13 @@
1
1
  import type { CustomModelEntry } from './model-catalog.ts';
2
+ export interface CustomDownloads {
3
+ readonly files: ReadonlyArray<string>;
4
+ readonly dirs: ReadonlyArray<string>;
5
+ }
2
6
  export declare function customFileCachePath(src: string, modelsDir: string | undefined): string | null;
3
- export declare function downloadedCustomFiles(entry: CustomModelEntry, modelsDir: string | undefined): string[];
7
+ export interface CustomShardCache {
8
+ readonly dir: string;
9
+ readonly files: ReadonlyArray<string>;
10
+ }
11
+ export declare function customShardCache(src: string, modelsDir: string | undefined): CustomShardCache | null;
12
+ export declare function customDownloads(entry: CustomModelEntry, modelsDir: string | undefined): Promise<CustomDownloads>;
4
13
  export declare function isCustomModelCached(entry: CustomModelEntry, modelsDir: string | undefined): Promise<boolean>;
@@ -2,33 +2,61 @@ import crypto from 'crypto';
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
  import { isRemoteSource, localSourcePath } from './gguf-header.js';
5
- const SHARD = /-\d{5}-of-\d{5}\.\w+$/;
5
+ import { shardSet } from './shards.js';
6
6
  const ARCHIVE = /\.(tar|tgz|zip)(\.\w+)?$/i;
7
7
  export function customFileCachePath(src, modelsDir) {
8
- if (!isRemoteSource(src))
8
+ if (!isRemoteSource(src)) {
9
9
  return localSourcePath(src);
10
- const filename = src.split('/').pop()?.split('?')[0] || 'model.gguf';
11
- if (!modelsDir || SHARD.test(filename) || ARCHIVE.test(filename))
10
+ }
11
+ const filename = filenameOf(src);
12
+ if (!modelsDir || shardSet(src) || ARCHIVE.test(filename)) {
12
13
  return null;
13
- const hash = crypto.createHash('sha256').update(src, 'utf8').digest('hex').slice(0, 16);
14
- return path.join(modelsDir, `${hash}_${filename}`);
14
+ }
15
+ return path.join(modelsDir, `${shortHash(src)}_${filename}`);
15
16
  }
16
- export function downloadedCustomFiles(entry, modelsDir) {
17
- const srcs = entry.projectionSrc ? [entry.src, entry.projectionSrc] : [entry.src];
18
- return srcs
19
- .filter(isRemoteSource)
20
- .map((src) => customFileCachePath(src, modelsDir))
21
- .filter((file) => file !== null);
17
+ export function customShardCache(src, modelsDir) {
18
+ const set = shardSet(src);
19
+ if (!modelsDir || !isRemoteSource(src) || !set) {
20
+ return null;
21
+ }
22
+ const dir = path.join(modelsDir, 'sharded', shortHash(`${set.baseUrl}/${set.base}`));
23
+ return { dir, files: set.shards.map((shard) => path.join(dir, shard.filename)) };
24
+ }
25
+ export async function customDownloads(entry, modelsDir) {
26
+ const remote = sourcesOf(entry).filter(isRemoteSource);
27
+ const shardDirs = remote.flatMap((src) => customShardCache(src, modelsDir)?.dir ?? []);
28
+ const inShardDirs = await Promise.all(shardDirs.map(listFiles));
29
+ const singles = remote.flatMap((src) => customFileCachePath(src, modelsDir) ?? []);
30
+ return { files: [...singles, ...inShardDirs.flat()], dirs: shardDirs };
22
31
  }
23
32
  export async function isCustomModelCached(entry, modelsDir) {
24
- const srcs = entry.projectionSrc ? [entry.src, entry.projectionSrc] : [entry.src];
25
- const sizes = await Promise.all(srcs.map((src) => onDiskSize(customFileCachePath(src, modelsDir))));
33
+ const expected = sourcesOf(entry).flatMap((src) => customShardCache(src, modelsDir)?.files ?? [customFileCachePath(src, modelsDir)]);
34
+ const sizes = await Promise.all(expected.map(onDiskSize));
26
35
  const present = sizes.filter((size) => size !== null);
27
- if (present.length < sizes.length)
36
+ if (present.length < sizes.length) {
28
37
  return false;
38
+ }
29
39
  const total = present.reduce((sum, size) => sum + size, 0);
30
40
  return entry.expectedSize ? total === entry.expectedSize : true;
31
41
  }
42
+ function sourcesOf(entry) {
43
+ return entry.projectionSrc ? [entry.src, entry.projectionSrc] : [entry.src];
44
+ }
45
+ function filenameOf(src) {
46
+ return src.split('/').pop()?.split('?')[0] || 'model.gguf';
47
+ }
48
+ function shortHash(input) {
49
+ return crypto.createHash('sha256').update(input, 'utf8').digest('hex').slice(0, 16);
50
+ }
51
+ async function listFiles(dir) {
52
+ try {
53
+ const names = await fs.promises.readdir(dir);
54
+ return names.map((name) => path.join(dir, String(name)));
55
+ }
56
+ catch {
57
+ return [];
58
+ }
59
+ }
32
60
  async function onDiskSize(file) {
33
61
  if (file === null)
34
62
  return null;
@@ -23,8 +23,9 @@ export declare const GGUF_HEADER_BYTES = 1048576;
23
23
  export declare const GGUF_HEADER_TIMEOUT_MS = 30000;
24
24
  export declare function isRemoteSource(src: string): boolean;
25
25
  export declare function localSourcePath(src: string): string | null;
26
- export declare function readGgufHeaderBytes(src: string, opts?: {
26
+ export interface ReadGgufHeaderOptions {
27
27
  fetch?: GgufRangeFetch;
28
28
  maxBytes?: number;
29
- }): Promise<Uint8Array | null>;
29
+ }
30
+ export declare function readGgufHeaderBytes(src: string, opts?: ReadGgufHeaderOptions): Promise<Uint8Array | null>;
30
31
  export declare function parseGgufHeader(bytes: Uint8Array): GgufHeader | null;
@@ -5,6 +5,7 @@ import os from 'os';
5
5
  import path from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  import defaultFetch from '#fetch';
8
+ const rangeFetch = defaultFetch;
8
9
  export const GGUF_HEADER_BYTES = 1_048_576;
9
10
  export const GGUF_HEADER_TIMEOUT_MS = 30_000;
10
11
  const UINT8 = 0;
@@ -24,14 +25,17 @@ export function isRemoteSource(src) {
24
25
  return /^https?:\/\//i.test(src);
25
26
  }
26
27
  export function localSourcePath(src) {
27
- if (/^file:/i.test(src))
28
+ if (/^file:/i.test(src)) {
28
29
  return filePath(src);
30
+ }
29
31
  if (/^[a-z][a-z0-9+.-]+:/i.test(src))
30
32
  return null;
31
- if (src === '~')
33
+ if (src === '~') {
32
34
  return os.homedir();
33
- if (src.startsWith('~/'))
35
+ }
36
+ if (src.startsWith('~/')) {
34
37
  return path.join(os.homedir(), src.slice(2));
38
+ }
35
39
  return src;
36
40
  }
37
41
  function filePath(src) {
@@ -45,7 +49,7 @@ function filePath(src) {
45
49
  export function readGgufHeaderBytes(src, opts = {}) {
46
50
  const maxBytes = opts.maxBytes ?? GGUF_HEADER_BYTES;
47
51
  return isRemoteSource(src)
48
- ? fetchHead(src, maxBytes, opts.fetch ?? defaultFetch)
52
+ ? fetchHead(src, maxBytes, opts.fetch ?? rangeFetch)
49
53
  : readLocalHead(src, maxBytes);
50
54
  }
51
55
  async function fetchHead(src, maxBytes, fetch) {
@@ -56,11 +60,13 @@ async function fetchHead(src, maxBytes, fetch) {
56
60
  headers: { Range: `bytes=0-${maxBytes - 1}` },
57
61
  signal: controller.signal
58
62
  });
59
- if (!response.ok)
63
+ if (!response.ok) {
60
64
  return null;
65
+ }
61
66
  const length = response.headers.get('content-length');
62
- if (length === null || !(Number(length) <= maxBytes))
67
+ if (length === null || !(Number(length) <= maxBytes)) {
63
68
  return null;
69
+ }
64
70
  return new Uint8Array(await response.arrayBuffer()).subarray(0, maxBytes);
65
71
  }
66
72
  catch {
@@ -72,8 +78,9 @@ async function fetchHead(src, maxBytes, fetch) {
72
78
  }
73
79
  async function readLocalHead(src, maxBytes) {
74
80
  const file = localSourcePath(src);
75
- if (file === null)
81
+ if (file === null) {
76
82
  return null;
83
+ }
77
84
  let handle;
78
85
  try {
79
86
  handle = await fs.promises.open(file, 'r');
@@ -98,32 +105,42 @@ export function parseGgufHeader(bytes) {
98
105
  bytes,
99
106
  offset: 16
100
107
  };
101
- if (c.view.getUint32(4, true) < 2)
108
+ if (c.view.getUint32(4, true) < 2) {
102
109
  return null;
110
+ }
103
111
  const count = readU64(c);
104
112
  const header = {};
105
113
  const contextLengths = new Map();
106
114
  for (let i = 0; i < count; i += 1) {
107
115
  const key = readString(c);
108
- if (key === null || !has(c, 4))
116
+ if (key === null || !has(c, 4)) {
109
117
  break;
118
+ }
110
119
  const value = readValue(c, readU32(c), key === 'general.tags');
111
- if (!value.ok)
120
+ if (!value.ok) {
112
121
  break;
113
- if (key === 'general.architecture')
122
+ }
123
+ if (key === 'general.architecture') {
114
124
  header.architecture = value.str;
115
- else if (key === 'general.name')
125
+ }
126
+ else if (key === 'general.name') {
116
127
  header.name = value.str;
117
- else if (key === 'general.type')
128
+ }
129
+ else if (key === 'general.type') {
118
130
  header.type = value.str;
119
- else if (key === 'general.license')
131
+ }
132
+ else if (key === 'general.license') {
120
133
  header.license = value.str;
121
- else if (key === 'general.size_label')
134
+ }
135
+ else if (key === 'general.size_label') {
122
136
  header.sizeLabel = value.str;
123
- else if (key === 'general.tags')
137
+ }
138
+ else if (key === 'general.tags') {
124
139
  header.tags = value.strs;
125
- else if (key.endsWith('.context_length'))
140
+ }
141
+ else if (key.endsWith('.context_length')) {
126
142
  contextLengths.set(key, value.num);
143
+ }
127
144
  }
128
145
  header.contextLength =
129
146
  contextLengths.get(`${header.architecture}.context_length`) ??
@@ -157,17 +174,20 @@ function readValue(c, type, collect = false) {
157
174
  return str === null ? { ok: false } : { ok: true, str };
158
175
  }
159
176
  case ARRAY: {
160
- if (!has(c, 12))
177
+ if (!has(c, 12)) {
161
178
  return { ok: false };
179
+ }
162
180
  const elementType = readU32(c);
163
181
  const length = readU64(c);
164
182
  const strs = new Array();
165
183
  for (let i = 0; i < length; i += 1) {
166
184
  const element = readValue(c, elementType);
167
- if (!element.ok)
185
+ if (!element.ok) {
168
186
  return { ok: false };
169
- if (collect && element.str !== undefined)
187
+ }
188
+ if (collect && element.str !== undefined) {
170
189
  strs.push(element.str);
190
+ }
171
191
  }
172
192
  return collect ? { ok: true, strs } : { ok: true };
173
193
  }
@@ -176,8 +196,9 @@ function readValue(c, type, collect = false) {
176
196
  }
177
197
  }
178
198
  function fixed(c, width, read) {
179
- if (!has(c, width))
199
+ if (!has(c, width)) {
180
200
  return { ok: false };
201
+ }
181
202
  const num = read();
182
203
  c.offset += width;
183
204
  return { ok: true, num };
@@ -200,8 +221,9 @@ function readString(c) {
200
221
  if (!has(c, 8))
201
222
  return null;
202
223
  const length = readU64(c);
203
- if (!has(c, length))
224
+ if (!has(c, length)) {
204
225
  return null;
226
+ }
205
227
  const str = b4a.toString(c.bytes.subarray(c.offset, c.offset + length), 'utf8');
206
228
  c.offset += length;
207
229
  return str;
@@ -0,0 +1,14 @@
1
+ export interface IdleReaperOptions {
2
+ sweep: () => Promise<void>;
3
+ waitMs: number;
4
+ onError: (err: unknown) => void;
5
+ now?: () => number;
6
+ tickMs?: number;
7
+ }
8
+ export interface IdleReaper {
9
+ configure(timeoutMs: number): void;
10
+ enter(): Promise<void>;
11
+ exit(): void;
12
+ close(): void;
13
+ }
14
+ export declare function createIdleReaper({ sweep, waitMs, onError, now, tickMs }: IdleReaperOptions): IdleReaper;
@@ -0,0 +1,51 @@
1
+ import { raceTimeout } from '../helpers/race-timeout.js';
2
+ const DEFAULT_TICK_MS = 60_000;
3
+ export function createIdleReaper({ sweep, waitMs, onError, now = Date.now, tickMs = DEFAULT_TICK_MS }) {
4
+ let timeoutMs = 0;
5
+ let inflight = 0;
6
+ let lastActivity = now();
7
+ let sweeping = null;
8
+ let timer = null;
9
+ const tick = () => {
10
+ if (sweeping !== null || inflight > 0 || now() - lastActivity < timeoutMs) {
11
+ return;
12
+ }
13
+ const run = sweep().catch(onError).catch(noop);
14
+ sweeping = raceTimeout(run, { ms: waitMs, fallback: undefined }).finally(() => {
15
+ sweeping = null;
16
+ lastActivity = now();
17
+ });
18
+ };
19
+ const disarm = () => {
20
+ if (timer !== null) {
21
+ clearInterval(timer);
22
+ }
23
+ timer = null;
24
+ };
25
+ return {
26
+ configure(ms) {
27
+ timeoutMs = ms;
28
+ disarm();
29
+ if (ms <= 0) {
30
+ return;
31
+ }
32
+ timer = setInterval(tick, Math.min(tickMs, ms));
33
+ if (typeof timer.unref === 'function') {
34
+ timer.unref();
35
+ }
36
+ },
37
+ async enter() {
38
+ inflight++;
39
+ lastActivity = now();
40
+ if (sweeping !== null) {
41
+ await sweeping;
42
+ }
43
+ },
44
+ exit() {
45
+ inflight--;
46
+ lastActivity = now();
47
+ },
48
+ close: disarm
49
+ };
50
+ }
51
+ function noop() { }
@@ -1,4 +1,5 @@
1
- export declare function adrenoSafeKvCache(device: 'cpu' | 'gpu' | undefined, requestedK: string | undefined, requestedV: string | undefined, hostPlatform?: string): {
1
+ export interface KvCacheTypes {
2
2
  k: string | undefined;
3
3
  v: string | undefined;
4
- };
4
+ }
5
+ export declare function adrenoSafeKvCache(device: 'cpu' | 'gpu' | undefined, { k, v }: KvCacheTypes, hostPlatform?: string): KvCacheTypes;
@@ -1,6 +1,7 @@
1
1
  import { platform } from 'os';
2
- export function adrenoSafeKvCache(device, requestedK, requestedV, hostPlatform = platform()) {
3
- if (device === 'cpu' || hostPlatform !== 'android')
4
- return { k: requestedK, v: requestedV };
2
+ export function adrenoSafeKvCache(device, { k, v }, hostPlatform = platform()) {
3
+ if (device === 'cpu' || hostPlatform !== 'android') {
4
+ return { k, v };
5
+ }
5
6
  return { k: 'f16', v: 'f16' };
6
7
  }