@qvac/harness 0.2.9 → 0.2.12
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/README.md +15 -7
- package/dist/harness.bundle +11 -62
- package/dist/prebuilds-manifest.json +0 -8
- package/dist-lib/index.d.ts +16 -1
- package/dist-lib/index.js +31 -1
- package/dist-lib/lib/capabilities/completion.d.ts +1 -0
- package/dist-lib/lib/capabilities/completion.js +66 -9
- package/dist-lib/lib/capabilities/detect-language.d.ts +11 -0
- package/dist-lib/lib/capabilities/detect-language.js +13 -0
- package/dist-lib/lib/capabilities/invoke.d.ts +15 -0
- package/dist-lib/lib/capabilities/invoke.js +33 -0
- package/dist-lib/lib/connect.d.ts +4 -1
- package/dist-lib/lib/connect.js +51 -22
- package/dist-lib/lib/contract-types.d.ts +21 -0
- package/dist-lib/lib/docx/index.js +4 -27
- package/dist-lib/lib/hooks/built-in/index.d.ts +1 -0
- package/dist-lib/lib/hooks/built-in/index.js +1 -0
- package/dist-lib/lib/hooks/built-in/sandbox.d.ts +5 -0
- package/dist-lib/lib/hooks/built-in/sandbox.js +45 -0
- package/dist-lib/lib/hooks/built-in/skill-catalog.js +7 -2
- package/dist-lib/lib/hooks/registry.js +2 -1
- package/dist-lib/lib/hooks/types.d.ts +1 -0
- package/dist-lib/lib/office/index.d.ts +6 -0
- package/dist-lib/lib/office/index.js +38 -0
- package/dist-lib/lib/office/pptx.d.ts +1 -0
- package/dist-lib/lib/office/pptx.js +26 -0
- package/dist-lib/lib/office/python.d.ts +7 -0
- package/dist-lib/lib/office/python.js +32 -0
- package/dist-lib/lib/office/xlsx.d.ts +1 -0
- package/dist-lib/lib/office/xlsx.js +26 -0
- package/dist-lib/lib/serve.js +32 -4
- package/dist-lib/lib/tools/edit-image.js +1 -1
- package/dist-lib/lib/tools/generate-image.js +1 -1
- package/dist-lib/lib/tools/generate-music.js +1 -1
- package/dist-lib/lib/tools/http-request.js +4 -4
- package/dist-lib/lib/tools/mcp-call.js +3 -3
- package/dist-lib/lib/tools/skill.js +4 -2
- package/dist-lib/lib/tools/skills/auth-resolver.d.ts +1 -0
- package/dist-lib/lib/tools/skills/auth-resolver.js +59 -29
- package/dist-lib/lib/tools/skills/manifest-parse.d.ts +1 -0
- package/dist-lib/lib/tools/skills/manifest-parse.js +92 -1
- package/dist-lib/lib/tools/skills/manifest.d.ts +12 -0
- package/dist-lib/lib/tools/toolsets.d.ts +1 -0
- package/dist-lib/lib/tools/toolsets.js +1 -0
- package/dist-lib/lib/tools/weather-lookup.js +9 -3
- package/dist-lib/lib/tools/web-fetch.js +18 -5
- package/dist-lib/lib/tools/web-search.js +5 -2
- package/dist-lib/schema/harness.js +54 -4
- package/dist-lib/spec/bind.js +3 -0
- package/dist-lib/spec/calls.js +1 -0
- package/dist-lib/spec/hrpc/hrpc.json +13 -0
- package/dist-lib/spec/hrpc/index.js +15 -3
- package/dist-lib/spec/hrpc/messages.js +207 -13
- package/dist-lib/spec/hyperschema/index.js +207 -13
- package/dist-lib/spec/hyperschema/schema.json +163 -1
- package/dist-lib/spec/hyperschema/types.d.ts +43 -2
- package/package.json +7 -2
|
@@ -29,6 +29,17 @@ export interface SkillSetupField {
|
|
|
29
29
|
readonly secret?: boolean;
|
|
30
30
|
readonly required?: boolean;
|
|
31
31
|
}
|
|
32
|
+
export interface SkillOAuthConfig {
|
|
33
|
+
readonly stateKey: string;
|
|
34
|
+
readonly authUrl: string;
|
|
35
|
+
readonly tokenUrl: string;
|
|
36
|
+
readonly scopes: readonly string[];
|
|
37
|
+
readonly tokenAuth: 'pkce-only' | 'secret-in-body' | 'secret-in-basic';
|
|
38
|
+
readonly extraAuthParams?: Readonly<Record<string, string>>;
|
|
39
|
+
readonly registrationEndpoint?: string;
|
|
40
|
+
readonly port?: number;
|
|
41
|
+
readonly sharedIosSession?: boolean;
|
|
42
|
+
}
|
|
32
43
|
export interface SkillSetupRoute {
|
|
33
44
|
readonly kind: 'oauth' | 'token' | 'install' | 'picker' | 'instructions';
|
|
34
45
|
readonly label: string;
|
|
@@ -40,6 +51,7 @@ export interface SkillSetupRoute {
|
|
|
40
51
|
readonly configSteps?: readonly string[];
|
|
41
52
|
readonly fields?: readonly SkillSetupField[];
|
|
42
53
|
readonly satisfied?: boolean;
|
|
54
|
+
readonly oauth?: SkillOAuthConfig;
|
|
43
55
|
}
|
|
44
56
|
export interface SkillSetup {
|
|
45
57
|
readonly summary?: string;
|
|
@@ -4,5 +4,6 @@ export declare function asToolset(value?: string | null): Toolset | null;
|
|
|
4
4
|
export interface ToolsetScoped {
|
|
5
5
|
toolset?: string | null;
|
|
6
6
|
}
|
|
7
|
+
export declare const NETWORK_TOOLSETS: ReadonlyArray<Toolset>;
|
|
7
8
|
export declare function permittedTools<T extends ToolsetScoped>(tools: ReadonlyArray<T>, disabled?: ReadonlyArray<string> | null): T[];
|
|
8
9
|
export declare function toolsetEnabled(toolset: Toolset, disabled?: ReadonlyArray<string> | null): boolean;
|
|
@@ -9,6 +9,7 @@ export const TOOLSETS = [
|
|
|
9
9
|
export function asToolset(value) {
|
|
10
10
|
return TOOLSETS.find((toolset) => toolset === value) ?? null;
|
|
11
11
|
}
|
|
12
|
+
export const NETWORK_TOOLSETS = ['web-search', 'http-request', 'mcp-call'];
|
|
12
13
|
export function permittedTools(tools, disabled) {
|
|
13
14
|
if (!disabled?.length)
|
|
14
15
|
return [...tools];
|
|
@@ -63,7 +63,7 @@ export function createWeatherLookupTool({ fetch: fetchImpl, resolveHost = lookup
|
|
|
63
63
|
if (/location not found/i.test(body))
|
|
64
64
|
return { content: notFound(location) };
|
|
65
65
|
if (response.status !== 200) {
|
|
66
|
-
return { error: unavailable(`it answered HTTP ${response.status}`) };
|
|
66
|
+
return { error: unavailable(`it answered HTTP ${response.status}`), code: 'transport' };
|
|
67
67
|
}
|
|
68
68
|
let json;
|
|
69
69
|
try {
|
|
@@ -91,9 +91,15 @@ export function createWeatherLookupTool({ fetch: fetchImpl, resolveHost = lookup
|
|
|
91
91
|
if (ctx?.signal?.aborted)
|
|
92
92
|
return { error: 'weather_lookup cancelled' };
|
|
93
93
|
if (controller.signal.aborted) {
|
|
94
|
-
return {
|
|
94
|
+
return {
|
|
95
|
+
error: `weather_lookup timed out after ${LOOKUP_TIMEOUT_MS} ms`,
|
|
96
|
+
code: 'timeout'
|
|
97
|
+
};
|
|
95
98
|
}
|
|
96
|
-
return {
|
|
99
|
+
return {
|
|
100
|
+
error: unavailable(err instanceof Error ? err.message : String(err)),
|
|
101
|
+
code: 'transport'
|
|
102
|
+
};
|
|
97
103
|
}
|
|
98
104
|
finally {
|
|
99
105
|
clearTimeout(timeout);
|
|
@@ -28,7 +28,11 @@ export function createWebFetchTool({ fetch: fetchImpl, resolveHost = lookupHost
|
|
|
28
28
|
toolset: 'web-search',
|
|
29
29
|
description: 'Fetch a public web page and return its readable text. ' +
|
|
30
30
|
'Use this to read the full content behind a web_search result URL, or a page the user ' +
|
|
31
|
-
'shares, when a search snippet is not enough to answer.'
|
|
31
|
+
'shares, when a search snippet is not enough to answer. ' +
|
|
32
|
+
'When the URL serves a file rather than a page — PDF, .docx, .xlsx, .pptx, an image, any ' +
|
|
33
|
+
'binary — this is also the tool that downloads it: the bytes become a chat attachment and ' +
|
|
34
|
+
'the result carries its attachmentId. It is the only way to get a remote file into the ' +
|
|
35
|
+
'chat; exec has no network and http_request truncates binary bodies.',
|
|
32
36
|
parameters: {
|
|
33
37
|
type: 'object',
|
|
34
38
|
properties: {
|
|
@@ -79,7 +83,10 @@ export function createWebFetchTool({ fetch: fetchImpl, resolveHost = lookupHost
|
|
|
79
83
|
return { error: got.error };
|
|
80
84
|
const { response, finalUrl: currentUrl } = got;
|
|
81
85
|
if (response.status < 200 || response.status >= 300) {
|
|
82
|
-
return {
|
|
86
|
+
return {
|
|
87
|
+
error: `HTTP ${response.status} fetching ${currentUrl.toString()}`,
|
|
88
|
+
status: response.status
|
|
89
|
+
};
|
|
83
90
|
}
|
|
84
91
|
const rawBody = await response.text();
|
|
85
92
|
const body = rawBody.length > MAX_HTML_CHARS ? rawBody.slice(0, MAX_HTML_CHARS) : rawBody;
|
|
@@ -140,9 +147,9 @@ export function createWebFetchTool({ fetch: fetchImpl, resolveHost = lookupHost
|
|
|
140
147
|
return { error: 'web_fetch cancelled' };
|
|
141
148
|
}
|
|
142
149
|
if (controller.signal.aborted) {
|
|
143
|
-
return { error: `${url.hostname} timed out after ${PAGE_TIMEOUT_MS}ms
|
|
150
|
+
return { error: `${url.hostname} timed out after ${PAGE_TIMEOUT_MS}ms`, code: 'timeout' };
|
|
144
151
|
}
|
|
145
|
-
return { error: err instanceof Error ? err.message : String(err) };
|
|
152
|
+
return { error: err instanceof Error ? err.message : String(err), code: 'transport' };
|
|
146
153
|
}
|
|
147
154
|
finally {
|
|
148
155
|
clearTimeout(timeout);
|
|
@@ -175,8 +182,14 @@ async function attachDocument(response, url, mimeType, attach) {
|
|
|
175
182
|
raw: !mime.startsWith('image/')
|
|
176
183
|
});
|
|
177
184
|
return {
|
|
185
|
+
attachmentId: record.id,
|
|
186
|
+
fileName: record.fileName,
|
|
187
|
+
byteLength: record.byteLength,
|
|
178
188
|
content: `Attached the file "${record.fileName}" (${Math.round(record.byteLength / 1024)}KB, ${mime}) from ${url.toString()} — ` +
|
|
179
|
-
'the user can open or save it. Its contents were NOT read
|
|
189
|
+
'the user can open or save it. Its contents were NOT read, so do not describe what is inside ' +
|
|
190
|
+
'yet. To read it, stage it with exec as ' +
|
|
191
|
+
`inputs [{ "attachmentId": "${record.id}", "path": "${record.fileName}" }] — never ask the ` +
|
|
192
|
+
'user to attach a file this call already attached.'
|
|
180
193
|
};
|
|
181
194
|
}
|
|
182
195
|
const MIME_EXTENSIONS = {
|
|
@@ -43,12 +43,15 @@ export function createWebSearchTool({ fetch } = {}) {
|
|
|
43
43
|
}
|
|
44
44
|
const failed = engineSummary.filter((e) => e.status !== 'ok').length;
|
|
45
45
|
if (engineSummary.length > 0 && failed === engineSummary.length) {
|
|
46
|
-
return {
|
|
46
|
+
return {
|
|
47
|
+
error: `web_search unavailable: all ${failed} engines failed (timeout/error)`,
|
|
48
|
+
code: 'transport'
|
|
49
|
+
};
|
|
47
50
|
}
|
|
48
51
|
return { content: `No web results found for "${query}".` };
|
|
49
52
|
}
|
|
50
53
|
catch (err) {
|
|
51
|
-
return { error: err instanceof Error ? err.message : String(err) };
|
|
54
|
+
return { error: err instanceof Error ? err.message : String(err), code: 'transport' };
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
};
|
|
@@ -23,7 +23,8 @@ export function registerHarnessTypes(ns) {
|
|
|
23
23
|
{ name: 'imageWidth', type: 'uint' },
|
|
24
24
|
{ name: 'imageHeight', type: 'uint' },
|
|
25
25
|
{ name: 'approvalMode', type: '@harness/approval-mode' },
|
|
26
|
-
{ name: 'disabledToolsets', type: 'string', array: true }
|
|
26
|
+
{ name: 'disabledToolsets', type: 'string', array: true },
|
|
27
|
+
{ name: 'sandboxMode', type: 'bool' }
|
|
27
28
|
]
|
|
28
29
|
});
|
|
29
30
|
ns.register({
|
|
@@ -68,7 +69,8 @@ export function registerHarnessTypes(ns) {
|
|
|
68
69
|
'fetch-result',
|
|
69
70
|
'rag-result',
|
|
70
71
|
'question-result',
|
|
71
|
-
'drive-result'
|
|
72
|
+
'drive-result',
|
|
73
|
+
'invoke'
|
|
72
74
|
],
|
|
73
75
|
strings: true
|
|
74
76
|
});
|
|
@@ -108,7 +110,9 @@ export function registerHarnessTypes(ns) {
|
|
|
108
110
|
{ name: 'grantedDrives', type: '@harness/granted-drive', array: true },
|
|
109
111
|
{ name: 'payload', type: 'json' },
|
|
110
112
|
{ name: 'grantedWriteDirs', type: 'string', array: true },
|
|
111
|
-
{ name: 'captureInferenceCalls', type: 'bool' }
|
|
113
|
+
{ name: 'captureInferenceCalls', type: 'bool' },
|
|
114
|
+
{ name: 'capability', type: 'string' },
|
|
115
|
+
{ name: 'arguments', type: 'json' }
|
|
112
116
|
]
|
|
113
117
|
});
|
|
114
118
|
ns.register({
|
|
@@ -458,6 +462,25 @@ export function registerHarnessTypes(ns) {
|
|
|
458
462
|
name: 'transcribe-response',
|
|
459
463
|
fields: [{ name: 'text', type: 'string', required: true }]
|
|
460
464
|
});
|
|
465
|
+
ns.register({
|
|
466
|
+
name: 'detect-language-request',
|
|
467
|
+
fields: [
|
|
468
|
+
{ name: 'text', type: 'string', required: true },
|
|
469
|
+
{ name: 'topK', type: 'uint' }
|
|
470
|
+
]
|
|
471
|
+
});
|
|
472
|
+
ns.register({
|
|
473
|
+
name: 'detected-language',
|
|
474
|
+
fields: [
|
|
475
|
+
{ name: 'code', type: 'string', required: true },
|
|
476
|
+
{ name: 'language', type: 'string', required: true },
|
|
477
|
+
{ name: 'probability', type: 'float64' }
|
|
478
|
+
]
|
|
479
|
+
});
|
|
480
|
+
ns.register({
|
|
481
|
+
name: 'detect-language-response',
|
|
482
|
+
fields: [{ name: 'languages', type: '@harness/detected-language', array: true, required: true }]
|
|
483
|
+
});
|
|
461
484
|
ns.register({
|
|
462
485
|
name: 'speak-request',
|
|
463
486
|
fields: [
|
|
@@ -506,7 +529,8 @@ export function registerHarnessTypes(ns) {
|
|
|
506
529
|
{ name: 'steps', type: 'string', array: true },
|
|
507
530
|
{ name: 'configSteps', type: 'string', array: true },
|
|
508
531
|
{ name: 'fields', type: '@harness/skill-setup-field', array: true },
|
|
509
|
-
{ name: 'satisfied', type: 'bool' }
|
|
532
|
+
{ name: 'satisfied', type: 'bool' },
|
|
533
|
+
{ name: 'oauth', type: '@harness/skill-oauth-config' }
|
|
510
534
|
]
|
|
511
535
|
});
|
|
512
536
|
ns.register({
|
|
@@ -613,6 +637,27 @@ export function registerHarnessTypes(ns) {
|
|
|
613
637
|
name: 'kv-cache-delete-request',
|
|
614
638
|
fields: [{ name: 'kvCacheKey', type: 'string', required: true }]
|
|
615
639
|
});
|
|
640
|
+
ns.register({
|
|
641
|
+
name: 'skill-oauth-param',
|
|
642
|
+
fields: [
|
|
643
|
+
{ name: 'key', type: 'string', required: true },
|
|
644
|
+
{ name: 'value', type: 'string', required: true }
|
|
645
|
+
]
|
|
646
|
+
});
|
|
647
|
+
ns.register({
|
|
648
|
+
name: 'skill-oauth-config',
|
|
649
|
+
fields: [
|
|
650
|
+
{ name: 'stateKey', type: 'string', required: true },
|
|
651
|
+
{ name: 'authUrl', type: 'string', required: true },
|
|
652
|
+
{ name: 'tokenUrl', type: 'string', required: true },
|
|
653
|
+
{ name: 'tokenAuth', type: 'string', required: true },
|
|
654
|
+
{ name: 'scopes', type: 'string', array: true },
|
|
655
|
+
{ name: 'extraAuthParams', type: '@harness/skill-oauth-param', array: true },
|
|
656
|
+
{ name: 'registrationEndpoint', type: 'string' },
|
|
657
|
+
{ name: 'port', type: 'uint' },
|
|
658
|
+
{ name: 'sharedIosSession', type: 'bool' }
|
|
659
|
+
]
|
|
660
|
+
});
|
|
616
661
|
}
|
|
617
662
|
export function registerHarnessApi(api) {
|
|
618
663
|
api.register({
|
|
@@ -755,4 +800,9 @@ export function registerHarnessApi(api) {
|
|
|
755
800
|
request: { name: '@harness/kv-cache-delete-request', stream: false },
|
|
756
801
|
response: { name: '@harness/kv-cache-deletion-response', stream: false }
|
|
757
802
|
});
|
|
803
|
+
api.register({
|
|
804
|
+
name: 'detect-language',
|
|
805
|
+
request: { name: '@harness/detect-language-request', stream: false },
|
|
806
|
+
response: { name: '@harness/detect-language-response', stream: false }
|
|
807
|
+
});
|
|
758
808
|
}
|
package/dist-lib/spec/bind.js
CHANGED
package/dist-lib/spec/calls.js
CHANGED
|
@@ -38,5 +38,6 @@ export function harnessClient(rpc) {
|
|
|
38
38
|
copySkillForEdit: (input) => rpc.copySkillForEdit(input),
|
|
39
39
|
deleteOverlaySkill: (input) => rpc.deleteOverlaySkill(input),
|
|
40
40
|
deleteKvCache: (input) => rpc.deleteKvCache(input),
|
|
41
|
+
detectLanguage: (input) => rpc.detectLanguage(input),
|
|
41
42
|
};
|
|
42
43
|
}
|
|
@@ -364,6 +364,19 @@
|
|
|
364
364
|
"stream": false
|
|
365
365
|
},
|
|
366
366
|
"version": 1
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"id": 28,
|
|
370
|
+
"name": "@harness/detect-language",
|
|
371
|
+
"request": {
|
|
372
|
+
"name": "@harness/detect-language-request",
|
|
373
|
+
"stream": false
|
|
374
|
+
},
|
|
375
|
+
"response": {
|
|
376
|
+
"name": "@harness/detect-language-response",
|
|
377
|
+
"stream": false
|
|
378
|
+
},
|
|
379
|
+
"version": 1
|
|
367
380
|
}
|
|
368
381
|
]
|
|
369
382
|
}
|
|
@@ -61,7 +61,9 @@ const methods = new Map([
|
|
|
61
61
|
['@harness/delete-overlay-skill', 26],
|
|
62
62
|
[26, '@harness/delete-overlay-skill'],
|
|
63
63
|
['@harness/delete-kv-cache', 27],
|
|
64
|
-
[27, '@harness/delete-kv-cache']
|
|
64
|
+
[27, '@harness/delete-kv-cache'],
|
|
65
|
+
['@harness/detect-language', 28],
|
|
66
|
+
[28, '@harness/detect-language']
|
|
65
67
|
])
|
|
66
68
|
|
|
67
69
|
class HRPC {
|
|
@@ -96,7 +98,8 @@ class HRPC {
|
|
|
96
98
|
['@harness/write-skill-file', getEncoding('@harness/write-skill-request')],
|
|
97
99
|
['@harness/copy-skill-for-edit', getEncoding('@harness/skill-name-request')],
|
|
98
100
|
['@harness/delete-overlay-skill', getEncoding('@harness/skill-name-request')],
|
|
99
|
-
['@harness/delete-kv-cache', getEncoding('@harness/kv-cache-delete-request')]
|
|
101
|
+
['@harness/delete-kv-cache', getEncoding('@harness/kv-cache-delete-request')],
|
|
102
|
+
['@harness/detect-language', getEncoding('@harness/detect-language-request')]
|
|
100
103
|
])
|
|
101
104
|
this._responseEncodings = new Map([
|
|
102
105
|
['@harness/completion', getEncoding('@harness/completion-event')],
|
|
@@ -126,7 +129,8 @@ class HRPC {
|
|
|
126
129
|
['@harness/write-skill-file', getEncoding('@harness/ok-response')],
|
|
127
130
|
['@harness/copy-skill-for-edit', getEncoding('@harness/copy-skill-response')],
|
|
128
131
|
['@harness/delete-overlay-skill', getEncoding('@harness/delete-skill-response')],
|
|
129
|
-
['@harness/delete-kv-cache', getEncoding('@harness/kv-cache-deletion-response')]
|
|
132
|
+
['@harness/delete-kv-cache', getEncoding('@harness/kv-cache-deletion-response')],
|
|
133
|
+
['@harness/detect-language', getEncoding('@harness/detect-language-response')]
|
|
130
134
|
])
|
|
131
135
|
this._rpc = new RPC(stream, async (req) => {
|
|
132
136
|
const command = methods.get(req.command)
|
|
@@ -336,6 +340,10 @@ class HRPC {
|
|
|
336
340
|
return this._call('@harness/delete-kv-cache', args)
|
|
337
341
|
}
|
|
338
342
|
|
|
343
|
+
async detectLanguage(args) {
|
|
344
|
+
return this._call('@harness/detect-language', args)
|
|
345
|
+
}
|
|
346
|
+
|
|
339
347
|
onCompletion(responseFn) {
|
|
340
348
|
this._handlers['@harness/completion'] = responseFn
|
|
341
349
|
}
|
|
@@ -448,6 +456,10 @@ class HRPC {
|
|
|
448
456
|
this._handlers['@harness/delete-kv-cache'] = responseFn
|
|
449
457
|
}
|
|
450
458
|
|
|
459
|
+
onDetectLanguage(responseFn) {
|
|
460
|
+
this._handlers['@harness/detect-language'] = responseFn
|
|
461
|
+
}
|
|
462
|
+
|
|
451
463
|
_requestIsStream(command) {
|
|
452
464
|
return [
|
|
453
465
|
'@harness/completion',
|