@qvac/harness 0.8.9 → 0.8.11

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 (115) hide show
  1. package/CHANGELOG.md +73 -1
  2. package/dist-lib/index.d.ts +1 -1
  3. package/dist-lib/index.js +51 -50
  4. package/dist-lib/lib/audio-transcode-bare.js +2 -2
  5. package/dist-lib/lib/capabilities/completion.d.ts +1 -2
  6. package/dist-lib/lib/capabilities/completion.js +29 -25
  7. package/dist-lib/lib/capabilities/tool-prompt-log.js +3 -3
  8. package/dist-lib/lib/capabilities/voice.js +7 -6
  9. package/dist-lib/lib/connect.js +18 -17
  10. package/dist-lib/lib/errors.d.ts +266 -0
  11. package/dist-lib/lib/errors.js +189 -0
  12. package/dist-lib/lib/helpers/load-stall.d.ts +1 -1
  13. package/dist-lib/lib/helpers/load-stall.js +2 -2
  14. package/dist-lib/lib/helpers/race-abort.d.ts +2 -1
  15. package/dist-lib/lib/helpers/race-abort.js +3 -4
  16. package/dist-lib/lib/hooks/built-in/arg-repair.js +2 -2
  17. package/dist-lib/lib/hooks/pipeline.js +4 -4
  18. package/dist-lib/lib/hooks/run.js +2 -2
  19. package/dist-lib/lib/log.d.ts +26 -2
  20. package/dist-lib/lib/log.js +66 -16
  21. package/dist-lib/lib/models/model-pool.js +11 -6
  22. package/dist-lib/lib/models/model-source.js +3 -2
  23. package/dist-lib/lib/no-redirect-fetch-bare.js +2 -1
  24. package/dist-lib/lib/no-redirect-fetch-impl.js +2 -1
  25. package/dist-lib/lib/office/extract.js +4 -3
  26. package/dist-lib/lib/office/index.js +2 -1
  27. package/dist-lib/lib/resolution-detector.js +3 -3
  28. package/dist-lib/lib/rpc-session.js +2 -1
  29. package/dist-lib/lib/sdk-loader.js +2 -2
  30. package/dist-lib/lib/serve.js +33 -29
  31. package/dist-lib/lib/spawn.d.ts +2 -4
  32. package/dist-lib/lib/spawn.js +14 -7
  33. package/dist-lib/lib/tools/edit-image.js +2 -1
  34. package/dist-lib/lib/tools/exec/adapters/python/attachments.js +8 -8
  35. package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +7 -6
  36. package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.js +2 -2
  37. package/dist-lib/lib/tools/exec/adapters/python/memfs.js +2 -2
  38. package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +8 -8
  39. package/dist-lib/lib/tools/exec/adapters/python/run-session.js +11 -7
  40. package/dist-lib/lib/tools/exec/adapters/python/wheels.js +5 -5
  41. package/dist-lib/lib/tools/exec/adapters/python/worker/active.js +2 -2
  42. package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +9 -8
  43. package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -0
  44. package/dist-lib/lib/tools/exec/adapters/python/worker/serve.js +4 -4
  45. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn.d.ts +2 -1
  46. package/dist-lib/lib/tools/exec/adapters/python/worker/spawn.js +2 -1
  47. package/dist-lib/lib/tools/exec/obsidian/note-name.js +2 -1
  48. package/dist-lib/lib/tools/exec/obsidian/safe-path.js +5 -4
  49. package/dist-lib/lib/tools/exec/obsidian/vault-create.js +3 -2
  50. package/dist-lib/lib/tools/exec/obsidian/vault-fs.js +2 -2
  51. package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +3 -2
  52. package/dist-lib/lib/tools/exec/obsidian/vault-read.js +6 -5
  53. package/dist-lib/lib/tools/exec/obsidian/vault-write.js +13 -12
  54. package/dist-lib/lib/tools/exec/runner.js +2 -1
  55. package/dist-lib/lib/tools/files/edit-file.d.ts +3 -0
  56. package/dist-lib/lib/tools/files/edit-file.js +73 -0
  57. package/dist-lib/lib/tools/files/glob.d.ts +3 -0
  58. package/dist-lib/lib/tools/files/glob.js +63 -0
  59. package/dist-lib/lib/tools/files/grep.d.ts +3 -0
  60. package/dist-lib/lib/tools/files/grep.js +114 -0
  61. package/dist-lib/lib/tools/files/index.d.ts +3 -0
  62. package/dist-lib/lib/tools/files/index.js +23 -0
  63. package/dist-lib/lib/tools/files/list-dir.d.ts +5 -0
  64. package/dist-lib/lib/tools/files/list-dir.js +130 -0
  65. package/dist-lib/lib/tools/files/make-dir.d.ts +3 -0
  66. package/dist-lib/lib/tools/files/make-dir.js +47 -0
  67. package/dist-lib/lib/tools/files/read-file.d.ts +3 -0
  68. package/dist-lib/lib/tools/files/read-file.js +103 -0
  69. package/dist-lib/lib/tools/files/transfer.d.ts +4 -0
  70. package/dist-lib/lib/tools/files/transfer.js +82 -0
  71. package/dist-lib/lib/tools/files/trash-file.d.ts +4 -0
  72. package/dist-lib/lib/tools/files/trash-file.js +61 -0
  73. package/dist-lib/lib/tools/files/write-file.d.ts +3 -0
  74. package/dist-lib/lib/tools/files/write-file.js +55 -0
  75. package/dist-lib/lib/tools/fs/document-text.d.ts +2 -0
  76. package/dist-lib/lib/tools/fs/document-text.js +38 -0
  77. package/dist-lib/lib/tools/fs/edit-match.d.ts +11 -0
  78. package/dist-lib/lib/tools/fs/edit-match.js +85 -0
  79. package/dist-lib/lib/tools/fs/remote.d.ts +2 -1
  80. package/dist-lib/lib/tools/fs/remote.js +4 -4
  81. package/dist-lib/lib/tools/fs/scope.d.ts +1 -1
  82. package/dist-lib/lib/tools/fs/scope.js +3 -2
  83. package/dist-lib/lib/tools/fs/targets.d.ts +10 -0
  84. package/dist-lib/lib/tools/fs/targets.js +32 -0
  85. package/dist-lib/lib/tools/fs/tool-path.d.ts +24 -0
  86. package/dist-lib/lib/tools/fs/tool-path.js +58 -0
  87. package/dist-lib/lib/tools/fs/walk.js +3 -3
  88. package/dist-lib/lib/tools/generate-image.d.ts +2 -2
  89. package/dist-lib/lib/tools/generate-image.js +5 -6
  90. package/dist-lib/lib/tools/office.js +2 -1
  91. package/dist-lib/lib/tools/skill.js +2 -1
  92. package/dist-lib/lib/tools/skills/bundled-dir.js +4 -4
  93. package/dist-lib/lib/tools/skills/catalog.js +2 -2
  94. package/dist-lib/lib/tools/skills/cli-argv.js +25 -5
  95. package/dist-lib/lib/tools/skills/credential-health.js +3 -3
  96. package/dist-lib/lib/tools/skills/overlay-crud.js +3 -2
  97. package/dist-lib/lib/tools/translate.d.ts +2 -2
  98. package/dist-lib/lib/tools/translate.js +5 -6
  99. package/dist-lib/lib/tools/weather-lookup/geocode.js +5 -4
  100. package/dist-lib/lib/web-search/engines/bing.js +2 -1
  101. package/dist-lib/lib/web-search/engines/google.js +2 -1
  102. package/dist-lib/lib/web-search/fetch-html.d.ts +5 -5
  103. package/dist-lib/lib/web-search/fetch-html.js +12 -15
  104. package/package.json +67 -25
  105. package/worker/harness-sidecar.mjs +12 -13
  106. package/dist-lib/lib/tools/create-documents.d.ts +0 -3
  107. package/dist-lib/lib/tools/create-documents.js +0 -95
  108. package/dist-lib/lib/tools/edit-documents.d.ts +0 -3
  109. package/dist-lib/lib/tools/edit-documents.js +0 -132
  110. package/dist-lib/lib/tools/file-glob.d.ts +0 -14
  111. package/dist-lib/lib/tools/file-glob.js +0 -118
  112. package/dist-lib/lib/tools/grep.d.ts +0 -5
  113. package/dist-lib/lib/tools/grep.js +0 -184
  114. package/dist-lib/lib/tools/read-files.d.ts +0 -5
  115. package/dist-lib/lib/tools/read-files.js +0 -157
package/CHANGELOG.md CHANGED
@@ -8,6 +8,76 @@ While the major version is `0`, a breaking change bumps the minor version.
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.8.11] - 2026-10-16
12
+
13
+ ### Fixed
14
+
15
+ - A skill CLI call the schema rejects names what the command accepts: an
16
+ `unknown parameter`, `unknown flag` or `unexpected argument` error now ends
17
+ with the node's parameters or subcommands, e.g.
18
+ `unknown parameter "title"; create takes: name, path, …`. The message
19
+ prefixes are unchanged. (#523)
20
+
21
+ ## [0.8.10] - 2026-10-16
22
+
23
+ ### Added
24
+
25
+ - Harness log lines carry a level, `error`, `warn`, `info` or `debug`, and run
26
+ on `@qvac/logging`. A sink set with `setHarnessLogSink` receives the level as
27
+ its second argument, `(line, level) => …`, and `harnessLog`,
28
+ `setHarnessLogLevel` and the `HarnessLog` and `LogLevel` types are exported.
29
+ `hlog` keeps working and logs at `info`. (#471)
30
+ - `@qvac/harness/errors`: every runtime failure the harness detects throws a
31
+ `HarnessError`, a `@qvac/error` error with a code in 102001–104000, and its
32
+ message is unchanged. `isHarnessError(err, name)` matches the code, also once
33
+ bare-rpc has turned it into a string, and `harnessErrorCode(name)` and
34
+ `harnessErrorCodes()` give the numbers. A code reaches a host on a unary RPC
35
+ call; completion, model and download events still carry only the message.
36
+ (#472)
37
+
38
+ ### Changed
39
+
40
+ - `QVAC_LOG_LEVEL` (the app's `--log-level`) now filters harness lines too. At
41
+ `info`, round stats and `sdk tool definitions`, which are `debug`, leave
42
+ `harness.log`. With it unset, an enabled log keeps every line, as before.
43
+ (#471)
44
+ - The `logger` a host passes to `spawnHarness` or `harnessChild` sets harness's
45
+ log level, in the sidecar's `harness.log` as well as in the host's process,
46
+ when it has `getLevel()`; `QVAC_LOG_LEVEL` still wins when set. The lines
47
+ harness writes in the host's process go to that logger at their level, not to
48
+ stdout, whose unread pipe can halt Bare on Windows. A sink set with
49
+ `setHarnessLogSink` still takes them first. (#471)
50
+ - **Breaking** for a caller that reads `.name`: `AbortedError`, `TimeoutError`,
51
+ `AbortError`, `JsonParseError` and `DriveReadTruncatedError` now extend
52
+ `HarnessError` and carry its registry name (`AbortedError` is `ABORTED`,
53
+ `TimeoutError` is `FETCH_TIMED_OUT`), and the Python sandbox's
54
+ `fetch refused: …` errors are no longer `TypeError`s. Match with `instanceof`
55
+ or the code. (#472)
56
+ - **Breaking** for a host that names file tools: with a folder grant, ten
57
+ flat tools replace `read_files`, `file_glob`, `grep`, `create_documents` and
58
+ `edit_documents`: `read_file`, `list_dir`, `glob`, `grep`, `write_file`,
59
+ `edit_file`, `move_file`, `copy_file`, `make_dir` and `trash_file`. Each call
60
+ names one file with plain string arguments, and `grep` takes one `pattern`
61
+ instead of `queries[]`. A relative path resolves inside the shared folder,
62
+ the first read grant or else the first write grant, and a write outside the
63
+ write grant is refused. `read_file` numbers its lines, takes a range, caps a
64
+ read at 20,000 characters and reads PDF, Word and PowerPoint as text.
65
+ Creating a file, `copy_file` and `make_dir` run unprompted; overwriting,
66
+ editing, moving and trashing ask first, and `trash_file` moves the file into
67
+ the folder's own `.trash`. All ten belong to the `files` toolset. A host that
68
+ labels, groups or describes tools by name needs the new names; old
69
+ tool-activity records keep the old ones. A run without a folder grant sends
70
+ the same prompt as before. (#490)
71
+ - `@qvac/sdk` is `^0.21.0`, not `0.21.0`, so a host resolves the newest 0.21.x
72
+ patch. (#520)
73
+
74
+ ### Removed
75
+
76
+ - **Breaking:** the `lib/tools/read-files.ts`, `file-glob.ts`, `grep.ts`,
77
+ `create-documents.ts` and `edit-documents.ts` subpaths, which `./lib/tools/*`
78
+ exposed. Their tools left the sidecar in #490; the new ones are under
79
+ `lib/tools/files/`. (#491)
80
+
11
81
  ## [0.8.9] - 2026-10-15
12
82
 
13
83
  ### Changed
@@ -2008,7 +2078,9 @@ Install 0.2.12 or later to get them.
2008
2078
  - Every dependency installs from the public npm registry: GPU stats are read
2009
2079
  through `bare-gpu-info`. (#12)
2010
2080
 
2011
- [Unreleased]: https://github.com/tetherto/qvac-harness/compare/v0.8.9...HEAD
2081
+ [Unreleased]: https://github.com/tetherto/qvac-harness/compare/v0.8.11...HEAD
2082
+ [0.8.11]: https://github.com/tetherto/qvac-harness/compare/v0.8.10...v0.8.11
2083
+ [0.8.10]: https://github.com/tetherto/qvac-harness/compare/v0.8.9...v0.8.10
2012
2084
  [0.8.9]: https://github.com/tetherto/qvac-harness/compare/v0.8.8...v0.8.9
2013
2085
  [0.8.8]: https://github.com/tetherto/qvac-harness/compare/v0.8.7...v0.8.8
2014
2086
  [0.8.7]: https://github.com/tetherto/qvac-harness/compare/v0.8.6...v0.8.7
@@ -56,7 +56,7 @@ export type { KvCacheDeletionResult } from './lib/capabilities/kv-cache.ts';
56
56
  export type { AgentKnobs, ApprovalCall, ChatMessage, CredentialEntry, DownloadContext, HarnessContract, HarnessDiagnostics, HarnessEvent, HarnessInfo, HarnessStatus, InfraModel, ModelState, SkillsOptions, SkillWriteResult } from './lib/contract-types.ts';
57
57
  import type { DownloadsAllowedOptions, HarnessInfo } from './lib/contract-types.ts';
58
58
  export { serveHarness } from './lib/serve.ts';
59
- export { hlog, setHarnessLog, setHarnessLogSink } from './lib/log.ts';
59
+ export { hlog, log as harnessLog, setHarnessLog, setHarnessLogLevel, setHarnessLogSink, type HarnessLog, type LogLevel } from './lib/log.ts';
60
60
  export { harnessHooks } from './lib/hooks/registry.ts';
61
61
  export { EgressGuardHook, SkillCatalogGuardHook } from './lib/hooks/built-in/index.ts';
62
62
  export type { ToolCallDisplay, ToolExecutionOutcome } from './lib/tool-call-display.ts';
package/dist-lib/index.js CHANGED
@@ -3,6 +3,7 @@ import ScopeLock from 'scope-lock';
3
3
  import { platform, totalmem } from 'os';
4
4
  import process from 'process';
5
5
  import ReadyResource from 'ready-resource';
6
+ import { HarnessError } from './lib/errors.js';
6
7
  import { probeGpuStats } from './lib/hardware/gpu-probe.js';
7
8
  import { probeCapabilityProfile } from './lib/hardware/capability-probe.js';
8
9
  import { closePythonWorker } from './lib/tools/exec/adapters/python/worker/active.js';
@@ -46,7 +47,7 @@ import { ensureSkillsOverlayDir } from './lib/tools/skills/overlay-dir.js';
46
47
  import { copyBaseForEdit, deleteOverlaySkill, listManagedSkills, readSkillFile, writeSkillFile } from './lib/tools/skills/overlay-crud.js';
47
48
  import { watchSkillsOverlay } from './lib/tools/skills/overlay-watch.js';
48
49
  import { errorMessage, isRecord } from './lib/guards.js';
49
- import { hlog } from './lib/log.js';
50
+ import { log } from './lib/log.js';
50
51
  import { harness } from './spec/index.js';
51
52
  import { installResolutionDetector, resolutionDetail } from './lib/resolution-detector.js';
52
53
  import { probeBinVersions } from './lib/tools/skills/bin-check.js';
@@ -74,7 +75,7 @@ export { readSetup as normalizeSkillSetup } from './lib/tools/skills/manifest-pa
74
75
  export { isAvailableState, SKILL_STATE } from './lib/tools/skills/skill-state.js';
75
76
  export { connectHarness, HARNESS_DIED, RemoteHarness } from './lib/connect.js';
76
77
  export { serveHarness } from './lib/serve.js';
77
- export { hlog, setHarnessLog, setHarnessLogSink } from './lib/log.js';
78
+ export { hlog, log as harnessLog, setHarnessLog, setHarnessLogLevel, setHarnessLogSink } from './lib/log.js';
78
79
  export { harnessHooks } from './lib/hooks/registry.js';
79
80
  export { EgressGuardHook, SkillCatalogGuardHook } from './lib/hooks/built-in/index.js';
80
81
  const HarnessState = harness.getEnum('@harness/state');
@@ -243,7 +244,7 @@ export class Harness extends ReadyResource {
243
244
  throw new AbortedError();
244
245
  }
245
246
  if (!evicted) {
246
- throw new Error('the chat model is busy; try the image again once the current reply finishes');
247
+ throw new HarnessError('CHAT_MODEL_BUSY', ['image']);
247
248
  }
248
249
  }
249
250
  })
@@ -272,7 +273,7 @@ export class Harness extends ReadyResource {
272
273
  throw new AbortedError();
273
274
  }
274
275
  if (!evicted) {
275
- throw new Error('the chat model is busy; try the music again once the current reply finishes');
276
+ throw new HarnessError('CHAT_MODEL_BUSY', ['music']);
276
277
  }
277
278
  }
278
279
  })
@@ -316,14 +317,14 @@ export class Harness extends ReadyResource {
316
317
  this._idle = createIdleReaper({
317
318
  sweep: () => this._unloadIdleModels(),
318
319
  waitMs: WEDGED_EVICT_WAIT_MS,
319
- onError: (err) => hlog('idle unload failed', { err: errorMessage(err) })
320
+ onError: (err) => log.warn('idle unload failed', { err: errorMessage(err) })
320
321
  });
321
322
  }
322
323
  async _open() {
323
324
  installResolutionDetector(import.meta);
324
325
  await this._reloadSkills();
325
326
  await this._startOverlayWatch();
326
- hlog('open', { skills: this._supportedSkills.length, tools: this._tools.length });
327
+ log.info('open', { skills: this._supportedSkills.length, tools: this._tools.length });
327
328
  }
328
329
  get suspended() {
329
330
  return this._state === HarnessState.SUSPEND;
@@ -377,7 +378,7 @@ export class Harness extends ReadyResource {
377
378
  async writeSkillFile(name, content) {
378
379
  const overlay = await ensureSkillsOverlayDir();
379
380
  if (!overlay) {
380
- throw new Error('no writable skills overlay is configured');
381
+ throw new HarnessError('NO_SKILLS_OVERLAY');
381
382
  }
382
383
  await writeSkillFile(overlay, name, content);
383
384
  await this._reloadSkills();
@@ -386,7 +387,7 @@ export class Harness extends ReadyResource {
386
387
  async copyBaseForEdit(name) {
387
388
  const overlay = await ensureSkillsOverlayDir();
388
389
  if (!overlay) {
389
- throw new Error('no writable skills overlay is configured');
390
+ throw new HarnessError('NO_SKILLS_OVERLAY');
390
391
  }
391
392
  const result = await copyBaseForEdit(await this._baseSkillsDir(), overlay, name);
392
393
  if (result.copied) {
@@ -397,7 +398,7 @@ export class Harness extends ReadyResource {
397
398
  async deleteOverlaySkill(name) {
398
399
  const overlay = await ensureSkillsOverlayDir();
399
400
  if (!overlay) {
400
- throw new Error('no writable skills overlay is configured');
401
+ throw new HarnessError('NO_SKILLS_OVERLAY');
401
402
  }
402
403
  const result = await deleteOverlaySkill(overlay, name);
403
404
  if (result.removed) {
@@ -471,11 +472,11 @@ export class Harness extends ReadyResource {
471
472
  return;
472
473
  }
473
474
  this._overlayWatcher = watchSkillsOverlay(overlay, () => {
474
- this._reloadSkills().catch((err) => hlog('overlay reload failed', { err: errorMessage(err) }));
475
+ this._reloadSkills().catch((err) => log.warn('overlay reload failed', { err: errorMessage(err) }));
475
476
  });
476
477
  }
477
478
  catch (err) {
478
- hlog('overlay watch unavailable', { err: errorMessage(err) });
479
+ log.warn('overlay watch unavailable', { err: errorMessage(err) });
479
480
  }
480
481
  }
481
482
  async tools() {
@@ -576,7 +577,7 @@ export class Harness extends ReadyResource {
576
577
  percent > 0 && !mobile && memoryBudget
577
578
  ? memoryCapBytes(percent, memoryBudget.availableBytes)
578
579
  : null;
579
- hlog('memory cap', { percent, capBytes: this._capBytes });
580
+ log.info('memory cap', { percent, capBytes: this._capBytes });
580
581
  if (this._capBytes !== null) {
581
582
  await this._busy(() => this._admit(() => this._makeRoom(0, { generators: true })));
582
583
  }
@@ -820,7 +821,7 @@ export class Harness extends ReadyResource {
820
821
  if (!this.opened)
821
822
  await this.ready();
822
823
  if (this.closing)
823
- throw new Error('harness is closing');
824
+ throw new HarnessError('HARNESS_CLOSING');
824
825
  if (this._embedFaults.device === device) {
825
826
  return;
826
827
  }
@@ -837,10 +838,10 @@ export class Harness extends ReadyResource {
837
838
  if (this.closing)
838
839
  return;
839
840
  this._state = HarnessState.PRESUSPEND;
840
- hlog('presuspend');
841
+ log.info('presuspend');
841
842
  }
842
843
  async suspend() {
843
- hlog('suspend called');
844
+ log.info('suspend called');
844
845
  if (this._state === HarnessState.SUSPEND) {
845
846
  return;
846
847
  }
@@ -848,7 +849,7 @@ export class Harness extends ReadyResource {
848
849
  return;
849
850
  }
850
851
  this._state = HarnessState.SUSPEND;
851
- hlog('suspending', { models: loadedModels(this._models).map((entry) => entry.model) });
852
+ log.info('suspending', { models: loadedModels(this._models).map((entry) => entry.model) });
852
853
  this._downloads.abandon();
853
854
  const pending = this._sdk.peek();
854
855
  if (pending) {
@@ -859,7 +860,7 @@ export class Harness extends ReadyResource {
859
860
  this._byHand.clear();
860
861
  await this._sdkLifecycle('suspend');
861
862
  invalidateModelStatusCache(this._modelStatus);
862
- hlog('suspended');
863
+ log.info('suspended');
863
864
  }
864
865
  async _sdkLifecycle(op) {
865
866
  const sdk = await this._sdk.peek()?.catch(noop);
@@ -870,12 +871,12 @@ export class Harness extends ReadyResource {
870
871
  await sdk[op]();
871
872
  }
872
873
  catch (err) {
873
- hlog(`sdk ${op} failed`, { err: errorMessage(err) });
874
+ log.error(`sdk ${op} failed`, { err: errorMessage(err) });
874
875
  if (op === 'suspend') {
875
876
  return;
876
877
  }
877
878
  await sdk.resume().catch((retryErr) => {
878
- hlog('sdk resume retry failed', {
879
+ log.error('sdk resume retry failed', {
879
880
  err: errorMessage(retryErr)
880
881
  });
881
882
  });
@@ -895,13 +896,13 @@ export class Harness extends ReadyResource {
895
896
  if (outcome.status !== 'rejected')
896
897
  continue;
897
898
  const err = outcome.reason;
898
- hlog('unload failed', { err: errorMessage(err) });
899
+ log.warn('unload failed', { err: errorMessage(err) });
899
900
  }
900
901
  }
901
902
  async _unloadIdleModels() {
902
903
  if (!this.opened || this.closing || this.suspended)
903
904
  return;
904
- hlog('idle unload', { models: this._loadedModels().map((entry) => entry.model) });
905
+ log.info('idle unload', { models: this._loadedModels().map((entry) => entry.model) });
905
906
  const released = await Promise.allSettled([
906
907
  evictIdle(this._models),
907
908
  evictIdle(this._embedModels),
@@ -915,12 +916,12 @@ export class Harness extends ReadyResource {
915
916
  for (const outcome of released) {
916
917
  if (outcome.status !== 'rejected')
917
918
  continue;
918
- hlog('idle unload failed', { err: errorMessage(outcome.reason) });
919
+ log.warn('idle unload failed', { err: errorMessage(outcome.reason) });
919
920
  }
920
921
  invalidateModelStatusCache(this._modelStatus);
921
922
  }
922
923
  async resume() {
923
- hlog('resume called');
924
+ log.info('resume called');
924
925
  if (this._state === HarnessState.RESUME) {
925
926
  return;
926
927
  }
@@ -932,7 +933,7 @@ export class Harness extends ReadyResource {
932
933
  return;
933
934
  }
934
935
  this._state = HarnessState.RESUME;
935
- hlog('resuming', { pools: 'recreated' });
936
+ log.info('resuming', { pools: 'recreated' });
936
937
  this._loads.undrain();
937
938
  this._models = this._createPool();
938
939
  this._embedModels = this._createEmbedPool();
@@ -944,11 +945,11 @@ export class Harness extends ReadyResource {
944
945
  if (this.closing)
945
946
  return;
946
947
  this._state = HarnessState.WAKEUP;
947
- hlog('wakeup');
948
+ log.info('wakeup');
948
949
  await this.resume();
949
950
  }
950
951
  async _close() {
951
- hlog('closing');
952
+ log.info('closing');
952
953
  this._idle.close();
953
954
  this._downloads.abandon();
954
955
  this._overlayWatcher?.stop();
@@ -990,7 +991,7 @@ export class Harness extends ReadyResource {
990
991
  if (!this.opened)
991
992
  await this.ready();
992
993
  if (this.suspended)
993
- throw new Error('harness is suspended');
994
+ throw new HarnessError('HARNESS_SUSPENDED');
994
995
  if (texts.length === 0) {
995
996
  return [];
996
997
  }
@@ -1057,7 +1058,7 @@ export class Harness extends ReadyResource {
1057
1058
  if (!this.opened)
1058
1059
  await this.ready();
1059
1060
  if (this.suspended)
1060
- throw new Error('harness is suspended');
1061
+ throw new HarnessError('HARNESS_SUSPENDED');
1061
1062
  const addon = category === 'tts' ? 'tts' : 'parakeet';
1062
1063
  const name = residentSrc(model || (await this._defaultVoiceSrc(category)), addon);
1063
1064
  const handle = pool.open(name, name);
@@ -1117,7 +1118,7 @@ export class Harness extends ReadyResource {
1117
1118
  if (!this.opened)
1118
1119
  await this.ready();
1119
1120
  if (this.suspended)
1120
- throw new Error('harness is suspended');
1121
+ throw new HarnessError('HARNESS_SUSPENDED');
1121
1122
  const name = residentSrc(model || (await this._defaultVoiceSrc('tts')), 'tts');
1122
1123
  const pool = this._ttsModels;
1123
1124
  const handle = pool.open(name, name);
@@ -1252,7 +1253,7 @@ export class Harness extends ReadyResource {
1252
1253
  return;
1253
1254
  const target = this._residency(model, agent);
1254
1255
  const resident = isResident(target.pool, target.key);
1255
- hlog('warm model', { model, resident });
1256
+ log.info('warm model', { model, resident });
1256
1257
  if (target.pool === this._models && !resident) {
1257
1258
  const roomed = await this._makeRoomForCompletion(model, {
1258
1259
  ctxSize: target.ctxSize,
@@ -1273,11 +1274,11 @@ export class Harness extends ReadyResource {
1273
1274
  if (!this.opened)
1274
1275
  await this.ready();
1275
1276
  if (this.suspended) {
1276
- throw new Error('harness is suspended');
1277
+ throw new HarnessError('HARNESS_SUSPENDED');
1277
1278
  }
1278
1279
  const target = this._residency(model, agent);
1279
1280
  const resident = isResident(target.pool, target.key);
1280
- hlog('load model', { model, resident });
1281
+ log.info('load model', { model, resident });
1281
1282
  if (!resident && !(await this._admitByHand(model, target))) {
1282
1283
  return { loaded: false, reason: 'memory' };
1283
1284
  }
@@ -1308,7 +1309,7 @@ export class Harness extends ReadyResource {
1308
1309
  const target = this._residency(model);
1309
1310
  const name = modelOf(target.key);
1310
1311
  const unloaded = await evictModel(target.pool, name, { deadlineMs: UNLOAD_LEASE_WAIT_MS });
1311
- hlog('unload model', { model, unloaded });
1312
+ log.info('unload model', { model, unloaded });
1312
1313
  if (unloaded) {
1313
1314
  for (const key of this._pinned) {
1314
1315
  if (modelOf(key) === name) {
@@ -1344,7 +1345,7 @@ export class Harness extends ReadyResource {
1344
1345
  name: asset.name,
1345
1346
  contextTokens: ctxSize || defaultContextSize(asset)
1346
1347
  }).catch((err) => {
1347
- hlog('fit estimate failed', { model, err: errorMessage(err) });
1348
+ log.warn('fit estimate failed', { model, err: errorMessage(err) });
1348
1349
  return null;
1349
1350
  });
1350
1351
  }
@@ -1496,7 +1497,7 @@ export class Harness extends ReadyResource {
1496
1497
  };
1497
1498
  }
1498
1499
  catch (err) {
1499
- hlog('fit estimate failed', { model, err: errorMessage(err) });
1500
+ log.warn('fit estimate failed', { model, err: errorMessage(err) });
1500
1501
  return {
1501
1502
  bytes: downloaded === undefined ? undefined : downloaded + projection,
1502
1503
  tooLarge: false,
@@ -1519,7 +1520,7 @@ export class Harness extends ReadyResource {
1519
1520
  return sized.confirmed && (room === null || (sized.bytes ?? Infinity) <= room);
1520
1521
  }
1521
1522
  });
1522
- hlog('context chosen', { model: load.model, tier, ctxSize });
1523
+ log.info('context chosen', { model: load.model, tier, ctxSize });
1523
1524
  return ctxSize;
1524
1525
  }
1525
1526
  _capRoom(key) {
@@ -1554,7 +1555,7 @@ export class Harness extends ReadyResource {
1554
1555
  while (this._usedBytes() + need > capBytes || tooLarge) {
1555
1556
  const victim = nextVictim(this._capVictims(room).filter(({ id }) => !tried.has(id)));
1556
1557
  if (!victim) {
1557
- hlog('model over memory cap', { need, used: this._usedBytes(), capBytes, tooLarge });
1558
+ log.warn('model over memory cap', { need, used: this._usedBytes(), capBytes, tooLarge });
1558
1559
  return;
1559
1560
  }
1560
1561
  tried.add(victim.id);
@@ -1654,7 +1655,7 @@ export class Harness extends ReadyResource {
1654
1655
  const modelType = addon === 'tts' ? 'tts-ggml' : 'parakeet-transcription';
1655
1656
  return { pool, key, modelType, open: () => pool.open(key, key) };
1656
1657
  }
1657
- throw new Error(`${model} loads for each generation and is not kept in memory`);
1658
+ throw new HarnessError('MODEL_NOT_RESIDENT', [model]);
1658
1659
  }
1659
1660
  _completionLoadOptions(model, agent) {
1660
1661
  return resolveLoadOptions({
@@ -1673,7 +1674,7 @@ export class Harness extends ReadyResource {
1673
1674
  if (this.suspended) {
1674
1675
  return yield { type: 'aborted' };
1675
1676
  }
1676
- hlog('invoke start', { capability, enhance });
1677
+ log.info('invoke start', { capability, enhance });
1677
1678
  try {
1678
1679
  yield* invoke({
1679
1680
  capability,
@@ -1691,7 +1692,7 @@ export class Harness extends ReadyResource {
1691
1692
  });
1692
1693
  }
1693
1694
  finally {
1694
- hlog('invoke end', { capability });
1695
+ log.info('invoke end', { capability });
1695
1696
  }
1696
1697
  }
1697
1698
  completion(context) {
@@ -1701,13 +1702,13 @@ export class Harness extends ReadyResource {
1701
1702
  if (!this.opened)
1702
1703
  await this.ready();
1703
1704
  if (this.suspended)
1704
- throw new Error('harness is suspended');
1705
+ throw new HarnessError('HARNESS_SUSPENDED');
1705
1706
  const loadOpts = this._completionLoadOptions(model, agent);
1706
1707
  const projectionModel = loadOpts.projectionModel;
1707
1708
  const key = modelKey(model, loadOpts);
1708
1709
  const pool = this._models;
1709
1710
  const resident = isResident(pool, key);
1710
- hlog('completion start', { model, resident, projection: Boolean(projectionModel) });
1711
+ log.info('completion start', { model, resident, projection: Boolean(projectionModel) });
1711
1712
  if (!resident) {
1712
1713
  await this._makeRoomForCompletion(model, { ctxSize: this._ctxSizeOf(key, loadOpts), signal });
1713
1714
  await this._yieldGenerators();
@@ -1745,7 +1746,7 @@ export class Harness extends ReadyResource {
1745
1746
  this._replying.push(key);
1746
1747
  try {
1747
1748
  const { modelId, ctxSize } = await raceAbort(handle.load(), signal);
1748
- hlog('model ready', { model });
1749
+ log.info('model ready', { model });
1749
1750
  const sdk = await this._sdk.load();
1750
1751
  const turns = projectionModel ? messages : messages.map(({ attachments, ...turn }) => turn);
1751
1752
  const remoteRoots = driveOp && grantedDrives?.length ? createRemoteRoots(grantedDrives, driveOp) : [];
@@ -1789,12 +1790,12 @@ export class Harness extends ReadyResource {
1789
1790
  }
1790
1791
  catch (err) {
1791
1792
  if (err instanceof AbortedError || this.suspended) {
1792
- hlog('completion aborted', { model, suspended: this.suspended });
1793
+ log.info('completion aborted', { model, suspended: this.suspended });
1793
1794
  yield { type: 'aborted' };
1794
1795
  }
1795
1796
  else {
1796
1797
  const message = errorMessage(err);
1797
- hlog('completion failed', { model, ...resolutionDetail(err), err: message });
1798
+ log.error('completion failed', { model, ...resolutionDetail(err), err: message });
1798
1799
  const wedged = isSdkModelWedgedError(err) ? await this._evictWedgedModel(model, key) : null;
1799
1800
  yield { type: 'error', message: wedged ? wedged.message : message };
1800
1801
  if (wedged?.fatal) {
@@ -1803,7 +1804,7 @@ export class Harness extends ReadyResource {
1803
1804
  }
1804
1805
  }
1805
1806
  finally {
1806
- hlog('completion end', { model });
1807
+ log.info('completion end', { model });
1807
1808
  this._replying.splice(this._replying.indexOf(key), 1);
1808
1809
  releaseHeld();
1809
1810
  void this._imageRuntime?.unloadIdle().catch(noop);
@@ -1825,7 +1826,7 @@ export class Harness extends ReadyResource {
1825
1826
  });
1826
1827
  }
1827
1828
  catch (err) {
1828
- hlog('enhance failed', { err: errorMessage(err) });
1829
+ log.warn('enhance failed', { err: errorMessage(err) });
1829
1830
  return null;
1830
1831
  }
1831
1832
  finally {
@@ -1845,7 +1846,7 @@ export class Harness extends ReadyResource {
1845
1846
  evictable: (key) => !this._pinned.has(key) || modelOf(key) === model
1846
1847
  });
1847
1848
  if (!roomed) {
1848
- hlog('model room wait gave up', {
1849
+ log.warn('model room wait gave up', {
1849
1850
  cap: this._completionModelCap,
1850
1851
  loaded: loadedModels(this._models).length
1851
1852
  });
@@ -1860,7 +1861,7 @@ export class Harness extends ReadyResource {
1860
1861
  const evicted = leased
1861
1862
  ? false
1862
1863
  : await evictModelWithin(this._models, key, { deadlineMs: WEDGED_EVICT_WAIT_MS });
1863
- hlog('wedged model evicted', { model, faults, evicted, leased });
1864
+ log.warn('wedged model evicted', { model, faults, evicted, leased });
1864
1865
  if (leased) {
1865
1866
  this._evictAfterLeases(model, key);
1866
1867
  }
@@ -1879,7 +1880,7 @@ export class Harness extends ReadyResource {
1879
1880
  }
1880
1881
  _evictAfterLeases(model, key) {
1881
1882
  void evictModelWithin(this._models, key, { deadlineMs: WEDGED_EVICT_LEASED_MS })
1882
- .then((evicted) => hlog('wedged model evicted after leases', { model, evicted }))
1883
+ .then((evicted) => log.warn('wedged model evicted after leases', { model, evicted }))
1883
1884
  .catch(noop);
1884
1885
  }
1885
1886
  async _busy(run) {
@@ -1,7 +1,7 @@
1
1
  import ffmpeg from 'bare-ffmpeg';
2
2
  import b4a from 'b4a';
3
3
  import { errorMessage } from './guards.js';
4
- import { hlog } from './log.js';
4
+ import { log } from './log.js';
5
5
  import { buildWavBuffer, TARGET_SAMPLE_RATE } from './wav-encode.js';
6
6
  function bufferReadIO(input) {
7
7
  let offset = 0;
@@ -145,7 +145,7 @@ export async function transcodeToWav16kMono(input) {
145
145
  }
146
146
  }
147
147
  catch (e) {
148
- hlog('audio decode/resample failed', { error: errorMessage(e) });
148
+ log.warn('audio decode/resample failed', { error: errorMessage(e) });
149
149
  return null;
150
150
  }
151
151
  return chunks.length === 0 ? null : buildWavBuffer(b4a.concat(chunks));
@@ -330,11 +330,10 @@ interface CompletionArgs {
330
330
  fsWriteRoots?: readonly DriveRoot[];
331
331
  captureInferenceCalls?: boolean;
332
332
  }
333
- export declare const CONTEXT_OVERFLOW_MESSAGE = "This message is too large for the model context window.";
333
+ export { CONTEXT_OVERFLOW_MESSAGE } from '../errors.ts';
334
334
  export declare function trustedSourcesFrom(history: readonly Message[]): TrustedSource[];
335
335
  export declare function toSdkTool(tool: ToolDefinition): SdkTool;
336
336
  export type CompletionSkillEntry = SkillCatalogEntry & {
337
337
  readonly status?: SkillState;
338
338
  };
339
339
  export declare function completion(sdk: CompletionSdk, { modelId, acquireRound, model, agent, ctxSize, cacheKey, session, thinkingDefault, generationParams: generationParamsOverride, responseFormat, messages: storedMessages, typedText, tools: allTools, skillCatalog, approve, attach, fetchAttachment, ragSearch, progress, askUser, signal, toolConfigs, fsRoots, fsWriteRoots, captureInferenceCalls }: CompletionArgs): AsyncGenerator<CompletionEvent>;
340
- export {};