@qvac/harness 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +740 -141
- package/README.md +75 -7
- package/dist/harness.bundle +72 -50
- package/dist-lib/index.d.ts +49 -39
- package/dist-lib/index.js +448 -161
- 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 +13 -32
- package/dist-lib/lib/connect.js +170 -112
- package/dist-lib/lib/context-window.js +25 -21
- package/dist-lib/lib/contract-types.d.ts +35 -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 +15 -20
- package/dist-lib/lib/models/model-catalog.js +66 -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 +48 -10
- package/dist-lib/lib/models/model-pool.js +106 -45
- 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 +122 -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 +39 -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 +9 -0
- package/dist-lib/spec/calls.js +3 -0
- package/dist-lib/spec/hrpc/hrpc.json +39 -0
- package/dist-lib/spec/hrpc/index.js +39 -3
- package/dist-lib/spec/hrpc/messages.js +105 -8
- package/dist-lib/spec/hyperschema/index.js +105 -8
- package/dist-lib/spec/hyperschema/schema.json +77 -1
- package/dist-lib/spec/hyperschema/types.d.ts +25 -1
- package/package.json +14 -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/dist-lib/lib/serve.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AbortController } from 'bare-abort-controller';
|
|
2
|
+
import b4a from 'b4a';
|
|
2
3
|
import fs from 'fs';
|
|
3
4
|
import { settleOnClose } from './rpc-session.js';
|
|
4
5
|
import { pipeline, Readable } from 'streamx';
|
|
@@ -7,7 +8,7 @@ import HRPC from '../spec/hrpc/index.js';
|
|
|
7
8
|
import { isCapability } from './capabilities/invoke.js';
|
|
8
9
|
import { CUSTOM_TASK_TYPES } from './models/model-catalog.js';
|
|
9
10
|
import { unsupportedKvCacheDeletion } from './capabilities/kv-cache.js';
|
|
10
|
-
import { errorMessage } from './
|
|
11
|
+
import { errorMessage } from './guards.js';
|
|
11
12
|
import { hlog } from './log.js';
|
|
12
13
|
import { pushQueue } from './queue.js';
|
|
13
14
|
import { TextCoalescer } from './helpers/text-coalescer.js';
|
|
@@ -45,6 +46,10 @@ export function serveHarness(stream, harness) {
|
|
|
45
46
|
await harness.setSkillSettings?.(entries ?? []);
|
|
46
47
|
return { ok: true };
|
|
47
48
|
},
|
|
49
|
+
setModelIdleTimeout: async ({ ms }) => {
|
|
50
|
+
await harness.setModelIdleTimeout?.(ms);
|
|
51
|
+
return { ok: true };
|
|
52
|
+
},
|
|
48
53
|
listManagedSkills: async () => ({
|
|
49
54
|
skills: (await harness.listManagedSkills?.()) ?? []
|
|
50
55
|
}),
|
|
@@ -55,8 +60,9 @@ export function serveHarness(stream, harness) {
|
|
|
55
60
|
: { found: false, content: '', origin: '' };
|
|
56
61
|
},
|
|
57
62
|
writeSkillFile: async ({ name, content }) => {
|
|
58
|
-
if (!harness.writeSkillFile)
|
|
63
|
+
if (!harness.writeSkillFile) {
|
|
59
64
|
return { ok: false };
|
|
65
|
+
}
|
|
60
66
|
return harness.writeSkillFile(name, content);
|
|
61
67
|
},
|
|
62
68
|
copySkillForEdit: async ({ name }) => ({
|
|
@@ -66,17 +72,21 @@ export function serveHarness(stream, harness) {
|
|
|
66
72
|
removed: (await harness.deleteOverlaySkill?.(name))?.removed ?? false
|
|
67
73
|
}),
|
|
68
74
|
setCustomModels: async ({ models }) => {
|
|
69
|
-
await harness.setCustomModels?.((models ?? []).flatMap((
|
|
75
|
+
await harness.setCustomModels?.((models ?? []).flatMap((model) => {
|
|
76
|
+
const { name, src, projectionSrc, taskType, expectedSize, label, contextLength } = model;
|
|
70
77
|
const known = CUSTOM_TASK_TYPES.find((task) => task === taskType);
|
|
71
|
-
if (taskType && !known)
|
|
78
|
+
if (taskType && !known) {
|
|
72
79
|
return [];
|
|
80
|
+
}
|
|
73
81
|
return [
|
|
74
82
|
{
|
|
75
83
|
name,
|
|
76
84
|
src,
|
|
77
85
|
projectionSrc: projectionSrc ?? undefined,
|
|
78
86
|
taskType: known,
|
|
79
|
-
expectedSize: expectedSize || undefined
|
|
87
|
+
expectedSize: expectedSize || undefined,
|
|
88
|
+
label: label || undefined,
|
|
89
|
+
contextLength: contextLength || undefined
|
|
80
90
|
}
|
|
81
91
|
];
|
|
82
92
|
}));
|
|
@@ -107,15 +117,17 @@ export function serveHarness(stream, harness) {
|
|
|
107
117
|
return packVectors(await harness.embed({ model, texts: texts ?? [] }));
|
|
108
118
|
},
|
|
109
119
|
transcribe: async ({ model, audio }) => {
|
|
110
|
-
if (!harness.transcribe)
|
|
120
|
+
if (!harness.transcribe) {
|
|
111
121
|
throw new Error('harness does not support transcribe');
|
|
122
|
+
}
|
|
112
123
|
return {
|
|
113
|
-
text: await harness.transcribe({ model: model ?? '', audio: audio ??
|
|
124
|
+
text: await harness.transcribe({ model: model ?? '', audio: audio ?? b4a.alloc(0) })
|
|
114
125
|
};
|
|
115
126
|
},
|
|
116
127
|
detectLanguage: async ({ text, topK }) => {
|
|
117
|
-
if (!harness.detectLanguage)
|
|
128
|
+
if (!harness.detectLanguage) {
|
|
118
129
|
throw new Error('harness does not support detectLanguage');
|
|
130
|
+
}
|
|
119
131
|
const languages = await harness.detectLanguage({
|
|
120
132
|
text: text ?? '',
|
|
121
133
|
topK: topK ?? undefined
|
|
@@ -129,13 +141,15 @@ export function serveHarness(stream, harness) {
|
|
|
129
141
|
},
|
|
130
142
|
speakStream: speakStream(harness),
|
|
131
143
|
getEmbeddingDevice: async () => {
|
|
132
|
-
if (!harness.getEmbeddingDevice)
|
|
144
|
+
if (!harness.getEmbeddingDevice) {
|
|
133
145
|
throw unsupportedEmbeddingDeviceControl();
|
|
146
|
+
}
|
|
134
147
|
return { device: await harness.getEmbeddingDevice() };
|
|
135
148
|
},
|
|
136
149
|
setEmbeddingDevice: async ({ device }) => {
|
|
137
|
-
if (!harness.setEmbeddingDevice)
|
|
150
|
+
if (!harness.setEmbeddingDevice) {
|
|
138
151
|
throw unsupportedEmbeddingDeviceControl();
|
|
152
|
+
}
|
|
139
153
|
await harness.setEmbeddingDevice(device);
|
|
140
154
|
return { ok: true };
|
|
141
155
|
},
|
|
@@ -150,7 +164,7 @@ export function serveHarness(stream, harness) {
|
|
|
150
164
|
}),
|
|
151
165
|
listModels: async (req) => ({
|
|
152
166
|
models: (await harness.list?.({
|
|
153
|
-
type:
|
|
167
|
+
type: req?.type || undefined,
|
|
154
168
|
cachedOnly: req?.cachedOnly ?? undefined,
|
|
155
169
|
fullRegistry: req?.fullRegistry ?? undefined
|
|
156
170
|
})) ?? []
|
|
@@ -165,16 +179,30 @@ export function serveHarness(stream, harness) {
|
|
|
165
179
|
models: (await harness.infraModels?.(profile ?? '')) ?? []
|
|
166
180
|
}),
|
|
167
181
|
capability: async () => {
|
|
168
|
-
if (!harness.capability)
|
|
182
|
+
if (!harness.capability) {
|
|
169
183
|
throw new Error('harness does not support capability');
|
|
184
|
+
}
|
|
170
185
|
return harness.capability();
|
|
171
186
|
},
|
|
172
187
|
warmModel: async ({ model, agent }) => {
|
|
173
|
-
if (!harness.warmModel)
|
|
188
|
+
if (!harness.warmModel) {
|
|
174
189
|
throw new Error('harness does not support warmModel');
|
|
190
|
+
}
|
|
175
191
|
await harness.warmModel({ model, agent: agent ?? {} });
|
|
176
192
|
return { ok: true };
|
|
177
193
|
},
|
|
194
|
+
loadModel: async ({ model, agent }) => {
|
|
195
|
+
if (!harness.loadModel) {
|
|
196
|
+
throw new Error('harness does not support loadModel');
|
|
197
|
+
}
|
|
198
|
+
return harness.loadModel({ model, agent: agent ?? {} });
|
|
199
|
+
},
|
|
200
|
+
unloadModel: async ({ model }) => {
|
|
201
|
+
if (!harness.unloadModel) {
|
|
202
|
+
throw new Error('harness does not support unloadModel');
|
|
203
|
+
}
|
|
204
|
+
return harness.unloadModel({ model });
|
|
205
|
+
},
|
|
178
206
|
modelEvents: () => modelEvents(harness),
|
|
179
207
|
completion: completion(harness)
|
|
180
208
|
});
|
|
@@ -199,19 +227,15 @@ function toWireSkill(skill) {
|
|
|
199
227
|
allowList: skill.allowList,
|
|
200
228
|
platforms: skill.platform,
|
|
201
229
|
status: skill.status,
|
|
202
|
-
|
|
203
|
-
? {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
...(skill.setup ? { setup: skill.setup, ...(setupV2 ? { setupV2 } : {}) } : {}),
|
|
212
|
-
...(skill.stub !== undefined ? { stub: skill.stub } : {}),
|
|
213
|
-
...(skill.minCapabilityProfile ? { minCapabilityProfile: skill.minCapabilityProfile } : {}),
|
|
214
|
-
...(skill.emoji ? { emoji: skill.emoji } : {})
|
|
230
|
+
requirements: skill.requires
|
|
231
|
+
? { binaries, credentials: skill.requires.credentials ?? [] }
|
|
232
|
+
: undefined,
|
|
233
|
+
install: skill.install || undefined,
|
|
234
|
+
setup: skill.setup || undefined,
|
|
235
|
+
setupV2,
|
|
236
|
+
stub: skill.stub,
|
|
237
|
+
minCapabilityProfile: skill.minCapabilityProfile || undefined,
|
|
238
|
+
emoji: skill.emoji || undefined
|
|
215
239
|
};
|
|
216
240
|
}
|
|
217
241
|
function unsupportedEmbeddingDeviceControl() {
|
|
@@ -246,8 +270,9 @@ function modelEvents(harness) {
|
|
|
246
270
|
catalogChanged,
|
|
247
271
|
...loaded.map((entry) => `${entry.model}:${entry.refs}`)
|
|
248
272
|
].join('\0');
|
|
249
|
-
if (key === last)
|
|
273
|
+
if (key === last) {
|
|
250
274
|
return;
|
|
275
|
+
}
|
|
251
276
|
last = key;
|
|
252
277
|
const frame = {
|
|
253
278
|
status: state.status,
|
|
@@ -285,16 +310,18 @@ function modelEvents(harness) {
|
|
|
285
310
|
}
|
|
286
311
|
function speakStream(harness) {
|
|
287
312
|
return async function* (duplex) {
|
|
288
|
-
if (!harness.speakStream)
|
|
313
|
+
if (!harness.speakStream) {
|
|
289
314
|
throw new Error('harness does not support speakStream');
|
|
315
|
+
}
|
|
290
316
|
const text = pushQueue();
|
|
291
317
|
const end = () => text.end();
|
|
292
318
|
duplex.on('close', end);
|
|
293
319
|
duplex.on('end', end);
|
|
294
320
|
duplex.readStream?.on('close', end);
|
|
295
321
|
duplex.on('data', (frame) => {
|
|
296
|
-
if (frame.text)
|
|
322
|
+
if (frame.text) {
|
|
297
323
|
text.push(frame.text);
|
|
324
|
+
}
|
|
298
325
|
});
|
|
299
326
|
for await (const chunk of harness.speakStream({ model: '', text })) {
|
|
300
327
|
yield { audio: chunk.audio, sampleRate: chunk.sampleRate };
|
|
@@ -302,13 +329,15 @@ function speakStream(harness) {
|
|
|
302
329
|
};
|
|
303
330
|
}
|
|
304
331
|
function runFrame(harness, frame, context) {
|
|
305
|
-
if (frame.type !== 'invoke')
|
|
332
|
+
if (frame.type !== 'invoke') {
|
|
306
333
|
return harness.completion(context);
|
|
334
|
+
}
|
|
307
335
|
if (!harness.invoke)
|
|
308
336
|
throw new Error('harness does not support invoke');
|
|
309
337
|
const capability = frame.capability ?? '';
|
|
310
|
-
if (!isCapability(capability))
|
|
338
|
+
if (!isCapability(capability)) {
|
|
311
339
|
throw new Error(`unknown capability: ${capability}`);
|
|
340
|
+
}
|
|
312
341
|
return harness.invoke({
|
|
313
342
|
capability,
|
|
314
343
|
args: frame.arguments ?? {},
|
|
@@ -334,26 +363,31 @@ function completion(harness) {
|
|
|
334
363
|
let started = false;
|
|
335
364
|
const gone = () => {
|
|
336
365
|
controller.abort('client gone');
|
|
337
|
-
for (const resolve of pending.values())
|
|
366
|
+
for (const resolve of pending.values()) {
|
|
338
367
|
resolve(false);
|
|
368
|
+
}
|
|
339
369
|
pending.clear();
|
|
340
370
|
for (const { reject, staged } of pendingAttach.values()) {
|
|
341
371
|
void fs.promises.unlink(staged).catch(noop);
|
|
342
372
|
reject(new Error('client gone'));
|
|
343
373
|
}
|
|
344
374
|
pendingAttach.clear();
|
|
345
|
-
for (const { reject } of pendingFetch.values())
|
|
375
|
+
for (const { reject } of pendingFetch.values()) {
|
|
346
376
|
reject(new Error('client gone'));
|
|
377
|
+
}
|
|
347
378
|
pendingFetch.clear();
|
|
348
|
-
for (const { reject } of pendingRag.values())
|
|
379
|
+
for (const { reject } of pendingRag.values()) {
|
|
349
380
|
reject(new Error('client gone'));
|
|
381
|
+
}
|
|
350
382
|
pendingRag.clear();
|
|
351
|
-
for (const { resolve } of pendingQuestion.values())
|
|
383
|
+
for (const { resolve } of pendingQuestion.values()) {
|
|
352
384
|
resolve(null);
|
|
385
|
+
}
|
|
353
386
|
pendingQuestion.clear();
|
|
354
387
|
for (const { reject, path } of pendingDrive.values()) {
|
|
355
|
-
if (path)
|
|
388
|
+
if (path) {
|
|
356
389
|
void fs.promises.unlink(path).catch(noop);
|
|
390
|
+
}
|
|
357
391
|
reject(new Error('client gone'));
|
|
358
392
|
}
|
|
359
393
|
pendingDrive.clear();
|
|
@@ -364,35 +398,43 @@ function completion(harness) {
|
|
|
364
398
|
duplex.readStream?.on('close', gone);
|
|
365
399
|
duplex.on('data', (frame) => {
|
|
366
400
|
if (frame.type === 'decision') {
|
|
367
|
-
if (typeof frame.callId !== 'string')
|
|
401
|
+
if (typeof frame.callId !== 'string') {
|
|
368
402
|
return;
|
|
403
|
+
}
|
|
369
404
|
pending.get(frame.callId)?.(frame.approved === true);
|
|
370
405
|
pending.delete(frame.callId);
|
|
371
406
|
return;
|
|
372
407
|
}
|
|
373
408
|
if (frame.type === 'attach-result') {
|
|
374
|
-
if (typeof frame.callId !== 'string')
|
|
409
|
+
if (typeof frame.callId !== 'string') {
|
|
375
410
|
return;
|
|
411
|
+
}
|
|
376
412
|
const waiter = pendingAttach.get(frame.callId);
|
|
377
413
|
pendingAttach.delete(frame.callId);
|
|
378
|
-
if (!waiter)
|
|
414
|
+
if (!waiter) {
|
|
379
415
|
return;
|
|
416
|
+
}
|
|
380
417
|
void fs.promises.unlink(waiter.staged).catch(noop);
|
|
381
|
-
if (frame.attachment)
|
|
418
|
+
if (frame.attachment) {
|
|
382
419
|
waiter.resolve(frame.attachment);
|
|
383
|
-
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
384
422
|
waiter.reject(new Error(frame.message || 'the host failed to store the attachment'));
|
|
423
|
+
}
|
|
385
424
|
return;
|
|
386
425
|
}
|
|
387
426
|
if (frame.type === 'fetch-result') {
|
|
388
|
-
if (typeof frame.callId !== 'string')
|
|
427
|
+
if (typeof frame.callId !== 'string') {
|
|
389
428
|
return;
|
|
429
|
+
}
|
|
390
430
|
const waiter = pendingFetch.get(frame.callId);
|
|
391
431
|
pendingFetch.delete(frame.callId);
|
|
392
|
-
if (!waiter)
|
|
432
|
+
if (!waiter) {
|
|
393
433
|
return;
|
|
394
|
-
|
|
434
|
+
}
|
|
435
|
+
if (frame.message) {
|
|
395
436
|
return waiter.reject(new Error(frame.message));
|
|
437
|
+
}
|
|
396
438
|
const staged = frame.path;
|
|
397
439
|
const { fileName, mimeType, attachmentId, size } = frame;
|
|
398
440
|
if (frame.meta === true) {
|
|
@@ -403,8 +445,9 @@ function completion(harness) {
|
|
|
403
445
|
byteLength: typeof size === 'number' ? size : 0
|
|
404
446
|
});
|
|
405
447
|
}
|
|
406
|
-
if (!staged)
|
|
448
|
+
if (!staged) {
|
|
407
449
|
return waiter.resolve(null);
|
|
450
|
+
}
|
|
408
451
|
fs.promises.readFile(staged).then((bytes) => waiter.resolve({
|
|
409
452
|
id: attachmentId ?? '',
|
|
410
453
|
fileName: fileName ?? '',
|
|
@@ -415,42 +458,52 @@ function completion(harness) {
|
|
|
415
458
|
return;
|
|
416
459
|
}
|
|
417
460
|
if (frame.type === 'rag-result') {
|
|
418
|
-
if (typeof frame.callId !== 'string')
|
|
461
|
+
if (typeof frame.callId !== 'string') {
|
|
419
462
|
return;
|
|
463
|
+
}
|
|
420
464
|
const waiter = pendingRag.get(frame.callId);
|
|
421
465
|
pendingRag.delete(frame.callId);
|
|
422
|
-
if (!waiter)
|
|
466
|
+
if (!waiter) {
|
|
423
467
|
return;
|
|
424
|
-
|
|
468
|
+
}
|
|
469
|
+
if (frame.message) {
|
|
425
470
|
return waiter.reject(new Error(frame.message));
|
|
471
|
+
}
|
|
426
472
|
waiter.resolve(frame.result ?? { enabled: false, indexEmpty: true, hasKnowledge: false, hits: [] });
|
|
427
473
|
}
|
|
428
474
|
if (frame.type === 'question-result') {
|
|
429
|
-
if (typeof frame.callId !== 'string')
|
|
475
|
+
if (typeof frame.callId !== 'string') {
|
|
430
476
|
return;
|
|
477
|
+
}
|
|
431
478
|
const waiter = pendingQuestion.get(frame.callId);
|
|
432
479
|
pendingQuestion.delete(frame.callId);
|
|
433
|
-
if (!waiter)
|
|
480
|
+
if (!waiter) {
|
|
434
481
|
return;
|
|
435
|
-
|
|
482
|
+
}
|
|
483
|
+
if (frame.message) {
|
|
436
484
|
hlog('question failed', { callId: frame.callId, message: frame.message });
|
|
485
|
+
}
|
|
437
486
|
waiter.resolve(frame.answer || null);
|
|
438
487
|
return;
|
|
439
488
|
}
|
|
440
489
|
if (frame.type === 'drive-result') {
|
|
441
|
-
if (typeof frame.callId !== 'string')
|
|
490
|
+
if (typeof frame.callId !== 'string') {
|
|
442
491
|
return;
|
|
492
|
+
}
|
|
443
493
|
const waiter = pendingDrive.get(frame.callId);
|
|
444
494
|
pendingDrive.delete(frame.callId);
|
|
445
|
-
if (!waiter)
|
|
495
|
+
if (!waiter) {
|
|
446
496
|
return;
|
|
447
|
-
|
|
497
|
+
}
|
|
498
|
+
if (frame.message) {
|
|
448
499
|
return waiter.reject(new Error(frame.message));
|
|
449
|
-
|
|
500
|
+
}
|
|
501
|
+
waiter.resolve(frame.payload ?? {});
|
|
450
502
|
return;
|
|
451
503
|
}
|
|
452
|
-
if (started || (frame.type !== 'start' && frame.type !== 'invoke'))
|
|
504
|
+
if (started || (frame.type !== 'start' && frame.type !== 'invoke')) {
|
|
453
505
|
return;
|
|
506
|
+
}
|
|
454
507
|
started = true;
|
|
455
508
|
const context = {
|
|
456
509
|
model: frame.model ?? '',
|
|
@@ -463,9 +516,9 @@ function completion(harness) {
|
|
|
463
516
|
messages: (frame.messages ?? []).map(({ role, content, attachments }) => ({
|
|
464
517
|
role,
|
|
465
518
|
content,
|
|
466
|
-
|
|
467
|
-
?
|
|
468
|
-
:
|
|
519
|
+
attachments: attachments?.length
|
|
520
|
+
? attachments.map(({ path: file }) => ({ path: file }))
|
|
521
|
+
: undefined
|
|
469
522
|
})),
|
|
470
523
|
toolConfigs: frame.toolConfigs ?? undefined,
|
|
471
524
|
grantedDirs: frame.grantedDirs ?? undefined,
|
|
@@ -483,7 +536,7 @@ function completion(harness) {
|
|
|
483
536
|
pendingDrive.set(callId, {
|
|
484
537
|
resolve,
|
|
485
538
|
reject,
|
|
486
|
-
|
|
539
|
+
path: request.op === 'write' ? request.path : undefined
|
|
487
540
|
});
|
|
488
541
|
events.push({ type: 'drive-ask', callId, op: request.op, payload: request });
|
|
489
542
|
})
|
|
@@ -532,7 +585,7 @@ function completion(harness) {
|
|
|
532
585
|
type: 'fetch-ask',
|
|
533
586
|
callId,
|
|
534
587
|
attachmentId,
|
|
535
|
-
|
|
588
|
+
meta: meta === true ? true : undefined
|
|
536
589
|
});
|
|
537
590
|
}),
|
|
538
591
|
ragSearch: ({ query, limit }) => controller.signal.aborted
|
|
@@ -627,6 +680,13 @@ function downloadStream(harness, req) {
|
|
|
627
680
|
pendingProgress = null;
|
|
628
681
|
lastProgressAt = at;
|
|
629
682
|
};
|
|
683
|
+
const queueProgress = (wire) => {
|
|
684
|
+
const now = Date.now();
|
|
685
|
+
pendingProgress = wire;
|
|
686
|
+
if (lastProgressAt === 0 || now - lastProgressAt >= DOWNLOAD_PROGRESS_INTERVAL_MS) {
|
|
687
|
+
flushProgress(now);
|
|
688
|
+
}
|
|
689
|
+
};
|
|
630
690
|
try {
|
|
631
691
|
for await (const event of harness.download({
|
|
632
692
|
model: req.model,
|
|
@@ -636,11 +696,7 @@ function downloadStream(harness, req) {
|
|
|
636
696
|
})) {
|
|
637
697
|
const wire = toDownloadWire(event);
|
|
638
698
|
if (wire.type === 'progress') {
|
|
639
|
-
|
|
640
|
-
pendingProgress = wire;
|
|
641
|
-
if (lastProgressAt === 0 || now - lastProgressAt >= DOWNLOAD_PROGRESS_INTERVAL_MS) {
|
|
642
|
-
flushProgress(now);
|
|
643
|
-
}
|
|
699
|
+
queueProgress(wire);
|
|
644
700
|
continue;
|
|
645
701
|
}
|
|
646
702
|
flushProgress();
|
package/dist-lib/lib/spawn.js
CHANGED
|
@@ -3,8 +3,9 @@ import { connectHarness } from './connect.js';
|
|
|
3
3
|
import { setHarnessLog } from './log.js';
|
|
4
4
|
import { createEmbedConfig } from './models/embed-config.js';
|
|
5
5
|
export function harnessChild(entry, opts) {
|
|
6
|
-
if (opts.logger)
|
|
6
|
+
if (opts.logger) {
|
|
7
7
|
setHarnessLog(true);
|
|
8
|
+
}
|
|
8
9
|
const { runner, skillsDir, gpuStats, wheelsDir, sdkStorage, embedDevice, embedGpuLayers, embedBatchSize, imageModel, imageClamps, musicModel, musicClamps, llm, onIpc, logger = null, ...spec } = opts;
|
|
9
10
|
const embedConfig = createEmbedConfig({
|
|
10
11
|
device: embedDevice,
|
|
@@ -36,8 +37,9 @@ export function harnessChild(entry, opts) {
|
|
|
36
37
|
}
|
|
37
38
|
export function spawnHarness(opts) {
|
|
38
39
|
const logger = opts.logger ?? null;
|
|
39
|
-
if (logger)
|
|
40
|
+
if (logger) {
|
|
40
41
|
setHarnessLog(true);
|
|
42
|
+
}
|
|
41
43
|
const embedConfig = createEmbedConfig({
|
|
42
44
|
device: opts.embedDevice,
|
|
43
45
|
gpuLayers: opts.embedGpuLayers,
|
|
@@ -45,7 +47,7 @@ export function spawnHarness(opts) {
|
|
|
45
47
|
});
|
|
46
48
|
return connectHarness(async () => {
|
|
47
49
|
const { default: Sidecar } = await import('bare-sidecar');
|
|
48
|
-
const { wrap } =
|
|
50
|
+
const { wrap } = await import('bare-stow/host');
|
|
49
51
|
const sidecar = new Sidecar(opts.entry, [
|
|
50
52
|
opts.skillsDir ?? '',
|
|
51
53
|
JSON.stringify(opts.sdkStorage),
|
|
@@ -66,8 +68,9 @@ export function spawnHarness(opts) {
|
|
|
66
68
|
sidecar.once('exit', (code) => resolve(`harness exited (${code}) before ready`));
|
|
67
69
|
});
|
|
68
70
|
const failure = await Promise.race([ipc.ready.then(() => null), died]);
|
|
69
|
-
if (failure)
|
|
71
|
+
if (failure) {
|
|
70
72
|
throw new Error(failure);
|
|
73
|
+
}
|
|
71
74
|
return ipc;
|
|
72
75
|
});
|
|
73
76
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
export interface TmpOwner {
|
|
2
|
+
uid: number;
|
|
3
|
+
username: string;
|
|
4
|
+
}
|
|
5
|
+
export interface UserTmpOptions {
|
|
6
|
+
readonly readOwner?: () => TmpOwner;
|
|
7
|
+
}
|
|
7
8
|
export declare function userTmpPath(name: string, opts?: UserTmpOptions): string;
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { canonicalDenyRoots, defaultRealpath, resolveKey } from './fs/scope.js';
|
|
2
2
|
import { writeText } from './fs/write.js';
|
|
3
|
+
import { errorMessage } from '../guards.js';
|
|
3
4
|
function documentRequests(raw) {
|
|
4
5
|
if (!Array.isArray(raw) || raw.length === 0)
|
|
5
6
|
return null;
|
|
6
7
|
const documents = [];
|
|
7
8
|
for (const entry of raw) {
|
|
8
|
-
|
|
9
|
-
if (typeof record?.path !== 'string' || !record.path.trim())
|
|
9
|
+
if (!isDocumentRequest(entry))
|
|
10
10
|
return null;
|
|
11
|
-
|
|
12
|
-
return null;
|
|
13
|
-
documents.push({ path: record.path, content: record.content });
|
|
11
|
+
documents.push({ path: entry.path, content: entry.content });
|
|
14
12
|
}
|
|
15
13
|
return documents;
|
|
16
14
|
}
|
|
15
|
+
function isDocumentRequest(value) {
|
|
16
|
+
if (typeof value !== 'object' || value === null)
|
|
17
|
+
return false;
|
|
18
|
+
if (!('path' in value) || typeof value.path !== 'string' || !value.path.trim()) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return 'content' in value && typeof value.content === 'string';
|
|
22
|
+
}
|
|
17
23
|
export function createCreateDocumentsTool({ realpath = defaultRealpath, deniedRoots } = {}) {
|
|
18
24
|
return {
|
|
19
25
|
name: 'create_documents',
|
|
@@ -48,8 +54,9 @@ export function createCreateDocumentsTool({ realpath = defaultRealpath, deniedRo
|
|
|
48
54
|
},
|
|
49
55
|
resource(args) {
|
|
50
56
|
const documents = documentRequests(args.documents);
|
|
51
|
-
if (!documents)
|
|
57
|
+
if (!documents) {
|
|
52
58
|
return null;
|
|
59
|
+
}
|
|
53
60
|
const bytes = documents.reduce((sum, doc) => sum + doc.content.length, 0);
|
|
54
61
|
const first = documents[0];
|
|
55
62
|
const suffix = documents.length > 1 ? ` (+${documents.length - 1} more)` : '';
|
|
@@ -58,13 +65,14 @@ export function createCreateDocumentsTool({ realpath = defaultRealpath, deniedRo
|
|
|
58
65
|
async execute(rawArgs, ctx) {
|
|
59
66
|
const roots = ctx?.fsWriteRoots ?? [];
|
|
60
67
|
const documents = documentRequests(rawArgs.documents);
|
|
61
|
-
if (!documents)
|
|
68
|
+
if (!documents) {
|
|
62
69
|
return { error: 'documents must be a non-empty array of { path, content }' };
|
|
70
|
+
}
|
|
63
71
|
const created = [];
|
|
64
72
|
const failed = [];
|
|
65
73
|
const deny = await canonicalDenyRoots(deniedRoots, realpath);
|
|
66
74
|
for (const doc of documents) {
|
|
67
|
-
const resolved = await resolveKey(doc.path, roots, realpath, deny);
|
|
75
|
+
const resolved = await resolveKey(doc.path, roots, { realpath, deny });
|
|
68
76
|
if ('error' in resolved) {
|
|
69
77
|
failed.push({ path: doc.path, error: resolved.error });
|
|
70
78
|
continue;
|
|
@@ -78,10 +86,10 @@ export function createCreateDocumentsTool({ realpath = defaultRealpath, deniedRo
|
|
|
78
86
|
created.push(doc.path);
|
|
79
87
|
}
|
|
80
88
|
catch (err) {
|
|
81
|
-
failed.push({ path: doc.path, error: err
|
|
89
|
+
failed.push({ path: doc.path, error: errorMessage(err) });
|
|
82
90
|
}
|
|
83
91
|
}
|
|
84
|
-
return { created,
|
|
92
|
+
return { created, failed: failed.length ? failed : undefined };
|
|
85
93
|
}
|
|
86
94
|
};
|
|
87
95
|
}
|
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
2
|
import { canonicalDenyRoots, defaultRealpath, resolveKey } from './fs/scope.js';
|
|
3
3
|
import { writeText } from './fs/write.js';
|
|
4
|
+
import { errorMessage } from '../guards.js';
|
|
4
5
|
function editRequests(raw) {
|
|
5
6
|
if (!Array.isArray(raw) || raw.length === 0)
|
|
6
7
|
return null;
|
|
7
8
|
const edits = [];
|
|
8
9
|
for (const entry of raw) {
|
|
9
|
-
|
|
10
|
-
if (typeof record?.path !== 'string' || !record.path.trim())
|
|
10
|
+
if (!isEditRequest(entry))
|
|
11
11
|
return null;
|
|
12
|
-
|
|
13
|
-
return null;
|
|
14
|
-
if (typeof record.replace !== 'string')
|
|
15
|
-
return null;
|
|
16
|
-
edits.push({ path: record.path, search: record.search, replace: record.replace });
|
|
12
|
+
edits.push({ path: entry.path, search: entry.search, replace: entry.replace });
|
|
17
13
|
}
|
|
18
14
|
return edits;
|
|
19
15
|
}
|
|
16
|
+
function isEditRequest(value) {
|
|
17
|
+
if (typeof value !== 'object' || value === null)
|
|
18
|
+
return false;
|
|
19
|
+
if (!('path' in value) || typeof value.path !== 'string' || !value.path.trim()) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (!('search' in value) || typeof value.search !== 'string' || !value.search) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return 'replace' in value && typeof value.replace === 'string';
|
|
26
|
+
}
|
|
20
27
|
function applyEdits(content, edits) {
|
|
21
28
|
let next = content;
|
|
22
29
|
for (const edit of edits) {
|
|
23
30
|
const first = next.indexOf(edit.search);
|
|
24
|
-
if (first === -1)
|
|
31
|
+
if (first === -1) {
|
|
25
32
|
return { error: 'search text not found' };
|
|
33
|
+
}
|
|
26
34
|
if (next.indexOf(edit.search, first + edit.search.length) !== -1) {
|
|
27
35
|
return { error: 'search text is ambiguous (matches more than once)' };
|
|
28
36
|
}
|
|
@@ -66,8 +74,9 @@ export function createEditDocumentsTool({ realpath = defaultRealpath, deniedRoot
|
|
|
66
74
|
},
|
|
67
75
|
resource(args) {
|
|
68
76
|
const edits = editRequests(args.edits);
|
|
69
|
-
if (!edits)
|
|
77
|
+
if (!edits) {
|
|
70
78
|
return null;
|
|
79
|
+
}
|
|
71
80
|
const paths = [...new Set(edits.map((edit) => edit.path))];
|
|
72
81
|
const label = paths.slice(0, 3).join(', ');
|
|
73
82
|
return {
|
|
@@ -78,8 +87,9 @@ export function createEditDocumentsTool({ realpath = defaultRealpath, deniedRoot
|
|
|
78
87
|
async execute(rawArgs, ctx) {
|
|
79
88
|
const roots = ctx?.fsWriteRoots ?? [];
|
|
80
89
|
const edits = editRequests(rawArgs.edits);
|
|
81
|
-
if (!edits)
|
|
90
|
+
if (!edits) {
|
|
82
91
|
return { error: 'edits must be a non-empty array of { path, search, replace }' };
|
|
92
|
+
}
|
|
83
93
|
const byPath = new Map();
|
|
84
94
|
for (const edit of edits) {
|
|
85
95
|
const list = byPath.get(edit.path) ?? [];
|
|
@@ -90,7 +100,7 @@ export function createEditDocumentsTool({ realpath = defaultRealpath, deniedRoot
|
|
|
90
100
|
const failed = [];
|
|
91
101
|
const deny = await canonicalDenyRoots(deniedRoots, realpath);
|
|
92
102
|
for (const [file, fileEdits] of byPath) {
|
|
93
|
-
const resolved = await resolveKey(file, roots, realpath, deny);
|
|
103
|
+
const resolved = await resolveKey(file, roots, { realpath, deny });
|
|
94
104
|
if ('error' in resolved) {
|
|
95
105
|
failed.push({ path: file, error: resolved.error });
|
|
96
106
|
continue;
|
|
@@ -113,10 +123,10 @@ export function createEditDocumentsTool({ realpath = defaultRealpath, deniedRoot
|
|
|
113
123
|
edited.push(file);
|
|
114
124
|
}
|
|
115
125
|
catch (err) {
|
|
116
|
-
failed.push({ path: file, error: err
|
|
126
|
+
failed.push({ path: file, error: errorMessage(err) });
|
|
117
127
|
}
|
|
118
128
|
}
|
|
119
|
-
return { edited,
|
|
129
|
+
return { edited, failed: failed.length ? failed : undefined };
|
|
120
130
|
}
|
|
121
131
|
};
|
|
122
132
|
}
|