@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
@@ -5,8 +5,9 @@ function isTurnBoundary(message) {
5
5
  }
6
6
  export function contextCandidates(history) {
7
7
  const { prefix, turns } = splitTurns(history);
8
- if (!turns.length)
8
+ if (!turns.length) {
9
9
  return [{ history, droppedTurns: 0 }];
10
+ }
10
11
  const maxDropped = Math.max(0, turns.length - 1);
11
12
  const candidates = dropCounts(maxDropped).map((droppedTurns) => ({
12
13
  history: droppedTurns ? [...prefix, ...turns.slice(droppedTurns).flat()] : history,
@@ -17,57 +18,60 @@ export function contextCandidates(history) {
17
18
  }
18
19
  function splitTurns(history) {
19
20
  const firstUser = history.findIndex(isTurnBoundary);
20
- if (firstUser === -1)
21
+ if (firstUser === -1) {
21
22
  return { prefix: history, turns: [] };
23
+ }
22
24
  const prefix = history.slice(0, firstUser);
23
25
  const starts = [];
24
- for (let i = firstUser; i < history.length; i++) {
25
- if (isTurnBoundary(history[i]))
26
+ for (const [i, message] of history.entries()) {
27
+ if (i >= firstUser && isTurnBoundary(message)) {
26
28
  starts.push(i);
29
+ }
27
30
  }
28
31
  const turns = starts.map((start, index) => history.slice(start, starts[index + 1]));
29
32
  return { prefix, turns };
30
33
  }
31
34
  function dropCounts(max) {
32
35
  const counts = [0];
33
- for (let step = 1; counts[counts.length - 1] < max; step *= 2) {
36
+ let last = 0;
37
+ for (let step = 1; last < max; step *= 2) {
34
38
  const next = Math.min(max, step);
35
- if (next !== counts[counts.length - 1])
36
- counts.push(next);
39
+ if (next === last) {
40
+ continue;
41
+ }
42
+ counts.push(next);
43
+ last = next;
37
44
  }
38
45
  return counts;
39
46
  }
40
47
  function appendToolCompactions(candidates) {
41
48
  const last = candidates[candidates.length - 1];
42
- if (!last)
49
+ if (!last) {
43
50
  return;
51
+ }
44
52
  const tools = compactableTools(last.history);
45
53
  let history = last.history;
46
54
  while (true) {
47
55
  const selected = longestTool(tools);
48
- if (!selected)
56
+ if (!selected) {
49
57
  return;
58
+ }
50
59
  selected.target = Math.max(TOOL_RESULT_TRUNCATION_MARKER.length, Math.floor(selected.target / 2));
51
60
  history = history.map(compactTool.bind(null, selected));
52
61
  candidates.push({ history, droppedTurns: last.droppedTurns });
53
62
  }
54
63
  }
55
64
  function compactableTools(history) {
56
- let newestUser = -1;
57
- for (let index = history.length - 1; index >= 0; index--) {
58
- if (!isTurnBoundary(history[index]))
59
- continue;
60
- newestUser = index;
61
- break;
62
- }
63
- if (newestUser === -1)
65
+ const newestUser = history.findLastIndex(isTurnBoundary);
66
+ if (newestUser === -1) {
64
67
  return [];
68
+ }
65
69
  const tools = [];
66
- for (let index = newestUser + 1; index < history.length; index++) {
67
- const message = history[index];
68
- if (message.role !== 'tool' || message.content.length <= TOOL_RESULT_TRUNCATION_MARKER.length) {
70
+ for (const [index, message] of history.entries()) {
71
+ if (index <= newestUser || message.role !== 'tool')
72
+ continue;
73
+ if (message.content.length <= TOOL_RESULT_TRUNCATION_MARKER.length)
69
74
  continue;
70
- }
71
75
  tools.push({ index, original: message.content, target: message.content.length });
72
76
  }
73
77
  return tools;
@@ -113,6 +113,17 @@ export interface WarmModelContext {
113
113
  agent?: AgentKnobs;
114
114
  signal?: CompletionContext['signal'];
115
115
  }
116
+ export type LoadModelContext = WarmModelContext;
117
+ export interface LoadModelResult {
118
+ loaded: boolean;
119
+ reason?: 'memory';
120
+ }
121
+ export interface UnloadModelContext {
122
+ model: string;
123
+ }
124
+ export interface UnloadModelResult {
125
+ unloaded: boolean;
126
+ }
116
127
  export interface DownloadContext {
117
128
  model: string;
118
129
  addon?: string;
@@ -165,6 +176,24 @@ export interface CapabilityInfo {
165
176
  recommendedModelName: string;
166
177
  memoryBudget?: MemoryBudget;
167
178
  }
179
+ export interface CredentialEntry {
180
+ key: string;
181
+ token?: string | null;
182
+ expiresAt?: number | null;
183
+ agentId?: string | null;
184
+ skill?: string | null;
185
+ }
186
+ export interface SkillsOptions {
187
+ readonly refresh?: boolean;
188
+ readonly skipCredentialProbe?: boolean;
189
+ }
190
+ export interface SkillWriteResult {
191
+ ok: boolean;
192
+ }
193
+ export interface InfraModel {
194
+ name: string;
195
+ addon: string;
196
+ }
168
197
  export interface HarnessContract {
169
198
  completion(context: CompletionContext): AsyncIterable<HarnessEvent>;
170
199
  invoke?(context: InvokeContext): AsyncIterable<HarnessEvent>;
@@ -183,24 +212,16 @@ export interface HarnessContract {
183
212
  suspend?(): Promise<void> | void;
184
213
  wakeup?(): Promise<void> | void;
185
214
  resume?(): Promise<void> | void;
186
- skills?(agentId?: string | null, modelName?: string | null, options?: {
187
- readonly refresh?: boolean;
188
- readonly skipCredentialProbe?: boolean;
189
- }): Promise<ResolvedSkillCatalogEntry[]>;
215
+ skills?(agentId?: string | null, modelName?: string | null, options?: SkillsOptions): Promise<ResolvedSkillCatalogEntry[]>;
190
216
  tools?(): Promise<ToolCatalogEntry[]>;
191
- setCredentials?(entries: ReadonlyArray<{
192
- key: string;
193
- token?: string | null;
194
- expiresAt?: number | null;
195
- agentId?: string | null;
196
- skill?: string | null;
197
- }>): void | Promise<void>;
217
+ setCredentials?(entries: ReadonlyArray<CredentialEntry>): void | Promise<void>;
198
218
  setSkillSettings?(entries: ReadonlyArray<{
199
219
  skill: string;
200
220
  key: string;
201
221
  value: string;
202
222
  agentId?: string | null;
203
223
  }>): void | Promise<void>;
224
+ setModelIdleTimeout?(ms: number): void | Promise<void>;
204
225
  listManagedSkills?(): Promise<ManagedSkill[]>;
205
226
  readSkillFile?(name: string): Promise<SkillFileContent | null>;
206
227
  writeSkillFile?(name: string, content: string): Promise<{
@@ -214,13 +235,12 @@ export interface HarnessContract {
214
235
  }>;
215
236
  list?(options?: ListModelsOptions): Promise<ModelInfo[]>;
216
237
  warmModel?(context: WarmModelContext): Promise<void>;
238
+ loadModel?(context: LoadModelContext): Promise<LoadModelResult>;
239
+ unloadModel?(context: UnloadModelContext): Promise<UnloadModelResult>;
217
240
  deleteModel?(name: string): Promise<DeleteModelSummary>;
218
241
  clearKvCache?(): Promise<KvCacheDeletionResult>;
219
242
  deleteKvCache?(kvCacheKey: string): Promise<KvCacheDeletionResult>;
220
- infraModels?(profile: string): Promise<ReadonlyArray<{
221
- name: string;
222
- addon: string;
223
- }>>;
243
+ infraModels?(profile: string): Promise<ReadonlyArray<InfraModel>>;
224
244
  setCustomModels?(entries: ReadonlyArray<CustomModelEntry>): void | Promise<void>;
225
245
  previewModelSource?(context: PreviewModelSourceContext): Promise<ModelSourcePreview>;
226
246
  capability?(): Promise<CapabilityInfo>;
@@ -4,18 +4,18 @@ export declare function diagramRepairPromptCount(content: string, parentRunId?:
4
4
  export declare function isDiagramRepairPrompt(content: string): boolean;
5
5
  export declare function completeMermaidDiagramCount(content: string): number;
6
6
  export declare function diagramRepairAccepted(text: string): boolean;
7
- type DiagramRepairRecord = {
7
+ interface DiagramRepairRecord {
8
8
  readonly id: string;
9
9
  readonly seq: number;
10
10
  readonly runId?: string | null;
11
11
  readonly parentId?: string | null;
12
- };
13
- export type DiagramRepairLink = {
12
+ }
13
+ export interface DiagramRepairLink {
14
14
  readonly expectedCount: number;
15
15
  readonly parentId: string;
16
16
  readonly promptId: string;
17
17
  readonly promptText: string;
18
18
  readonly runId: string;
19
- };
19
+ }
20
20
  export declare function diagramRepairLinks<T extends DiagramRepairRecord>(records: ReadonlyArray<T>, textOf: (record: T) => string | null): ReadonlyArray<DiagramRepairLink>;
21
21
  export {};
@@ -3,8 +3,9 @@ export const DIAGRAM_REPAIR_PROMPT_SUFFIX = 'Rewrite the complete previous answe
3
3
  'Common fixes: put every label with spaces or punctuation in double quotes like A["Send (HTTP)"]; never use lowercase end as a node label - write "End"; the first line inside the fence must be the diagram type; close the fence before any prose - never leave a sentence inside it; give every node a unique id.';
4
4
  export function diagramRepairPromptCount(content, parentRunId) {
5
5
  const match = /^<!-- qvac:diagram-repair:(\d+):([^\s>]+) -->\n(\d+) Mermaid diagram(?:s)? in your previous answer fail(?:s)? to render\.\n/.exec(content);
6
- if (!match || !content.endsWith(`\n\n${DIAGRAM_REPAIR_PROMPT_SUFFIX}`))
6
+ if (!match || !content.endsWith(`\n\n${DIAGRAM_REPAIR_PROMPT_SUFFIX}`)) {
7
7
  return null;
8
+ }
8
9
  const expected = Number(match[1]);
9
10
  return Number.isSafeInteger(expected) &&
10
11
  expected > 0 &&
@@ -31,8 +32,9 @@ export function diagramRepairLinks(records, textOf) {
31
32
  if (!child.runId || !child.parentId)
32
33
  return [];
33
34
  const parent = byId.get(child.parentId);
34
- if (!parent?.runId)
35
+ if (!parent?.runId) {
35
36
  return [];
37
+ }
36
38
  const prompt = [...ordered]
37
39
  .reverse()
38
40
  .find((candidate) => candidate.seq > parent.seq &&
@@ -41,8 +43,9 @@ export function diagramRepairLinks(records, textOf) {
41
43
  diagramRepairPromptCount(textOf(candidate) ?? '', parent.runId ?? undefined) !== null);
42
44
  const promptText = prompt ? textOf(prompt) : null;
43
45
  const expectedCount = diagramRepairPromptCount(promptText ?? '', parent.runId);
44
- if (prompt)
46
+ if (prompt) {
45
47
  used.add(prompt.id);
48
+ }
46
49
  return prompt && promptText && expectedCount
47
50
  ? [
48
51
  {
@@ -1,5 +1,6 @@
1
1
  import type { AbortSignal } from 'bare-abort-controller';
2
2
  export { closePythonWorker as closeDocxWorker } from '../tools/exec/adapters/python/worker/client.ts';
3
- export declare function extractDocxText(bytes: Uint8Array, fileName: string, options?: {
3
+ export interface DocxExtractOptions {
4
4
  readonly signal?: AbortSignal;
5
- }): Promise<string>;
5
+ }
6
+ export declare function extractDocxText(bytes: Uint8Array, fileName: string, options?: DocxExtractOptions): Promise<string>;
@@ -1,2 +1,3 @@
1
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
1
2
  export declare function errorMessage(err: unknown): string;
2
3
  export declare function errorCode(err: unknown): string | undefined;
@@ -1,3 +1,6 @@
1
+ export function isRecord(value) {
2
+ return typeof value === 'object' && value !== null;
3
+ }
1
4
  export function errorMessage(err) {
2
5
  return err instanceof Error ? err.message : String(err);
3
6
  }
@@ -13,8 +13,9 @@ function pickTier(availableBytes) {
13
13
  return 'xhigh';
14
14
  if (availableBytes >= 30 * GIB)
15
15
  return 'high';
16
- if (availableBytes >= 8 * GIB)
16
+ if (availableBytes >= 8 * GIB) {
17
17
  return 'mid';
18
+ }
18
19
  return 'low';
19
20
  }
20
21
  function tierProfile(budget) {
@@ -38,8 +39,9 @@ function recommend(budget) {
38
39
  async function probeSysfs(reads) {
39
40
  for (const path of SYSFS_VRAM_PATHS) {
40
41
  const raw = await reads.readFile(path);
41
- if (!raw)
42
+ if (!raw) {
42
43
  continue;
44
+ }
43
45
  const bytes = Number.parseInt(raw.trim(), 10);
44
46
  if (!Number.isNaN(bytes) && bytes > 0) {
45
47
  return { totalBytes: bytes, availableBytes: bytes, source: 'vram' };
@@ -50,25 +52,31 @@ async function probeSysfs(reads) {
50
52
  async function probeDesktop(reads) {
51
53
  const ram = reads.totalmem();
52
54
  const addon = probeWithAddon(await reads.readGpuStats(), ram);
53
- if (addon && addon.source === 'vram')
55
+ if (addon && addon.source === 'vram') {
54
56
  return addon;
57
+ }
55
58
  const nvidia = await reads.run('nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits');
56
59
  const nvidiaBudget = nvidia ? parseNvidiaSmiOutput(nvidia) : null;
57
- if (nvidiaBudget)
60
+ if (nvidiaBudget) {
58
61
  return nvidiaBudget;
62
+ }
59
63
  const rocm = await reads.run('rocm-smi --showmeminfo vram');
60
64
  const rocmBudget = rocm ? parseRocmSmiOutput(rocm) : null;
61
- if (rocmBudget)
65
+ if (rocmBudget) {
62
66
  return rocmBudget;
67
+ }
63
68
  const sysfs = await probeSysfs(reads);
64
- if (sysfs)
69
+ if (sysfs) {
65
70
  return sysfs;
71
+ }
66
72
  const registry = await reads.run(`powershell -Command "${WINDOWS_REGISTRY_QUERY}"`);
67
73
  const registryBudget = registry ? parseWindowsRegistryOutput(registry, ram) : null;
68
- if (registryBudget)
74
+ if (registryBudget) {
69
75
  return registryBudget;
70
- if (addon)
76
+ }
77
+ if (addon) {
71
78
  return addon;
79
+ }
72
80
  return { ...sharedRamBudget(ram), source: 'no-gpu' };
73
81
  }
74
82
  async function isPixel(reads) {
@@ -77,10 +85,12 @@ async function isPixel(reads) {
77
85
  }
78
86
  async function probeMemoryBudget(reads) {
79
87
  const os = reads.platform();
80
- if (os === 'android' || os === 'ios')
88
+ if (os === 'android' || os === 'ios') {
81
89
  return mobileBudget(reads.totalmem(), await isPixel(reads));
82
- if (os === 'darwin')
90
+ }
91
+ if (os === 'darwin') {
83
92
  return sharedRamBudget(reads.totalmem());
93
+ }
84
94
  return probeDesktop(reads);
85
95
  }
86
96
  function forcedProfile() {
@@ -19,14 +19,17 @@ const INTEGRATED_GPU = [
19
19
  ];
20
20
  const SOFTWARE_GPU = [/llvmpipe/i, /swrast/i, /mesa/i];
21
21
  export function classifyGpu(name) {
22
- for (const re of DISCRETE_GPU)
22
+ for (const re of DISCRETE_GPU) {
23
23
  if (re.test(name))
24
24
  return 'discrete';
25
- for (const re of INTEGRATED_GPU)
25
+ }
26
+ for (const re of INTEGRATED_GPU) {
26
27
  if (re.test(name))
27
28
  return 'integrated';
28
- for (const re of SOFTWARE_GPU)
29
+ }
30
+ for (const re of SOFTWARE_GPU) {
29
31
  if (re.test(name))
30
32
  return 'software';
33
+ }
31
34
  return 'integrated';
32
35
  }
@@ -12,8 +12,9 @@ const StatsSchema = z.object({
12
12
  function classifyBestDevice(devices, systemRamBytes) {
13
13
  for (const device of devices) {
14
14
  const cls = classifyGpu(device.name);
15
- if (cls === 'software')
15
+ if (cls === 'software') {
16
16
  continue;
17
+ }
17
18
  if (cls === 'discrete') {
18
19
  const vram = device.videoMemoryBytes;
19
20
  if (vram > systemRamBytes * IGPU_LIE_THRESHOLD) {
@@ -26,15 +27,18 @@ function classifyBestDevice(devices, systemRamBytes) {
26
27
  }
27
28
  export function probeWithAddon(rawStats, systemRamBytes) {
28
29
  const parsed = StatsSchema.safeParse(rawStats);
29
- if (!parsed.success)
30
+ if (!parsed.success) {
30
31
  return null;
32
+ }
31
33
  const devices = parsed.data.gpu?.devices ?? [];
32
- if (devices.length === 0)
34
+ if (devices.length === 0) {
33
35
  return null;
36
+ }
34
37
  const totalRamBytes = parsed.data.memory?.totalBytes ?? systemRamBytes;
35
38
  const best = classifyBestDevice(devices, totalRamBytes);
36
- if (best)
39
+ if (best) {
37
40
  return best;
41
+ }
38
42
  if (devices.every((device) => classifyGpu(device.name) === 'software')) {
39
43
  return { ...sharedRamBudget(totalRamBytes), source: 'software' };
40
44
  }
@@ -1,3 +1,4 @@
1
+ import b4a from 'b4a';
1
2
  import { spawn } from 'child_process';
2
3
  import os from 'os';
3
4
  import { readSystemStats } from '#hw-gpu-stats';
@@ -11,8 +12,9 @@ export function runGpuProbeIfRequested() {
11
12
  }
12
13
  export function parseGpuProbeOutput(output) {
13
14
  const line = output.trim().split('\n').at(-1) ?? '';
14
- if (line.length === 0)
15
+ if (line.length === 0) {
15
16
  return null;
17
+ }
16
18
  try {
17
19
  const parsed = JSON.parse(line);
18
20
  return typeof parsed === 'object' ? parsed : null;
@@ -32,7 +34,7 @@ export function spawnGpuProbe(entry = Bare.argv[1]) {
32
34
  resolve({ ok: false, stats: null });
33
35
  }, TIMEOUT_MS);
34
36
  child.stdout?.on('data', (chunk) => {
35
- output += Buffer.from(chunk).toString();
37
+ output += b4a.toString(chunk);
36
38
  });
37
39
  child.stderr?.on('data', () => { });
38
40
  child.on('error', () => {
@@ -1,22 +1,21 @@
1
1
  const os = require('os')
2
2
 
3
- // loaded on first read: the addon drags the graphics stack (and on Linux a
4
- // second LLVM) into whichever process imports it
3
+ // lazy: the addon drags the graphics stack (and on Linux a second LLVM) into its importer
5
4
  let GPUInfo
6
5
 
7
- // Shaped for probeWithAddon (gpu-probe-addon.ts): the probe only wants each
8
- // device's name and VRAM total, plus system RAM to catch integrated-GPU lies.
6
+ // system RAM is here to catch integrated-GPU lies
9
7
  exports.readSystemStats = function readSystemStats() {
10
8
  try {
11
9
  GPUInfo ??= require('bare-gpu-info')
12
10
  const info = new GPUInfo()
13
11
  try {
14
- const devices = []
15
- for (const gpu of info) {
16
- if (typeof gpu.memory !== 'number' || !gpu.name) continue
17
- devices.push({ name: gpu.name, videoMemoryBytes: gpu.memory })
12
+ const devices = [...info]
13
+ .filter((gpu) => typeof gpu.memory === 'number' && gpu.name)
14
+ .map((gpu) => ({ name: gpu.name, videoMemoryBytes: gpu.memory }))
15
+ if (devices.length === 0) {
16
+ return null
18
17
  }
19
- if (devices.length === 0) return null
18
+
20
19
  return { memory: { totalBytes: os.totalmem() }, gpu: { devices } }
21
20
  } finally {
22
21
  info.destroy()
@@ -7,25 +7,29 @@ export const IGPU_LIE_THRESHOLD = 0.9;
7
7
  export function parseNvidiaSmiOutput(stdout) {
8
8
  const firstLine = stdout.split('\n')[0] ?? '';
9
9
  const mib = Number.parseInt(firstLine, 10);
10
- if (Number.isNaN(mib) || mib <= 0)
10
+ if (Number.isNaN(mib) || mib <= 0) {
11
11
  return null;
12
+ }
12
13
  const bytes = mib * 1024 * 1024;
13
14
  return { totalBytes: bytes, availableBytes: bytes, source: 'vram', gpuClass: 'discrete' };
14
15
  }
15
16
  export function parseRocmSmiOutput(stdout) {
16
17
  const match = /Total.*?:\s*(\d+)/i.exec(stdout);
17
- if (!match)
18
+ if (!match) {
18
19
  return null;
20
+ }
19
21
  const bytes = Number.parseInt(match[1] ?? '', 10);
20
- if (Number.isNaN(bytes) || bytes <= 0)
22
+ if (Number.isNaN(bytes) || bytes <= 0) {
21
23
  return null;
24
+ }
22
25
  return { totalBytes: bytes, availableBytes: bytes, source: 'vram' };
23
26
  }
24
27
  export function parseWindowsRegistryOutput(stdout, systemRamBytes) {
25
28
  const firstLine = stdout.split('\n')[0] ?? '';
26
29
  const bytes = Number.parseInt(firstLine, 10);
27
- if (Number.isNaN(bytes) || bytes <= 0)
30
+ if (Number.isNaN(bytes) || bytes <= 0) {
28
31
  return null;
32
+ }
29
33
  if (bytes > systemRamBytes * IGPU_LIE_THRESHOLD) {
30
34
  return { ...sharedRamBudget(systemRamBytes), source: 'integrated' };
31
35
  }
@@ -7,14 +7,16 @@ export async function* bufferBurst(tokens, ms) {
7
7
  continue;
8
8
  }
9
9
  if (Date.now() >= deadline) {
10
- if (burst)
10
+ if (burst) {
11
11
  yield burst;
12
+ }
12
13
  burst = null;
13
14
  yield token;
14
15
  continue;
15
16
  }
16
17
  burst += token;
17
18
  }
18
- if (burst)
19
+ if (burst) {
19
20
  yield burst;
21
+ }
20
22
  }
@@ -19,14 +19,16 @@ export async function* stopAtControlToken(tokens, onStop) {
19
19
  const safe = scanner({ stop: STOP_MARKERS });
20
20
  for await (const token of tokens) {
21
21
  const shown = safe.push(token);
22
- if (shown)
22
+ if (shown) {
23
23
  yield shown;
24
+ }
24
25
  if (safe.stopped !== null) {
25
26
  onStop?.(safe.stopped);
26
27
  return;
27
28
  }
28
29
  }
29
30
  const rest = safe.flush();
30
- if (rest)
31
+ if (rest) {
31
32
  yield rest;
33
+ }
32
34
  }
@@ -1,6 +1,6 @@
1
- export type JoinGuard = {
1
+ export interface JoinGuard {
2
2
  readonly push: (text: string) => string;
3
3
  readonly cut: () => void;
4
4
  readonly flush: () => string;
5
- };
5
+ }
6
6
  export declare function createJoinGuard(markers: readonly string[]): JoinGuard;
@@ -30,8 +30,9 @@ export function createJoinGuard(markers) {
30
30
  function mend(text, at, markers) {
31
31
  for (;;) {
32
32
  const found = spanning(text, at, markers);
33
- if (found === null)
33
+ if (found === null) {
34
34
  return { text, at };
35
+ }
35
36
  text = text.slice(0, found.index) + text.slice(found.index + found.marker.length);
36
37
  at = found.index;
37
38
  }
@@ -39,8 +40,9 @@ function mend(text, at, markers) {
39
40
  function spanning(text, at, markers) {
40
41
  for (const marker of markers) {
41
42
  const index = text.indexOf(marker, Math.max(0, at - marker.length + 1));
42
- if (index !== -1 && index < at)
43
+ if (index !== -1 && index < at) {
43
44
  return { index, marker };
45
+ }
44
46
  }
45
47
  return null;
46
48
  }
@@ -5,8 +5,9 @@ export function createLoadStall(ms, message) {
5
5
  rejectStall = reject;
6
6
  });
7
7
  const arm = () => {
8
- if (timer !== null)
8
+ if (timer !== null) {
9
9
  clearTimeout(timer);
10
+ }
10
11
  timer = setTimeout(() => {
11
12
  rejectStall?.(new Error(message));
12
13
  }, ms);
@@ -15,8 +16,9 @@ export function createLoadStall(ms, message) {
15
16
  return {
16
17
  bump: arm,
17
18
  clear: () => {
18
- if (timer !== null)
19
+ if (timer !== null) {
19
20
  clearTimeout(timer);
21
+ }
20
22
  timer = null;
21
23
  },
22
24
  promise
@@ -1,5 +1,5 @@
1
- export type MarkupStreamFilter = {
1
+ export interface MarkupStreamFilter {
2
2
  readonly push: (token: string) => string;
3
3
  readonly flush: () => string;
4
- };
4
+ }
5
5
  export declare function createMarkupStreamFilter(): MarkupStreamFilter;
@@ -22,8 +22,9 @@ export function createMarkupStreamFilter() {
22
22
  for (;;) {
23
23
  const step = state.closer ? consumeSuppressed(state) : emitUntilOpener(state);
24
24
  out += step.out;
25
- if (step.done)
25
+ if (step.done) {
26
26
  return out;
27
+ }
27
28
  }
28
29
  },
29
30
  flush() {
@@ -52,8 +53,9 @@ function dropResumeNewline(state) {
52
53
  if (!state.stripNewline || state.buffer.length === 0)
53
54
  return;
54
55
  state.stripNewline = false;
55
- if (state.buffer[0] === '\n')
56
+ if (state.buffer[0] === '\n') {
56
57
  state.buffer = state.buffer.slice(1);
58
+ }
57
59
  }
58
60
  function emitUntilOpener(state) {
59
61
  dropResumeNewline(state);
@@ -63,8 +65,9 @@ function emitUntilOpener(state) {
63
65
  state.guard.cut();
64
66
  state.buffer = state.buffer.slice(marker.index + marker.marker.length);
65
67
  const closer = MARKUP_CLOSERS[marker.marker];
66
- if (closer === undefined)
68
+ if (closer === undefined) {
67
69
  state.stripNewline = true;
70
+ }
68
71
  state.closer = closer ?? null;
69
72
  return { out, done: false };
70
73
  }
@@ -8,8 +8,9 @@ export class AbortedError extends Error {
8
8
  export function raceAbort(promise, signal) {
9
9
  if (!signal)
10
10
  return promise;
11
- if (signal.aborted)
11
+ if (signal.aborted) {
12
12
  return Promise.reject(new AbortedError());
13
+ }
13
14
  return new Promise((resolve, reject) => {
14
15
  const onAbort = () => reject(new AbortedError());
15
16
  signal.addEventListener('abort', onAbort, { once: true });
@@ -0,0 +1,5 @@
1
+ export interface RaceTimeoutOptions<T> {
2
+ ms: number;
3
+ fallback: T;
4
+ }
5
+ export declare function raceTimeout<T>(pending: Promise<T>, { ms, fallback }: RaceTimeoutOptions<T>): Promise<T>;
@@ -0,0 +1,14 @@
1
+ export async function raceTimeout(pending, { ms, fallback }) {
2
+ let timer = null;
3
+ const expired = new Promise((resolve) => {
4
+ timer = setTimeout(() => resolve(fallback), ms);
5
+ });
6
+ try {
7
+ return await Promise.race([pending, expired]);
8
+ }
9
+ finally {
10
+ if (timer !== null) {
11
+ clearTimeout(timer);
12
+ }
13
+ }
14
+ }