@qvac/harness 0.8.8 → 0.8.10
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 +80 -1
- package/dist-lib/index.d.ts +1 -1
- package/dist-lib/index.js +51 -50
- package/dist-lib/lib/audio-transcode-bare.js +2 -2
- package/dist-lib/lib/capabilities/completion.d.ts +1 -2
- package/dist-lib/lib/capabilities/completion.js +29 -25
- package/dist-lib/lib/capabilities/tool-prompt-log.js +3 -3
- package/dist-lib/lib/capabilities/voice.js +7 -6
- package/dist-lib/lib/connect.js +18 -17
- package/dist-lib/lib/errors.d.ts +266 -0
- package/dist-lib/lib/errors.js +189 -0
- package/dist-lib/lib/helpers/load-stall.d.ts +1 -1
- package/dist-lib/lib/helpers/load-stall.js +2 -2
- package/dist-lib/lib/helpers/race-abort.d.ts +2 -1
- package/dist-lib/lib/helpers/race-abort.js +3 -4
- package/dist-lib/lib/hooks/built-in/arg-repair.js +2 -2
- package/dist-lib/lib/hooks/pipeline.js +4 -4
- package/dist-lib/lib/hooks/run.js +2 -2
- package/dist-lib/lib/log.d.ts +26 -2
- package/dist-lib/lib/log.js +66 -16
- package/dist-lib/lib/models/model-pool.js +11 -6
- package/dist-lib/lib/models/model-source.js +3 -2
- package/dist-lib/lib/no-redirect-fetch-bare.js +2 -1
- package/dist-lib/lib/no-redirect-fetch-impl.js +2 -1
- package/dist-lib/lib/office/extract.js +4 -3
- package/dist-lib/lib/office/index.js +2 -1
- package/dist-lib/lib/resolution-detector.js +3 -3
- package/dist-lib/lib/rpc-session.js +2 -1
- package/dist-lib/lib/sdk-loader.js +2 -2
- package/dist-lib/lib/serve.js +33 -29
- package/dist-lib/lib/spawn.d.ts +2 -4
- package/dist-lib/lib/spawn.js +14 -7
- package/dist-lib/lib/tools/edit-image.js +2 -1
- package/dist-lib/lib/tools/exec/adapters/python/attachments.js +8 -8
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +7 -6
- package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.js +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/memfs.js +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +8 -8
- package/dist-lib/lib/tools/exec/adapters/python/run-session.js +11 -7
- package/dist-lib/lib/tools/exec/adapters/python/wheels.js +5 -5
- package/dist-lib/lib/tools/exec/adapters/python/worker/active.js +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +9 -8
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/serve.js +4 -4
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn.d.ts +2 -1
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/note-name.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/safe-path.js +5 -4
- package/dist-lib/lib/tools/exec/obsidian/vault-create.js +3 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-fs.js +2 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +3 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-read.js +6 -5
- package/dist-lib/lib/tools/exec/obsidian/vault-write.js +13 -12
- package/dist-lib/lib/tools/exec/runner.js +2 -1
- package/dist-lib/lib/tools/files/edit-file.d.ts +3 -0
- package/dist-lib/lib/tools/files/edit-file.js +73 -0
- package/dist-lib/lib/tools/files/glob.d.ts +3 -0
- package/dist-lib/lib/tools/files/glob.js +63 -0
- package/dist-lib/lib/tools/files/grep.d.ts +3 -0
- package/dist-lib/lib/tools/files/grep.js +114 -0
- package/dist-lib/lib/tools/files/index.d.ts +3 -0
- package/dist-lib/lib/tools/files/index.js +23 -0
- package/dist-lib/lib/tools/files/list-dir.d.ts +5 -0
- package/dist-lib/lib/tools/files/list-dir.js +130 -0
- package/dist-lib/lib/tools/files/make-dir.d.ts +3 -0
- package/dist-lib/lib/tools/files/make-dir.js +47 -0
- package/dist-lib/lib/tools/files/read-file.d.ts +3 -0
- package/dist-lib/lib/tools/files/read-file.js +103 -0
- package/dist-lib/lib/tools/files/transfer.d.ts +4 -0
- package/dist-lib/lib/tools/files/transfer.js +82 -0
- package/dist-lib/lib/tools/files/trash-file.d.ts +4 -0
- package/dist-lib/lib/tools/files/trash-file.js +61 -0
- package/dist-lib/lib/tools/files/write-file.d.ts +3 -0
- package/dist-lib/lib/tools/files/write-file.js +55 -0
- package/dist-lib/lib/tools/fs/document-text.d.ts +2 -0
- package/dist-lib/lib/tools/fs/document-text.js +38 -0
- package/dist-lib/lib/tools/fs/edit-match.d.ts +11 -0
- package/dist-lib/lib/tools/fs/edit-match.js +85 -0
- package/dist-lib/lib/tools/fs/remote.d.ts +2 -1
- package/dist-lib/lib/tools/fs/remote.js +4 -4
- package/dist-lib/lib/tools/fs/scope.d.ts +1 -1
- package/dist-lib/lib/tools/fs/scope.js +3 -2
- package/dist-lib/lib/tools/fs/targets.d.ts +10 -0
- package/dist-lib/lib/tools/fs/targets.js +32 -0
- package/dist-lib/lib/tools/fs/tool-path.d.ts +24 -0
- package/dist-lib/lib/tools/fs/tool-path.js +58 -0
- package/dist-lib/lib/tools/fs/walk.js +3 -3
- package/dist-lib/lib/tools/generate-image.d.ts +2 -2
- package/dist-lib/lib/tools/generate-image.js +5 -6
- package/dist-lib/lib/tools/office.js +2 -1
- package/dist-lib/lib/tools/skill.js +2 -1
- package/dist-lib/lib/tools/skills/bundled-dir.js +4 -4
- package/dist-lib/lib/tools/skills/catalog.js +2 -2
- package/dist-lib/lib/tools/skills/credential-health.js +3 -3
- package/dist-lib/lib/tools/skills/mention.js +7 -2
- package/dist-lib/lib/tools/skills/overlay-crud.js +3 -2
- package/dist-lib/lib/tools/translate.d.ts +2 -2
- package/dist-lib/lib/tools/translate.js +5 -6
- package/dist-lib/lib/tools/weather-lookup/geocode.js +5 -4
- package/dist-lib/lib/web-search/engines/bing.js +2 -1
- package/dist-lib/lib/web-search/engines/google.js +2 -1
- package/dist-lib/lib/web-search/fetch-html.d.ts +5 -5
- package/dist-lib/lib/web-search/fetch-html.js +12 -15
- package/package.json +67 -25
- package/worker/harness-sidecar.mjs +12 -13
- package/dist-lib/lib/tools/create-documents.d.ts +0 -3
- package/dist-lib/lib/tools/create-documents.js +0 -95
- package/dist-lib/lib/tools/edit-documents.d.ts +0 -3
- package/dist-lib/lib/tools/edit-documents.js +0 -132
- package/dist-lib/lib/tools/file-glob.d.ts +0 -14
- package/dist-lib/lib/tools/file-glob.js +0 -118
- package/dist-lib/lib/tools/grep.d.ts +0 -5
- package/dist-lib/lib/tools/grep.js +0 -184
- package/dist-lib/lib/tools/read-files.d.ts +0 -5
- package/dist-lib/lib/tools/read-files.js +0 -157
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { errorMessage } from '../../guards.js';
|
|
2
|
-
import {
|
|
2
|
+
import { log } from '../../log.js';
|
|
3
3
|
import { displayPath } from './scope.js';
|
|
4
|
-
export const WALK_SKIP_DIRS = new Set(['.git', 'node_modules']);
|
|
4
|
+
export const WALK_SKIP_DIRS = new Set(['.git', 'node_modules', '.trash']);
|
|
5
5
|
export const MAX_WALK_ENTRIES = 10_000;
|
|
6
6
|
export const MAX_WALK_MS = 30_000;
|
|
7
7
|
export async function walkFiles(root, folder = '/') {
|
|
@@ -29,7 +29,7 @@ export async function walkFiles(root, folder = '/') {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
catch (err) {
|
|
32
|
-
|
|
32
|
+
log.warn('warn: walk aborted mid-listing', {
|
|
33
33
|
root: displayPath(root, '/'),
|
|
34
34
|
folder,
|
|
35
35
|
visited,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AbortSignal as BareAbortSignal } from 'bare-abort-controller';
|
|
2
|
+
import { IMAGE_LOAD_STALL_ERROR } from '../errors.ts';
|
|
2
3
|
import type { ToolDefinition } from '../capabilities/completion.ts';
|
|
3
4
|
import type { ModelSrc } from '../models/context.ts';
|
|
4
5
|
import { type ImageClamps } from '../models/model-catalog.ts';
|
|
@@ -88,7 +89,7 @@ export interface DiffusionRuntime {
|
|
|
88
89
|
release(): void;
|
|
89
90
|
}
|
|
90
91
|
export declare function generationPercentTick(onPercent: (percentage: number) => void, encodePhases?: number): (tick: DiffusionTick) => void;
|
|
91
|
-
export
|
|
92
|
+
export { IMAGE_LOAD_STALL_ERROR };
|
|
92
93
|
export declare function createDiffusionRuntime({ loadSdk, onProgress, onLoaded, onBeforeLoad, hold, loads, stallMs, ...base }: DiffusionRuntimeOptions): DiffusionRuntime;
|
|
93
94
|
export declare const BUSY_ERROR = "an image generation is already running; try again when it finishes";
|
|
94
95
|
export interface ImageToolError {
|
|
@@ -113,4 +114,3 @@ export declare function settingsDimension(value: number | undefined, maxDim?: nu
|
|
|
113
114
|
export declare function settingsSteps(value: number | undefined, maxSteps?: number): number | undefined;
|
|
114
115
|
export declare function effectiveSteps(parsedSteps: number | undefined, settingsValue: number | undefined, stepsCap: number | undefined): number | undefined;
|
|
115
116
|
export declare function createGenerateImageTool(runtime: DiffusionRuntime, clamps?: ImageClamps): ToolDefinition;
|
|
116
|
-
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ScopeLock from 'scope-lock';
|
|
2
|
+
import { HarnessError, IMAGE_LOAD_STALL_ERROR, isHarnessError } from '../errors.js';
|
|
2
3
|
import { AbortedError, raceAbort } from '../helpers/race-abort.js';
|
|
3
4
|
import { DIFFUSION_CATALOG, imageModelFor } from '../models/model-catalog.js';
|
|
4
5
|
import { createLoadStall } from '../helpers/load-stall.js';
|
|
@@ -38,7 +39,7 @@ function diffusionProgressName(set) {
|
|
|
38
39
|
const entry = DIFFUSION_CATALOG.find((candidate) => candidate.model.src === set.model);
|
|
39
40
|
return entry?.name ?? set.model;
|
|
40
41
|
}
|
|
41
|
-
export
|
|
42
|
+
export { IMAGE_LOAD_STALL_ERROR };
|
|
42
43
|
export function createDiffusionRuntime({ loadSdk, onProgress, onLoaded, onBeforeLoad, hold, loads = createLoadTracker(), stallMs = IMAGE_LOAD_STALL_MS, ...base }) {
|
|
43
44
|
let loading = null;
|
|
44
45
|
let residentFingerprint = null;
|
|
@@ -102,7 +103,7 @@ export function createDiffusionRuntime({ loadSdk, onProgress, onLoaded, onBefore
|
|
|
102
103
|
config.vaeModelSrc = await resolveModelSrc(sdk, set.vaeModel, 'diffusion');
|
|
103
104
|
}
|
|
104
105
|
const modelConfig = Object.keys(config).length ? config : undefined;
|
|
105
|
-
const stall = createLoadStall(stallMs,
|
|
106
|
+
const stall = createLoadStall(stallMs, () => new HarnessError('IMAGE_LOAD_STALLED'));
|
|
106
107
|
const pending = sdk.loadModel({
|
|
107
108
|
modelSrc,
|
|
108
109
|
modelType: 'diffusion',
|
|
@@ -118,9 +119,7 @@ export function createDiffusionRuntime({ loadSdk, onProgress, onLoaded, onBefore
|
|
|
118
119
|
return { modelId, prediction: set.prediction };
|
|
119
120
|
}
|
|
120
121
|
catch (err) {
|
|
121
|
-
if (err
|
|
122
|
-
err.message === IMAGE_LOAD_STALL_ERROR &&
|
|
123
|
-
pending.requestId) {
|
|
122
|
+
if (isHarnessError(err, 'IMAGE_LOAD_STALLED') && pending.requestId) {
|
|
124
123
|
void sdk.cancel?.({ requestId: pending.requestId }).catch(noop);
|
|
125
124
|
}
|
|
126
125
|
throw err;
|
|
@@ -371,7 +370,7 @@ export function createGenerateImageTool(runtime, clamps) {
|
|
|
371
370
|
if (err instanceof AbortedError) {
|
|
372
371
|
return { error: 'image generation canceled' };
|
|
373
372
|
}
|
|
374
|
-
if (err
|
|
373
|
+
if (isHarnessError(err, 'IMAGE_LOAD_STALLED')) {
|
|
375
374
|
return { error: IMAGE_LOAD_STALL_ERROR };
|
|
376
375
|
}
|
|
377
376
|
throw err;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
|
+
import { HarnessError } from '../errors.js';
|
|
2
3
|
import { errorMessage, isRecord } from '../guards.js';
|
|
3
4
|
import { DocsError, docx, pptx, units, xlsx } from '../office/engine/index.js';
|
|
4
5
|
import { stageInputs } from './exec/adapters/python/attachments.js';
|
|
@@ -356,7 +357,7 @@ async function attachOutput(job, ctx) {
|
|
|
356
357
|
function runOp(document, op, input) {
|
|
357
358
|
const method = Reflect.get(document, op.name);
|
|
358
359
|
if (typeof method !== 'function') {
|
|
359
|
-
throw new
|
|
360
|
+
throw new HarnessError('OFFICE_OP_UNAVAILABLE', [op.name]);
|
|
360
361
|
}
|
|
361
362
|
return Reflect.apply(method, document, opArguments(op.spec, input, units.inches));
|
|
362
363
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import b4a from 'b4a';
|
|
3
3
|
import Localdrive from 'localdrive';
|
|
4
|
+
import { HarnessError } from '../errors.js';
|
|
4
5
|
import { OBSIDIAN_VAULT_MODE_NOTE } from './exec/obsidian/guidance.js';
|
|
5
6
|
import { unquoteArg } from './model-args.js';
|
|
6
7
|
import { credentialRecord } from './skills/auth-resolver.js';
|
|
@@ -101,7 +102,7 @@ export function createSkillTool({ skillsDir, overlayDir = resolveSkillsOverlayDi
|
|
|
101
102
|
return { raw: b4a.toString(buf, 'utf8'), root: root ?? undefined };
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
|
-
throw new
|
|
105
|
+
throw new HarnessError('SKILL_FILE_NOT_FOUND', [name, file]);
|
|
105
106
|
}
|
|
106
107
|
return {
|
|
107
108
|
name: 'skill',
|
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import process from 'process';
|
|
5
|
+
import { HarnessError } from '../../errors.js';
|
|
5
6
|
import { userTmpPath } from '../../tmp-path.js';
|
|
6
7
|
import { hashBundledSkills } from './bundled-hash.js';
|
|
7
8
|
import { SKILLS as BUNDLED_SKILLS } from '@qvac/skills/bundled';
|
|
@@ -26,7 +27,7 @@ async function materialize() {
|
|
|
26
27
|
try {
|
|
27
28
|
await writeTree(staging);
|
|
28
29
|
if (!(await validTree(staging))) {
|
|
29
|
-
throw new
|
|
30
|
+
throw new HarnessError('BUNDLED_SKILLS_INVALID', [staging]);
|
|
30
31
|
}
|
|
31
32
|
if (await tryRename(staging, dir)) {
|
|
32
33
|
return dir;
|
|
@@ -58,12 +59,11 @@ async function materialize() {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
function tamperError(dir) {
|
|
61
|
-
return new
|
|
62
|
-
'another process is tampering with it; remove it and restart');
|
|
62
|
+
return new HarnessError('BUNDLED_SKILLS_TAMPERED', [dir]);
|
|
63
63
|
}
|
|
64
64
|
function occupiedError(dir, cause) {
|
|
65
65
|
const why = cause instanceof Error ? ` (${cause.message})` : '';
|
|
66
|
-
return new
|
|
66
|
+
return new HarnessError('BUNDLED_SKILLS_OCCUPIED', [dir, why]);
|
|
67
67
|
}
|
|
68
68
|
export function randomSuffix() {
|
|
69
69
|
return Math.random().toString(36).slice(2, 10);
|
|
@@ -10,7 +10,7 @@ import { parseToolGrant } from './tool-grants.js';
|
|
|
10
10
|
import { deriveSetup } from './setup-routes.js';
|
|
11
11
|
import { resolveSkillsOverlayDir } from './overlay-dir.js';
|
|
12
12
|
import { parseOperationsManifest } from '../operations/manifest.js';
|
|
13
|
-
import {
|
|
13
|
+
import { log } from '../../log.js';
|
|
14
14
|
export function skillSupportsPlatform(entry, platform) {
|
|
15
15
|
return supportsPlatform(entry, platform);
|
|
16
16
|
}
|
|
@@ -193,7 +193,7 @@ async function loadOperations(skillDir, name) {
|
|
|
193
193
|
}
|
|
194
194
|
const operations = parseOperationsManifest(raw);
|
|
195
195
|
if (!operations) {
|
|
196
|
-
|
|
196
|
+
log.warn('skill operations manifest rejected', { skill: name });
|
|
197
197
|
return [];
|
|
198
198
|
}
|
|
199
199
|
return operations;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log } from '../../log.js';
|
|
2
2
|
import { scopeKey } from './scope-key.js';
|
|
3
3
|
const rejected = new Set();
|
|
4
4
|
const lastProbedAt = new Map();
|
|
@@ -16,12 +16,12 @@ export function markCredentialValid(key, agentId, skill) {
|
|
|
16
16
|
}
|
|
17
17
|
export function recordCredentialHealth(key, status, agentId) {
|
|
18
18
|
if (status === 401 || status === 403) {
|
|
19
|
-
|
|
19
|
+
log.warn('credential rejected by provider', { key, status });
|
|
20
20
|
markCredentialRejected(key, agentId);
|
|
21
21
|
}
|
|
22
22
|
else if (status < 400) {
|
|
23
23
|
if (isCredentialRejected(key, agentId)) {
|
|
24
|
-
|
|
24
|
+
log.info('credential recovered', { key });
|
|
25
25
|
}
|
|
26
26
|
markCredentialValid(key, agentId);
|
|
27
27
|
}
|
|
@@ -7,7 +7,7 @@ export function resolveSkillMention(text, catalog) {
|
|
|
7
7
|
return null;
|
|
8
8
|
}
|
|
9
9
|
return (catalog.find((skill) => skill.displayName?.toLowerCase() === name) ??
|
|
10
|
-
catalog.find((skill) => skill.name.toLowerCase() === name) ??
|
|
10
|
+
catalog.find((skill) => !skill.displayName && skill.name.toLowerCase() === name) ??
|
|
11
11
|
null);
|
|
12
12
|
}
|
|
13
13
|
const MIN_IMPLICIT_NAME_LENGTH = 4;
|
|
@@ -18,7 +18,12 @@ function matchesName(text, candidate) {
|
|
|
18
18
|
}
|
|
19
19
|
return true;
|
|
20
20
|
}
|
|
21
|
+
function withoutMention(text) {
|
|
22
|
+
const name = skillMentionName(text);
|
|
23
|
+
return name ? text.trimStart().slice(name.length + 1) : text;
|
|
24
|
+
}
|
|
21
25
|
export function resolveImplicitSkill(text, activeSkills) {
|
|
26
|
+
const prose = withoutMention(text);
|
|
22
27
|
let best = null;
|
|
23
28
|
let bestLength = 0;
|
|
24
29
|
for (const skill of activeSkills) {
|
|
@@ -28,7 +33,7 @@ export function resolveImplicitSkill(text, activeSkills) {
|
|
|
28
33
|
if (candidate.length < MIN_IMPLICIT_NAME_LENGTH) {
|
|
29
34
|
continue;
|
|
30
35
|
}
|
|
31
|
-
if (!matchesName(
|
|
36
|
+
if (!matchesName(prose, candidate)) {
|
|
32
37
|
continue;
|
|
33
38
|
}
|
|
34
39
|
const weight = candidate.replace(/\+/g, '').length;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { HarnessError } from '../../errors.js';
|
|
3
4
|
import { randomSuffix } from './bundled-dir.js';
|
|
4
5
|
const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
5
6
|
function assertSafeName(name) {
|
|
6
7
|
if (!SAFE_NAME.test(name)) {
|
|
7
|
-
throw new
|
|
8
|
+
throw new HarnessError('INVALID_SKILL_NAME', [name]);
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
11
|
async function dirNames(root) {
|
|
@@ -66,7 +67,7 @@ export async function copyBaseForEdit(baseDir, overlayDir, name) {
|
|
|
66
67
|
}
|
|
67
68
|
const src = path.join(baseDir, name);
|
|
68
69
|
if (!(await exists(src))) {
|
|
69
|
-
throw new
|
|
70
|
+
throw new HarnessError('SKILL_NOT_FOUND', [name]);
|
|
70
71
|
}
|
|
71
72
|
await copyDir(src, dest);
|
|
72
73
|
return { copied: true };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AbortSignal as BareAbortSignal } from 'bare-abort-controller';
|
|
2
|
+
import { TRANSLATION_LOAD_STALL_ERROR } from '../errors.ts';
|
|
2
3
|
import type { ToolDefinition } from '../capabilities/completion.ts';
|
|
3
4
|
import type { ModelSrc } from '../models/context.ts';
|
|
4
5
|
import { type NmtPlan } from '../models/nmt-catalog.ts';
|
|
@@ -38,7 +39,7 @@ export type TranslationSdk = ModelRegistrySdk & {
|
|
|
38
39
|
};
|
|
39
40
|
};
|
|
40
41
|
export declare const TRANSLATION_LOAD_STALL_MS: number;
|
|
41
|
-
export
|
|
42
|
+
export { TRANSLATION_LOAD_STALL_ERROR };
|
|
42
43
|
export interface TranslationRuntimeOptions {
|
|
43
44
|
loadSdk: () => Promise<TranslationSdk>;
|
|
44
45
|
loads?: LoadTracker;
|
|
@@ -76,4 +77,3 @@ export interface TranslateToolOptions {
|
|
|
76
77
|
slm?: SlmTranslateFn;
|
|
77
78
|
}
|
|
78
79
|
export declare function createTranslateTool(runtime: TranslationRuntime, { slm }?: TranslateToolOptions): ToolDefinition;
|
|
79
|
-
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ScopeLock from 'scope-lock';
|
|
2
2
|
import { getLangName } from '@qvac/langdetect-text';
|
|
3
|
+
import { HarnessError, isHarnessError, TRANSLATION_LOAD_STALL_ERROR } from '../errors.js';
|
|
3
4
|
import { detectLanguage, UNDETERMINED } from '../capabilities/detect-language.js';
|
|
4
5
|
import { AbortedError, raceAbort } from '../helpers/race-abort.js';
|
|
5
6
|
import { ENGLISH, NMT_FROM_ENGLISH, NMT_TO_ENGLISH, languageLabel, SLM_LANGUAGE_CODES, sameLanguage, translationPlanFor } from '../models/nmt-catalog.js';
|
|
@@ -7,7 +8,7 @@ import { createLoadStall } from '../helpers/load-stall.js';
|
|
|
7
8
|
import { createLoadTracker } from '../models/model-pool.js';
|
|
8
9
|
import { resolveModelSrc } from '../models/model-source.js';
|
|
9
10
|
export const TRANSLATION_LOAD_STALL_MS = 15 * 60_000;
|
|
10
|
-
export
|
|
11
|
+
export { TRANSLATION_LOAD_STALL_ERROR };
|
|
11
12
|
function packName(plan) {
|
|
12
13
|
const pair = plan.model.match(/model\.([a-z]+)\.intgemm/)?.[1];
|
|
13
14
|
if (pair) {
|
|
@@ -38,7 +39,7 @@ export function createTranslationRuntime({ loadSdk, loads = createLoadTracker(),
|
|
|
38
39
|
modelConfig.pivotModel = { modelSrc: await resolveModelSrc(sdk, plan.pivot, 'nmt') };
|
|
39
40
|
}
|
|
40
41
|
const name = packName(plan);
|
|
41
|
-
const stall = createLoadStall(stallMs,
|
|
42
|
+
const stall = createLoadStall(stallMs, () => new HarnessError('TRANSLATION_LOAD_STALLED'));
|
|
42
43
|
onProgress?.(name, {});
|
|
43
44
|
const pending = sdk.loadModel({
|
|
44
45
|
modelSrc,
|
|
@@ -54,9 +55,7 @@ export function createTranslationRuntime({ loadSdk, loads = createLoadTracker(),
|
|
|
54
55
|
return { modelId };
|
|
55
56
|
}
|
|
56
57
|
catch (err) {
|
|
57
|
-
if (err
|
|
58
|
-
err.message === TRANSLATION_LOAD_STALL_ERROR &&
|
|
59
|
-
pending.requestId) {
|
|
58
|
+
if (isHarnessError(err, 'TRANSLATION_LOAD_STALLED') && pending.requestId) {
|
|
60
59
|
void sdk.cancel?.({ requestId: pending.requestId }).catch(noop);
|
|
61
60
|
}
|
|
62
61
|
throw err;
|
|
@@ -216,7 +215,7 @@ export function createTranslateTool(runtime, { slm } = {}) {
|
|
|
216
215
|
entered = true;
|
|
217
216
|
if (plan.route === 'slm') {
|
|
218
217
|
if (!slm) {
|
|
219
|
-
throw new
|
|
218
|
+
throw new HarnessError('SLM_TRANSLATION_UNAVAILABLE');
|
|
220
219
|
}
|
|
221
220
|
const text = await slm(plan.model, { text: parsed.text, from: plan.from, to: plan.to }, ctx?.signal);
|
|
222
221
|
return {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { AbortController } from 'bare-abort-controller';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { HarnessError } from '../../errors.js';
|
|
3
4
|
import { errorMessage } from '../../guards.js';
|
|
4
|
-
import {
|
|
5
|
+
import { log } from '../../log.js';
|
|
5
6
|
import { resolveOutboundUrl } from '../url-guard.js';
|
|
6
7
|
import { checkQualifier, describePlace, fold } from './place.js';
|
|
7
8
|
export const GEOCODER_HOST = 'geocoding-api.open-meteo.com';
|
|
@@ -33,7 +34,7 @@ export async function geocode(city, lookup) {
|
|
|
33
34
|
if (lookup.signal.aborted) {
|
|
34
35
|
throw err;
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
+
log.warn('warn: weather geocoder failed, asking wttr.in by name', { err: errorMessage(err) });
|
|
37
38
|
return [];
|
|
38
39
|
}
|
|
39
40
|
finally {
|
|
@@ -46,7 +47,7 @@ async function search(city, { fetch, resolveHost }, signal) {
|
|
|
46
47
|
const url = new URL(`https://${GEOCODER_HOST}/v1/search?${query}`);
|
|
47
48
|
const check = await resolveOutboundUrl(url, resolveHost, signal);
|
|
48
49
|
if (check.blocked) {
|
|
49
|
-
throw new
|
|
50
|
+
throw new HarnessError('EGRESS_BLOCKED', [check.blocked]);
|
|
50
51
|
}
|
|
51
52
|
const response = await fetch(url, {
|
|
52
53
|
method: 'GET',
|
|
@@ -57,7 +58,7 @@ async function search(city, { fetch, resolveHost }, signal) {
|
|
|
57
58
|
maxBytes: MAX_BODY_BYTES
|
|
58
59
|
});
|
|
59
60
|
if (response.status !== 200) {
|
|
60
|
-
throw new
|
|
61
|
+
throw new HarnessError('GEOCODER_HTTP_STATUS', [response.status]);
|
|
61
62
|
}
|
|
62
63
|
const { results = [] } = GeocodeSchema.parse(JSON.parse(await response.text()));
|
|
63
64
|
return results.map((result) => ({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HarnessError } from '../../errors.js';
|
|
1
2
|
import { fetchHtml } from '../fetch-html.js';
|
|
2
3
|
import { clean } from '../html-helpers.js';
|
|
3
4
|
import { urlMatchesHost } from '../url-helpers.js';
|
|
@@ -101,7 +102,7 @@ export async function searchBing(query, fetcher, signal) {
|
|
|
101
102
|
const url = `https://www.bing.com/search?q=${encodeURIComponent(query)}`;
|
|
102
103
|
const html = await fetchHtml(fetcher, url, { headers: { 'User-Agent': SAFARI_UA }, signal });
|
|
103
104
|
if (html.includes('captcha') || html.includes('challenge/verify') || html.includes('Turnstile')) {
|
|
104
|
-
throw new
|
|
105
|
+
throw new HarnessError('SEARCH_CAPTCHA', ['Bing']);
|
|
105
106
|
}
|
|
106
107
|
const primary = parseBAlgoBlocks(html);
|
|
107
108
|
if (primary.length > 0) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HarnessError } from '../../errors.js';
|
|
1
2
|
import { fetchHtml } from '../fetch-html.js';
|
|
2
3
|
import { clean } from '../html-helpers.js';
|
|
3
4
|
import { urlMatchesHost } from '../url-helpers.js';
|
|
@@ -86,7 +87,7 @@ export async function searchGoogle(query, fetcher, signal) {
|
|
|
86
87
|
signal
|
|
87
88
|
});
|
|
88
89
|
if (html.includes('detected unusual traffic') || html.includes('g-recaptcha')) {
|
|
89
|
-
throw new
|
|
90
|
+
throw new HarnessError('SEARCH_CAPTCHA', ['Google']);
|
|
90
91
|
}
|
|
91
92
|
const primary = parseDataVedAnchors(html);
|
|
92
93
|
if (primary.length > 0) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type AbortSignal as BareAbortSignal } from 'bare-abort-controller';
|
|
2
|
+
import { HarnessError } from '../errors.ts';
|
|
2
3
|
import type { Fetcher } from './types.ts';
|
|
3
4
|
export declare const ENGINE_TIMEOUT_MS = 8000;
|
|
4
5
|
export declare const MAX_HTML_CHARS = 2000000;
|
|
5
|
-
export declare class TimeoutError extends
|
|
6
|
+
export declare class TimeoutError extends HarnessError<'FETCH_TIMED_OUT'> {
|
|
6
7
|
constructor(ms: number);
|
|
7
8
|
}
|
|
8
|
-
export declare class AbortError extends
|
|
9
|
+
export declare class AbortError extends HarnessError<'FETCH_CANCELLED'> {
|
|
9
10
|
constructor();
|
|
10
11
|
}
|
|
11
12
|
interface FetchOptions {
|
|
@@ -18,9 +19,8 @@ interface FetchOptions {
|
|
|
18
19
|
readonly signal?: BareAbortSignal;
|
|
19
20
|
}
|
|
20
21
|
export declare function fetchHtml(fetcher: Fetcher, url: string, options?: FetchOptions): Promise<string>;
|
|
21
|
-
export declare class JsonParseError extends
|
|
22
|
-
|
|
23
|
-
constructor(message: string, cause?: unknown);
|
|
22
|
+
export declare class JsonParseError extends HarnessError<'RESPONSE_NOT_JSON'> {
|
|
23
|
+
constructor(chars: number, cause: unknown);
|
|
24
24
|
}
|
|
25
25
|
export declare function fetchJson(fetcher: Fetcher, url: string, options?: FetchOptions): Promise<unknown>;
|
|
26
26
|
export {};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { AbortController } from 'bare-abort-controller';
|
|
2
|
+
import { errorMessage } from '../guards.js';
|
|
3
|
+
import { HarnessError } from '../errors.js';
|
|
2
4
|
import { BROWSER_UA } from './user-agents.js';
|
|
3
5
|
export const ENGINE_TIMEOUT_MS = 8000;
|
|
4
6
|
export const MAX_HTML_CHARS = 2_000_000;
|
|
5
|
-
export class TimeoutError extends
|
|
7
|
+
export class TimeoutError extends HarnessError {
|
|
6
8
|
constructor(ms) {
|
|
7
|
-
super(
|
|
8
|
-
this.name = 'TimeoutError';
|
|
9
|
+
super('FETCH_TIMED_OUT', [ms]);
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
|
-
export class AbortError extends
|
|
12
|
+
export class AbortError extends HarnessError {
|
|
12
13
|
constructor() {
|
|
13
|
-
super('
|
|
14
|
-
this.name = 'AbortError';
|
|
14
|
+
super('FETCH_CANCELLED');
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
function buildInit(options, signal) {
|
|
@@ -74,16 +74,13 @@ async function fetchWithTimeout(fetcher, url, options) {
|
|
|
74
74
|
export async function fetchHtml(fetcher, url, options) {
|
|
75
75
|
const { ok, status, text } = await fetchWithTimeout(fetcher, url, options);
|
|
76
76
|
if (!ok) {
|
|
77
|
-
throw new
|
|
77
|
+
throw new HarnessError('HTTP_STATUS', [status]);
|
|
78
78
|
}
|
|
79
79
|
return text;
|
|
80
80
|
}
|
|
81
|
-
export class JsonParseError extends
|
|
82
|
-
cause
|
|
83
|
-
|
|
84
|
-
super(message);
|
|
85
|
-
this.cause = cause;
|
|
86
|
-
this.name = 'JsonParseError';
|
|
81
|
+
export class JsonParseError extends HarnessError {
|
|
82
|
+
constructor(chars, cause) {
|
|
83
|
+
super('RESPONSE_NOT_JSON', [chars, errorMessage(cause)], { cause });
|
|
87
84
|
}
|
|
88
85
|
}
|
|
89
86
|
export async function fetchJson(fetcher, url, options) {
|
|
@@ -92,12 +89,12 @@ export async function fetchJson(fetcher, url, options) {
|
|
|
92
89
|
accept: options?.accept ?? 'application/json'
|
|
93
90
|
});
|
|
94
91
|
if (!ok) {
|
|
95
|
-
throw new
|
|
92
|
+
throw new HarnessError('HTTP_STATUS', [status]);
|
|
96
93
|
}
|
|
97
94
|
try {
|
|
98
95
|
return JSON.parse(text);
|
|
99
96
|
}
|
|
100
97
|
catch (err) {
|
|
101
|
-
throw new JsonParseError(
|
|
98
|
+
throw new JsonParseError(text.length, err);
|
|
102
99
|
}
|
|
103
100
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qvac/harness",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"bare": ">=1.34.0"
|
|
@@ -163,6 +163,10 @@
|
|
|
163
163
|
"types": "./dist-lib/lib/helpers/race-abort.d.ts",
|
|
164
164
|
"default": "./dist-lib/lib/helpers/race-abort.js"
|
|
165
165
|
},
|
|
166
|
+
"./errors": {
|
|
167
|
+
"types": "./dist-lib/lib/errors.d.ts",
|
|
168
|
+
"default": "./dist-lib/lib/errors.js"
|
|
169
|
+
},
|
|
166
170
|
"./sdk-config-loader": {
|
|
167
171
|
"bare": {
|
|
168
172
|
"types": "./dist-lib/lib/sdk-config-loader.bare.d.ts",
|
|
@@ -190,14 +194,6 @@
|
|
|
190
194
|
"types": "./dist-lib/lib/tools/catalog.d.ts",
|
|
191
195
|
"default": "./dist-lib/lib/tools/catalog.js"
|
|
192
196
|
},
|
|
193
|
-
"./lib/tools/create-documents.ts": {
|
|
194
|
-
"types": "./dist-lib/lib/tools/create-documents.d.ts",
|
|
195
|
-
"default": "./dist-lib/lib/tools/create-documents.js"
|
|
196
|
-
},
|
|
197
|
-
"./lib/tools/edit-documents.ts": {
|
|
198
|
-
"types": "./dist-lib/lib/tools/edit-documents.d.ts",
|
|
199
|
-
"default": "./dist-lib/lib/tools/edit-documents.js"
|
|
200
|
-
},
|
|
201
197
|
"./lib/tools/edit-image.ts": {
|
|
202
198
|
"types": "./dist-lib/lib/tools/edit-image.d.ts",
|
|
203
199
|
"default": "./dist-lib/lib/tools/edit-image.js"
|
|
@@ -374,14 +370,58 @@
|
|
|
374
370
|
"types": "./dist-lib/lib/tools/exec/video-tools.d.ts",
|
|
375
371
|
"default": "./dist-lib/lib/tools/exec/video-tools.js"
|
|
376
372
|
},
|
|
377
|
-
"./lib/tools/file
|
|
378
|
-
"types": "./dist-lib/lib/tools/file
|
|
379
|
-
"default": "./dist-lib/lib/tools/file
|
|
373
|
+
"./lib/tools/files/edit-file.ts": {
|
|
374
|
+
"types": "./dist-lib/lib/tools/files/edit-file.d.ts",
|
|
375
|
+
"default": "./dist-lib/lib/tools/files/edit-file.js"
|
|
376
|
+
},
|
|
377
|
+
"./lib/tools/files/glob.ts": {
|
|
378
|
+
"types": "./dist-lib/lib/tools/files/glob.d.ts",
|
|
379
|
+
"default": "./dist-lib/lib/tools/files/glob.js"
|
|
380
|
+
},
|
|
381
|
+
"./lib/tools/files/grep.ts": {
|
|
382
|
+
"types": "./dist-lib/lib/tools/files/grep.d.ts",
|
|
383
|
+
"default": "./dist-lib/lib/tools/files/grep.js"
|
|
384
|
+
},
|
|
385
|
+
"./lib/tools/files/index.ts": {
|
|
386
|
+
"types": "./dist-lib/lib/tools/files/index.d.ts",
|
|
387
|
+
"default": "./dist-lib/lib/tools/files/index.js"
|
|
388
|
+
},
|
|
389
|
+
"./lib/tools/files/list-dir.ts": {
|
|
390
|
+
"types": "./dist-lib/lib/tools/files/list-dir.d.ts",
|
|
391
|
+
"default": "./dist-lib/lib/tools/files/list-dir.js"
|
|
392
|
+
},
|
|
393
|
+
"./lib/tools/files/make-dir.ts": {
|
|
394
|
+
"types": "./dist-lib/lib/tools/files/make-dir.d.ts",
|
|
395
|
+
"default": "./dist-lib/lib/tools/files/make-dir.js"
|
|
396
|
+
},
|
|
397
|
+
"./lib/tools/files/read-file.ts": {
|
|
398
|
+
"types": "./dist-lib/lib/tools/files/read-file.d.ts",
|
|
399
|
+
"default": "./dist-lib/lib/tools/files/read-file.js"
|
|
400
|
+
},
|
|
401
|
+
"./lib/tools/files/transfer.ts": {
|
|
402
|
+
"types": "./dist-lib/lib/tools/files/transfer.d.ts",
|
|
403
|
+
"default": "./dist-lib/lib/tools/files/transfer.js"
|
|
404
|
+
},
|
|
405
|
+
"./lib/tools/files/trash-file.ts": {
|
|
406
|
+
"types": "./dist-lib/lib/tools/files/trash-file.d.ts",
|
|
407
|
+
"default": "./dist-lib/lib/tools/files/trash-file.js"
|
|
408
|
+
},
|
|
409
|
+
"./lib/tools/files/write-file.ts": {
|
|
410
|
+
"types": "./dist-lib/lib/tools/files/write-file.d.ts",
|
|
411
|
+
"default": "./dist-lib/lib/tools/files/write-file.js"
|
|
412
|
+
},
|
|
413
|
+
"./lib/tools/fs/document-text.ts": {
|
|
414
|
+
"types": "./dist-lib/lib/tools/fs/document-text.d.ts",
|
|
415
|
+
"default": "./dist-lib/lib/tools/fs/document-text.js"
|
|
380
416
|
},
|
|
381
417
|
"./lib/tools/fs/drive.ts": {
|
|
382
418
|
"types": "./dist-lib/lib/tools/fs/drive.d.ts",
|
|
383
419
|
"default": "./dist-lib/lib/tools/fs/drive.js"
|
|
384
420
|
},
|
|
421
|
+
"./lib/tools/fs/edit-match.ts": {
|
|
422
|
+
"types": "./dist-lib/lib/tools/fs/edit-match.d.ts",
|
|
423
|
+
"default": "./dist-lib/lib/tools/fs/edit-match.js"
|
|
424
|
+
},
|
|
385
425
|
"./lib/tools/fs/glob.ts": {
|
|
386
426
|
"types": "./dist-lib/lib/tools/fs/glob.d.ts",
|
|
387
427
|
"default": "./dist-lib/lib/tools/fs/glob.js"
|
|
@@ -406,6 +446,14 @@
|
|
|
406
446
|
"types": "./dist-lib/lib/tools/fs/scope.d.ts",
|
|
407
447
|
"default": "./dist-lib/lib/tools/fs/scope.js"
|
|
408
448
|
},
|
|
449
|
+
"./lib/tools/fs/targets.ts": {
|
|
450
|
+
"types": "./dist-lib/lib/tools/fs/targets.d.ts",
|
|
451
|
+
"default": "./dist-lib/lib/tools/fs/targets.js"
|
|
452
|
+
},
|
|
453
|
+
"./lib/tools/fs/tool-path.ts": {
|
|
454
|
+
"types": "./dist-lib/lib/tools/fs/tool-path.d.ts",
|
|
455
|
+
"default": "./dist-lib/lib/tools/fs/tool-path.js"
|
|
456
|
+
},
|
|
409
457
|
"./lib/tools/fs/walk.ts": {
|
|
410
458
|
"types": "./dist-lib/lib/tools/fs/walk.d.ts",
|
|
411
459
|
"default": "./dist-lib/lib/tools/fs/walk.js"
|
|
@@ -422,10 +470,6 @@
|
|
|
422
470
|
"types": "./dist-lib/lib/tools/generate-music.d.ts",
|
|
423
471
|
"default": "./dist-lib/lib/tools/generate-music.js"
|
|
424
472
|
},
|
|
425
|
-
"./lib/tools/grep.ts": {
|
|
426
|
-
"types": "./dist-lib/lib/tools/grep.d.ts",
|
|
427
|
-
"default": "./dist-lib/lib/tools/grep.js"
|
|
428
|
-
},
|
|
429
473
|
"./lib/tools/http-methods.ts": {
|
|
430
474
|
"types": "./dist-lib/lib/tools/http-methods.d.ts",
|
|
431
475
|
"default": "./dist-lib/lib/tools/http-methods.js"
|
|
@@ -502,10 +546,6 @@
|
|
|
502
546
|
"types": "./dist-lib/lib/tools/private-ip.d.ts",
|
|
503
547
|
"default": "./dist-lib/lib/tools/private-ip.js"
|
|
504
548
|
},
|
|
505
|
-
"./lib/tools/read-files.ts": {
|
|
506
|
-
"types": "./dist-lib/lib/tools/read-files.d.ts",
|
|
507
|
-
"default": "./dist-lib/lib/tools/read-files.js"
|
|
508
|
-
},
|
|
509
549
|
"./lib/tools/skills/auth-resolver.ts": {
|
|
510
550
|
"types": "./dist-lib/lib/tools/skills/auth-resolver.d.ts",
|
|
511
551
|
"default": "./dist-lib/lib/tools/skills/auth-resolver.js"
|
|
@@ -642,7 +682,7 @@
|
|
|
642
682
|
"scripts": {
|
|
643
683
|
"build:schema": "bare schema/build.ts",
|
|
644
684
|
"test": "npm run vendor:wheels && npm run test:bare && npm run test:addons",
|
|
645
|
-
"test:bare": "QVAC_NO_SKILL_BIN_PROBE=1 brittle-jobs 'test/{,!(helpers)
|
|
685
|
+
"test:bare": "QVAC_NO_SKILL_BIN_PROBE=1 brittle-jobs 'test/{,!(helpers)/,!(helpers|fixtures)/*/}*.ts'",
|
|
646
686
|
"format": "prettier . --write",
|
|
647
687
|
"lint": "prettier . --check && lunte && tsc --noEmit",
|
|
648
688
|
"build:bundle": "node scripts/build-bundle.mjs",
|
|
@@ -744,10 +784,12 @@
|
|
|
744
784
|
}
|
|
745
785
|
},
|
|
746
786
|
"dependencies": {
|
|
747
|
-
"@qvac/
|
|
748
|
-
"@qvac/
|
|
749
|
-
"@qvac/
|
|
750
|
-
"@qvac/
|
|
787
|
+
"@qvac/error": "^0.1.1",
|
|
788
|
+
"@qvac/inference": "^0.21.0",
|
|
789
|
+
"@qvac/langdetect-text": "^0.1.2",
|
|
790
|
+
"@qvac/logging": "0.1.1",
|
|
791
|
+
"@qvac/sdk": "^0.21.0",
|
|
792
|
+
"@qvac/skills": "0.1.15",
|
|
751
793
|
"@xmldom/xmldom": "^0.9.12",
|
|
752
794
|
"asenion": "^0.1.0",
|
|
753
795
|
"b4a": "^1.9.0",
|