harnery 0.24.0 → 0.25.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/dist/commands/harness.d.ts +2 -1
- package/dist/commands/harness.d.ts.map +1 -1
- package/dist/commands/harness.js +105 -3
- package/dist/commands/supervisor.d.ts.map +1 -1
- package/dist/commands/supervisor.js +3 -13
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +2 -7
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +3 -13
- package/dist/core/harnesses/attest.d.ts +48 -0
- package/dist/core/harnesses/attest.d.ts.map +1 -0
- package/dist/core/harnesses/attest.js +110 -0
- package/dist/core/harnesses/attestation.d.ts +75 -0
- package/dist/core/harnesses/attestation.d.ts.map +1 -0
- package/dist/core/harnesses/attestation.js +168 -0
- package/dist/core/harnesses/bench.d.ts +23 -1
- package/dist/core/harnesses/bench.d.ts.map +1 -1
- package/dist/core/harnesses/bench.js +135 -32
- package/dist/core/harnesses/index.d.ts +6 -2
- package/dist/core/harnesses/index.d.ts.map +1 -1
- package/dist/core/harnesses/index.js +3 -1
- package/dist/core/workflow/engine.js +3 -0
- package/dist/core/workflow/proof.d.ts +4 -1
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +3 -2
- package/dist/core/workflow/types.d.ts +14 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/commands/harness.ts +123 -2
- package/src/commands/supervisor.ts +11 -15
- package/src/commands/work.ts +8 -8
- package/src/commands/workflow.ts +11 -15
- package/src/core/harnesses/attest.ts +162 -0
- package/src/core/harnesses/attestation.ts +231 -0
- package/src/core/harnesses/bench.ts +187 -32
- package/src/core/harnesses/index.ts +26 -1
- package/src/core/workflow/engine.ts +3 -0
- package/src/core/workflow/proof.ts +7 -1
- package/src/core/workflow/types.ts +15 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
import type { EmitContext } from "../commander.js";
|
|
3
|
-
import { type BenchResult, type HarnessBenchReport } from "../core/harnesses/index.js";
|
|
3
|
+
import { type BenchResult, type HarnessAttestationReport, type HarnessBenchReport } from "../core/harnesses/index.js";
|
|
4
4
|
import type { HarnessProfile } from "../core/harnesses/types.js";
|
|
5
5
|
export declare function registerHarnessCommand(program: Command, emit: EmitContext): void;
|
|
6
|
+
export declare function renderAttestationReport(report: HarnessAttestationReport): string;
|
|
6
7
|
export declare function renderProfileTable(profiles: readonly HarnessProfile[]): string;
|
|
7
8
|
export declare function renderProfile(profile: HarnessProfile): string;
|
|
8
9
|
export declare function renderBenchReport(report: HarnessBenchReport): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/commands/harness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,kBAAkB,
|
|
1
|
+
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/commands/harness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EAIxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAiBjE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CA6IhF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,wBAAwB,GAAG,MAAM,CAgBhF;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,CAU9E;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAkB7D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CA6BpE;AAgBD,mFAAmF;AACnF,YAAY,EAAE,WAAW,EAAE,CAAC"}
|
package/dist/commands/harness.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createBuiltinHarnessRegistry, runHarnessBench, } from "../core/harnesses/index.js";
|
|
1
|
+
import { createBuiltinHarnessRegistry, listAttestations, runHarnessAttestation, runHarnessBench, } from "../core/harnesses/index.js";
|
|
2
2
|
const registry = createBuiltinHarnessRegistry();
|
|
3
3
|
export function registerHarnessCommand(program, emit) {
|
|
4
4
|
const command = program
|
|
@@ -62,6 +62,96 @@ export function registerHarnessCommand(program, emit) {
|
|
|
62
62
|
emit.setExitCode(1);
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
|
+
command
|
|
66
|
+
.command("attest [harnesses...]")
|
|
67
|
+
.description("Record what the installed vendor CLIs actually do. Runs one real model turn each; needs --yes.")
|
|
68
|
+
.option("--yes", "Confirm that this spends real vendor tokens")
|
|
69
|
+
.option("--timeout <ms>", "Per-harness probe timeout in milliseconds")
|
|
70
|
+
.option("--json", "Machine-readable attestation report")
|
|
71
|
+
.action(async (harnesses, opts) => {
|
|
72
|
+
if (!opts.yes) {
|
|
73
|
+
emit.error({
|
|
74
|
+
code: "harness_attest_unconfirmed",
|
|
75
|
+
message: "harness attest runs one real model turn per harness and spends vendor tokens. Re-run with --yes.",
|
|
76
|
+
});
|
|
77
|
+
emit.setExitCode(1);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const timeoutMs = opts.timeout ? Number(opts.timeout) : undefined;
|
|
81
|
+
if (timeoutMs !== undefined && (!Number.isFinite(timeoutMs) || timeoutMs <= 0)) {
|
|
82
|
+
emit.error({
|
|
83
|
+
code: "harness_attest_bad_timeout",
|
|
84
|
+
message: "--timeout must be a positive number",
|
|
85
|
+
});
|
|
86
|
+
emit.setExitCode(1);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const report = await runHarnessAttestation(registry, { harnesses, timeoutMs });
|
|
91
|
+
if (opts.json) {
|
|
92
|
+
emit.config({ format: "json" });
|
|
93
|
+
emit.data(report);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
emit.text(renderAttestationReport(report));
|
|
97
|
+
}
|
|
98
|
+
// A sweep that recorded nothing at all is a failure; a partial sweep is
|
|
99
|
+
// reported honestly and still exits 0, because a host is not required
|
|
100
|
+
// to install every vendor CLI.
|
|
101
|
+
emit.setExitCode(report.recorded === 0 ? 1 : 0);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
emit.error({ code: "harness_attest_failed", message: error.message });
|
|
105
|
+
emit.setExitCode(1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
command
|
|
109
|
+
.command("attestations")
|
|
110
|
+
.description("Show the recorded live attestations without running any model turns.")
|
|
111
|
+
.option("--json", "Machine-readable attestation list")
|
|
112
|
+
.action((opts) => {
|
|
113
|
+
try {
|
|
114
|
+
const records = listAttestations();
|
|
115
|
+
if (opts.json) {
|
|
116
|
+
emit.config({ format: "json" });
|
|
117
|
+
emit.data({ attestations: records });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (records.length === 0) {
|
|
121
|
+
emit.text("No attestations recorded. Run `harness attest --yes` to create one.");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
emit.text(renderTable(["HARNESS", "VERSION", "OBSERVED AT", "OBSERVATIONS"], records.map((record) => [
|
|
125
|
+
record.harness,
|
|
126
|
+
record.binary_version,
|
|
127
|
+
record.observed_at,
|
|
128
|
+
Object.entries(record.observations)
|
|
129
|
+
.map(([dimension, support]) => `${dimension}=${support}`)
|
|
130
|
+
.join(" "),
|
|
131
|
+
])));
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
emit.error({ code: "harness_attestations_failed", message: error.message });
|
|
135
|
+
emit.setExitCode(1);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
export function renderAttestationReport(report) {
|
|
140
|
+
const rows = report.results.map((result) => [
|
|
141
|
+
result.harness,
|
|
142
|
+
result.outcome,
|
|
143
|
+
result.binaryVersion ?? "",
|
|
144
|
+
result.observations
|
|
145
|
+
? Object.entries(result.observations)
|
|
146
|
+
.map(([dimension, support]) => `${dimension}=${support}`)
|
|
147
|
+
.join(" ")
|
|
148
|
+
: result.note,
|
|
149
|
+
]);
|
|
150
|
+
const table = renderTable(["HARNESS", "OUTCOME", "VERSION", "OBSERVED"], rows);
|
|
151
|
+
const tail = report.incomplete
|
|
152
|
+
? "Some harnesses were not attested; their bench rows keep an adapter basis."
|
|
153
|
+
: "Every selected harness was attested.";
|
|
154
|
+
return `${table}\n\nrecorded: ${report.recorded}/${report.results.length}\n${tail}`;
|
|
65
155
|
}
|
|
66
156
|
export function renderProfileTable(profiles) {
|
|
67
157
|
const rows = profiles.map((profile) => [
|
|
@@ -100,13 +190,25 @@ export function renderBenchReport(report) {
|
|
|
100
190
|
result.declared,
|
|
101
191
|
result.observed,
|
|
102
192
|
result.verdict,
|
|
193
|
+
result.basis,
|
|
103
194
|
]);
|
|
104
|
-
const table = renderTable(["HARNESS", "DIMENSION", "DECLARED", "OBSERVED", "VERDICT"], rows);
|
|
195
|
+
const table = renderTable(["HARNESS", "DIMENSION", "DECLARED", "OBSERVED", "VERDICT", "BASIS"], rows);
|
|
105
196
|
const summary = Object.entries(report.summary)
|
|
106
197
|
.filter(([, count]) => count > 0)
|
|
107
198
|
.map(([verdict, count]) => `${verdict}=${count}`)
|
|
108
199
|
.join(", ");
|
|
109
|
-
|
|
200
|
+
const basis = Object.entries(report.basisSummary)
|
|
201
|
+
.filter(([, count]) => count > 0)
|
|
202
|
+
.map(([name, count]) => `${name}=${count}`)
|
|
203
|
+
.join(", ");
|
|
204
|
+
return [
|
|
205
|
+
table,
|
|
206
|
+
"",
|
|
207
|
+
`mode: ${report.mode} (no model calls)`,
|
|
208
|
+
summary,
|
|
209
|
+
`basis: ${basis}`,
|
|
210
|
+
"adapter = checked against Harnery's planner/normalizer/fixture, not the installed CLI",
|
|
211
|
+
].join("\n");
|
|
110
212
|
}
|
|
111
213
|
function renderTable(headers, rows) {
|
|
112
214
|
const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => row[index]?.length ?? 0)));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../src/commands/supervisor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../src/commands/supervisor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA0EnD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAgGnF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
3
|
import { workflowSubscriptionOnly } from "../core/config.js";
|
|
4
|
-
import { createBuiltinHarnessRegistry } from "../core/harnesses/index.js";
|
|
4
|
+
import { createBuiltinHarnessRegistry, harnessProofInputs, probeBinaryVersion, } from "../core/harnesses/index.js";
|
|
5
5
|
import { findCoordRoot } from "../core/hooks/resolve/coord-root.js";
|
|
6
6
|
import { loadPolicyFile } from "../core/policy/index.js";
|
|
7
7
|
import { approveSupervisorPlan, configureSupervisorService, createSupervisor, listSupervisors, readSupervisor, readSupervisorPlan, readSupervisorServiceConfig, readSupervisorServiceStatus, rejectSupervisorPlan, requestSupervisorServiceStop, retrySupervisorPlan, runSupervisor, runSupervisorServiceDaemon, spawnSupervisorService, supervisorServiceLogPath, } from "../core/supervisor/index.js";
|
|
@@ -332,12 +332,7 @@ function serviceEngine(config, registry) {
|
|
|
332
332
|
cwd: config.engine.cwd,
|
|
333
333
|
subscriptionOnly: config.engine.subscription_only,
|
|
334
334
|
allowApiBilling: config.engine.allow_api_billing,
|
|
335
|
-
|
|
336
|
-
.list()
|
|
337
|
-
.map((adapter) => [
|
|
338
|
-
adapter.profile.id,
|
|
339
|
-
{ toolEvidence: adapter.profile.capabilities.toolEvidence },
|
|
340
|
-
])),
|
|
335
|
+
...harnessProofInputs(registry.list().map((adapter) => adapter.profile), { versionProbe: probeBinaryVersion }),
|
|
341
336
|
policy: config.engine.policy,
|
|
342
337
|
approvalMode: "park",
|
|
343
338
|
approvalAddressee: config.engine.approval_addressee,
|
|
@@ -413,12 +408,7 @@ function registerRunCommand(supervisor, mode, registry, emit) {
|
|
|
413
408
|
cwd: opts.cwd,
|
|
414
409
|
subscriptionOnly: opts.subscriptionOnly === true ? true : workflowSubscriptionOnly(coordRoot),
|
|
415
410
|
allowApiBilling: opts.allowApiBilling,
|
|
416
|
-
|
|
417
|
-
.list()
|
|
418
|
-
.map((adapter) => [
|
|
419
|
-
adapter.profile.id,
|
|
420
|
-
{ toolEvidence: adapter.profile.capabilities.toolEvidence },
|
|
421
|
-
])),
|
|
411
|
+
...harnessProofInputs(registry.list().map((adapter) => adapter.profile), { versionProbe: probeBinaryVersion }),
|
|
422
412
|
policy: opts.policy ? loadPolicyFile(opts.policy) : undefined,
|
|
423
413
|
approvalMode: "park",
|
|
424
414
|
approvalAddressee: opts.approvalTo,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work.d.ts","sourceRoot":"","sources":["../../src/commands/work.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"work.d.ts","sourceRoot":"","sources":["../../src/commands/work.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAuDnD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CA8F7E"}
|
package/dist/commands/work.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { workflowSubscriptionOnly } from "../core/config.js";
|
|
2
|
-
import { createBuiltinHarnessRegistry } from "../core/harnesses/index.js";
|
|
2
|
+
import { createBuiltinHarnessRegistry, harnessProofInputs, probeBinaryVersion, } from "../core/harnesses/index.js";
|
|
3
3
|
import { findCoordRoot } from "../core/hooks/resolve/coord-root.js";
|
|
4
4
|
import { loadPolicyFile } from "../core/policy/index.js";
|
|
5
5
|
import { acceptWorkItem, cancelWorkItem, createWorkItem, listWorkItems, readWorkItem, reconcileAllWorkItems, reconcileWorkItem, reopenWorkItem, runWorkItem, } from "../core/work/index.js";
|
|
@@ -132,12 +132,7 @@ function registerRunCommand(work, name, retry, registry, harnesses, emit) {
|
|
|
132
132
|
cwd: opts.cwd,
|
|
133
133
|
subscriptionOnly: opts.subscriptionOnly === true ? true : workflowSubscriptionOnly(coordRoot),
|
|
134
134
|
allowApiBilling: opts.allowApiBilling,
|
|
135
|
-
|
|
136
|
-
.list()
|
|
137
|
-
.map((adapter) => [
|
|
138
|
-
adapter.profile.id,
|
|
139
|
-
{ toolEvidence: adapter.profile.capabilities.toolEvidence },
|
|
140
|
-
])),
|
|
135
|
+
...harnessProofInputs(registry.list().map((adapter) => adapter.profile), { versionProbe: probeBinaryVersion }),
|
|
141
136
|
policy: opts.policy ? loadPolicyFile(opts.policy) : undefined,
|
|
142
137
|
approvalMode: "park",
|
|
143
138
|
approvalAddressee: opts.approvalTo,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/commands/workflow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/commands/workflow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAqEnD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CA6pBjF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { resolveBinName, workflowSubscriptionOnly } from "../core/config.js";
|
|
4
|
-
import { createBuiltinHarnessRegistry } from "../core/harnesses/index.js";
|
|
4
|
+
import { createBuiltinHarnessRegistry, harnessProofInputs, probeBinaryVersion, } from "../core/harnesses/index.js";
|
|
5
5
|
import { findCoordRoot } from "../core/hooks/resolve/coord-root.js";
|
|
6
6
|
import { loadPolicyFile } from "../core/policy/index.js";
|
|
7
7
|
import { WorkflowParkedError } from "../core/workflow/engine.js";
|
|
@@ -88,12 +88,7 @@ export function registerWorkflowCommand(program, emit) {
|
|
|
88
88
|
maxAgents: opts.maxAgents ? Number.parseInt(opts.maxAgents, 10) : undefined,
|
|
89
89
|
concurrency: opts.concurrency ? Number.parseInt(opts.concurrency, 10) : undefined,
|
|
90
90
|
cwd: opts.cwd,
|
|
91
|
-
|
|
92
|
-
.list()
|
|
93
|
-
.map((adapter) => [
|
|
94
|
-
adapter.profile.id,
|
|
95
|
-
{ toolEvidence: adapter.profile.capabilities.toolEvidence },
|
|
96
|
-
])),
|
|
91
|
+
...harnessProofInputs(registry.list().map((adapter) => adapter.profile), { versionProbe: probeBinaryVersion }),
|
|
97
92
|
policy: opts.policy ? loadPolicyFile(opts.policy) : undefined,
|
|
98
93
|
approvalMode: "park",
|
|
99
94
|
approvalAddressee: opts.approvalTo,
|
|
@@ -179,12 +174,7 @@ export function registerWorkflowCommand(program, emit) {
|
|
|
179
174
|
writableRoots: [binding.writable_root.configured],
|
|
180
175
|
}
|
|
181
176
|
: undefined,
|
|
182
|
-
|
|
183
|
-
.list()
|
|
184
|
-
.map((adapter) => [
|
|
185
|
-
adapter.profile.id,
|
|
186
|
-
{ toolEvidence: adapter.profile.capabilities.toolEvidence },
|
|
187
|
-
])),
|
|
177
|
+
...harnessProofInputs(registry.list().map((adapter) => adapter.profile), { versionProbe: probeBinaryVersion }),
|
|
188
178
|
});
|
|
189
179
|
if (opts.json) {
|
|
190
180
|
emit.config({ format: "json" });
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opt-in live probe that produces an attestation (ADR 0038).
|
|
3
|
+
*
|
|
4
|
+
* One bounded turn per harness, through the same `spawn` the workflow engine
|
|
5
|
+
* uses, so what gets attested is the path production takes rather than a
|
|
6
|
+
* parallel test rig.
|
|
7
|
+
*/
|
|
8
|
+
import type { SpawnResult } from "../workflow/types.js";
|
|
9
|
+
import type { AttestableDimension, HarnessAttestation } from "./attestation.js";
|
|
10
|
+
import type { HarnessRegistry } from "./registry.js";
|
|
11
|
+
import type { CapabilitySupport, HarnessId } from "./types.js";
|
|
12
|
+
/** Fixed and content-free, so nothing user-supplied can reach the record and
|
|
13
|
+
* the turn stays as cheap as a turn can be. */
|
|
14
|
+
export declare const ATTESTATION_PROMPT = "Reply with the single word: ok";
|
|
15
|
+
export declare const DEFAULT_ATTESTATION_TIMEOUT_MS = 120000;
|
|
16
|
+
export type AttestationOutcome = "recorded" | "skipped" | "unreachable" | "failed";
|
|
17
|
+
export interface HarnessAttestationResult {
|
|
18
|
+
harness: HarnessId;
|
|
19
|
+
outcome: AttestationOutcome;
|
|
20
|
+
binaryVersion?: string;
|
|
21
|
+
observations?: Partial<Record<AttestableDimension, CapabilitySupport>>;
|
|
22
|
+
durationMs?: number;
|
|
23
|
+
note: string;
|
|
24
|
+
}
|
|
25
|
+
export interface HarnessAttestationReport {
|
|
26
|
+
generatedAt: string;
|
|
27
|
+
harnesses: HarnessId[];
|
|
28
|
+
results: HarnessAttestationResult[];
|
|
29
|
+
recorded: number;
|
|
30
|
+
/** True when at least one selected harness could not be attested, so a
|
|
31
|
+
* caller can tell a partial sweep from a complete one. */
|
|
32
|
+
incomplete: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface RunHarnessAttestationOptions {
|
|
35
|
+
harnesses?: readonly string[];
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
cwd?: string;
|
|
38
|
+
coordRoot?: string;
|
|
39
|
+
/** Test seam and alternate host probe. A null version means unavailable. */
|
|
40
|
+
versionProbe?: (binary: string) => string | null;
|
|
41
|
+
/** Test seam. Defaults to the adapter's production spawner. */
|
|
42
|
+
spawn?: (harness: HarnessId, prompt: string, timeoutMs: number) => Promise<SpawnResult>;
|
|
43
|
+
/** Test seam. Defaults to writing under the coord root. */
|
|
44
|
+
persist?: (record: HarnessAttestation) => void;
|
|
45
|
+
now?: () => Date;
|
|
46
|
+
}
|
|
47
|
+
export declare function runHarnessAttestation(registry: HarnessRegistry, opts?: RunHarnessAttestationOptions): Promise<HarnessAttestationReport>;
|
|
48
|
+
//# sourceMappingURL=attest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attest.d.ts","sourceRoot":"","sources":["../../../src/core/harnesses/attest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAGhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE/D;+CAC+C;AAC/C,eAAO,MAAM,kBAAkB,mCAAmC,CAAC;AAEnE,eAAO,MAAM,8BAA8B,SAAU,CAAC;AAetD,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC;AAEnF,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,mBAAmB,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB;8DAC0D;IAC1D,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,4BAA4B;IAC3C,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACjD,+DAA+D;IAC/D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACxF,2DAA2D;IAC3D,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC/C,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,eAAe,EACzB,IAAI,GAAE,4BAAiC,GACtC,OAAO,CAAC,wBAAwB,CAAC,CAyFnC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opt-in live probe that produces an attestation (ADR 0038).
|
|
3
|
+
*
|
|
4
|
+
* One bounded turn per harness, through the same `spawn` the workflow engine
|
|
5
|
+
* uses, so what gets attested is the path production takes rather than a
|
|
6
|
+
* parallel test rig.
|
|
7
|
+
*/
|
|
8
|
+
import { profileDigest, sealAttestation, writeAttestation } from "./attestation.js";
|
|
9
|
+
import { probeBinaryVersion } from "./bench.js";
|
|
10
|
+
/** Fixed and content-free, so nothing user-supplied can reach the record and
|
|
11
|
+
* the turn stays as cheap as a turn can be. */
|
|
12
|
+
export const ATTESTATION_PROMPT = "Reply with the single word: ok";
|
|
13
|
+
export const DEFAULT_ATTESTATION_TIMEOUT_MS = 120_000;
|
|
14
|
+
/** Vendor failures arrive as whole console transcripts, banner and prompt echo
|
|
15
|
+
* included. Reports are bounded evidence, so the reason is collapsed to one
|
|
16
|
+
* short line and the echoed prompt is removed. */
|
|
17
|
+
const MAX_NOTE_REASON_CHARS = 200;
|
|
18
|
+
function boundedReason(reason) {
|
|
19
|
+
if (!reason)
|
|
20
|
+
return "no error reported";
|
|
21
|
+
const collapsed = reason.split(ATTESTATION_PROMPT).join("<prompt>").replace(/\s+/g, " ").trim();
|
|
22
|
+
return collapsed.length > MAX_NOTE_REASON_CHARS
|
|
23
|
+
? `${collapsed.slice(0, MAX_NOTE_REASON_CHARS)}…`
|
|
24
|
+
: collapsed;
|
|
25
|
+
}
|
|
26
|
+
export async function runHarnessAttestation(registry, opts = {}) {
|
|
27
|
+
const ids = opts.harnesses?.length ? [...opts.harnesses] : registry.ids();
|
|
28
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_ATTESTATION_TIMEOUT_MS;
|
|
29
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
30
|
+
const versionProbe = opts.versionProbe ?? probeBinaryVersion;
|
|
31
|
+
const now = opts.now ?? (() => new Date());
|
|
32
|
+
const results = [];
|
|
33
|
+
for (const id of ids) {
|
|
34
|
+
const adapter = registry.require(id);
|
|
35
|
+
const binaryVersion = versionProbe(adapter.profile.binary);
|
|
36
|
+
if (!binaryVersion) {
|
|
37
|
+
results.push({
|
|
38
|
+
harness: id,
|
|
39
|
+
outcome: "skipped",
|
|
40
|
+
note: `${adapter.profile.binary} not found on PATH`,
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
let result;
|
|
45
|
+
try {
|
|
46
|
+
result = opts.spawn
|
|
47
|
+
? await opts.spawn(id, ATTESTATION_PROMPT, timeoutMs)
|
|
48
|
+
: await adapter.spawn({
|
|
49
|
+
prompt: ATTESTATION_PROMPT,
|
|
50
|
+
timeoutMs,
|
|
51
|
+
maxTurns: 1,
|
|
52
|
+
cwd,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
results.push({
|
|
57
|
+
harness: id,
|
|
58
|
+
outcome: "failed",
|
|
59
|
+
binaryVersion,
|
|
60
|
+
note: `probe threw: ${boundedReason(error.message)}`,
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Prerequisite rule: an unreachable subject cannot evidence anything, so a
|
|
65
|
+
// failed turn records nothing at all rather than a page of `unsupported`.
|
|
66
|
+
if (!result.ok) {
|
|
67
|
+
results.push({
|
|
68
|
+
harness: id,
|
|
69
|
+
outcome: "unreachable",
|
|
70
|
+
binaryVersion,
|
|
71
|
+
durationMs: result.durationMs,
|
|
72
|
+
note: `the probe turn did not complete (${boundedReason(result.error)}); nothing recorded`,
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const observations = {
|
|
77
|
+
invocation: "supported",
|
|
78
|
+
finalResult: result.text.trim().length > 0 ? "supported" : "unsupported",
|
|
79
|
+
sessionId: result.sessionId ? "supported" : "unsupported",
|
|
80
|
+
cost: result.costUsd !== undefined ? "supported" : "unsupported",
|
|
81
|
+
};
|
|
82
|
+
const record = sealAttestation({
|
|
83
|
+
schema_version: 1,
|
|
84
|
+
harness: id,
|
|
85
|
+
binary_version: binaryVersion,
|
|
86
|
+
profile_digest: profileDigest(adapter.profile),
|
|
87
|
+
observed_at: now().toISOString(),
|
|
88
|
+
observations,
|
|
89
|
+
});
|
|
90
|
+
if (opts.persist)
|
|
91
|
+
opts.persist(record);
|
|
92
|
+
else
|
|
93
|
+
writeAttestation(record, { coordRoot: opts.coordRoot });
|
|
94
|
+
results.push({
|
|
95
|
+
harness: id,
|
|
96
|
+
outcome: "recorded",
|
|
97
|
+
binaryVersion,
|
|
98
|
+
observations,
|
|
99
|
+
durationMs: result.durationMs,
|
|
100
|
+
note: `observed on ${binaryVersion}`,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
generatedAt: now().toISOString(),
|
|
105
|
+
harnesses: ids,
|
|
106
|
+
results,
|
|
107
|
+
recorded: results.filter((row) => row.outcome === "recorded").length,
|
|
108
|
+
incomplete: results.some((row) => row.outcome !== "recorded"),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable record of what an installed vendor CLI was actually observed doing
|
|
3
|
+
* (ADR 0038).
|
|
4
|
+
*
|
|
5
|
+
* The conformance bench proves Harnery's own planner and normalizer against a
|
|
6
|
+
* committed fixture. That is an adapter check, not a vendor check
|
|
7
|
+
* (ADR 0037). An attestation is the other half: one bounded live turn, its
|
|
8
|
+
* observations, and the vendor version they were observed on.
|
|
9
|
+
*
|
|
10
|
+
* A record stores structural facts only. No prompt text, no completion text,
|
|
11
|
+
* no host paths. It lives under the host's coordination directory and is never
|
|
12
|
+
* published.
|
|
13
|
+
*/
|
|
14
|
+
import type { CapabilitySupport, HarnessId, HarnessProfile } from "./types.js";
|
|
15
|
+
export declare const ATTESTATION_SCHEMA_VERSION = 1;
|
|
16
|
+
/** Dimensions one minimal live turn can honestly establish. Everything else
|
|
17
|
+
* needs a purpose-built scenario and stays outside the record rather than
|
|
18
|
+
* being guessed at. */
|
|
19
|
+
export declare const ATTESTABLE_DIMENSIONS: readonly ["invocation", "finalResult", "sessionId", "cost"];
|
|
20
|
+
export type AttestableDimension = (typeof ATTESTABLE_DIMENSIONS)[number];
|
|
21
|
+
export interface HarnessAttestation {
|
|
22
|
+
schema_version: number;
|
|
23
|
+
harness: HarnessId;
|
|
24
|
+
/** What the vendor binary reported when this was recorded. Staleness is
|
|
25
|
+
* keyed on this, so a vendor upgrade invalidates the record automatically. */
|
|
26
|
+
binary_version: string;
|
|
27
|
+
/** Digest of the capability declaration at record time, so an edited
|
|
28
|
+
* declaration also invalidates the record. */
|
|
29
|
+
profile_digest: string;
|
|
30
|
+
observed_at: string;
|
|
31
|
+
/** Only what the probe actually saw. A dimension absent from this map was
|
|
32
|
+
* not observed, which is not the same as unsupported. */
|
|
33
|
+
observations: Partial<Record<AttestableDimension, CapabilitySupport>>;
|
|
34
|
+
/** Over every field above. A hand-edited record fails to load. */
|
|
35
|
+
record_digest: string;
|
|
36
|
+
}
|
|
37
|
+
export interface AttestationStoreOptions {
|
|
38
|
+
/** Test seam and alternate host. Defaults to the resolved coord root. */
|
|
39
|
+
coordRoot?: string;
|
|
40
|
+
}
|
|
41
|
+
export declare function attestationsDir(opts?: AttestationStoreOptions): string;
|
|
42
|
+
/** Stable identity of a declaration, so editing a claim invalidates the
|
|
43
|
+
* attestation that was recorded against the old one. */
|
|
44
|
+
export declare function profileDigest(profile: HarnessProfile): string;
|
|
45
|
+
export declare function sealAttestation(record: Omit<HarnessAttestation, "record_digest">): HarnessAttestation;
|
|
46
|
+
/** Replace-in-place write. Unlike a workflow record an attestation is meant to
|
|
47
|
+
* be re-recorded, so this is a mutable atomic swap rather than an immutable
|
|
48
|
+
* create. */
|
|
49
|
+
export declare function writeAttestation(record: HarnessAttestation, opts?: AttestationStoreOptions): string;
|
|
50
|
+
/** Null for absent, unreadable, malformed, wrong-schema, or tampered records.
|
|
51
|
+
* A record that fails its own digest is discarded rather than trusted, because
|
|
52
|
+
* the whole point of the file is that it was not hand-written. */
|
|
53
|
+
export declare function readAttestation(harness: HarnessId, opts?: AttestationStoreOptions): HarnessAttestation | null;
|
|
54
|
+
export declare function validateAttestation(value: unknown, harness?: HarnessId): HarnessAttestation | null;
|
|
55
|
+
/** An attestation speaks only for the vendor version and declaration it was
|
|
56
|
+
* recorded against. */
|
|
57
|
+
export declare function isAttestationCurrent(record: HarnessAttestation | null, binaryVersion: string | null, profile: HarnessProfile): record is HarnessAttestation;
|
|
58
|
+
export declare function listAttestations(opts?: AttestationStoreOptions): HarnessAttestation[];
|
|
59
|
+
/** Both harness-derived proof inputs, read once, for a workflow run
|
|
60
|
+
* (ADR 0038). Callers inject the result so the engine performs no capability
|
|
61
|
+
* lookups of its own. A harness with no current attestation simply has no
|
|
62
|
+
* citation; that absence is not a proof unknown. */
|
|
63
|
+
export declare function harnessProofInputs(profiles: readonly HarnessProfile[], opts: AttestationStoreOptions & {
|
|
64
|
+
versionProbe: (binary: string) => string | null;
|
|
65
|
+
}): {
|
|
66
|
+
harnessEvidence: Record<string, {
|
|
67
|
+
toolEvidence: HarnessProfile["capabilities"]["toolEvidence"];
|
|
68
|
+
}>;
|
|
69
|
+
harnessAttestations: Record<string, {
|
|
70
|
+
binary_version: string;
|
|
71
|
+
observed_at: string;
|
|
72
|
+
record_digest: string;
|
|
73
|
+
}>;
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=attestation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../../../src/core/harnesses/attestation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/E,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;uBAEuB;AACvB,eAAO,MAAM,qBAAqB,6DAA8D,CAAC;AAEjG,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzE,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,SAAS,CAAC;IACnB;kFAC8E;IAC9E,cAAc,EAAE,MAAM,CAAC;IACvB;kDAC8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB;6DACyD;IACzD,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,mBAAmB,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACtE,kEAAkE;IAClE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,IAAI,GAAE,uBAA4B,GAAG,MAAM,CAI1E;AAED;wDACwD;AACxD,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAM7D;AAMD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,eAAe,CAAC,GAChD,kBAAkB,CAEpB;AASD;;aAEa;AACb,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,kBAAkB,EAC1B,IAAI,GAAE,uBAA4B,GACjC,MAAM,CAmBR;AAED;;kEAEkE;AAClE,wBAAgB,eAAe,CAC7B,OAAO,EAAE,SAAS,EAClB,IAAI,GAAE,uBAA4B,GACjC,kBAAkB,GAAG,IAAI,CAc3B;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,SAAS,GAClB,kBAAkB,GAAG,IAAI,CAe3B;AAED;uBACuB;AACvB,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,kBAAkB,GAAG,IAAI,EACjC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,OAAO,EAAE,cAAc,GACtB,MAAM,IAAI,kBAAkB,CAI9B;AAED,wBAAgB,gBAAgB,CAAC,IAAI,GAAE,uBAA4B,GAAG,kBAAkB,EAAE,CAczF;AAED;;;oDAGoD;AACpD,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,cAAc,EAAE,EACnC,IAAI,EAAE,uBAAuB,GAAG;IAC9B,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACjD,GACA;IACD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,YAAY,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC,cAAc,CAAC,CAAA;KAAE,CAAC,CAAC;IAClG,mBAAmB,EAAE,MAAM,CACzB,MAAM,EACN;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CACvE,CAAC;CACH,CA2BA"}
|