@qvac/harness 0.7.0 → 1.0.0-rc.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 +42 -1
- package/README.md +239 -2
- package/dist-lib/index.d.ts +58 -3
- package/dist-lib/index.js +736 -239
- package/dist-lib/lib/capabilities/completion-engine/abort-polyfill.d.ts +1 -0
- package/dist-lib/lib/capabilities/completion-engine/abort-polyfill.js +10 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/answering.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/answering.js +17 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/approval.d.ts +4 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/approval.js +29 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/direct-search.d.ts +4 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/direct-search.js +73 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/execute.d.ts +41 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/execute.js +49 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/executing.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/executing.js +102 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/execution-plan.d.ts +47 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/execution-plan.js +87 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/finishing.d.ts +2 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/finishing.js +32 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/inspect-preload.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/inspect-preload.js +75 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/inspecting.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/inspecting.js +152 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/loading-skill.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/loading-skill.js +94 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preload-failure.d.ts +9 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preload-failure.js +11 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preloading.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preloading.js +114 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preparing.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preparing.js +198 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/rejecting.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/rejecting.js +71 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/rejection.d.ts +18 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/rejection.js +70 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/resolving.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/resolving.js +60 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/stream.d.ts +21 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/stream.js +48 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/streaming.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/streaming.js +156 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/wrap-up.d.ts +7 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/wrap-up.js +61 -0
- package/dist-lib/lib/capabilities/completion-engine/driver.d.ts +25 -0
- package/dist-lib/lib/capabilities/completion-engine/driver.js +553 -0
- package/dist-lib/lib/capabilities/completion-engine/durable.d.ts +61 -0
- package/dist-lib/lib/capabilities/completion-engine/durable.js +150 -0
- package/dist-lib/lib/capabilities/completion-engine/operations.d.ts +6 -0
- package/dist-lib/lib/capabilities/completion-engine/operations.js +239 -0
- package/dist-lib/lib/capabilities/completion-engine/ports.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/ports.js +267 -0
- package/dist-lib/lib/capabilities/completion-engine/session.d.ts +76 -0
- package/dist-lib/lib/capabilities/completion-engine/session.js +70 -0
- package/dist-lib/lib/capabilities/completion-loop/index.d.ts +80 -0
- package/dist-lib/lib/capabilities/completion-loop/index.js +158 -0
- package/dist-lib/lib/capabilities/completion-loop/metrics.d.ts +5 -0
- package/dist-lib/lib/capabilities/completion-loop/metrics.js +29 -0
- package/dist-lib/lib/capabilities/completion-loop/round.d.ts +33 -0
- package/dist-lib/lib/capabilities/completion-loop/round.js +353 -0
- package/dist-lib/lib/capabilities/completion-loop/tool-execution.d.ts +60 -0
- package/dist-lib/lib/capabilities/completion-loop/tool-execution.js +270 -0
- package/dist-lib/lib/capabilities/completion-loop/turns.d.ts +23 -0
- package/dist-lib/lib/capabilities/completion-loop/turns.js +112 -0
- package/dist-lib/lib/capabilities/completion-runtime.d.ts +46 -0
- package/dist-lib/lib/capabilities/completion-runtime.js +74 -0
- package/dist-lib/lib/capabilities/completion.d.ts +18 -335
- package/dist-lib/lib/capabilities/completion.js +20 -1301
- package/dist-lib/lib/capabilities/download.d.ts +1 -1
- package/dist-lib/lib/capabilities/download.js +7 -2
- package/dist-lib/lib/capabilities/invoke.js +5 -3
- package/dist-lib/lib/capabilities/tool-plan.d.ts +35 -0
- package/dist-lib/lib/capabilities/tool-plan.js +66 -0
- package/dist-lib/lib/capabilities/tool-turns.d.ts +11 -2
- package/dist-lib/lib/capabilities/tool-turns.js +30 -2
- package/dist-lib/lib/capabilities/types.d.ts +287 -0
- package/dist-lib/lib/capabilities/types.js +1 -0
- package/dist-lib/lib/completion-lifecycle.d.ts +14 -0
- package/dist-lib/lib/completion-lifecycle.js +126 -0
- package/dist-lib/lib/connect.d.ts +10 -3
- package/dist-lib/lib/connect.js +329 -23
- package/dist-lib/lib/contract-types.d.ts +19 -2
- package/dist-lib/lib/harness-lifecycle.d.ts +66 -0
- package/dist-lib/lib/harness-lifecycle.js +129 -0
- package/dist-lib/lib/helpers/detach-json.d.ts +1 -0
- package/dist-lib/lib/helpers/detach-json.js +146 -0
- package/dist-lib/lib/helpers/markup-scanner.d.ts +26 -0
- package/dist-lib/lib/helpers/markup-scanner.js +156 -0
- package/dist-lib/lib/helpers/markup-stream-filter.js +138 -35
- package/dist-lib/lib/helpers/race-abort.d.ts +9 -4
- package/dist-lib/lib/helpers/race-abort.js +2 -7
- package/dist-lib/lib/helpers/recovery-required.d.ts +1 -0
- package/dist-lib/lib/helpers/recovery-required.js +1 -0
- package/dist-lib/lib/helpers/run-outcome.d.ts +10 -2
- package/dist-lib/lib/helpers/run-outcome.js +8 -5
- package/dist-lib/lib/helpers/tool-call-markup.d.ts +2 -0
- package/dist-lib/lib/helpers/tool-call-markup.js +197 -82
- package/dist-lib/lib/hooks/built-in/arg-repair.d.ts +1 -0
- package/dist-lib/lib/hooks/built-in/arg-repair.js +1 -0
- package/dist-lib/lib/hooks/built-in/egress.d.ts +1 -0
- package/dist-lib/lib/hooks/built-in/egress.js +2 -1
- package/dist-lib/lib/hooks/built-in/sandbox.d.ts +1 -0
- package/dist-lib/lib/hooks/built-in/sandbox.js +6 -1
- package/dist-lib/lib/hooks/built-in/skill-catalog.d.ts +1 -0
- package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
- package/dist-lib/lib/hooks/pipeline.d.ts +4 -1
- package/dist-lib/lib/hooks/pipeline.js +24 -2
- package/dist-lib/lib/hooks/run.d.ts +2 -1
- package/dist-lib/lib/hooks/run.js +19 -7
- package/dist-lib/lib/hooks/types.d.ts +17 -6
- package/dist-lib/lib/machine/completion/actions/approval.d.ts +3 -0
- package/dist-lib/lib/machine/completion/actions/approval.js +16 -0
- package/dist-lib/lib/machine/completion/actions/completion.d.ts +23 -0
- package/dist-lib/lib/machine/completion/actions/completion.js +142 -0
- package/dist-lib/lib/machine/completion/actions/skills.d.ts +9 -0
- package/dist-lib/lib/machine/completion/actions/skills.js +83 -0
- package/dist-lib/lib/machine/completion/actions/tools.d.ts +16 -0
- package/dist-lib/lib/machine/completion/actions/tools.js +173 -0
- package/dist-lib/lib/machine/completion/context.d.ts +9 -0
- package/dist-lib/lib/machine/completion/context.js +44 -0
- package/dist-lib/lib/machine/completion/contract.d.ts +6 -0
- package/dist-lib/lib/machine/completion/contract.js +1 -0
- package/dist-lib/lib/machine/completion/guards/retry.d.ts +9 -0
- package/dist-lib/lib/machine/completion/guards/retry.js +25 -0
- package/dist-lib/lib/machine/completion/implementations.d.ts +81 -0
- package/dist-lib/lib/machine/completion/implementations.js +152 -0
- package/dist-lib/lib/machine/completion/machine.d.ts +375 -0
- package/dist-lib/lib/machine/completion/machine.js +511 -0
- package/dist-lib/lib/machine/completion/ports.d.ts +65 -0
- package/dist-lib/lib/machine/completion/ports.js +224 -0
- package/dist-lib/lib/machine/completion/rules.d.ts +39 -0
- package/dist-lib/lib/machine/completion/rules.js +115 -0
- package/dist-lib/lib/machine/completion/types.d.ts +493 -0
- package/dist-lib/lib/machine/completion/types.js +1 -0
- package/dist-lib/lib/machine/engine.d.ts +93 -0
- package/dist-lib/lib/machine/engine.js +483 -0
- package/dist-lib/lib/machine/errors.d.ts +44 -0
- package/dist-lib/lib/machine/errors.js +373 -0
- package/dist-lib/lib/machine/guards.d.ts +2 -0
- package/dist-lib/lib/machine/guards.js +6 -0
- package/dist-lib/lib/machine/index.d.ts +20 -0
- package/dist-lib/lib/machine/index.js +11 -0
- package/dist-lib/lib/machine/replay-output.d.ts +24 -0
- package/dist-lib/lib/machine/replay-output.js +50 -0
- package/dist-lib/lib/machine/stable-stringify.d.ts +1 -0
- package/dist-lib/lib/machine/stable-stringify.js +11 -0
- package/dist-lib/lib/models/model-handle.d.ts +5 -1
- package/dist-lib/lib/models/model-handle.js +8 -3
- package/dist-lib/lib/models/model-list.d.ts +5 -3
- package/dist-lib/lib/models/model-pool.d.ts +18 -7
- package/dist-lib/lib/models/model-pool.js +126 -38
- package/dist-lib/lib/run-log/history-codec.d.ts +23 -0
- package/dist-lib/lib/run-log/history-codec.js +973 -0
- package/dist-lib/lib/run-log/patch.d.ts +6 -0
- package/dist-lib/lib/run-log/patch.js +93 -0
- package/dist-lib/lib/run-log/persisted-actor.d.ts +18 -0
- package/dist-lib/lib/run-log/persisted-actor.js +218 -0
- package/dist-lib/lib/run-log/protocol-types.d.ts +153 -0
- package/dist-lib/lib/run-log/protocol-types.js +1 -0
- package/dist-lib/lib/run-log/protocol.d.ts +27 -0
- package/dist-lib/lib/run-log/protocol.js +1056 -0
- package/dist-lib/lib/run-log/replay.d.ts +25 -0
- package/dist-lib/lib/run-log/replay.js +129 -0
- package/dist-lib/lib/run-log/run-journal.d.ts +125 -0
- package/dist-lib/lib/run-log/run-journal.js +783 -0
- package/dist-lib/lib/run-log-inspect.d.ts +25 -0
- package/dist-lib/lib/run-log-inspect.js +627 -0
- package/dist-lib/lib/run-log-protocol.d.ts +2 -0
- package/dist-lib/lib/run-log-protocol.js +1 -0
- package/dist-lib/lib/run-log-redact.d.ts +27 -0
- package/dist-lib/lib/run-log-redact.js +1107 -0
- package/dist-lib/lib/serve.js +308 -12
- package/dist-lib/lib/spawn.js +3 -2
- package/dist-lib/lib/tool-call-display.d.ts +1 -21
- package/dist-lib/lib/tools/exec.d.ts +2 -1
- package/dist-lib/lib/tools/exec.js +57 -2
- 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.d.ts +2 -1
- package/dist-lib/lib/tools/http-request.js +2 -1
- package/dist-lib/lib/tools/mcp-call.js +8 -1
- package/dist-lib/lib/tools/operations/builders.d.ts +1 -0
- package/dist-lib/lib/tools/operations/builders.js +1 -0
- package/dist-lib/lib/tools/operations/compile.js +11 -1
- package/dist-lib/lib/tools/operations/manifest.js +1 -0
- package/dist-lib/lib/tools/outbound-guard.d.ts +8 -6
- package/dist-lib/lib/tools/outbound-guard.js +25 -8
- package/dist-lib/lib/tools/skills/catalog.d.ts +1 -0
- package/dist-lib/lib/tools/skills/catalog.js +35 -0
- package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +1 -0
- package/dist-lib/lib/tools/skills/cli-schema.js +25 -0
- package/dist-lib/lib/wire.d.ts +5 -0
- package/dist-lib/lib/wire.js +128 -5
- package/dist-lib/schema/harness.js +22 -6
- package/dist-lib/spec/hrpc/messages.js +100 -18
- package/dist-lib/spec/hyperschema/index.js +100 -18
- package/dist-lib/spec/hyperschema/schema.json +74 -1
- package/dist-lib/spec/hyperschema/types.d.ts +11 -3
- package/package.json +19 -1
- package/patches/@qvac%2Finference@0.20.0.patch +67 -0
- package/patches/bare-stow@0.1.5.patch +68 -0
- package/patches/hypercore-blob-server@1.15.0.patch +15 -0
- package/patches/localwatch@1.4.3.patch +13 -0
- package/patches/pyodide@314.0.2.patch +144 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { BUILDERS, BUILDER_POLICY_VERSION } from './builders.js';
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import { stableStringify } from '../../machine/index.js';
|
|
2
5
|
import { READ_METHODS } from '../http-methods.js';
|
|
3
6
|
import { truncateResponseBody } from '../http-request/truncate-response.js';
|
|
4
|
-
import { BUILDERS } from './builders.js';
|
|
5
7
|
import { DECODERS } from './decoders.js';
|
|
6
8
|
export function compileOperationTools(catalog, { httpRequestTool, mcpCallTool }) {
|
|
7
9
|
return catalog.flatMap((entry) => (entry.operations ?? []).flatMap((operation) => {
|
|
@@ -15,6 +17,14 @@ function operationTool(owningSkill, operation, transportTool) {
|
|
|
15
17
|
const read = !mcp && READ_METHODS.has(operation.request.method);
|
|
16
18
|
return {
|
|
17
19
|
name: operation.tool,
|
|
20
|
+
policyVersion: createHash('sha256')
|
|
21
|
+
.update(stableStringify({
|
|
22
|
+
owningSkill,
|
|
23
|
+
operation,
|
|
24
|
+
builderVersion: BUILDER_POLICY_VERSION,
|
|
25
|
+
transportVersion: transportTool.policyVersion ?? null
|
|
26
|
+
}))
|
|
27
|
+
.digest('hex'),
|
|
18
28
|
owningSkill,
|
|
19
29
|
description: operation.description,
|
|
20
30
|
parameters: operation.parameters,
|
|
@@ -4,6 +4,7 @@ import { DECODER_NAMES } from './decoders.js';
|
|
|
4
4
|
const ParameterType = z.enum(['string', 'number', 'boolean', 'object', 'integer', 'array', 'null']);
|
|
5
5
|
const ParameterSchema = z.lazy(() => z.object({
|
|
6
6
|
type: z.union([ParameterType, z.array(ParameterType).min(1)]),
|
|
7
|
+
sensitive: z.literal(true).optional(),
|
|
7
8
|
description: z.string().optional(),
|
|
8
9
|
enum: z.array(z.string()).optional(),
|
|
9
10
|
items: ParameterSchema.optional(),
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import type { TrustedOrigin, TrustedSource, TrustedSourceFacts } from '../machine/index.ts';
|
|
1
2
|
export declare const REFUSAL = "Refusing to send:";
|
|
2
|
-
export type TrustedOrigin
|
|
3
|
-
export interface TrustedSource {
|
|
4
|
-
origin: TrustedOrigin;
|
|
5
|
-
text: string;
|
|
6
|
-
}
|
|
3
|
+
export type { TrustedOrigin, TrustedSource };
|
|
7
4
|
export interface GuardOptions {
|
|
8
5
|
readonly sources: readonly TrustedSource[];
|
|
9
6
|
externalWrite?: boolean;
|
|
10
7
|
}
|
|
11
|
-
export
|
|
8
|
+
export interface GuardFactOptions extends GuardOptions {
|
|
9
|
+
readonly sourceFacts?: readonly TrustedSourceFacts[];
|
|
10
|
+
}
|
|
11
|
+
export declare function trustedSourceFacts(origin: TrustedSourceFacts['origin'], text: string): TrustedSourceFacts;
|
|
12
|
+
export declare function guardToolCall(toolName: string, args: Record<string, unknown>, options: GuardOptions): string | null;
|
|
13
|
+
export declare function guardToolCallWithFacts(toolName: string, args: Record<string, unknown>, { sources, sourceFacts, externalWrite }: GuardFactOptions): string | null;
|
|
@@ -9,7 +9,18 @@ const OUTBOUND_TOOLS = new Set(['http_request', 'mcp_call']);
|
|
|
9
9
|
const MCP_READ_METHOD = /^(tools\/list|resources\/\w+|(get|list|search|read|find|fetch)[_.-])/i;
|
|
10
10
|
const HINT_LIMIT = 5;
|
|
11
11
|
export const REFUSAL = 'Refusing to send:';
|
|
12
|
-
export function
|
|
12
|
+
export function trustedSourceFacts(origin, text) {
|
|
13
|
+
const addresses = text.includes('@') ? unique(text.match(ADDRESS) ?? []) : [];
|
|
14
|
+
return {
|
|
15
|
+
origin,
|
|
16
|
+
addresses,
|
|
17
|
+
refused: text.includes(REFUSAL)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function guardToolCall(toolName, args, options) {
|
|
21
|
+
return guardToolCallWithFacts(toolName, args, options);
|
|
22
|
+
}
|
|
23
|
+
export function guardToolCallWithFacts(toolName, args, { sources, sourceFacts = [], externalWrite = false }) {
|
|
13
24
|
if (!externalWrite && !isOutbound(toolName, args))
|
|
14
25
|
return null;
|
|
15
26
|
const addresses = unique(scanPayload(args));
|
|
@@ -20,15 +31,15 @@ export function guardToolCall(toolName, args, { sources, externalWrite = false }
|
|
|
20
31
|
if (reserved) {
|
|
21
32
|
return `${REFUSAL} ${mask(reserved)} is a reserved documentation domain (RFC 2606) that cannot receive mail — a placeholder copied from an example, not a real address. Ask the user for the actual address.`;
|
|
22
33
|
}
|
|
23
|
-
const trusted = addressesIn(sources, (origin) => origin !== 'skill');
|
|
34
|
+
const trusted = addressesIn(sources, sourceFacts, (origin) => origin !== 'skill');
|
|
24
35
|
const unverified = addresses.filter((value) => !grounded(value, trusted).ok);
|
|
25
36
|
if (!unverified.length) {
|
|
26
37
|
return null;
|
|
27
38
|
}
|
|
28
|
-
if (alreadyRefusedThisTurn(sources)) {
|
|
39
|
+
if (alreadyRefusedThisTurn(sources, sourceFacts)) {
|
|
29
40
|
return `${REFUSAL} ${masked(unverified)} still cannot be verified, and an earlier call in this turn was already refused for the same reason. Stop calling this tool. Reply to the user in plain text and ask them for the address.`;
|
|
30
41
|
}
|
|
31
|
-
const fromSkill = addressesIn(sources, (origin) => origin === 'skill');
|
|
42
|
+
const fromSkill = addressesIn(sources, sourceFacts, (origin) => origin === 'skill');
|
|
32
43
|
const placeholders = unverified.filter((value) => grounded(value, fromSkill).ok);
|
|
33
44
|
if (placeholders.length) {
|
|
34
45
|
return `${REFUSAL} ${masked(placeholders)} appears only in the skill's own documentation — an example placeholder, not a real address. Ask the user for the actual address.`;
|
|
@@ -53,16 +64,17 @@ function invented(unverified, trusted) {
|
|
|
53
64
|
}
|
|
54
65
|
return `${base} Addresses that do appear: ${trusted.slice(0, HINT_LIMIT).join(', ')}. Use one of those if it is the intended target; otherwise ask the user instead of retrying.`;
|
|
55
66
|
}
|
|
56
|
-
function alreadyRefusedThisTurn(sources) {
|
|
67
|
+
function alreadyRefusedThisTurn(sources, sourceFacts) {
|
|
57
68
|
let turnStart = 0;
|
|
58
69
|
for (const [index, source] of sources.entries()) {
|
|
59
70
|
if (source.origin === 'user') {
|
|
60
71
|
turnStart = index + 1;
|
|
61
72
|
}
|
|
62
73
|
}
|
|
63
|
-
return sources
|
|
74
|
+
return (sources
|
|
64
75
|
.slice(turnStart)
|
|
65
|
-
.some((source) => source.origin === 'tool-result' && source.text.includes(REFUSAL))
|
|
76
|
+
.some((source) => source.origin === 'tool-result' && source.text.includes(REFUSAL)) ||
|
|
77
|
+
sourceFacts.some((source) => source.origin === 'tool-result' && source.refused));
|
|
66
78
|
}
|
|
67
79
|
function isOutbound(toolName, args) {
|
|
68
80
|
if (!OUTBOUND_TOOLS.has(toolName))
|
|
@@ -100,13 +112,18 @@ function decodeEncoded(value) {
|
|
|
100
112
|
return null;
|
|
101
113
|
}
|
|
102
114
|
}
|
|
103
|
-
function addressesIn(sources, accept) {
|
|
115
|
+
function addressesIn(sources, sourceFacts, accept) {
|
|
104
116
|
const found = [];
|
|
105
117
|
for (const source of sources) {
|
|
106
118
|
if (!accept(source.origin))
|
|
107
119
|
continue;
|
|
108
120
|
found.push(...(source.text.match(ADDRESS) ?? []));
|
|
109
121
|
}
|
|
122
|
+
for (const source of sourceFacts) {
|
|
123
|
+
if (!accept(source.origin))
|
|
124
|
+
continue;
|
|
125
|
+
found.push(...source.addresses);
|
|
126
|
+
}
|
|
110
127
|
return unique(found);
|
|
111
128
|
}
|
|
112
129
|
function unique(values) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
2
3
|
import { platform } from 'os';
|
|
3
4
|
import path from 'path';
|
|
5
|
+
import { errorCode } from '../../guards.js';
|
|
4
6
|
import { parseSkillFrontMatter } from './frontmatter-parser.js';
|
|
5
7
|
import { buildCliValidator, loadSkillCli } from './cli-schema.js';
|
|
6
8
|
import { isModelCompatible, isStub, isToolSupported, resolveSkillState, SKILL_STATE, supportsPlatform } from './skill-state.js';
|
|
@@ -79,6 +81,9 @@ function mergeOverlay(base, overlay) {
|
|
|
79
81
|
const { origin: _origin, mcpReads: _mcpReads, operations: _operations, ...rest } = overlay;
|
|
80
82
|
return {
|
|
81
83
|
...rest,
|
|
84
|
+
contentDigest: createHash('sha256')
|
|
85
|
+
.update(`${base.contentDigest ?? ''}:${overlay.contentDigest ?? ''}`)
|
|
86
|
+
.digest('hex'),
|
|
82
87
|
credentials: base.credentials,
|
|
83
88
|
allowList: base.allowList,
|
|
84
89
|
platform: base.platform,
|
|
@@ -151,11 +156,18 @@ async function loadEntry(skillsDir, dir, origin) {
|
|
|
151
156
|
const tools = meta.tools ?? [];
|
|
152
157
|
const cli = await loadSkillCli(skillDir, tools);
|
|
153
158
|
const referenceFiles = await loadReferenceFiles(skillDir);
|
|
159
|
+
const contentHash = createHash('sha256').update(raw);
|
|
160
|
+
for (const file of referenceFiles) {
|
|
161
|
+
contentHash.update(file);
|
|
162
|
+
contentHash.update(await fs.promises.readFile(path.join(skillDir, file)));
|
|
163
|
+
}
|
|
164
|
+
await hashSkillScripts(path.join(skillDir, 'scripts'), contentHash);
|
|
154
165
|
const operations = origin === 'overlay' ? [] : await loadOperations(skillDir, dir);
|
|
155
166
|
const platform = platformsOf(meta.platform, origin);
|
|
156
167
|
return {
|
|
157
168
|
name: dir,
|
|
158
169
|
description: meta.description ?? '',
|
|
170
|
+
contentDigest: contentHash.digest('hex'),
|
|
159
171
|
aliases: meta.aliases,
|
|
160
172
|
preloadOnName: meta.preloadOnName === false ? false : undefined,
|
|
161
173
|
tools,
|
|
@@ -194,6 +206,29 @@ async function loadOperations(skillDir, name) {
|
|
|
194
206
|
}
|
|
195
207
|
return operations;
|
|
196
208
|
}
|
|
209
|
+
async function hashSkillScripts(directory, hash, prefix = 'scripts') {
|
|
210
|
+
let entries;
|
|
211
|
+
try {
|
|
212
|
+
entries = await fs.promises.readdir(directory, { withFileTypes: true });
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
if (errorCode(error) === 'ENOENT') {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
221
|
+
const file = path.join(directory, entry.name);
|
|
222
|
+
const relative = `${prefix}/${entry.name}`;
|
|
223
|
+
if (entry.isDirectory()) {
|
|
224
|
+
await hashSkillScripts(file, hash, relative);
|
|
225
|
+
}
|
|
226
|
+
else if (entry.isFile() && entry.name.endsWith('.py')) {
|
|
227
|
+
hash.update(relative);
|
|
228
|
+
hash.update(await fs.promises.readFile(file));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
197
232
|
async function loadReferenceFiles(skillDir) {
|
|
198
233
|
try {
|
|
199
234
|
const entries = await fs.promises.readdir(path.join(skillDir, 'references'));
|
|
@@ -24,6 +24,7 @@ export interface SkillCli {
|
|
|
24
24
|
export declare const CLI_SCHEMA_FILENAME = "cli.schema.json";
|
|
25
25
|
export interface CliValidator {
|
|
26
26
|
readonly bin: string;
|
|
27
|
+
readonly fingerprint: string;
|
|
27
28
|
readonly check: (argv: readonly string[]) => string | null;
|
|
28
29
|
readonly annotation: (argv: readonly string[]) => 'read' | 'write';
|
|
29
30
|
readonly effect: (argv: readonly string[]) => 'read' | 'write';
|
|
@@ -2,11 +2,35 @@ export { CLI_SCHEMA_FILENAME } from './cli-schema-types.js';
|
|
|
2
2
|
export { argvToObject } from './cli-argv.js';
|
|
3
3
|
export { loadSkillCli, resolveCliSchema, execBinFromTools } from './cli-schema-load.js';
|
|
4
4
|
import { buildSchemaAnnotation, buildSchemaCheck } from './cli-schema-check.js';
|
|
5
|
+
import { createHash } from 'crypto';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import { errorCode } from '../../guards.js';
|
|
8
|
+
import { stableStringify } from '../../machine/index.js';
|
|
9
|
+
const CLI_POLICY_VERSION = 1;
|
|
10
|
+
function resourceIdentity(resourcePath) {
|
|
11
|
+
try {
|
|
12
|
+
const digest = createHash('sha256').update(fs.readFileSync(resourcePath)).digest('hex');
|
|
13
|
+
return [resourcePath, digest];
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
const code = errorCode(error) ?? 'unreadable';
|
|
17
|
+
return [resourcePath, `unreadable:${code}`];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
5
20
|
export function buildCliValidator(def) {
|
|
6
21
|
const bin = def.bin.toLowerCase();
|
|
22
|
+
const fingerprint = createHash('sha256')
|
|
23
|
+
.update(stableStringify({
|
|
24
|
+
version: CLI_POLICY_VERSION,
|
|
25
|
+
bin,
|
|
26
|
+
schema: def.schema ?? null,
|
|
27
|
+
resources: (def.resourcePaths ?? []).map(resourceIdentity)
|
|
28
|
+
}))
|
|
29
|
+
.digest('hex');
|
|
7
30
|
if (!def.schema) {
|
|
8
31
|
return {
|
|
9
32
|
bin,
|
|
33
|
+
fingerprint,
|
|
10
34
|
check: () => 'skill cli schema is missing',
|
|
11
35
|
annotation: () => 'write',
|
|
12
36
|
effect: () => 'write'
|
|
@@ -16,6 +40,7 @@ export function buildCliValidator(def) {
|
|
|
16
40
|
const annotation = buildSchemaAnnotation(def.schema);
|
|
17
41
|
return {
|
|
18
42
|
bin,
|
|
43
|
+
fingerprint,
|
|
19
44
|
check,
|
|
20
45
|
annotation,
|
|
21
46
|
effect: (argv) => (check(argv) === null ? annotation(argv) : 'write')
|
package/dist-lib/lib/wire.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { HarnessEvent } from './contract-types.ts';
|
|
2
2
|
export interface WireEvent {
|
|
3
|
+
runId?: string | null;
|
|
4
|
+
round?: number | null;
|
|
5
|
+
replacedTypes?: string[] | null;
|
|
3
6
|
type: string;
|
|
4
7
|
text?: string | null;
|
|
5
8
|
name?: string | null;
|
|
@@ -40,6 +43,8 @@ export interface WireDownloadEvent {
|
|
|
40
43
|
assetId?: string | null;
|
|
41
44
|
message?: string | null;
|
|
42
45
|
}
|
|
46
|
+
export declare function isPlainWireValue(value: unknown): boolean;
|
|
47
|
+
export declare function redactedRecoveryFields(source: Record<string, unknown>): Record<string, unknown>;
|
|
43
48
|
export declare function toWire(event: HarnessEvent): WireEvent;
|
|
44
49
|
export declare function fromWire(event: WireEvent): HarnessEvent;
|
|
45
50
|
export declare function toDownloadWire(event: HarnessEvent): WireDownloadEvent;
|
package/dist-lib/lib/wire.js
CHANGED
|
@@ -3,14 +3,66 @@ import { isRecord } from './guards.js';
|
|
|
3
3
|
function stringField(value) {
|
|
4
4
|
return typeof value === 'string' ? value : undefined;
|
|
5
5
|
}
|
|
6
|
+
function isRunVersion(value) {
|
|
7
|
+
return (typeof value === 'string' &&
|
|
8
|
+
value.length <= 40 &&
|
|
9
|
+
/^\d{1,10}(?:\.\d{1,10}){0,3}(?:-[A-Za-z0-9.-]{1,16})?$/.test(value));
|
|
10
|
+
}
|
|
6
11
|
function objectField(value) {
|
|
7
12
|
return isRecord(value) ? value : {};
|
|
8
13
|
}
|
|
14
|
+
export function isPlainWireValue(value) {
|
|
15
|
+
if (value === null)
|
|
16
|
+
return true;
|
|
17
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return (Array.isArray(value) &&
|
|
21
|
+
value.every((item) => typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean'));
|
|
22
|
+
}
|
|
9
23
|
function isResource(value) {
|
|
10
24
|
return isRecord(value) && typeof value.kind === 'string' && typeof value.label === 'string';
|
|
11
25
|
}
|
|
26
|
+
const REDACTED_RECOVERY_KEYS = new Set(['forkAt', 'forkSafe', 'recoveryOperationIds']);
|
|
27
|
+
export function redactedRecoveryFields(source) {
|
|
28
|
+
const fields = {};
|
|
29
|
+
for (const [key, value] of Object.entries(source)) {
|
|
30
|
+
if (!REDACTED_RECOVERY_KEYS.has(key))
|
|
31
|
+
continue;
|
|
32
|
+
if (isPlainWireValue(value)) {
|
|
33
|
+
fields[key] = value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return fields;
|
|
37
|
+
}
|
|
12
38
|
export function toWire(event) {
|
|
39
|
+
const wire = toWirePayload(event);
|
|
40
|
+
if (typeof event.runId === 'string') {
|
|
41
|
+
wire.runId = event.runId;
|
|
42
|
+
}
|
|
43
|
+
if (typeof event.round === 'number') {
|
|
44
|
+
wire.round = event.round;
|
|
45
|
+
}
|
|
46
|
+
if (typeof event.callId === 'string') {
|
|
47
|
+
wire.callId = event.callId;
|
|
48
|
+
}
|
|
49
|
+
return wire;
|
|
50
|
+
}
|
|
51
|
+
function toWirePayload(event) {
|
|
13
52
|
switch (event.type) {
|
|
53
|
+
case 'run-started':
|
|
54
|
+
case 'run-resumed':
|
|
55
|
+
case 'paused':
|
|
56
|
+
return { type: event.type };
|
|
57
|
+
case 'round-reset': {
|
|
58
|
+
const wire = { type: event.type };
|
|
59
|
+
if (Array.isArray(event.replacedTypes)) {
|
|
60
|
+
wire.replacedTypes = event.replacedTypes.filter((value) => typeof value === 'string');
|
|
61
|
+
}
|
|
62
|
+
return wire;
|
|
63
|
+
}
|
|
64
|
+
case 'recovery-required':
|
|
65
|
+
return { type: event.type, payload: event.operation };
|
|
14
66
|
case 'content':
|
|
15
67
|
case 'thinking':
|
|
16
68
|
return { type: event.type, text: event.text };
|
|
@@ -39,7 +91,7 @@ export function toWire(event) {
|
|
|
39
91
|
decision: stringField(event.decision)
|
|
40
92
|
};
|
|
41
93
|
case 'metrics': {
|
|
42
|
-
const { type, ...metrics } = event;
|
|
94
|
+
const { type, runId, ...metrics } = event;
|
|
43
95
|
return { type, metrics };
|
|
44
96
|
}
|
|
45
97
|
case 'inference-call':
|
|
@@ -56,8 +108,29 @@ export function toWire(event) {
|
|
|
56
108
|
reason: event.reason
|
|
57
109
|
}
|
|
58
110
|
};
|
|
59
|
-
case 'error':
|
|
60
|
-
|
|
111
|
+
case 'error': {
|
|
112
|
+
const diagnostic = {};
|
|
113
|
+
if (event.code === 'PENDING_ARGS_REDACTED') {
|
|
114
|
+
diagnostic.code = event.code;
|
|
115
|
+
Object.assign(diagnostic, redactedRecoveryFields(event));
|
|
116
|
+
}
|
|
117
|
+
if (event.name === 'RunIncompatible' &&
|
|
118
|
+
(event.kind === 'machine' || event.kind === 'log' || event.kind === 'xstate') &&
|
|
119
|
+
isRunVersion(event.found) &&
|
|
120
|
+
isRunVersion(event.expected)) {
|
|
121
|
+
diagnostic.kind = event.kind;
|
|
122
|
+
diagnostic.found = event.found;
|
|
123
|
+
diagnostic.expected = event.expected;
|
|
124
|
+
}
|
|
125
|
+
const wire = { type: 'error', message: event.message };
|
|
126
|
+
if (typeof event.name === 'string') {
|
|
127
|
+
wire.name = event.name;
|
|
128
|
+
}
|
|
129
|
+
if (Object.keys(diagnostic).length > 0) {
|
|
130
|
+
wire.payload = diagnostic;
|
|
131
|
+
}
|
|
132
|
+
return wire;
|
|
133
|
+
}
|
|
61
134
|
case 'aborted':
|
|
62
135
|
return { type: 'aborted' };
|
|
63
136
|
default:
|
|
@@ -65,7 +138,34 @@ export function toWire(event) {
|
|
|
65
138
|
}
|
|
66
139
|
}
|
|
67
140
|
export function fromWire(event) {
|
|
141
|
+
const decoded = fromWirePayload(event);
|
|
142
|
+
if (event.runId) {
|
|
143
|
+
decoded.runId = event.runId;
|
|
144
|
+
}
|
|
145
|
+
if (event.runId &&
|
|
146
|
+
event.round !== null &&
|
|
147
|
+
event.round !== undefined &&
|
|
148
|
+
['content', 'thinking', 'tool-call', 'tool-result', 'tool-activity', 'round-reset'].includes(event.type)) {
|
|
149
|
+
decoded.round = event.round;
|
|
150
|
+
}
|
|
151
|
+
if (event.callId) {
|
|
152
|
+
decoded.callId = event.callId;
|
|
153
|
+
}
|
|
154
|
+
return decoded;
|
|
155
|
+
}
|
|
156
|
+
function fromWirePayload(event) {
|
|
68
157
|
switch (event.type) {
|
|
158
|
+
case 'run-started':
|
|
159
|
+
case 'run-resumed':
|
|
160
|
+
case 'paused':
|
|
161
|
+
return { type: event.type };
|
|
162
|
+
case 'round-reset':
|
|
163
|
+
return {
|
|
164
|
+
type: event.type,
|
|
165
|
+
replacedTypes: event.replacedTypes ?? undefined
|
|
166
|
+
};
|
|
167
|
+
case 'recovery-required':
|
|
168
|
+
return { type: event.type, operation: event.payload };
|
|
69
169
|
case 'content':
|
|
70
170
|
case 'thinking':
|
|
71
171
|
return { type: event.type, text: event.text ?? '' };
|
|
@@ -109,8 +209,31 @@ export function fromWire(event) {
|
|
|
109
209
|
reason: sent.reason || undefined
|
|
110
210
|
};
|
|
111
211
|
}
|
|
112
|
-
case 'error':
|
|
113
|
-
|
|
212
|
+
case 'error': {
|
|
213
|
+
const diagnostic = objectField(event.payload);
|
|
214
|
+
const decoded = {
|
|
215
|
+
type: 'error',
|
|
216
|
+
message: event.message ?? 'harness error'
|
|
217
|
+
};
|
|
218
|
+
if (event.name) {
|
|
219
|
+
decoded.name = event.name;
|
|
220
|
+
}
|
|
221
|
+
if (diagnostic.code === 'PENDING_ARGS_REDACTED') {
|
|
222
|
+
decoded.code = diagnostic.code;
|
|
223
|
+
Object.assign(decoded, redactedRecoveryFields(diagnostic));
|
|
224
|
+
}
|
|
225
|
+
if (event.name === 'RunIncompatible' &&
|
|
226
|
+
(diagnostic.kind === 'machine' ||
|
|
227
|
+
diagnostic.kind === 'log' ||
|
|
228
|
+
diagnostic.kind === 'xstate') &&
|
|
229
|
+
isRunVersion(diagnostic.found) &&
|
|
230
|
+
isRunVersion(diagnostic.expected)) {
|
|
231
|
+
decoded.kind = diagnostic.kind;
|
|
232
|
+
decoded.found = diagnostic.found;
|
|
233
|
+
decoded.expected = diagnostic.expected;
|
|
234
|
+
}
|
|
235
|
+
return decoded;
|
|
236
|
+
}
|
|
114
237
|
case 'aborted':
|
|
115
238
|
return { type: 'aborted' };
|
|
116
239
|
default:
|
|
@@ -70,7 +70,9 @@ export function registerHarnessTypes(ns) {
|
|
|
70
70
|
'rag-result',
|
|
71
71
|
'question-result',
|
|
72
72
|
'drive-result',
|
|
73
|
-
'invoke'
|
|
73
|
+
'invoke',
|
|
74
|
+
'run-log-result',
|
|
75
|
+
'pause'
|
|
74
76
|
],
|
|
75
77
|
strings: true
|
|
76
78
|
});
|
|
@@ -113,7 +115,8 @@ export function registerHarnessTypes(ns) {
|
|
|
113
115
|
{ name: 'captureInferenceCalls', type: 'bool' },
|
|
114
116
|
{ name: 'capability', type: 'string' },
|
|
115
117
|
{ name: 'arguments', type: 'json' },
|
|
116
|
-
{ name: 'enhance', type: 'string' }
|
|
118
|
+
{ name: 'enhance', type: 'string' },
|
|
119
|
+
{ name: 'runId', type: 'string' }
|
|
117
120
|
]
|
|
118
121
|
});
|
|
119
122
|
ns.register({
|
|
@@ -136,7 +139,13 @@ export function registerHarnessTypes(ns) {
|
|
|
136
139
|
'tool-activity',
|
|
137
140
|
'drive-ask',
|
|
138
141
|
'inference-call',
|
|
139
|
-
'inference-sent'
|
|
142
|
+
'inference-sent',
|
|
143
|
+
'run-started',
|
|
144
|
+
'run-resumed',
|
|
145
|
+
'paused',
|
|
146
|
+
'recovery-required',
|
|
147
|
+
'round-reset',
|
|
148
|
+
'run-log-append'
|
|
140
149
|
],
|
|
141
150
|
strings: true
|
|
142
151
|
});
|
|
@@ -170,7 +179,10 @@ export function registerHarnessTypes(ns) {
|
|
|
170
179
|
{ name: 'payload', type: 'json' },
|
|
171
180
|
{ name: 'intent', type: 'string' },
|
|
172
181
|
{ name: 'display', type: 'json' },
|
|
173
|
-
{ name: 'rawCall', type: 'string' }
|
|
182
|
+
{ name: 'rawCall', type: 'string' },
|
|
183
|
+
{ name: 'runId', type: 'string' },
|
|
184
|
+
{ name: 'round', type: 'uint' },
|
|
185
|
+
{ name: 'replacedTypes', type: 'string', array: true }
|
|
174
186
|
]
|
|
175
187
|
});
|
|
176
188
|
ns.register({
|
|
@@ -339,7 +351,8 @@ export function registerHarnessTypes(ns) {
|
|
|
339
351
|
{ name: 'supportsImages', type: 'bool', required: true },
|
|
340
352
|
{ name: 'expectedSize', type: 'uint', required: true },
|
|
341
353
|
{ name: 'actualSize', type: 'uint' },
|
|
342
|
-
{ name: 'label', type: 'string' }
|
|
354
|
+
{ name: 'label', type: 'string' },
|
|
355
|
+
{ name: 'isLoading', type: 'bool' }
|
|
343
356
|
]
|
|
344
357
|
});
|
|
345
358
|
ns.register({
|
|
@@ -451,7 +464,10 @@ export function registerHarnessTypes(ns) {
|
|
|
451
464
|
{ name: 'capabilityProfile', type: '@harness/capability-profile', required: true },
|
|
452
465
|
{ name: 'recommendedModelName', type: 'string', required: true },
|
|
453
466
|
{ name: 'memoryBudget', type: '@harness/memory-budget' },
|
|
454
|
-
{ name: 'mobile', type: 'bool' }
|
|
467
|
+
{ name: 'mobile', type: 'bool' },
|
|
468
|
+
{ name: 'completionRecovery', type: 'bool' },
|
|
469
|
+
{ name: 'hostRunLog', type: 'bool' },
|
|
470
|
+
{ name: 'hostRunLogVersion', type: 'uint' }
|
|
455
471
|
]
|
|
456
472
|
});
|
|
457
473
|
ns.register({
|