@qvac/harness 0.3.3 → 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 +700 -126
- package/README.md +156 -0
- package/dist/harness.bundle +71 -103
- package/dist/prebuilds-manifest.json +0 -8
- package/dist-lib/index.d.ts +42 -40
- package/dist-lib/index.js +307 -117
- 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 +194 -135
- 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 +69 -0
- package/dist-lib/lib/capabilities/preview-model-source.js +439 -0
- 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 +15 -37
- package/dist-lib/lib/connect.js +161 -112
- package/dist-lib/lib/context-window.js +25 -21
- package/dist-lib/lib/contract-types.d.ts +26 -20
- 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 +13 -0
- package/dist-lib/lib/models/custom-model-cache.js +70 -0
- package/dist-lib/lib/models/gguf-header.d.ts +31 -0
- package/dist-lib/lib/models/gguf-header.js +230 -0
- 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 +19 -21
- package/dist-lib/lib/models/model-catalog.js +66 -33
- package/dist-lib/lib/models/model-delete.d.ts +4 -1
- package/dist-lib/lib/models/model-delete.js +30 -15
- package/dist-lib/lib/models/model-handle.js +2 -1
- package/dist-lib/lib/models/model-list.d.ts +14 -3
- package/dist-lib/lib/models/model-list.js +33 -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 +11 -6
- 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 +128 -68
- 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 +55 -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 +6 -0
- package/dist-lib/spec/calls.js +2 -0
- package/dist-lib/spec/hrpc/hrpc.json +26 -0
- package/dist-lib/spec/hrpc/index.js +27 -3
- package/dist-lib/spec/hrpc/messages.js +207 -8
- package/dist-lib/spec/hyperschema/index.js +207 -8
- package/dist-lib/spec/hyperschema/schema.json +165 -1
- package/dist-lib/spec/hyperschema/types.d.ts +42 -1
- package/package.json +13 -14
- package/worker/harness-sidecar.mjs +16 -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,12 +1,14 @@
|
|
|
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';
|
|
5
6
|
import { bindApi } from '../spec/bind.js';
|
|
6
7
|
import HRPC from '../spec/hrpc/index.js';
|
|
7
8
|
import { isCapability } from './capabilities/invoke.js';
|
|
9
|
+
import { CUSTOM_TASK_TYPES } from './models/model-catalog.js';
|
|
8
10
|
import { unsupportedKvCacheDeletion } from './capabilities/kv-cache.js';
|
|
9
|
-
import { errorMessage } from './
|
|
11
|
+
import { errorMessage } from './guards.js';
|
|
10
12
|
import { hlog } from './log.js';
|
|
11
13
|
import { pushQueue } from './queue.js';
|
|
12
14
|
import { TextCoalescer } from './helpers/text-coalescer.js';
|
|
@@ -44,6 +46,10 @@ export function serveHarness(stream, harness) {
|
|
|
44
46
|
await harness.setSkillSettings?.(entries ?? []);
|
|
45
47
|
return { ok: true };
|
|
46
48
|
},
|
|
49
|
+
setModelIdleTimeout: async ({ ms }) => {
|
|
50
|
+
await harness.setModelIdleTimeout?.(ms);
|
|
51
|
+
return { ok: true };
|
|
52
|
+
},
|
|
47
53
|
listManagedSkills: async () => ({
|
|
48
54
|
skills: (await harness.listManagedSkills?.()) ?? []
|
|
49
55
|
}),
|
|
@@ -54,8 +60,9 @@ export function serveHarness(stream, harness) {
|
|
|
54
60
|
: { found: false, content: '', origin: '' };
|
|
55
61
|
},
|
|
56
62
|
writeSkillFile: async ({ name, content }) => {
|
|
57
|
-
if (!harness.writeSkillFile)
|
|
63
|
+
if (!harness.writeSkillFile) {
|
|
58
64
|
return { ok: false };
|
|
65
|
+
}
|
|
59
66
|
return harness.writeSkillFile(name, content);
|
|
60
67
|
},
|
|
61
68
|
copySkillForEdit: async ({ name }) => ({
|
|
@@ -65,13 +72,32 @@ export function serveHarness(stream, harness) {
|
|
|
65
72
|
removed: (await harness.deleteOverlaySkill?.(name))?.removed ?? false
|
|
66
73
|
}),
|
|
67
74
|
setCustomModels: async ({ models }) => {
|
|
68
|
-
await harness.setCustomModels?.((models ?? []).
|
|
69
|
-
name,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
await harness.setCustomModels?.((models ?? []).flatMap((model) => {
|
|
76
|
+
const { name, src, projectionSrc, taskType, expectedSize, label, contextLength } = model;
|
|
77
|
+
const known = CUSTOM_TASK_TYPES.find((task) => task === taskType);
|
|
78
|
+
if (taskType && !known) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
return [
|
|
82
|
+
{
|
|
83
|
+
name,
|
|
84
|
+
src,
|
|
85
|
+
projectionSrc: projectionSrc ?? undefined,
|
|
86
|
+
taskType: known,
|
|
87
|
+
expectedSize: expectedSize || undefined,
|
|
88
|
+
label: label || undefined,
|
|
89
|
+
contextLength: contextLength || undefined
|
|
90
|
+
}
|
|
91
|
+
];
|
|
92
|
+
}));
|
|
73
93
|
return { ok: true };
|
|
74
94
|
},
|
|
95
|
+
previewModelSource: async ({ src, mmprojSrc }) => {
|
|
96
|
+
if (!harness.previewModelSource) {
|
|
97
|
+
throw new Error('harness does not support previewModelSource');
|
|
98
|
+
}
|
|
99
|
+
return harness.previewModelSource({ src: src ?? '', mmprojSrc: mmprojSrc ?? undefined });
|
|
100
|
+
},
|
|
75
101
|
describe: async () => (await harness.describe?.()) ?? {
|
|
76
102
|
toolNames: [],
|
|
77
103
|
skills: [],
|
|
@@ -91,15 +117,17 @@ export function serveHarness(stream, harness) {
|
|
|
91
117
|
return packVectors(await harness.embed({ model, texts: texts ?? [] }));
|
|
92
118
|
},
|
|
93
119
|
transcribe: async ({ model, audio }) => {
|
|
94
|
-
if (!harness.transcribe)
|
|
120
|
+
if (!harness.transcribe) {
|
|
95
121
|
throw new Error('harness does not support transcribe');
|
|
122
|
+
}
|
|
96
123
|
return {
|
|
97
|
-
text: await harness.transcribe({ model: model ?? '', audio: audio ??
|
|
124
|
+
text: await harness.transcribe({ model: model ?? '', audio: audio ?? b4a.alloc(0) })
|
|
98
125
|
};
|
|
99
126
|
},
|
|
100
127
|
detectLanguage: async ({ text, topK }) => {
|
|
101
|
-
if (!harness.detectLanguage)
|
|
128
|
+
if (!harness.detectLanguage) {
|
|
102
129
|
throw new Error('harness does not support detectLanguage');
|
|
130
|
+
}
|
|
103
131
|
const languages = await harness.detectLanguage({
|
|
104
132
|
text: text ?? '',
|
|
105
133
|
topK: topK ?? undefined
|
|
@@ -113,13 +141,15 @@ export function serveHarness(stream, harness) {
|
|
|
113
141
|
},
|
|
114
142
|
speakStream: speakStream(harness),
|
|
115
143
|
getEmbeddingDevice: async () => {
|
|
116
|
-
if (!harness.getEmbeddingDevice)
|
|
144
|
+
if (!harness.getEmbeddingDevice) {
|
|
117
145
|
throw unsupportedEmbeddingDeviceControl();
|
|
146
|
+
}
|
|
118
147
|
return { device: await harness.getEmbeddingDevice() };
|
|
119
148
|
},
|
|
120
149
|
setEmbeddingDevice: async ({ device }) => {
|
|
121
|
-
if (!harness.setEmbeddingDevice)
|
|
150
|
+
if (!harness.setEmbeddingDevice) {
|
|
122
151
|
throw unsupportedEmbeddingDeviceControl();
|
|
152
|
+
}
|
|
123
153
|
await harness.setEmbeddingDevice(device);
|
|
124
154
|
return { ok: true };
|
|
125
155
|
},
|
|
@@ -134,7 +164,7 @@ export function serveHarness(stream, harness) {
|
|
|
134
164
|
}),
|
|
135
165
|
listModels: async (req) => ({
|
|
136
166
|
models: (await harness.list?.({
|
|
137
|
-
type:
|
|
167
|
+
type: req?.type || undefined,
|
|
138
168
|
cachedOnly: req?.cachedOnly ?? undefined,
|
|
139
169
|
fullRegistry: req?.fullRegistry ?? undefined
|
|
140
170
|
})) ?? []
|
|
@@ -149,13 +179,15 @@ export function serveHarness(stream, harness) {
|
|
|
149
179
|
models: (await harness.infraModels?.(profile ?? '')) ?? []
|
|
150
180
|
}),
|
|
151
181
|
capability: async () => {
|
|
152
|
-
if (!harness.capability)
|
|
182
|
+
if (!harness.capability) {
|
|
153
183
|
throw new Error('harness does not support capability');
|
|
184
|
+
}
|
|
154
185
|
return harness.capability();
|
|
155
186
|
},
|
|
156
187
|
warmModel: async ({ model, agent }) => {
|
|
157
|
-
if (!harness.warmModel)
|
|
188
|
+
if (!harness.warmModel) {
|
|
158
189
|
throw new Error('harness does not support warmModel');
|
|
190
|
+
}
|
|
159
191
|
await harness.warmModel({ model, agent: agent ?? {} });
|
|
160
192
|
return { ok: true };
|
|
161
193
|
},
|
|
@@ -183,19 +215,15 @@ function toWireSkill(skill) {
|
|
|
183
215
|
allowList: skill.allowList,
|
|
184
216
|
platforms: skill.platform,
|
|
185
217
|
status: skill.status,
|
|
186
|
-
|
|
187
|
-
? {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
...(skill.setup ? { setup: skill.setup, ...(setupV2 ? { setupV2 } : {}) } : {}),
|
|
196
|
-
...(skill.stub !== undefined ? { stub: skill.stub } : {}),
|
|
197
|
-
...(skill.minCapabilityProfile ? { minCapabilityProfile: skill.minCapabilityProfile } : {}),
|
|
198
|
-
...(skill.emoji ? { emoji: skill.emoji } : {})
|
|
218
|
+
requirements: skill.requires
|
|
219
|
+
? { binaries, credentials: skill.requires.credentials ?? [] }
|
|
220
|
+
: undefined,
|
|
221
|
+
install: skill.install || undefined,
|
|
222
|
+
setup: skill.setup || undefined,
|
|
223
|
+
setupV2,
|
|
224
|
+
stub: skill.stub,
|
|
225
|
+
minCapabilityProfile: skill.minCapabilityProfile || undefined,
|
|
226
|
+
emoji: skill.emoji || undefined
|
|
199
227
|
};
|
|
200
228
|
}
|
|
201
229
|
function unsupportedEmbeddingDeviceControl() {
|
|
@@ -230,8 +258,9 @@ function modelEvents(harness) {
|
|
|
230
258
|
catalogChanged,
|
|
231
259
|
...loaded.map((entry) => `${entry.model}:${entry.refs}`)
|
|
232
260
|
].join('\0');
|
|
233
|
-
if (key === last)
|
|
261
|
+
if (key === last) {
|
|
234
262
|
return;
|
|
263
|
+
}
|
|
235
264
|
last = key;
|
|
236
265
|
const frame = {
|
|
237
266
|
status: state.status,
|
|
@@ -269,16 +298,18 @@ function modelEvents(harness) {
|
|
|
269
298
|
}
|
|
270
299
|
function speakStream(harness) {
|
|
271
300
|
return async function* (duplex) {
|
|
272
|
-
if (!harness.speakStream)
|
|
301
|
+
if (!harness.speakStream) {
|
|
273
302
|
throw new Error('harness does not support speakStream');
|
|
303
|
+
}
|
|
274
304
|
const text = pushQueue();
|
|
275
305
|
const end = () => text.end();
|
|
276
306
|
duplex.on('close', end);
|
|
277
307
|
duplex.on('end', end);
|
|
278
308
|
duplex.readStream?.on('close', end);
|
|
279
309
|
duplex.on('data', (frame) => {
|
|
280
|
-
if (frame.text)
|
|
310
|
+
if (frame.text) {
|
|
281
311
|
text.push(frame.text);
|
|
312
|
+
}
|
|
282
313
|
});
|
|
283
314
|
for await (const chunk of harness.speakStream({ model: '', text })) {
|
|
284
315
|
yield { audio: chunk.audio, sampleRate: chunk.sampleRate };
|
|
@@ -286,13 +317,15 @@ function speakStream(harness) {
|
|
|
286
317
|
};
|
|
287
318
|
}
|
|
288
319
|
function runFrame(harness, frame, context) {
|
|
289
|
-
if (frame.type !== 'invoke')
|
|
320
|
+
if (frame.type !== 'invoke') {
|
|
290
321
|
return harness.completion(context);
|
|
322
|
+
}
|
|
291
323
|
if (!harness.invoke)
|
|
292
324
|
throw new Error('harness does not support invoke');
|
|
293
325
|
const capability = frame.capability ?? '';
|
|
294
|
-
if (!isCapability(capability))
|
|
326
|
+
if (!isCapability(capability)) {
|
|
295
327
|
throw new Error(`unknown capability: ${capability}`);
|
|
328
|
+
}
|
|
296
329
|
return harness.invoke({
|
|
297
330
|
capability,
|
|
298
331
|
args: frame.arguments ?? {},
|
|
@@ -318,26 +351,31 @@ function completion(harness) {
|
|
|
318
351
|
let started = false;
|
|
319
352
|
const gone = () => {
|
|
320
353
|
controller.abort('client gone');
|
|
321
|
-
for (const resolve of pending.values())
|
|
354
|
+
for (const resolve of pending.values()) {
|
|
322
355
|
resolve(false);
|
|
356
|
+
}
|
|
323
357
|
pending.clear();
|
|
324
358
|
for (const { reject, staged } of pendingAttach.values()) {
|
|
325
359
|
void fs.promises.unlink(staged).catch(noop);
|
|
326
360
|
reject(new Error('client gone'));
|
|
327
361
|
}
|
|
328
362
|
pendingAttach.clear();
|
|
329
|
-
for (const { reject } of pendingFetch.values())
|
|
363
|
+
for (const { reject } of pendingFetch.values()) {
|
|
330
364
|
reject(new Error('client gone'));
|
|
365
|
+
}
|
|
331
366
|
pendingFetch.clear();
|
|
332
|
-
for (const { reject } of pendingRag.values())
|
|
367
|
+
for (const { reject } of pendingRag.values()) {
|
|
333
368
|
reject(new Error('client gone'));
|
|
369
|
+
}
|
|
334
370
|
pendingRag.clear();
|
|
335
|
-
for (const { resolve } of pendingQuestion.values())
|
|
371
|
+
for (const { resolve } of pendingQuestion.values()) {
|
|
336
372
|
resolve(null);
|
|
373
|
+
}
|
|
337
374
|
pendingQuestion.clear();
|
|
338
375
|
for (const { reject, path } of pendingDrive.values()) {
|
|
339
|
-
if (path)
|
|
376
|
+
if (path) {
|
|
340
377
|
void fs.promises.unlink(path).catch(noop);
|
|
378
|
+
}
|
|
341
379
|
reject(new Error('client gone'));
|
|
342
380
|
}
|
|
343
381
|
pendingDrive.clear();
|
|
@@ -348,35 +386,43 @@ function completion(harness) {
|
|
|
348
386
|
duplex.readStream?.on('close', gone);
|
|
349
387
|
duplex.on('data', (frame) => {
|
|
350
388
|
if (frame.type === 'decision') {
|
|
351
|
-
if (typeof frame.callId !== 'string')
|
|
389
|
+
if (typeof frame.callId !== 'string') {
|
|
352
390
|
return;
|
|
391
|
+
}
|
|
353
392
|
pending.get(frame.callId)?.(frame.approved === true);
|
|
354
393
|
pending.delete(frame.callId);
|
|
355
394
|
return;
|
|
356
395
|
}
|
|
357
396
|
if (frame.type === 'attach-result') {
|
|
358
|
-
if (typeof frame.callId !== 'string')
|
|
397
|
+
if (typeof frame.callId !== 'string') {
|
|
359
398
|
return;
|
|
399
|
+
}
|
|
360
400
|
const waiter = pendingAttach.get(frame.callId);
|
|
361
401
|
pendingAttach.delete(frame.callId);
|
|
362
|
-
if (!waiter)
|
|
402
|
+
if (!waiter) {
|
|
363
403
|
return;
|
|
404
|
+
}
|
|
364
405
|
void fs.promises.unlink(waiter.staged).catch(noop);
|
|
365
|
-
if (frame.attachment)
|
|
406
|
+
if (frame.attachment) {
|
|
366
407
|
waiter.resolve(frame.attachment);
|
|
367
|
-
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
368
410
|
waiter.reject(new Error(frame.message || 'the host failed to store the attachment'));
|
|
411
|
+
}
|
|
369
412
|
return;
|
|
370
413
|
}
|
|
371
414
|
if (frame.type === 'fetch-result') {
|
|
372
|
-
if (typeof frame.callId !== 'string')
|
|
415
|
+
if (typeof frame.callId !== 'string') {
|
|
373
416
|
return;
|
|
417
|
+
}
|
|
374
418
|
const waiter = pendingFetch.get(frame.callId);
|
|
375
419
|
pendingFetch.delete(frame.callId);
|
|
376
|
-
if (!waiter)
|
|
420
|
+
if (!waiter) {
|
|
377
421
|
return;
|
|
378
|
-
|
|
422
|
+
}
|
|
423
|
+
if (frame.message) {
|
|
379
424
|
return waiter.reject(new Error(frame.message));
|
|
425
|
+
}
|
|
380
426
|
const staged = frame.path;
|
|
381
427
|
const { fileName, mimeType, attachmentId, size } = frame;
|
|
382
428
|
if (frame.meta === true) {
|
|
@@ -387,8 +433,9 @@ function completion(harness) {
|
|
|
387
433
|
byteLength: typeof size === 'number' ? size : 0
|
|
388
434
|
});
|
|
389
435
|
}
|
|
390
|
-
if (!staged)
|
|
436
|
+
if (!staged) {
|
|
391
437
|
return waiter.resolve(null);
|
|
438
|
+
}
|
|
392
439
|
fs.promises.readFile(staged).then((bytes) => waiter.resolve({
|
|
393
440
|
id: attachmentId ?? '',
|
|
394
441
|
fileName: fileName ?? '',
|
|
@@ -399,42 +446,52 @@ function completion(harness) {
|
|
|
399
446
|
return;
|
|
400
447
|
}
|
|
401
448
|
if (frame.type === 'rag-result') {
|
|
402
|
-
if (typeof frame.callId !== 'string')
|
|
449
|
+
if (typeof frame.callId !== 'string') {
|
|
403
450
|
return;
|
|
451
|
+
}
|
|
404
452
|
const waiter = pendingRag.get(frame.callId);
|
|
405
453
|
pendingRag.delete(frame.callId);
|
|
406
|
-
if (!waiter)
|
|
454
|
+
if (!waiter) {
|
|
407
455
|
return;
|
|
408
|
-
|
|
456
|
+
}
|
|
457
|
+
if (frame.message) {
|
|
409
458
|
return waiter.reject(new Error(frame.message));
|
|
459
|
+
}
|
|
410
460
|
waiter.resolve(frame.result ?? { enabled: false, indexEmpty: true, hasKnowledge: false, hits: [] });
|
|
411
461
|
}
|
|
412
462
|
if (frame.type === 'question-result') {
|
|
413
|
-
if (typeof frame.callId !== 'string')
|
|
463
|
+
if (typeof frame.callId !== 'string') {
|
|
414
464
|
return;
|
|
465
|
+
}
|
|
415
466
|
const waiter = pendingQuestion.get(frame.callId);
|
|
416
467
|
pendingQuestion.delete(frame.callId);
|
|
417
|
-
if (!waiter)
|
|
468
|
+
if (!waiter) {
|
|
418
469
|
return;
|
|
419
|
-
|
|
470
|
+
}
|
|
471
|
+
if (frame.message) {
|
|
420
472
|
hlog('question failed', { callId: frame.callId, message: frame.message });
|
|
473
|
+
}
|
|
421
474
|
waiter.resolve(frame.answer || null);
|
|
422
475
|
return;
|
|
423
476
|
}
|
|
424
477
|
if (frame.type === 'drive-result') {
|
|
425
|
-
if (typeof frame.callId !== 'string')
|
|
478
|
+
if (typeof frame.callId !== 'string') {
|
|
426
479
|
return;
|
|
480
|
+
}
|
|
427
481
|
const waiter = pendingDrive.get(frame.callId);
|
|
428
482
|
pendingDrive.delete(frame.callId);
|
|
429
|
-
if (!waiter)
|
|
483
|
+
if (!waiter) {
|
|
430
484
|
return;
|
|
431
|
-
|
|
485
|
+
}
|
|
486
|
+
if (frame.message) {
|
|
432
487
|
return waiter.reject(new Error(frame.message));
|
|
433
|
-
|
|
488
|
+
}
|
|
489
|
+
waiter.resolve(frame.payload ?? {});
|
|
434
490
|
return;
|
|
435
491
|
}
|
|
436
|
-
if (started || (frame.type !== 'start' && frame.type !== 'invoke'))
|
|
492
|
+
if (started || (frame.type !== 'start' && frame.type !== 'invoke')) {
|
|
437
493
|
return;
|
|
494
|
+
}
|
|
438
495
|
started = true;
|
|
439
496
|
const context = {
|
|
440
497
|
model: frame.model ?? '',
|
|
@@ -447,9 +504,9 @@ function completion(harness) {
|
|
|
447
504
|
messages: (frame.messages ?? []).map(({ role, content, attachments }) => ({
|
|
448
505
|
role,
|
|
449
506
|
content,
|
|
450
|
-
|
|
451
|
-
?
|
|
452
|
-
:
|
|
507
|
+
attachments: attachments?.length
|
|
508
|
+
? attachments.map(({ path: file }) => ({ path: file }))
|
|
509
|
+
: undefined
|
|
453
510
|
})),
|
|
454
511
|
toolConfigs: frame.toolConfigs ?? undefined,
|
|
455
512
|
grantedDirs: frame.grantedDirs ?? undefined,
|
|
@@ -467,7 +524,7 @@ function completion(harness) {
|
|
|
467
524
|
pendingDrive.set(callId, {
|
|
468
525
|
resolve,
|
|
469
526
|
reject,
|
|
470
|
-
|
|
527
|
+
path: request.op === 'write' ? request.path : undefined
|
|
471
528
|
});
|
|
472
529
|
events.push({ type: 'drive-ask', callId, op: request.op, payload: request });
|
|
473
530
|
})
|
|
@@ -516,7 +573,7 @@ function completion(harness) {
|
|
|
516
573
|
type: 'fetch-ask',
|
|
517
574
|
callId,
|
|
518
575
|
attachmentId,
|
|
519
|
-
|
|
576
|
+
meta: meta === true ? true : undefined
|
|
520
577
|
});
|
|
521
578
|
}),
|
|
522
579
|
ragSearch: ({ query, limit }) => controller.signal.aborted
|
|
@@ -611,6 +668,13 @@ function downloadStream(harness, req) {
|
|
|
611
668
|
pendingProgress = null;
|
|
612
669
|
lastProgressAt = at;
|
|
613
670
|
};
|
|
671
|
+
const queueProgress = (wire) => {
|
|
672
|
+
const now = Date.now();
|
|
673
|
+
pendingProgress = wire;
|
|
674
|
+
if (lastProgressAt === 0 || now - lastProgressAt >= DOWNLOAD_PROGRESS_INTERVAL_MS) {
|
|
675
|
+
flushProgress(now);
|
|
676
|
+
}
|
|
677
|
+
};
|
|
614
678
|
try {
|
|
615
679
|
for await (const event of harness.download({
|
|
616
680
|
model: req.model,
|
|
@@ -620,11 +684,7 @@ function downloadStream(harness, req) {
|
|
|
620
684
|
})) {
|
|
621
685
|
const wire = toDownloadWire(event);
|
|
622
686
|
if (wire.type === 'progress') {
|
|
623
|
-
|
|
624
|
-
pendingProgress = wire;
|
|
625
|
-
if (lastProgressAt === 0 || now - lastProgressAt >= DOWNLOAD_PROGRESS_INTERVAL_MS) {
|
|
626
|
-
flushProgress(now);
|
|
627
|
-
}
|
|
687
|
+
queueProgress(wire);
|
|
628
688
|
continue;
|
|
629
689
|
}
|
|
630
690
|
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
|
}
|