@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.
- package/CHANGELOG.md +73 -1
- package/dist-lib/index.d.ts +1 -1
- package/dist-lib/index.js +51 -50
- package/dist-lib/lib/audio-transcode-bare.js +2 -2
- package/dist-lib/lib/capabilities/completion.d.ts +1 -2
- package/dist-lib/lib/capabilities/completion.js +29 -25
- package/dist-lib/lib/capabilities/tool-prompt-log.js +3 -3
- package/dist-lib/lib/capabilities/voice.js +7 -6
- package/dist-lib/lib/connect.js +18 -17
- package/dist-lib/lib/errors.d.ts +266 -0
- package/dist-lib/lib/errors.js +189 -0
- package/dist-lib/lib/helpers/load-stall.d.ts +1 -1
- package/dist-lib/lib/helpers/load-stall.js +2 -2
- package/dist-lib/lib/helpers/race-abort.d.ts +2 -1
- package/dist-lib/lib/helpers/race-abort.js +3 -4
- package/dist-lib/lib/hooks/built-in/arg-repair.js +2 -2
- package/dist-lib/lib/hooks/pipeline.js +4 -4
- package/dist-lib/lib/hooks/run.js +2 -2
- package/dist-lib/lib/log.d.ts +26 -2
- package/dist-lib/lib/log.js +66 -16
- package/dist-lib/lib/models/model-pool.js +11 -6
- package/dist-lib/lib/models/model-source.js +3 -2
- package/dist-lib/lib/no-redirect-fetch-bare.js +2 -1
- package/dist-lib/lib/no-redirect-fetch-impl.js +2 -1
- package/dist-lib/lib/office/extract.js +4 -3
- package/dist-lib/lib/office/index.js +2 -1
- package/dist-lib/lib/resolution-detector.js +3 -3
- package/dist-lib/lib/rpc-session.js +2 -1
- package/dist-lib/lib/sdk-loader.js +2 -2
- package/dist-lib/lib/serve.js +33 -29
- package/dist-lib/lib/spawn.d.ts +2 -4
- package/dist-lib/lib/spawn.js +14 -7
- package/dist-lib/lib/tools/edit-image.js +2 -1
- package/dist-lib/lib/tools/exec/adapters/python/attachments.js +8 -8
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +7 -6
- package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.js +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/memfs.js +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +8 -8
- package/dist-lib/lib/tools/exec/adapters/python/run-session.js +11 -7
- package/dist-lib/lib/tools/exec/adapters/python/wheels.js +5 -5
- package/dist-lib/lib/tools/exec/adapters/python/worker/active.js +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +9 -8
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/serve.js +4 -4
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn.d.ts +2 -1
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/note-name.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/safe-path.js +5 -4
- package/dist-lib/lib/tools/exec/obsidian/vault-create.js +3 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-fs.js +2 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +3 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-read.js +6 -5
- package/dist-lib/lib/tools/exec/obsidian/vault-write.js +13 -12
- package/dist-lib/lib/tools/exec/runner.js +2 -1
- package/dist-lib/lib/tools/files/edit-file.d.ts +3 -0
- package/dist-lib/lib/tools/files/edit-file.js +73 -0
- package/dist-lib/lib/tools/files/glob.d.ts +3 -0
- package/dist-lib/lib/tools/files/glob.js +63 -0
- package/dist-lib/lib/tools/files/grep.d.ts +3 -0
- package/dist-lib/lib/tools/files/grep.js +114 -0
- package/dist-lib/lib/tools/files/index.d.ts +3 -0
- package/dist-lib/lib/tools/files/index.js +23 -0
- package/dist-lib/lib/tools/files/list-dir.d.ts +5 -0
- package/dist-lib/lib/tools/files/list-dir.js +130 -0
- package/dist-lib/lib/tools/files/make-dir.d.ts +3 -0
- package/dist-lib/lib/tools/files/make-dir.js +47 -0
- package/dist-lib/lib/tools/files/read-file.d.ts +3 -0
- package/dist-lib/lib/tools/files/read-file.js +103 -0
- package/dist-lib/lib/tools/files/transfer.d.ts +4 -0
- package/dist-lib/lib/tools/files/transfer.js +82 -0
- package/dist-lib/lib/tools/files/trash-file.d.ts +4 -0
- package/dist-lib/lib/tools/files/trash-file.js +61 -0
- package/dist-lib/lib/tools/files/write-file.d.ts +3 -0
- package/dist-lib/lib/tools/files/write-file.js +55 -0
- package/dist-lib/lib/tools/fs/document-text.d.ts +2 -0
- package/dist-lib/lib/tools/fs/document-text.js +38 -0
- package/dist-lib/lib/tools/fs/edit-match.d.ts +11 -0
- package/dist-lib/lib/tools/fs/edit-match.js +85 -0
- package/dist-lib/lib/tools/fs/remote.d.ts +2 -1
- package/dist-lib/lib/tools/fs/remote.js +4 -4
- package/dist-lib/lib/tools/fs/scope.d.ts +1 -1
- package/dist-lib/lib/tools/fs/scope.js +3 -2
- package/dist-lib/lib/tools/fs/targets.d.ts +10 -0
- package/dist-lib/lib/tools/fs/targets.js +32 -0
- package/dist-lib/lib/tools/fs/tool-path.d.ts +24 -0
- package/dist-lib/lib/tools/fs/tool-path.js +58 -0
- package/dist-lib/lib/tools/fs/walk.js +3 -3
- package/dist-lib/lib/tools/generate-image.d.ts +2 -2
- package/dist-lib/lib/tools/generate-image.js +5 -6
- package/dist-lib/lib/tools/office.js +2 -1
- package/dist-lib/lib/tools/skill.js +2 -1
- package/dist-lib/lib/tools/skills/bundled-dir.js +4 -4
- package/dist-lib/lib/tools/skills/catalog.js +2 -2
- package/dist-lib/lib/tools/skills/cli-argv.js +25 -5
- package/dist-lib/lib/tools/skills/credential-health.js +3 -3
- package/dist-lib/lib/tools/skills/overlay-crud.js +3 -2
- package/dist-lib/lib/tools/translate.d.ts +2 -2
- package/dist-lib/lib/tools/translate.js +5 -6
- package/dist-lib/lib/tools/weather-lookup/geocode.js +5 -4
- package/dist-lib/lib/web-search/engines/bing.js +2 -1
- package/dist-lib/lib/web-search/engines/google.js +2 -1
- package/dist-lib/lib/web-search/fetch-html.d.ts +5 -5
- package/dist-lib/lib/web-search/fetch-html.js +12 -15
- package/package.json +67 -25
- package/worker/harness-sidecar.mjs +12 -13
- package/dist-lib/lib/tools/create-documents.d.ts +0 -3
- package/dist-lib/lib/tools/create-documents.js +0 -95
- package/dist-lib/lib/tools/edit-documents.d.ts +0 -3
- package/dist-lib/lib/tools/edit-documents.js +0 -132
- package/dist-lib/lib/tools/file-glob.d.ts +0 -14
- package/dist-lib/lib/tools/file-glob.js +0 -118
- package/dist-lib/lib/tools/grep.d.ts +0 -5
- package/dist-lib/lib/tools/grep.js +0 -184
- package/dist-lib/lib/tools/read-files.d.ts +0 -5
- 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.
|
|
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
|
package/dist-lib/index.d.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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) =>
|
|
475
|
+
this._reloadSkills().catch((err) => log.warn('overlay reload failed', { err: errorMessage(err) }));
|
|
475
476
|
});
|
|
476
477
|
}
|
|
477
478
|
catch (err) {
|
|
478
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
841
|
+
log.info('presuspend');
|
|
841
842
|
}
|
|
842
843
|
async suspend() {
|
|
843
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
919
|
+
log.warn('idle unload failed', { err: errorMessage(outcome.reason) });
|
|
919
920
|
}
|
|
920
921
|
invalidateModelStatusCache(this._modelStatus);
|
|
921
922
|
}
|
|
922
923
|
async resume() {
|
|
923
|
-
|
|
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
|
-
|
|
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
|
-
|
|
948
|
+
log.info('wakeup');
|
|
948
949
|
await this.resume();
|
|
949
950
|
}
|
|
950
951
|
async _close() {
|
|
951
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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 {};
|