@qvac/harness 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +689 -141
- package/README.md +29 -7
- package/dist/harness.bundle +71 -50
- package/dist-lib/index.d.ts +36 -39
- package/dist-lib/index.js +279 -114
- package/dist-lib/lib/audio-transcode-bare.d.ts +2 -2
- package/dist-lib/lib/audio-transcode-bare.js +28 -18
- package/dist-lib/lib/audio-transcode-node.d.ts +2 -2
- package/dist-lib/lib/capabilities/audio-resample.d.ts +7 -6
- package/dist-lib/lib/capabilities/audio-resample.js +30 -18
- package/dist-lib/lib/capabilities/completion.d.ts +20 -19
- package/dist-lib/lib/capabilities/completion.js +193 -132
- package/dist-lib/lib/capabilities/download.d.ts +6 -6
- package/dist-lib/lib/capabilities/download.js +15 -8
- package/dist-lib/lib/capabilities/embed.js +2 -1
- package/dist-lib/lib/capabilities/enhance.d.ts +6 -1
- package/dist-lib/lib/capabilities/enhance.js +7 -4
- package/dist-lib/lib/capabilities/invoke.js +7 -4
- package/dist-lib/lib/capabilities/kv-cache.js +2 -1
- package/dist-lib/lib/capabilities/preview-model-source.d.ts +9 -7
- package/dist-lib/lib/capabilities/preview-model-source.js +80 -28
- package/dist-lib/lib/capabilities/speech-text.js +5 -4
- package/dist-lib/lib/capabilities/tool-selection.js +10 -9
- package/dist-lib/lib/capabilities/tool-turns.js +30 -16
- package/dist-lib/lib/capabilities/usage-breakdown.js +2 -1
- package/dist-lib/lib/capabilities/voice.d.ts +5 -1
- package/dist-lib/lib/capabilities/voice.js +22 -12
- package/dist-lib/lib/connect.d.ts +11 -32
- package/dist-lib/lib/connect.js +152 -112
- package/dist-lib/lib/context-window.js +25 -21
- package/dist-lib/lib/contract-types.d.ts +22 -15
- package/dist-lib/lib/diagram-repair.d.ts +4 -4
- package/dist-lib/lib/diagram-repair.js +6 -3
- package/dist-lib/lib/docx/index.d.ts +3 -2
- package/dist-lib/lib/{error-fields.d.ts → guards.d.ts} +1 -0
- package/dist-lib/lib/{error-fields.js → guards.js} +3 -0
- package/dist-lib/lib/hardware/capability-probe.js +20 -10
- package/dist-lib/lib/hardware/gpu-classify.js +6 -3
- package/dist-lib/lib/hardware/gpu-probe-addon.js +8 -4
- package/dist-lib/lib/hardware/gpu-probe.js +4 -2
- package/dist-lib/lib/hardware/gpu-stats.native.cjs +8 -9
- package/dist-lib/lib/hardware/parsers.js +8 -4
- package/dist-lib/lib/helpers/buffer-burst.js +4 -2
- package/dist-lib/lib/helpers/control-tokens.js +4 -2
- package/dist-lib/lib/helpers/join-guard.d.ts +2 -2
- package/dist-lib/lib/helpers/join-guard.js +4 -2
- package/dist-lib/lib/helpers/load-stall.js +4 -2
- package/dist-lib/lib/helpers/markup-stream-filter.d.ts +2 -2
- package/dist-lib/lib/helpers/markup-stream-filter.js +6 -3
- package/dist-lib/lib/helpers/race-abort.js +2 -1
- package/dist-lib/lib/helpers/race-timeout.d.ts +5 -0
- package/dist-lib/lib/helpers/race-timeout.js +14 -0
- package/dist-lib/lib/helpers/run-outcome.d.ts +6 -8
- package/dist-lib/lib/helpers/run-outcome.js +36 -20
- package/dist-lib/lib/helpers/spawn-path.js +6 -3
- package/dist-lib/lib/helpers/text-coalescer.js +6 -3
- package/dist-lib/lib/helpers/thinking.js +38 -45
- package/dist-lib/lib/helpers/tool-call-markup.d.ts +9 -7
- package/dist-lib/lib/helpers/tool-call-markup.js +36 -20
- package/dist-lib/lib/helpers/tool-intent.d.ts +3 -2
- package/dist-lib/lib/helpers/tool-intent.js +4 -2
- package/dist-lib/lib/helpers/tool-label.js +18 -9
- package/dist-lib/lib/hooks/built-in/arg-repair.js +23 -13
- package/dist-lib/lib/hooks/built-in/egress.js +2 -1
- package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
- package/dist-lib/lib/hooks/pipeline.d.ts +5 -4
- package/dist-lib/lib/hooks/pipeline.js +6 -3
- package/dist-lib/lib/hooks/run.js +12 -8
- package/dist-lib/lib/model-download-progress.js +10 -8
- package/dist-lib/lib/models/context.d.ts +1 -1
- package/dist-lib/lib/models/context.js +5 -1
- package/dist-lib/lib/models/custom-model-cache.d.ts +10 -1
- package/dist-lib/lib/models/custom-model-cache.js +43 -15
- package/dist-lib/lib/models/gguf-header.d.ts +3 -2
- package/dist-lib/lib/models/gguf-header.js +44 -22
- package/dist-lib/lib/models/idle-unload.d.ts +14 -0
- package/dist-lib/lib/models/idle-unload.js +51 -0
- package/dist-lib/lib/models/kv-cache.d.ts +3 -2
- package/dist-lib/lib/models/kv-cache.js +4 -3
- package/dist-lib/lib/models/llm-presets.d.ts +2 -2
- package/dist-lib/lib/models/llm-presets.js +4 -2
- package/dist-lib/lib/models/model-catalog-constants.js +3 -2
- package/dist-lib/lib/models/model-catalog.d.ts +13 -20
- package/dist-lib/lib/models/model-catalog.js +59 -31
- package/dist-lib/lib/models/model-delete.js +18 -8
- package/dist-lib/lib/models/model-handle.js +2 -1
- package/dist-lib/lib/models/model-list.d.ts +8 -3
- package/dist-lib/lib/models/model-list.js +11 -6
- package/dist-lib/lib/models/model-pool.d.ts +44 -10
- package/dist-lib/lib/models/model-pool.js +47 -42
- package/dist-lib/lib/models/model-source.js +10 -5
- package/dist-lib/lib/models/music-catalog.d.ts +2 -2
- package/dist-lib/lib/models/nmt-catalog.d.ts +4 -3
- package/dist-lib/lib/models/nmt-catalog.js +10 -5
- package/dist-lib/lib/models/opencl-cache.d.ts +6 -1
- package/dist-lib/lib/models/opencl-cache.js +5 -3
- package/dist-lib/lib/models/sdk-faults.d.ts +11 -8
- package/dist-lib/lib/models/sdk-faults.js +9 -6
- package/dist-lib/lib/models/shards.d.ts +11 -0
- package/dist-lib/lib/models/shards.js +15 -0
- package/dist-lib/lib/models/tool-dialect.js +10 -5
- package/dist-lib/lib/no-redirect-fetch-bare.d.ts +3 -3
- package/dist-lib/lib/no-redirect-fetch-bare.js +1 -1
- package/dist-lib/lib/no-redirect-fetch-impl.js +6 -3
- package/dist-lib/lib/no-redirect-fetch-node.d.ts +1 -1
- package/dist-lib/lib/no-redirect-fetch-node.js +9 -9
- package/dist-lib/lib/office/index.d.ts +3 -2
- package/dist-lib/lib/office/python.d.ts +3 -2
- package/dist-lib/lib/office/python.js +2 -1
- package/dist-lib/lib/prompt.d.ts +5 -4
- package/dist-lib/lib/prompt.js +8 -4
- package/dist-lib/lib/queue.js +10 -5
- package/dist-lib/lib/resolution-detector.js +21 -11
- package/dist-lib/lib/rpc-session.js +2 -1
- package/dist-lib/lib/sdk-loader.js +2 -1
- package/dist-lib/lib/sdk-storage.js +4 -2
- package/dist-lib/lib/serve.js +110 -66
- package/dist-lib/lib/spawn.js +7 -4
- package/dist-lib/lib/tmp-path.d.ts +7 -6
- package/dist-lib/lib/tool-call-display.d.ts +2 -2
- package/dist-lib/lib/tools/create-documents.js +18 -10
- package/dist-lib/lib/tools/edit-documents.js +23 -13
- package/dist-lib/lib/tools/edit-image.d.ts +2 -2
- package/dist-lib/lib/tools/edit-image.js +60 -36
- package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +10 -10
- package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +17 -11
- package/dist-lib/lib/tools/exec/adapters/python/attachments.js +53 -40
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +60 -32
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts +33 -22
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +58 -29
- package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/loader.js +23 -12
- package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +13 -11
- package/dist-lib/lib/tools/exec/adapters/python/memfs.js +10 -5
- package/dist-lib/lib/tools/exec/adapters/python/net-shim.js +3 -1
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +6 -4
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +20 -10
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +14 -7
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +41 -34
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +316 -166
- package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +9 -8
- package/dist-lib/lib/tools/exec/adapters/python/run-session.js +56 -28
- package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +8 -10
- package/dist-lib/lib/tools/exec/adapters/python/wheels.js +22 -11
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +3 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +41 -26
- package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +27 -18
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +14 -13
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +4 -4
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +10 -5
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +4 -4
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +6 -1
- package/dist-lib/lib/tools/exec/obsidian/cli-process.js +8 -5
- package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +6 -6
- package/dist-lib/lib/tools/exec/obsidian/cli.js +4 -4
- package/dist-lib/lib/tools/exec/obsidian/command.d.ts +3 -2
- package/dist-lib/lib/tools/exec/obsidian/command.js +6 -3
- package/dist-lib/lib/tools/exec/obsidian/output.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/route.d.ts +4 -6
- package/dist-lib/lib/tools/exec/obsidian/route.js +17 -11
- package/dist-lib/lib/tools/exec/obsidian/safe-path.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/vault-create.js +15 -7
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +2 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +20 -10
- package/dist-lib/lib/tools/exec/obsidian/vault-read.js +20 -13
- package/dist-lib/lib/tools/exec/obsidian/vault-write.js +24 -12
- package/dist-lib/lib/tools/exec/runner.d.ts +14 -14
- package/dist-lib/lib/tools/exec/runner.js +5 -3
- package/dist-lib/lib/tools/exec/scope.js +52 -28
- package/dist-lib/lib/tools/exec/shell-argv.js +31 -18
- package/dist-lib/lib/tools/exec/shell-guidance.d.ts +6 -1
- package/dist-lib/lib/tools/exec/shell-guidance.js +35 -20
- package/dist-lib/lib/tools/exec/video-tools.js +2 -2
- package/dist-lib/lib/tools/exec.d.ts +6 -8
- package/dist-lib/lib/tools/exec.js +113 -54
- package/dist-lib/lib/tools/file-glob.d.ts +4 -5
- package/dist-lib/lib/tools/file-glob.js +16 -11
- package/dist-lib/lib/tools/fs/glob.js +13 -18
- package/dist-lib/lib/tools/fs/remote-protocol.d.ts +10 -10
- package/dist-lib/lib/tools/fs/remote.d.ts +3 -2
- package/dist-lib/lib/tools/fs/remote.js +13 -7
- package/dist-lib/lib/tools/fs/scope.d.ts +8 -3
- package/dist-lib/lib/tools/fs/scope.js +20 -12
- package/dist-lib/lib/tools/fs/walk.js +6 -3
- package/dist-lib/lib/tools/fs/write.d.ts +3 -2
- package/dist-lib/lib/tools/generate-image.d.ts +11 -9
- package/dist-lib/lib/tools/generate-image.js +73 -37
- package/dist-lib/lib/tools/generate-music.d.ts +22 -11
- package/dist-lib/lib/tools/generate-music.js +79 -48
- package/dist-lib/lib/tools/grep.js +66 -41
- package/dist-lib/lib/tools/http-request/encode-fields.js +18 -9
- package/dist-lib/lib/tools/http-request/{utils.js → request.js} +20 -11
- package/dist-lib/lib/tools/http-request/truncate-response.js +57 -46
- package/dist-lib/lib/tools/http-request.d.ts +1 -1
- package/dist-lib/lib/tools/http-request.js +33 -18
- package/dist-lib/lib/tools/mcp/protocol.d.ts +53 -0
- package/dist-lib/lib/tools/mcp/{utils.js → protocol.js} +45 -23
- package/dist-lib/lib/tools/mcp-call.d.ts +1 -1
- package/dist-lib/lib/tools/mcp-call.js +37 -16
- package/dist-lib/lib/tools/operations/builders.js +10 -6
- package/dist-lib/lib/tools/operations/compile.js +20 -10
- package/dist-lib/lib/tools/operations/decoders.js +12 -6
- package/dist-lib/lib/tools/operations/gmail-message.js +3 -2
- package/dist-lib/lib/tools/outbound-guard.d.ts +4 -3
- package/dist-lib/lib/tools/outbound-guard.js +14 -8
- package/dist-lib/lib/tools/private-ip.js +8 -7
- package/dist-lib/lib/tools/rag-search.js +14 -7
- package/dist-lib/lib/tools/read-files.js +23 -20
- package/dist-lib/lib/tools/skill.js +19 -10
- package/dist-lib/lib/tools/skills/auth-resolver.d.ts +11 -18
- package/dist-lib/lib/tools/skills/auth-resolver.js +30 -18
- package/dist-lib/lib/tools/skills/bin-check.d.ts +3 -2
- package/dist-lib/lib/tools/skills/bin-check.js +10 -3
- package/dist-lib/lib/tools/skills/bundled-dir.js +20 -10
- package/dist-lib/lib/tools/skills/bundled-hash.d.ts +3 -2
- package/dist-lib/lib/tools/skills/bundled-hash.js +6 -1
- package/dist-lib/lib/tools/skills/catalog.d.ts +3 -2
- package/dist-lib/lib/tools/skills/catalog.js +33 -28
- package/dist-lib/lib/tools/skills/cli-argv.d.ts +3 -2
- package/dist-lib/lib/tools/skills/cli-argv.js +55 -52
- package/dist-lib/lib/tools/skills/cli-schema-check.js +21 -10
- package/dist-lib/lib/tools/skills/cli-schema-load.js +28 -20
- package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +6 -6
- package/dist-lib/lib/tools/skills/cli-schema.js +2 -1
- package/dist-lib/lib/tools/skills/credential-check.d.ts +6 -3
- package/dist-lib/lib/tools/skills/credential-check.js +24 -12
- package/dist-lib/lib/tools/skills/credential-grants.js +1 -6
- package/dist-lib/lib/tools/skills/credential-health.d.ts +5 -1
- package/dist-lib/lib/tools/skills/credential-health.js +5 -3
- package/dist-lib/lib/tools/skills/frontmatter-parser.js +10 -5
- package/dist-lib/lib/tools/skills/manifest-parse.js +84 -64
- package/dist-lib/lib/tools/skills/mention.js +7 -6
- package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +2 -2
- package/dist-lib/lib/tools/skills/obsidian-readiness.js +11 -3
- package/dist-lib/lib/tools/skills/overlay-crud.d.ts +6 -4
- package/dist-lib/lib/tools/skills/overlay-crud.js +12 -6
- package/dist-lib/lib/tools/skills/overlay-dir.js +4 -2
- package/dist-lib/lib/tools/skills/overlay-watch.d.ts +3 -2
- package/dist-lib/lib/tools/skills/overlay-watch.js +8 -4
- package/dist-lib/lib/tools/skills/setup-routes.js +1 -3
- package/dist-lib/lib/tools/skills/skill-settings.js +4 -1
- package/dist-lib/lib/tools/skills/skill-state.d.ts +6 -11
- package/dist-lib/lib/tools/skills/skill-state.js +48 -29
- package/dist-lib/lib/tools/skills/tool-grants.js +6 -3
- package/dist-lib/lib/tools/toolsets.js +2 -1
- package/dist-lib/lib/tools/translate.d.ts +4 -5
- package/dist-lib/lib/tools/translate.js +31 -14
- package/dist-lib/lib/tools/url-guard.js +2 -1
- package/dist-lib/lib/tools/weather-lookup/place.js +8 -4
- package/dist-lib/lib/tools/weather-lookup/report.d.ts +7 -1
- package/dist-lib/lib/tools/weather-lookup/report.js +46 -9
- package/dist-lib/lib/tools/weather-lookup.js +18 -12
- package/dist-lib/lib/tools/web-fetch.js +47 -24
- package/dist-lib/lib/tools/web-search.js +8 -3
- package/dist-lib/lib/validating-lookup.d.ts +4 -4
- package/dist-lib/lib/wav-encode.d.ts +1 -1
- package/dist-lib/lib/wav-encode.js +11 -10
- package/dist-lib/lib/web-fetch/embedded-state.js +24 -12
- package/dist-lib/lib/web-fetch/extract.js +4 -2
- package/dist-lib/lib/web-fetch/images.js +8 -4
- package/dist-lib/lib/web-fetch/video.d.ts +2 -2
- package/dist-lib/lib/web-fetch/video.js +8 -4
- package/dist-lib/lib/web-search/engine-registry.d.ts +2 -2
- package/dist-lib/lib/web-search/engines/aol.js +1 -1
- package/dist-lib/lib/web-search/engines/bing.js +1 -1
- package/dist-lib/lib/web-search/engines/brave.js +4 -1
- package/dist-lib/lib/web-search/engines/duckduckgo.js +3 -2
- package/dist-lib/lib/web-search/engines/google.js +7 -3
- package/dist-lib/lib/web-search/engines/wikipedia.js +5 -4
- package/dist-lib/lib/web-search/engines/yahoo.js +1 -1
- package/dist-lib/lib/web-search/fetch-html.d.ts +5 -4
- package/dist-lib/lib/web-search/fetch-html.js +13 -7
- package/dist-lib/lib/web-search/orchestrator.d.ts +9 -5
- package/dist-lib/lib/web-search/orchestrator.js +3 -3
- package/dist-lib/lib/wire.d.ts +3 -2
- package/dist-lib/lib/wire.js +23 -15
- package/dist-lib/schema/baseline.js +10 -5
- package/dist-lib/schema/build.js +2 -1
- package/dist-lib/schema/harness.d.ts +15 -2
- package/dist-lib/schema/harness.js +14 -2
- package/dist-lib/schema/release-tags.d.ts +3 -2
- package/dist-lib/schema/release-tags.js +6 -3
- package/dist-lib/spec/bind.js +3 -0
- package/dist-lib/spec/calls.js +1 -0
- package/dist-lib/spec/hrpc/hrpc.json +13 -0
- package/dist-lib/spec/hrpc/index.js +15 -3
- package/dist-lib/spec/hrpc/messages.js +39 -8
- package/dist-lib/spec/hyperschema/index.js +39 -8
- package/dist-lib/spec/hyperschema/schema.json +30 -1
- package/dist-lib/spec/hyperschema/types.d.ts +9 -1
- package/package.json +13 -14
- package/worker/harness-sidecar.mjs +15 -22
- package/worker/stow-entry.mjs +7 -4
- package/dist-lib/lib/tools/mcp/utils.d.ts +0 -47
- /package/dist-lib/lib/tools/http-request/{utils.d.ts → request.d.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -8,130 +8,356 @@ While the major version is `0`, a breaking change bumps the minor version.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.5.0] - 2026-09-25
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `setModelIdleTimeout(ms)` frees memory once the device has been idle that
|
|
16
|
+
long: with no `completion`, `invoke`, `embed`, `transcribe`, `speak`,
|
|
17
|
+
`speakStream` or `warmModel` call in flight, every idle resident model is
|
|
18
|
+
unloaded, including the image, music and translation runtimes. The next call
|
|
19
|
+
loads what it needs again. `0`, the default, is Never, and arms no timer. A
|
|
20
|
+
turn waiting on a tool or an approval between rounds counts as busy, so it
|
|
21
|
+
keeps its model. Also on the wire as `set-model-idle-timeout`. (#131)
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
await harness.setModelIdleTimeout(30 * 60_000)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `setCustomModels` entries take an optional `label`, the name the user gave
|
|
28
|
+
the model, and `list()` rows carry it as `label`. The tool-call format is
|
|
29
|
+
matched against the label as well as the file name, so a custom model
|
|
30
|
+
registered under an opaque id still gets its family's format. (#124)
|
|
31
|
+
|
|
32
|
+
- `setCustomModels` entries take an optional `contextLength`, the model's
|
|
33
|
+
trained window. (#124)
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
await harness.setCustomModels([
|
|
37
|
+
{
|
|
38
|
+
name: 'custom-7f3a',
|
|
39
|
+
label: 'Gemma 4 E4B',
|
|
40
|
+
src: preview.src,
|
|
41
|
+
contextLength: preview.contextLength
|
|
42
|
+
}
|
|
43
|
+
])
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- A custom model loads with a context of the smaller of 51,200 tokens and its
|
|
49
|
+
trained `contextLength`, instead of the default. An asked-for context size
|
|
50
|
+
still wins. (#124)
|
|
51
|
+
|
|
52
|
+
### Fixed
|
|
53
|
+
|
|
54
|
+
- `previewModelSource` takes a direct link to one shard of a split `.gguf` as
|
|
55
|
+
the whole set: `src` is the first part, `sizeBytes` adds up every part, and
|
|
56
|
+
the label names the count, as in `model.gguf (3 parts)`. A missing part
|
|
57
|
+
refuses with `not-found` or `unreachable`. (#124)
|
|
58
|
+
|
|
59
|
+
- `isCached` reports a sharded custom download once every shard is on disk,
|
|
60
|
+
and `deleteModel` removes all of its shards. (#124)
|
|
61
|
+
|
|
11
62
|
## [0.4.0] - 2026-09-24
|
|
12
63
|
|
|
13
64
|
### Added
|
|
14
65
|
|
|
15
|
-
- `
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
66
|
+
- `previewModelSource({ src, mmprojSrc })` checks a Hugging Face link, a direct
|
|
67
|
+
`.gguf` link or a local path before a host adds it as a custom model. It
|
|
68
|
+
reads the Hugging Face metadata and the file's GGUF header, never the whole
|
|
69
|
+
model. The answer is a file to register (`src`, `sizeBytes`,
|
|
70
|
+
`contextLength`, `architecture`, `sizeLabel`, `taskType`, `license`, `fit`
|
|
71
|
+
and `fitReasons`), a list of `files` to pick from for a repo that holds
|
|
72
|
+
several, or `ok: false` with a `refusal` code and a `reason` to show. It is
|
|
73
|
+
served on the `RemoteHarness` client and on an in-process `Harness`, and its
|
|
74
|
+
types are exported from the package root. (#121)
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
const preview = await harness.previewModelSource({ src: link })
|
|
78
|
+
|
|
79
|
+
if (!preview.ok) return showError(preview.reason)
|
|
80
|
+
if (!preview.src) return pickOneOf(preview.files) // then preview files[i].src
|
|
81
|
+
if (preview.taskType !== 'llm') return showError('Only an llm runs as a custom model')
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- `setCustomModels` entries take `taskType` and `expectedSize`, and `list()`
|
|
85
|
+
returns the registered custom models after the catalog rows, with
|
|
86
|
+
`registrySource: 'custom'`, `isLoaded`, and an `isCached` that is false
|
|
87
|
+
until every file is on disk and, when `expectedSize` is set, adds up to it.
|
|
22
88
|
(#121)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
await harness.setCustomModels([
|
|
92
|
+
{
|
|
93
|
+
name: 'nanbeige-3b',
|
|
94
|
+
src: preview.src,
|
|
95
|
+
projectionSrc: preview.mmprojSrc,
|
|
96
|
+
taskType: preview.taskType,
|
|
97
|
+
expectedSize: preview.sizeBytes
|
|
98
|
+
}
|
|
99
|
+
])
|
|
100
|
+
|
|
101
|
+
const models = await harness.list({ type: 'llm' })
|
|
102
|
+
const custom = models.filter((model) => model.registrySource === 'custom')
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- A `modelsDir` Harness option says where downloads live, so `list()` and
|
|
106
|
+
`deleteModel` can find a custom model's files. The spawned sidecar sets it
|
|
107
|
+
from its SDK storage; an in-process `Harness` has to pass it. (#121)
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
const harness = new Harness({ modelsDir })
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
|
|
115
|
+
- `deleteModel(name)` removes the weight and projection a custom model
|
|
116
|
+
downloaded, where it used to remove nothing. It never touches a local path
|
|
117
|
+
the user pointed at, and the model stays registered, listed as not cached.
|
|
118
|
+
(#121)
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
const { removed, freedBytes } = await harness.deleteModel('nanbeige-3b')
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- A custom model name resolves only where an `llm` is loaded, never for
|
|
125
|
+
another task, and a `setCustomModels` entry whose `taskType` is anything
|
|
126
|
+
other than `llm` is not registered. An entry without `taskType` is an `llm`,
|
|
127
|
+
as before. (#121)
|
|
28
128
|
|
|
29
129
|
### Fixed
|
|
30
130
|
|
|
31
|
-
- A turn that preloads a skill with `@skill
|
|
32
|
-
SKILL.md body under the chat's KV key.
|
|
33
|
-
stranded in the cache, until a later turn
|
|
131
|
+
- A turn that preloads a skill with `@skill`, or names an active skill in its
|
|
132
|
+
prompt, no longer caches the inlined SKILL.md body under the chat's KV key.
|
|
133
|
+
Each such turn left about 7k tokens stranded in the cache, until a later turn
|
|
134
|
+
overflowed the context window and failed. (#120)
|
|
34
135
|
|
|
35
136
|
## [0.3.3] - 2026-09-24
|
|
36
137
|
|
|
37
138
|
### Changed
|
|
38
139
|
|
|
39
|
-
- `@qvac/sdk` 0.20.0. Some addons now ship their
|
|
40
|
-
packages (`@qvac/<addon>-<os>-<cpu>`,
|
|
41
|
-
0.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
name (QVAC-24436, QVAC-24502); the excel skill routes a chat image to create,
|
|
48
|
-
not edit; the Google OAuth routes drop the loopback port. (#115)
|
|
140
|
+
- `@qvac/sdk` and `@qvac/inference` 0.20.0. Some addons now ship their
|
|
141
|
+
prebuilds as per-platform packages (`@qvac/<addon>-<os>-<cpu>`, for
|
|
142
|
+
`audiogen-ggml` 0.4 and `tts-ggml` 0.9), which the addon names in its
|
|
143
|
+
`#host-addon` import map, and `@qvac/model-fit` and `@qvac/fabric` join the
|
|
144
|
+
native graph as deferred addons. A host that packages the addons resolves
|
|
145
|
+
prebuilds through that map, and exempts the new package names from any
|
|
146
|
+
install age gate: bun skips a blocked optional dependency without a word,
|
|
147
|
+
and the prebuild goes missing. (#85)
|
|
49
148
|
- A user message with several images reaches the SDK as one turn again. The
|
|
50
|
-
per-image split added for QVAC-24156 is gone
|
|
51
|
-
|
|
52
|
-
video frames and abort the encode.
|
|
53
|
-
|
|
149
|
+
per-image split added for QVAC-24156 is gone, since SDK 0.20 carries the
|
|
150
|
+
upstream fix (llama.cpp #27348) and equal-size still images no longer merge
|
|
151
|
+
as video frames and abort the encode. This release must not be paired with
|
|
152
|
+
SDK 0.19, where that crash comes back. `<key>-vision-history-v1` cache
|
|
153
|
+
directories left by the split are no longer read, and clearing all caches
|
|
54
154
|
removes them. (#116)
|
|
155
|
+
- `@qvac/skills` 0.1.9. The diagrams skill carries every recipe inline, so a 2B
|
|
156
|
+
model no longer loops on loading a reference file it cannot name. The excel
|
|
157
|
+
skill routes a chat image to create rather than edit, the Google OAuth routes
|
|
158
|
+
drop the loopback port, and the Asana skill's first setup step names the
|
|
159
|
+
Asana MCP app to connect. (#115, #118)
|
|
55
160
|
|
|
56
161
|
### Fixed
|
|
57
162
|
|
|
58
163
|
- Retrying or regenerating in a chat that held a multi-image message could
|
|
59
|
-
reuse a stale cached prefix
|
|
60
|
-
key the host never
|
|
61
|
-
|
|
62
|
-
app to connect. (#118)
|
|
164
|
+
reuse a stale cached prefix and condition on the superseded answer. The split
|
|
165
|
+
kept that chat's cache under a sibling key the host's reclaim never reached.
|
|
166
|
+
(#116)
|
|
63
167
|
|
|
64
168
|
## [0.3.2] - 2026-09-23
|
|
65
169
|
|
|
66
170
|
### Changed
|
|
67
171
|
|
|
68
|
-
- A networked
|
|
69
|
-
a URL,
|
|
70
|
-
|
|
71
|
-
`
|
|
72
|
-
host
|
|
172
|
+
- A networked Python run's approval label leads with the hosts its source names
|
|
173
|
+
in a URL, as `python · network: www.youtube.com …`, or says
|
|
174
|
+
`network: no host named in the code`. On an approved run those hosts, and
|
|
175
|
+
their `www.` form, are reached without asking; a subdomain is not. Any other
|
|
176
|
+
host, such as a CDN a library fans out to, a redirect or a URL built at
|
|
177
|
+
runtime, pauses the run and is asked about through the completion's
|
|
178
|
+
`askUser` before its name is looked up. A host allowed once stays allowed
|
|
179
|
+
for that run, and silence past a minute counts as a no. A host that does not
|
|
180
|
+
wire `askUser` refuses every unnamed host, and a skip-all run asks nothing.
|
|
181
|
+
(#109)
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
for await (const event of harness.completion({
|
|
185
|
+
model,
|
|
186
|
+
agent,
|
|
187
|
+
messages,
|
|
188
|
+
approve,
|
|
189
|
+
// options are ['Allow', 'Deny']; an answer starting with "Allow" allows it
|
|
190
|
+
askUser: ({ question, options }) => showQuestion(question, options)
|
|
191
|
+
})) {
|
|
192
|
+
render(event)
|
|
193
|
+
}
|
|
194
|
+
```
|
|
73
195
|
|
|
74
196
|
### Fixed
|
|
75
197
|
|
|
76
|
-
- A
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
- A timed-out
|
|
83
|
-
worker along with the interpreter's state.
|
|
84
|
-
run settles. (#112)
|
|
85
|
-
- A
|
|
86
|
-
file
|
|
198
|
+
- A timed-out Python run spun at full CPU until the process was killed when
|
|
199
|
+
Pyodide had no worker thread, as for a stowed bundle whose host does not set
|
|
200
|
+
`QVAC_PYODIDE_WORKER_ENTRY`. It is now interrupted at the deadline, rejects
|
|
201
|
+
with `Command timed out after <ms>ms`, and the interpreter keeps its state. A
|
|
202
|
+
blocking call such as a long `time.sleep` still sees the interrupt only once
|
|
203
|
+
it returns. (#104)
|
|
204
|
+
- A timed-out Python run on the worker thread could miss its SIGINT, so the
|
|
205
|
+
worker was killed after the grace window along with the interpreter's state.
|
|
206
|
+
The interrupt is resent every 100 ms until the run settles. (#112)
|
|
207
|
+
- A Python run that failed partway through a save attached the half-written
|
|
208
|
+
file, and each failing retry attached another copy under the same name. A
|
|
209
|
+
non-zero exit now attaches nothing and reports every declared output in
|
|
87
210
|
`missingOutputs`. (#110)
|
|
88
211
|
|
|
212
|
+
### Security
|
|
213
|
+
|
|
214
|
+
- Python in `exec` reached the network whenever the runtime allowed it,
|
|
215
|
+
including a call that auto-ran as a read with no approval prompt, so a
|
|
216
|
+
document build that loads an image from a URL could reach the web without
|
|
217
|
+
anyone seeing it. The network now opens only to a call a person approved or
|
|
218
|
+
that skip-all let through, and sandbox mode stays offline. 0.3.1 is affected.
|
|
219
|
+
(#108)
|
|
220
|
+
|
|
89
221
|
## [0.3.1] - 2026-09-23
|
|
90
222
|
|
|
91
223
|
### Added
|
|
92
224
|
|
|
93
225
|
- Python in `exec` reaches the network on Bare: `urllib`, `http.client` and
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
226
|
+
what is built on them, yt-dlp included, work unchanged, and `requests` works
|
|
227
|
+
through urllib3's emscripten backend. Every request goes through the guarded
|
|
228
|
+
fetch the web tools use, so each hop is checked before it is sent,
|
|
229
|
+
credentials do not follow a redirect to another origin, and a response is
|
|
230
|
+
capped at 100 MB. Where the global `fetch` is not the guarded one, as on
|
|
231
|
+
Node, Python stays offline, and sandbox mode keeps it offline everywhere. A
|
|
232
|
+
Python run may now declare `.mp4`, `.webm`, `.m4a` and `.mp3` outputs, each
|
|
233
|
+
checked by its file signature. (#94)
|
|
234
|
+
- `SkillOAuthConfig.verify`, parsed from a skill manifest's
|
|
235
|
+
`setup.routes[].oauth.verify` and carried over the wire, so a host can probe a
|
|
236
|
+
fresh token before saving it. A probe is an https `url` on the same site as
|
|
237
|
+
the route's `tokenUrl`, an optional `GET` or `POST` `method` and JSON `body`,
|
|
238
|
+
and `reject` rules. A rule matches a listed `status`, or a 2xx JSON body whose
|
|
239
|
+
`field` is not `notEqual`, and its `error` names the rejection. A probe that
|
|
240
|
+
breaks these rules is dropped and the route kept. `SkillOAuthVerify` and
|
|
241
|
+
`SkillOAuthVerifyRule` are exported. (#105)
|
|
242
|
+
|
|
243
|
+
```js
|
|
244
|
+
const skills = await harness.skills()
|
|
245
|
+
const route = skills
|
|
246
|
+
.find((skill) => skill.name === 'asana')
|
|
247
|
+
?.setup?.routes?.find((r) => r.kind === 'oauth')
|
|
248
|
+
|
|
249
|
+
// { url: 'https://mcp.asana.com/v2/mcp', method: 'POST',
|
|
250
|
+
// body: '{"jsonrpc":"2.0",…}',
|
|
251
|
+
// reject: [{ status: [401, 403], error: 'asana_not_mcp_app' }] }
|
|
252
|
+
const verify = route?.oauth?.verify
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
- A skill operation may name `response.decode` to turn a provider's JSON body
|
|
256
|
+
into a compact shape before the model sees it, starting with
|
|
257
|
+
`gmail-message`: the ids and labels, the From, To, Subject, Date, Message-ID
|
|
258
|
+
and References headers, the snippet, and the body as text, capped at 4000
|
|
259
|
+
characters. A `gmail-get` request builder joins it. A manifest naming both
|
|
260
|
+
`pick` and `decode`, or a builder or decoder this harness does not have, is
|
|
261
|
+
rejected. (#69)
|
|
262
|
+
|
|
263
|
+
### Changed
|
|
264
|
+
|
|
265
|
+
- A compiled skill operation whose HTTP method is `GET`, `HEAD` or `OPTIONS` is
|
|
266
|
+
a read: it auto-runs without an approval prompt, like the same request through
|
|
267
|
+
`http_request`, and takes no `intent` argument. MCP operations and every other
|
|
268
|
+
method are still writes that ask. (#69)
|
|
269
|
+
- `@qvac/skills` 0.1.7, now pinned exactly: Gmail reads a message with
|
|
270
|
+
`gmail_get` instead of telling the model to decode base64, which needs this
|
|
271
|
+
release's decoder, and Asana rejects a non-MCP app at connect time through
|
|
272
|
+
a `verify` probe. (#105)
|
|
100
273
|
|
|
101
274
|
### Fixed
|
|
102
275
|
|
|
103
276
|
- A malformed `<tool_call>` left its JSON arguments in the reply. The whole span
|
|
104
|
-
is now stripped.
|
|
277
|
+
is now stripped, and an unclosed one is stripped to the end of the text.
|
|
278
|
+
(#100)
|
|
105
279
|
- A misspelt, placeholder or repeated `skill` load returned an error the model
|
|
106
|
-
retried
|
|
107
|
-
body
|
|
108
|
-
|
|
109
|
-
|
|
280
|
+
retried until the turn ran out. A placeholder or the skill's own name now
|
|
281
|
+
reads the body, a near miss of one of its files loads that file, and a repeat
|
|
282
|
+
is acknowledged without re-reading it. (#101)
|
|
283
|
+
- When the model reached for `pip` or a video tool on the shell path, it was
|
|
284
|
+
only told what the path is not, and retried the same call. Each refusal now
|
|
285
|
+
names the call that works, such as `packages: ["requests"]` with the pin a
|
|
286
|
+
vendored package installs offline under. A missing import names the package
|
|
287
|
+
that provides it, and `python -c`, a heredoc or bare Python source runs as
|
|
288
|
+
Python instead of being sent back for a `language` argument. (#71)
|
|
110
289
|
|
|
111
290
|
## [0.3.0] - 2026-09-23
|
|
112
291
|
|
|
113
292
|
### Added
|
|
114
293
|
|
|
115
|
-
- `translate` capability
|
|
116
|
-
pair picks the route before anything loads
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
-
|
|
121
|
-
|
|
122
|
-
|
|
294
|
+
- A `translate` capability for `invoke`: it translates text without loading an
|
|
295
|
+
LLM. The language pair picks the route before anything loads. English and
|
|
296
|
+
one of nine European languages go through the TranslatePsy-EuroNano packs,
|
|
297
|
+
the other 43 languages Bergamot serves go through their per-pair models
|
|
298
|
+
(pivoting through English when neither side is English), and 16 languages
|
|
299
|
+
Bergamot has no model for go through TranslatePsy-AfriSLM, named by ISO 639-1
|
|
300
|
+
or FLORES-200 code. An omitted `from` is detected from the text, except for
|
|
301
|
+
the AfriSLM languages, which need it named. The `tool-result` carries the
|
|
302
|
+
text, the pair and the `engine` that answered. (#76)
|
|
303
|
+
|
|
304
|
+
```js
|
|
305
|
+
for await (const event of harness.invoke({
|
|
306
|
+
capability: 'translate',
|
|
307
|
+
args: { text: 'The weather is nice today.', to: 'de' }
|
|
308
|
+
})) {
|
|
309
|
+
if (event.type === 'tool-result') show(event.result.text)
|
|
310
|
+
if (event.type === 'error') showError(event.message)
|
|
311
|
+
}
|
|
312
|
+
// result: { text, from, to: 'de', engine: 'nmt', pivoted: false }
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
- `translationPlanFor`, `NMT_TO_ENGLISH`, `NMT_FROM_ENGLISH`,
|
|
316
|
+
`EURONANO_LANGUAGES`, `SLM_LANGUAGE_CODES` and `SLM_LANGUAGE_NAMES` exports,
|
|
317
|
+
so a host can build a language picker or preview a route without a registry
|
|
318
|
+
lookup. (#76)
|
|
319
|
+
|
|
320
|
+
```js
|
|
321
|
+
import { translationPlanFor, SLM_LANGUAGE_NAMES } from '@qvac/harness'
|
|
322
|
+
|
|
323
|
+
const plan = translationPlanFor('es', 'ja')
|
|
324
|
+
// { route: 'nmt', from: 'es', to: 'ja',
|
|
325
|
+
// model: 'model.esen.intgemm', pivot: 'model.enja.intgemm' }
|
|
326
|
+
if ('error' in plan) showError(plan.error)
|
|
327
|
+
```
|
|
328
|
+
|
|
123
329
|
- `enhance: 'never' | 'auto'` on `invoke`. With `'auto'`, an image prompt is
|
|
124
|
-
rewritten by a model already resident on the device and never triggers a
|
|
125
|
-
|
|
126
|
-
|
|
330
|
+
rewritten by a model already resident on the device, and never triggers a
|
|
331
|
+
load: with nothing resident, or a rewrite that fails, the run goes direct.
|
|
332
|
+
`metrics` reports the `route` taken (`direct` or `enhanced`), the
|
|
333
|
+
`routeModel` used and the tokens the rewrite spent. Omitting `enhance`
|
|
334
|
+
behaves as `'never'`, which passes the arguments through untouched. (#77)
|
|
335
|
+
|
|
336
|
+
```js
|
|
337
|
+
for await (const event of harness.invoke({
|
|
338
|
+
capability: 'image',
|
|
339
|
+
args: { prompt: 'funny meme' },
|
|
340
|
+
enhance: 'auto'
|
|
341
|
+
})) {
|
|
342
|
+
if (event.type === 'metrics') log(event.route, event.routeModel)
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Changed
|
|
347
|
+
|
|
348
|
+
- The bundle defers `@qvac/translation-nmtcpp` like the other inference
|
|
349
|
+
addons, so `prebuilds-manifest.json` now names its prebuilds and a host that
|
|
350
|
+
assembles them beside the bundle has to include it. It is already a
|
|
351
|
+
dependency of `@qvac/sdk`. (#76)
|
|
127
352
|
|
|
128
353
|
## [0.2.12] - 2026-09-22
|
|
129
354
|
|
|
130
355
|
### Fixed
|
|
131
356
|
|
|
132
|
-
- Sandbox mode refused every `exec` call that installs packages, which broke
|
|
133
|
-
pdf, presentations, word and excel skills.
|
|
134
|
-
|
|
357
|
+
- Sandbox mode refused every `exec` call that installs packages, which broke
|
|
358
|
+
the pdf, presentations, word and excel skills. A request whose packages are
|
|
359
|
+
all vendored now installs from the shipped wheels; one that would fall back
|
|
360
|
+
to PyPI is still refused. (#91)
|
|
135
361
|
|
|
136
362
|
## [0.2.11] - 2026-09-22
|
|
137
363
|
|
|
@@ -139,161 +365,483 @@ Tagged but never published to npm; these changes first shipped in 0.2.12.
|
|
|
139
365
|
|
|
140
366
|
### Added
|
|
141
367
|
|
|
142
|
-
- `
|
|
143
|
-
model, dispatch
|
|
144
|
-
`
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
`
|
|
368
|
+
- `invoke({ capability, args })` runs one capability on its own, with no
|
|
369
|
+
model, no dispatch and no approval callback: the caller already decided.
|
|
370
|
+
`image` is the first capability. It is a frame on the existing `completion`
|
|
371
|
+
stream, so it takes the same `attach`, `progress` and abort signal and emits
|
|
372
|
+
the same `tool-call`, `tool-result`, `metrics`, `error` and `aborted` events.
|
|
373
|
+
`metrics` reports `totalTokens: 0`. It is on `Harness` and `RemoteHarness`
|
|
374
|
+
alike, and optional on `HarnessContract`. (#74)
|
|
375
|
+
|
|
376
|
+
```js
|
|
377
|
+
for await (const event of harness.invoke({
|
|
378
|
+
capability: 'image',
|
|
379
|
+
args: { prompt: 'a lighthouse at dusk', width: 768, height: 512 },
|
|
380
|
+
attach,
|
|
381
|
+
progress,
|
|
382
|
+
signal
|
|
383
|
+
})) {
|
|
384
|
+
if (event.type === 'tool-result') showImage(event.result)
|
|
385
|
+
if (event.type === 'error') showError(event.message)
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
- `detectLanguage({ text, topK })`, served over hrpc as `detect-language` and
|
|
390
|
+
on `RemoteHarness`: language detection with no model to load. Without
|
|
391
|
+
`topK`, or with 1, it answers with the single best match and no
|
|
392
|
+
probability. Above that it returns ranked candidates with their
|
|
393
|
+
probabilities, best first, and may return fewer than asked. Text it cannot
|
|
394
|
+
place answers `und`. (#75)
|
|
395
|
+
|
|
396
|
+
```js
|
|
397
|
+
const [best] = await harness.detectLanguage({ text })
|
|
398
|
+
// { code, language }
|
|
399
|
+
|
|
400
|
+
const ranked = await harness.detectLanguage({ text, topK: 3 })
|
|
401
|
+
// [{ code, language, probability }, ...]
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
- `SkillOAuthConfig` on `SkillSetupRoute.oauth`, parsed from a skill
|
|
405
|
+
manifest's `setup.routes[].oauth` and carried over the wire, so a host can
|
|
406
|
+
run a skill's OAuth setup from its manifest instead of its own provider
|
|
407
|
+
table. A route missing `stateKey`, `authUrl`, `tokenUrl` or `tokenAuth`
|
|
408
|
+
carries no `oauth`. (#88)
|
|
409
|
+
|
|
410
|
+
### Changed
|
|
411
|
+
|
|
412
|
+
- `@qvac/skills` 0.1.6: the eight OAuth skills declare their setup routes'
|
|
413
|
+
OAuth config, the Spotify skill always makes the play call after a search,
|
|
414
|
+
and the weather skill looks the place up with `weather_lookup`. (#88)
|
|
149
415
|
|
|
150
416
|
### Fixed
|
|
151
417
|
|
|
152
|
-
-
|
|
153
|
-
|
|
154
|
-
`
|
|
418
|
+
- Asked to fetch a file, the model tried `http_request` and `exec` before
|
|
419
|
+
`web_fetch`, and once the file was attached it had no id to read it with.
|
|
420
|
+
`web_fetch` now offers file downloads (PDF, Office documents, images) in its
|
|
421
|
+
description, and its result carries the `attachmentId`, `fileName` and
|
|
422
|
+
`byteLength` it created, so the model can stage the file into `exec` and it
|
|
423
|
+
survives a history fold. (#89)
|
|
155
424
|
|
|
156
425
|
## [0.2.10] - 2026-09-22
|
|
157
426
|
|
|
158
427
|
### Added
|
|
159
428
|
|
|
160
429
|
- `sandboxMode` agent option: an agent with no network access. It withholds
|
|
161
|
-
every tool that reaches the network
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
430
|
+
every tool that reaches the network (the web search, HTTP request and MCP
|
|
431
|
+
toolsets, including skill operations over HTTP or MCP), not just web search.
|
|
432
|
+
`exec` stays, but refuses package installs and well-known network commands
|
|
433
|
+
such as `curl`, `git` and `npm`. That is a guard, not an OS-level sandbox.
|
|
434
|
+
(#73)
|
|
435
|
+
|
|
436
|
+
```js
|
|
437
|
+
for await (const event of harness.completion({
|
|
438
|
+
model,
|
|
439
|
+
messages,
|
|
440
|
+
agent: { ...agent, sandboxMode: true }
|
|
441
|
+
})) {
|
|
442
|
+
render(event)
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
- `@qvac/harness/office`: XLSX and PPTX text extraction for Knowledge beside
|
|
447
|
+
DOCX, through the same bundled Python worker. It keeps sheet names, column
|
|
448
|
+
positions, cached formula values, slide text, grouped shapes and tables;
|
|
449
|
+
speaker notes, images and charts are left out. The format comes from the
|
|
450
|
+
file extension, with `mimeType` as the fallback. `@qvac/harness/docx` is
|
|
451
|
+
unchanged. (#83)
|
|
452
|
+
|
|
453
|
+
```js
|
|
454
|
+
import { extractOfficeText, closeOfficeWorker } from '@qvac/harness/office'
|
|
455
|
+
|
|
456
|
+
const text = await extractOfficeText(bytes, 'report.xlsx', { signal, mimeType })
|
|
457
|
+
// at host shutdown, once extractions have settled
|
|
458
|
+
await closeOfficeWorker()
|
|
459
|
+
```
|
|
165
460
|
|
|
166
461
|
### Fixed
|
|
167
462
|
|
|
168
|
-
- A
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
463
|
+
- A small model could loop on the same failing tool call, up to the round
|
|
464
|
+
cap. A call that already failed this turn with the same arguments is now
|
|
465
|
+
answered from its first result without running again, until a later round
|
|
466
|
+
succeeds. Three such answers end the turn's tool rounds. A failure that may
|
|
467
|
+
pass on retry, such as a timeout, a 5xx, 408 or 429, still runs again, and
|
|
468
|
+
the tools that can fail that way now say so in a `code` or `status` field on
|
|
469
|
+
their result. (#78)
|
|
470
|
+
- A guessed `auth.tokenCredentialKey` on `http_request` or `mcp_call` was
|
|
471
|
+
refused with an error claiming the skill was not loaded. It now resolves to
|
|
472
|
+
the one connected credential allowed for that URL, and when none is
|
|
473
|
+
connected the error says so and tells the user to connect it in Skills.
|
|
474
|
+
(#72)
|
|
173
475
|
|
|
174
476
|
## [0.2.9] - 2026-09-22
|
|
175
477
|
|
|
176
478
|
### Added
|
|
177
479
|
|
|
178
|
-
- `captureInferenceCalls`
|
|
179
|
-
`inference-
|
|
180
|
-
|
|
181
|
-
|
|
480
|
+
- `captureInferenceCalls` on a completion's context: each round then emits an
|
|
481
|
+
`inference-call` event with the exact payload it is about to send (model,
|
|
482
|
+
messages, tools, generation params, cache key and prompt prefix), and an
|
|
483
|
+
`inference-sent` event once it ends, with the tokens it spent, its `outcome`
|
|
484
|
+
(`completed`, `overflowed` or `failed`) and, on the bundled patched SDK, the
|
|
485
|
+
slice of `messages` the KV cache let through. An attempt the overflow ladder
|
|
486
|
+
rejected is still recorded, with its `droppedTurns`. Off unless asked for,
|
|
487
|
+
since every record carries a whole prompt. The flag and both events are new on
|
|
488
|
+
the hrpc wire, so the host and the spawned harness both need 0.2.9. (#51)
|
|
489
|
+
|
|
490
|
+
```js
|
|
491
|
+
for await (const event of harness.completion({ model, messages, captureInferenceCalls: true })) {
|
|
492
|
+
if (event.type === 'inference-call') record(event.call.round, event.call.messages)
|
|
493
|
+
if (event.type === 'inference-sent') settle(event.round, event.outcome, event.tokens)
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
- The `metrics` event carries the last round's `stopReason`, and leaves it out
|
|
498
|
+
when the SDK reports none. A turn cut off by the context window ends cleanly
|
|
499
|
+
with `stopReason: 'length'`, so a host can tell it from a complete answer.
|
|
500
|
+
(#62)
|
|
501
|
+
|
|
502
|
+
```js
|
|
503
|
+
if (event.type === 'metrics' && event.stopReason === 'length') markTruncated()
|
|
504
|
+
```
|
|
182
505
|
|
|
183
506
|
### Changed
|
|
184
507
|
|
|
185
|
-
- The published tarball no longer
|
|
508
|
+
- The published tarball no longer carries `docs/`, `CLAUDE.md`, `.claude/` or
|
|
509
|
+
eval output. (#79)
|
|
186
510
|
|
|
187
511
|
## [0.2.8] - 2026-09-17
|
|
188
512
|
|
|
189
513
|
### Added
|
|
190
514
|
|
|
191
|
-
- `weather_lookup
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
515
|
+
- `weather_lookup`, a tool that looks weather up on wttr.in by `location` and
|
|
516
|
+
leads its report with the place the service actually resolved. When that
|
|
517
|
+
place disagrees with the country or state the user named, it refuses instead
|
|
518
|
+
of reporting the weather of wherever the geocoder landed: a nonexistent town
|
|
519
|
+
no longer gets a forecast, and a bare "Rome" the service places in Togo says
|
|
520
|
+
so. It is offered only once the weather skill is loaded, needs no approval,
|
|
521
|
+
and the `http-request` toolset switch turns it off. The spawned sidecar
|
|
522
|
+
registers it; an in-process host adds it to its tools. (#52)
|
|
195
523
|
|
|
196
|
-
|
|
524
|
+
```js
|
|
525
|
+
import { createWeatherLookupTool } from '@qvac/harness/lib/tools/weather-lookup.ts'
|
|
526
|
+
|
|
527
|
+
const harness = new Harness({ tools: [...tools, createWeatherLookupTool()] })
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Changed
|
|
197
531
|
|
|
198
|
-
- `@qvac/
|
|
199
|
-
|
|
532
|
+
- **Breaking:** `currentReleaseLine` from `@qvac/harness/schema/release-tags`,
|
|
533
|
+
the schema build tooling, is now `currentReleaseVersion`, and returns the
|
|
534
|
+
full `bare-semver` `Version` rather than a `major.minor` string. A host that
|
|
535
|
+
imports it imports `currentReleaseVersion` instead and reads the line off
|
|
536
|
+
the version. (#55)
|
|
537
|
+
|
|
538
|
+
```js
|
|
539
|
+
// 0.2.7
|
|
540
|
+
const line = currentReleaseLine(root) // '0.2'
|
|
541
|
+
|
|
542
|
+
// 0.2.8
|
|
543
|
+
const version = currentReleaseVersion(root) // Version 0.2.8
|
|
544
|
+
const line = `${version.major}.${version.minor}`
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
- `previousReleaseTags` from the same module now counts the newest shipped tag
|
|
548
|
+
on the line being built as a baseline, not only older lines, so a schema
|
|
549
|
+
change is checked against the build users are running. Only tags newer than
|
|
550
|
+
the package version are left out. A host that regenerates its `spec/`
|
|
551
|
+
against it re-derives hyperschema's version counters once, so expect one
|
|
552
|
+
regeneration commit after upgrading. (#55, #58)
|
|
553
|
+
- `@qvac/skills` 0.1.4: the Apple Notes create-note operation no longer
|
|
554
|
+
renames a note after creating it, which failed on iCloud accounts and left
|
|
555
|
+
duplicate notes behind. (#48)
|
|
200
556
|
|
|
201
557
|
## [0.2.7] - 2026-09-16
|
|
202
558
|
|
|
203
559
|
### Added
|
|
204
560
|
|
|
205
|
-
- `@qvac/harness/docx`:
|
|
206
|
-
|
|
561
|
+
- `@qvac/harness/docx`: `extractDocxText` pulls a DOCX file's text for
|
|
562
|
+
Knowledge with the bundled Python runtime, keeping paragraphs and tables in
|
|
563
|
+
document order, one row per line with cells joined by `' | '`. It takes up to
|
|
564
|
+
20 MiB and only the shipped wheels, never a network install. Set
|
|
565
|
+
`QVAC_PYODIDE_CACHE_DIR` to a writable directory first, so the worker seeds
|
|
566
|
+
its cache from those wheels and works offline. A bundled host also sets
|
|
567
|
+
`QVAC_PYODIDE_WORKER_ENTRY` and `QVAC_PYTHON_WHEELS_DIR` to its staged
|
|
568
|
+
worker and wheels. (#46)
|
|
569
|
+
|
|
570
|
+
```js
|
|
571
|
+
import { extractDocxText, closeDocxWorker } from '@qvac/harness/docx'
|
|
572
|
+
|
|
573
|
+
const text = await extractDocxText(bytes, 'notes.docx', { signal })
|
|
574
|
+
|
|
575
|
+
// at shutdown, once outstanding extractions have settled
|
|
576
|
+
await closeDocxWorker()
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### Fixed
|
|
580
|
+
|
|
581
|
+
- A Python run whose signal aborted while it waited for another run to finish
|
|
582
|
+
is no longer started. (#46)
|
|
207
583
|
|
|
208
584
|
## [0.2.6] - 2026-09-15
|
|
209
585
|
|
|
210
586
|
### Added
|
|
211
587
|
|
|
212
|
-
- `completionModelCap
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
588
|
+
- `completionModelCap`, default 1: how many completion models may be resident
|
|
589
|
+
at once. It is a `HarnessOptions` field, and rides the `llm` config through
|
|
590
|
+
`spawnHarness` and `harnessChild`. (#41)
|
|
591
|
+
|
|
592
|
+
```js
|
|
593
|
+
const harness = spawnHarness({
|
|
594
|
+
entry,
|
|
595
|
+
sdkStorage,
|
|
596
|
+
llm: { contextSize: 8192, completionModelCap: 2 }
|
|
597
|
+
})
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
- `writeClient` and `writeCapabilities` from `@qvac/harness/schema/generate`
|
|
601
|
+
take an options argument whose `optionalRequests` names the request types a
|
|
602
|
+
caller may omit: the generated client sends `{}` for them and the generated
|
|
603
|
+
capability types mark the input optional. Left out, only `@assistant/empty`
|
|
604
|
+
is optional, as before. A set you pass replaces that default, so include it
|
|
605
|
+
if you still want it. `writeClient` still takes an export name as a string.
|
|
606
|
+
(#44)
|
|
607
|
+
|
|
608
|
+
```js
|
|
609
|
+
import { writeClient, writeCapabilities } from '@qvac/harness/schema/generate'
|
|
610
|
+
|
|
611
|
+
const optionalRequests = new Set(['@assistant/empty', '@assistant/mesh-scope'])
|
|
612
|
+
writeClient(builder, 'spec/calls.js', { exportName: 'engineClient', optionalRequests })
|
|
613
|
+
writeCapabilities(builder, 'spec/capabilities.d.ts', { optionalRequests })
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Changed
|
|
617
|
+
|
|
618
|
+
- A completion that needs a different model no longer loads it beside one a
|
|
619
|
+
live run is holding. It waits for that run to release its model, for up to
|
|
620
|
+
five minutes, and then loads anyway rather than failing. A `warmModel` call
|
|
621
|
+
waits two seconds at most, and skips the warm if no room was made. (#41)
|
|
216
622
|
|
|
217
623
|
## [0.2.5] - 2026-09-14
|
|
218
624
|
|
|
219
625
|
### Added
|
|
220
626
|
|
|
221
|
-
- `
|
|
222
|
-
|
|
627
|
+
- A `gpuStats` option on `spawnHarness`, `harnessChild` and `HarnessOptions`.
|
|
628
|
+
A host that already has the GPU reading passes it, and the harness skips its
|
|
629
|
+
own probe. Left out, the harness probes for itself. (#36)
|
|
630
|
+
|
|
631
|
+
```js
|
|
632
|
+
import { spawnHarness } from '@qvac/harness/lib/spawn.ts'
|
|
633
|
+
|
|
634
|
+
const harness = spawnHarness({
|
|
635
|
+
entry,
|
|
636
|
+
sdkStorage,
|
|
637
|
+
gpuStats: {
|
|
638
|
+
memory: { totalBytes: os.totalmem() },
|
|
639
|
+
gpu: { devices: [{ name: 'NVIDIA RTX 4070', videoMemoryBytes: 12884901888 }] }
|
|
640
|
+
}
|
|
641
|
+
})
|
|
642
|
+
```
|
|
223
643
|
|
|
224
644
|
### Fixed
|
|
225
645
|
|
|
226
|
-
- On a Linux snap the harness exited about a second after `open`,
|
|
227
|
-
|
|
228
|
-
-
|
|
229
|
-
|
|
646
|
+
- On a Linux snap the harness exited about a second after `open`, and the
|
|
647
|
+
supervisor respawned it in a loop: the boot-time capability probe created a
|
|
648
|
+
second GPU-info instance beside the SDK's own. On Linux and Windows under
|
|
649
|
+
Bare, GPUs are now enumerated in a short-lived child process, which gives up
|
|
650
|
+
after 5 seconds and reports no GPU. (#36)
|
|
651
|
+
- On Windows, asking for an image after a presentation left the chat stuck on
|
|
652
|
+
"Loading … into memory". Pyodide's boot opened the process streams from the
|
|
653
|
+
Python worker thread, which blocked the image-model loader's progress. Boot
|
|
654
|
+
now never touches them; a run's own output capture is unchanged. (#34)
|
|
230
655
|
|
|
231
656
|
## [0.2.4] - 2026-09-12
|
|
232
657
|
|
|
233
658
|
### Added
|
|
234
659
|
|
|
235
|
-
-
|
|
236
|
-
|
|
660
|
+
- `@qvac/harness/worker/stow-entry.mjs`: the sidecar entry, published so a
|
|
661
|
+
host running from inside `node_modules` spawns the engine without the
|
|
662
|
+
prebuilt bundle. Every native addon then resolves beside the JS that calls
|
|
663
|
+
it, from one install. With the bundle, the addons come from the host's own
|
|
664
|
+
install, and a version skew there hangs the child on boot rather than
|
|
665
|
+
throwing: `@qvac/core` 0.1.2 shipped that way. The bundle stays published
|
|
666
|
+
for a host that relocates the code out of `node_modules`. (#31)
|
|
667
|
+
|
|
668
|
+
```js
|
|
669
|
+
import { harnessChild } from '@qvac/harness/lib/spawn.ts'
|
|
670
|
+
|
|
671
|
+
const entry = new URL(import.meta.resolve('@qvac/harness/worker/stow-entry.mjs')).pathname
|
|
672
|
+
const spec = harnessChild(entry, { runner, skillsDir, wheelsDir, sdkStorage: storage.sdk })
|
|
673
|
+
```
|
|
237
674
|
|
|
238
675
|
## [0.2.3] - 2026-09-12
|
|
239
676
|
|
|
677
|
+
### Added
|
|
678
|
+
|
|
679
|
+
- `schema/generate` emits `writeStream` on the generated `HandlerFrames`, so a
|
|
680
|
+
duplex handler's frames type without a local widening. (#29)
|
|
681
|
+
|
|
682
|
+
### Changed
|
|
683
|
+
|
|
684
|
+
- On Android and iOS, `resolveStorage` keeps models under the app's own
|
|
685
|
+
persistent root instead of the device HOME, which an app cannot write.
|
|
686
|
+
Desktop and Snap are unchanged, and `overrides.models` still wins. (#29)
|
|
687
|
+
|
|
688
|
+
```js
|
|
689
|
+
import { resolveStorage } from '@qvac/harness/storage'
|
|
690
|
+
|
|
691
|
+
const storage = resolveStorage('prod')
|
|
692
|
+
// mobile: <persistent root>/.qvac/models
|
|
693
|
+
storage.sdk.models
|
|
694
|
+
```
|
|
695
|
+
|
|
240
696
|
### Fixed
|
|
241
697
|
|
|
242
|
-
- Every `./lib/tools/*` subpath is published.
|
|
243
|
-
|
|
698
|
+
- Every `./lib/tools/*` subpath is published. The tarball listed only the ones
|
|
699
|
+
`qvac-core` imports, so the mobile worklet's file tools (`file-glob`, `grep`,
|
|
700
|
+
`read-files`, `create-documents`, `edit-documents`, `fs/scope`) failed with
|
|
701
|
+
`PACKAGE_PATH_NOT_EXPORTED`. 0.1.1 to 0.2.2 are affected. (#29)
|
|
244
702
|
|
|
245
703
|
## [0.2.2] - 2026-09-11
|
|
246
704
|
|
|
247
705
|
### Fixed
|
|
248
706
|
|
|
249
|
-
-
|
|
707
|
+
- A stale lockfile entry gave `@qvac/sdk` its own nested `@qvac/inference`
|
|
708
|
+
0.19.1, beside the patched 0.19.0 the harness pins. The bundle is built from
|
|
709
|
+
the pinned copy alone again. (#27)
|
|
250
710
|
|
|
251
711
|
## [0.2.1] - 2026-09-11
|
|
252
712
|
|
|
253
713
|
### Fixed
|
|
254
714
|
|
|
255
|
-
- The generated wire spec
|
|
715
|
+
- The generated wire spec in 0.2.0 stamped the fields it added (`downloaded`
|
|
716
|
+
on a download event and as a model status, and `kvCacheKey`) with schema
|
|
717
|
+
version 7, the version 0.1.2 already shipped. They are version 8 now, so the
|
|
718
|
+
spec matches the schema it is built from. (#25)
|
|
256
719
|
|
|
257
720
|
## [0.2.0] - 2026-09-11
|
|
258
721
|
|
|
259
722
|
### Added
|
|
260
723
|
|
|
261
|
-
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
-
|
|
265
|
-
|
|
724
|
+
- `deleteKvCache(kvCacheKey)` deletes one chat's KV cache, every file kept
|
|
725
|
+
under that key, where `clearKvCache()` deletes them all. It is safe on a key
|
|
726
|
+
still in use: the next turn under it primes from scratch. It rides a new
|
|
727
|
+
`delete-kv-cache` hrpc route, and resolves with `{ status }` like
|
|
728
|
+
`clearKvCache`. (#22)
|
|
729
|
+
|
|
730
|
+
```js
|
|
731
|
+
// the cacheKey the chat's completions ran under
|
|
732
|
+
const result = await harness.deleteKvCache(cacheKey)
|
|
733
|
+
if (result.status !== 'success') log(result.error)
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
- A download's `complete` event carries `downloaded`, `true` when bytes were
|
|
737
|
+
fetched and `false` when the model was already on disk. An image model's
|
|
738
|
+
`model` events report `loading` rather than `downloading` when it is cached,
|
|
739
|
+
and a new `downloaded` status once a fetched one has loaded, so a host that
|
|
740
|
+
switches over `status` has one more case. (#22)
|
|
741
|
+
|
|
742
|
+
```js
|
|
743
|
+
for await (const event of harness.download({ model })) {
|
|
744
|
+
if (event.type === 'complete' && event.downloaded) notifyDownloaded(model)
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
harness.on('model', ({ model, status }) => {
|
|
748
|
+
if (status === 'downloaded') notifyDownloaded(model)
|
|
749
|
+
})
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
- `ToolDefinition.prompt`: an optional compact `description` and `parameters`
|
|
753
|
+
the model is shown in place of the full ones, which still validate the call.
|
|
754
|
+
`exec` and `skill` ship one, so every turn spends less of the prompt on
|
|
755
|
+
their schemas. (#22)
|
|
756
|
+
- `CONTEXT_OVERFLOW_MESSAGE` is exported, the message a turn fails with when
|
|
757
|
+
it does not fit the model's context window, so a host can tell that failure
|
|
758
|
+
apart and compact the chat. (#22)
|
|
759
|
+
|
|
760
|
+
```js
|
|
761
|
+
import { CONTEXT_OVERFLOW_MESSAGE } from '@qvac/harness'
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
- With `QVAC_HARNESS_LOG=1`, each turn logs `sdk tool definitions`: the tool
|
|
765
|
+
schemas it sent, largest first, with each one's size and the skill that
|
|
766
|
+
granted it. (#22)
|
|
266
767
|
|
|
267
768
|
### Changed
|
|
268
769
|
|
|
269
|
-
-
|
|
270
|
-
import alias
|
|
271
|
-
-
|
|
770
|
+
- `@qvac/sdk` 0.19.0, pinned exactly. Under Bare the SDK is now served by
|
|
771
|
+
`@qvac/inference` 0.19.0, through a new `#sdk` import alias.
|
|
772
|
+
`@qvac/audiogen-ggml` joins the deferred addons, so `prebuilds-manifest.json`
|
|
773
|
+
names it and the host provides it too. (#22)
|
|
774
|
+
- Image and music generation schemas reach the model once their skill's
|
|
775
|
+
instructions are loaded, not as soon as the skill is enabled. Loading the
|
|
776
|
+
skill unlocks them within the same turn, at the cost of one cache re-prime
|
|
777
|
+
on that round. (#22)
|
|
778
|
+
- `@qvac/skills` 0.1.3: the weather skill keeps the place exactly as the user
|
|
779
|
+
wrote it, asks back on an ambiguous city name, and reports a failed lookup
|
|
780
|
+
instead of a temperature it never received. (#22)
|
|
781
|
+
- `bare-http1` is pinned to 4.5.9, as a dependency and as an override.
|
|
782
|
+
(#22, #24)
|
|
272
783
|
|
|
273
784
|
## [0.1.2] - 2026-09-11
|
|
274
785
|
|
|
275
786
|
### Fixed
|
|
276
787
|
|
|
277
|
-
- `@qvac/harness/sdk-storage` and
|
|
278
|
-
Bare
|
|
788
|
+
- `@qvac/harness/sdk-storage` and `@qvac/harness/sdk-config-loader` failed to
|
|
789
|
+
resolve under Bare with `MODULE_NOT_FOUND`: the tarball carried only the Node
|
|
790
|
+
loader behind `#sdk-config-loader`, so `configureSdkStorage()` could not run
|
|
791
|
+
on Bare. Both loaders ship now. 0.1.1 is affected. (#20)
|
|
792
|
+
|
|
793
|
+
```js
|
|
794
|
+
// Bare: loads again, nothing else changes
|
|
795
|
+
import { configureSdkStorage } from '@qvac/harness/sdk-storage'
|
|
796
|
+
```
|
|
279
797
|
|
|
280
798
|
## [0.1.1] - 2026-09-10
|
|
281
799
|
|
|
282
800
|
### Added
|
|
283
801
|
|
|
284
|
-
-
|
|
285
|
-
|
|
802
|
+
- The library surface ships compiled: `dist-lib/` carries JavaScript and a
|
|
803
|
+
`.d.ts` for every export, so `@qvac/harness` and its subpaths import from
|
|
804
|
+
npm under plain Node or Bare, with no TypeScript source or type-stripping
|
|
805
|
+
loader. Export keys are unchanged, `.ts`-suffixed deep paths included, so a
|
|
806
|
+
host that took the library from the repository switches to the package
|
|
807
|
+
without editing a specifier. Of `./lib/tools/*`, only the paths `qvac-core`
|
|
808
|
+
imports are published. (#18)
|
|
809
|
+
|
|
810
|
+
```js
|
|
811
|
+
import { harnessChild, spawnHarness } from '@qvac/harness/lib/spawn.ts'
|
|
812
|
+
import { AbortedError, raceAbort } from '@qvac/harness/race-abort'
|
|
813
|
+
```
|
|
286
814
|
|
|
287
815
|
## [0.1.0] - 2026-09-10
|
|
288
816
|
|
|
289
817
|
### Added
|
|
290
818
|
|
|
291
|
-
- First release of the QVAC engine as `@qvac/harness
|
|
292
|
-
`harness.bundle
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
819
|
+
- First release of the QVAC engine as `@qvac/harness`. The tarball is the
|
|
820
|
+
prebuilt `dist/harness.bundle`, one bundle for every platform, and the
|
|
821
|
+
generated `dist/harness.cjs` a host requires to spawn it as a bare-sidecar
|
|
822
|
+
process. It spawns and does not import: the export map points at library
|
|
823
|
+
source the tarball leaves out, so a host takes `spawnHarness`,
|
|
824
|
+
`harnessChild` and the transport from the repository until a compiled
|
|
825
|
+
surface ships. (#13)
|
|
826
|
+
|
|
827
|
+
```js
|
|
828
|
+
const harness = require('@qvac/harness/dist/harness.cjs')
|
|
829
|
+
|
|
830
|
+
const { ipc, sidecar, exit } = await harness.start(args)
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
- `dist/prebuilds-manifest.json` names every native prebuild directory the
|
|
834
|
+
bundle defers, keyed by its path relative to the bundle. The host assembles
|
|
835
|
+
those beside the bundle from its own `node_modules`, and installs the
|
|
836
|
+
`@qvac` inference addons and `pyodide` alongside. (#13)
|
|
837
|
+
- Skills come from `@qvac/skills` 0.1.2, a content-addressed bundle the
|
|
838
|
+
harness materializes at runtime. The Python wheels the tool runtime installs
|
|
839
|
+
ship in `wheels/`, each pinned by hash in `wheels/manifest.json`. (#13, #15)
|
|
840
|
+
- GPU stats are read through `bare-gpu-info`, so every dependency installs from
|
|
841
|
+
the public npm registry. (#12)
|
|
842
|
+
|
|
843
|
+
[Unreleased]: https://github.com/tetherto/qvac-harness/compare/v0.5.0...HEAD
|
|
844
|
+
[0.5.0]: https://github.com/tetherto/qvac-harness/compare/v0.4.0...v0.5.0
|
|
297
845
|
[0.4.0]: https://github.com/tetherto/qvac-harness/compare/v0.3.3...v0.4.0
|
|
298
846
|
[0.3.3]: https://github.com/tetherto/qvac-harness/compare/v0.3.2...v0.3.3
|
|
299
847
|
[0.3.2]: https://github.com/tetherto/qvac-harness/compare/v0.3.1...v0.3.2
|