assertledger 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.fr.md +5 -2
- package/README.md +5 -2
- package/conformance/schema-extensions.json +15 -0
- package/dist/build-info.json +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +213 -8
- package/dist/cli.js.map +1 -1
- package/dist/contracts/index.d.ts +467 -4
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +256 -3
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/qualification.d.ts +528 -0
- package/dist/contracts/qualification.d.ts.map +1 -0
- package/dist/contracts/qualification.js +357 -0
- package/dist/contracts/qualification.js.map +1 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +46 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/qualification.d.ts +17 -0
- package/dist/core/qualification.d.ts.map +1 -0
- package/dist/core/qualification.js +571 -0
- package/dist/core/qualification.js.map +1 -0
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +81 -4
- package/dist/diagnostics.js.map +1 -1
- package/dist/engine/adapters/bun-native.d.ts +35 -0
- package/dist/engine/adapters/bun-native.d.ts.map +1 -0
- package/dist/engine/adapters/bun-native.js +179 -0
- package/dist/engine/adapters/bun-native.js.map +1 -0
- package/dist/engine/adapters/bun-test-designated-profile.d.ts +23 -0
- package/dist/engine/adapters/bun-test-designated-profile.d.ts.map +1 -0
- package/dist/engine/adapters/bun-test-designated-profile.js +24 -0
- package/dist/engine/adapters/bun-test-designated-profile.js.map +1 -0
- package/dist/engine/container.d.ts +7 -0
- package/dist/engine/container.d.ts.map +1 -1
- package/dist/engine/container.js +22 -7
- package/dist/engine/container.js.map +1 -1
- package/dist/engine/git-regression.d.ts.map +1 -1
- package/dist/engine/git-regression.js +9 -2
- package/dist/engine/git-regression.js.map +1 -1
- package/dist/engine/index.d.ts +14 -0
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +828 -97
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/qualification.d.ts +154 -0
- package/dist/engine/qualification.d.ts.map +1 -0
- package/dist/engine/qualification.js +725 -0
- package/dist/engine/qualification.js.map +1 -0
- package/dist/engine/test-closure.d.ts +39 -0
- package/dist/engine/test-closure.d.ts.map +1 -0
- package/dist/engine/test-closure.js +513 -0
- package/dist/engine/test-closure.js.map +1 -0
- package/dist/engine/witness-import.d.ts +31 -0
- package/dist/engine/witness-import.d.ts.map +1 -0
- package/dist/engine/witness-import.js +224 -0
- package/dist/engine/witness-import.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +65 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/sdk/index.d.ts +292 -3
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +39 -1
- package/dist/sdk/index.js.map +1 -1
- package/docs/container-isolation.md +6 -0
- package/docs/migration-orchestration-qualification-v1.md +109 -0
- package/docs/migration-verification-v4.md +94 -0
- package/docs/orchestration-qualification.md +167 -0
- package/docs/reference.md +7 -0
- package/docs/witness-import.md +67 -0
- package/examples/orchestration/README.md +68 -0
- package/examples/orchestration/consumer-obligations.json +101 -0
- package/examples/orchestration/workspace/bitbucket-pipelines.yml +10 -0
- package/examples/orchestration/workspace/command-shape.test.mjs +25 -0
- package/examples/orchestration/workspace/commands.mjs +14 -0
- package/examples/orchestration/workspace/completion.mjs +39 -0
- package/examples/orchestration/workspace/faults.json +1 -0
- package/examples/orchestration/workspace/native.test.ts +4 -0
- package/examples/orchestration/workspace/package.json +6 -0
- package/examples/orchestration/workspace/packages/app/package.json +12 -0
- package/examples/orchestration/workspace/packages/app/verdict.txt +1 -0
- package/examples/orchestration/workspace/packages/leaf/package.json +9 -0
- package/examples/orchestration/workspace/packages/leaf/verdict.txt +1 -0
- package/examples/orchestration/workspace/pnpm-lock.yaml +170 -0
- package/examples/orchestration/workspace/pnpm-workspace.yaml +2 -0
- package/examples/orchestration/workspace/task.mjs +23 -0
- package/examples/orchestration/workspace/turbo.json +38 -0
- package/examples/orchestration/workspace/wrapper.mjs +45 -0
- package/integrations/bun/driver.d.mts +30 -1
- package/integrations/bun/driver.mjs +215 -6
- package/integrations/bun/preload.mjs +186 -5
- package/integrations/bun-native/README.md +86 -0
- package/integrations/bun-native/driver.mjs +396 -0
- package/integrations/bun-native/preload.mjs +375 -0
- package/integrations/skill/SKILL.md +25 -5
- package/package.json +1 -1
- package/schemas/evidence-manifest.v4.json +995 -0
- package/schemas/qualification-execution-request.v1.json +571 -0
- package/schemas/qualification-plan.v1.json +528 -0
- package/schemas/qualification-receipt.v1.json +821 -0
- package/schemas/qualification-replay-result.v1.json +37 -0
- package/schemas/verification-request.v4.json +590 -0
- package/schemas/witness-import-request.v1.json +231 -0
package/README.fr.md
CHANGED
|
@@ -14,7 +14,7 @@ Vous obtenez un verdict, les observations qui le justifient et un fichier de pre
|
|
|
14
14
|
Installez l’outil dans votre dépôt avec Node.js 22.15 ou une version ultérieure :
|
|
15
15
|
|
|
16
16
|
```sh
|
|
17
|
-
npm install --save-dev assertledger@1.
|
|
17
|
+
npm install --save-dev assertledger@1.5.0
|
|
18
18
|
npx assertledger doctor .
|
|
19
19
|
```
|
|
20
20
|
|
|
@@ -60,7 +60,7 @@ flowchart LR
|
|
|
60
60
|
Prérequis : **Git**, **Node.js 22.15+** et **pnpm 11.1.2**. L’exemple utilise l’adaptateur intégré `node:test`.
|
|
61
61
|
|
|
62
62
|
```sh
|
|
63
|
-
git clone --branch v1.
|
|
63
|
+
git clone --branch v1.5.0 https://github.com/hoklims/assertledger.git
|
|
64
64
|
cd assertledger
|
|
65
65
|
pnpm install --frozen-lockfile
|
|
66
66
|
pnpm build
|
|
@@ -181,6 +181,9 @@ L’adaptateur cible précisément Bun 1.4.2 et attribue les échecs de cet outi
|
|
|
181
181
|
natifs restent des erreurs d’exécution et ne comptent pas comme détection du défaut. Le
|
|
182
182
|
[guide de migration Bun](docs/migration-verification-v3.md) décrit la requête, l’exécution et le
|
|
183
183
|
rejeu. Ce mode exécute du code local de confiance sans bac à sable.
|
|
184
|
+
Pour vérifier un test `bun:test` existant désigné par son nom, ou rejouer un témoin rouge/vert
|
|
185
|
+
enregistré, utilisez une requête v4 ou `import-witness`, qui s’exécutent aussi en conteneur ou dans un
|
|
186
|
+
mode Windows natif déclaré ; voir le [guide de migration v4](docs/migration-verification-v4.md).
|
|
184
187
|
|
|
185
188
|
### Qualifier un test de régression commité
|
|
186
189
|
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ You get a verdict, the observations behind it and an evidence file you can repla
|
|
|
14
14
|
Install in your repository with Node.js 22.15 or later:
|
|
15
15
|
|
|
16
16
|
```sh
|
|
17
|
-
npm install --save-dev assertledger@1.
|
|
17
|
+
npm install --save-dev assertledger@1.5.0
|
|
18
18
|
npx assertledger doctor .
|
|
19
19
|
```
|
|
20
20
|
|
|
@@ -59,7 +59,7 @@ flowchart LR
|
|
|
59
59
|
You need **Git**, **Node.js 22.15+** and **pnpm 11.1.2**. The example uses the built-in `node:test` adapter.
|
|
60
60
|
|
|
61
61
|
```sh
|
|
62
|
-
git clone --branch v1.
|
|
62
|
+
git clone --branch v1.5.0 https://github.com/hoklims/assertledger.git
|
|
63
63
|
cd assertledger
|
|
64
64
|
pnpm install --frozen-lockfile
|
|
65
65
|
pnpm build
|
|
@@ -173,6 +173,9 @@ The Bun adapter pins version 1.4.2 and attributes failures from this helper. Nat
|
|
|
173
173
|
failures remain operational failures and cannot count as target detection. See the
|
|
174
174
|
[Bun migration guide](docs/migration-verification-v3.md) for the request, execution and replay
|
|
175
175
|
contract. The execution mode is explicitly unsandboxed and intended for trusted local code.
|
|
176
|
+
To verify an existing `bun:test` test by name, or to replay a recorded red/green witness, use a v4
|
|
177
|
+
request or `import-witness`, which also run in a container or a declared Windows-native backend; see
|
|
178
|
+
the [v4 migration guide](docs/migration-verification-v4.md).
|
|
176
179
|
|
|
177
180
|
### Qualify a committed regression test
|
|
178
181
|
|
|
@@ -27,6 +27,11 @@
|
|
|
27
27
|
"rawSha256": "sha256:460925ce29c51e47a38e439264f56dac9d444f52a07ec103f38b44e59679adbd",
|
|
28
28
|
"$id": "https://testforge.dev/schemas/evidence-manifest.v3.json"
|
|
29
29
|
},
|
|
30
|
+
{
|
|
31
|
+
"path": "schemas/evidence-manifest.v4.json",
|
|
32
|
+
"rawSha256": "sha256:56bad50457b84864decbe911d98e3257880e4562d69487b4a820f7d516c3d11b",
|
|
33
|
+
"$id": "https://testforge.dev/schemas/evidence-manifest.v4.json"
|
|
34
|
+
},
|
|
30
35
|
{
|
|
31
36
|
"path": "schemas/evidence-provider-manifest.v1.json",
|
|
32
37
|
"rawSha256": "sha256:6100995f772cd24b7369e4428fb1aba570f1ba6e1f61295bc42a1f7ce6b5bde3",
|
|
@@ -56,6 +61,16 @@
|
|
|
56
61
|
"path": "schemas/verification-request.v3.json",
|
|
57
62
|
"rawSha256": "sha256:3b88e4c58425aa9a45da330b27f4a48d778c1f9a2494ac2a9a4c01b29728fd77",
|
|
58
63
|
"$id": "https://testforge.dev/schemas/verification-request.v3.json"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"path": "schemas/verification-request.v4.json",
|
|
67
|
+
"rawSha256": "sha256:f08389ef40c7d73d7f1c2006aee2efcd2bf53c8b78338883f4bc9f21808db842",
|
|
68
|
+
"$id": "https://testforge.dev/schemas/verification-request.v4.json"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"path": "schemas/witness-import-request.v1.json",
|
|
72
|
+
"rawSha256": "sha256:ec2aa94fd0d50e6dec7ff6f6b9bea294febf8d7ba4598e01fe89177f97348974",
|
|
73
|
+
"$id": "https://testforge.dev/schemas/witness-import-request.v1.json"
|
|
59
74
|
}
|
|
60
75
|
]
|
|
61
76
|
}
|
package/dist/build-info.json
CHANGED
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAcA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAOlD,OAAO,EAAgD,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAYlG,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,eAAe,CAAC;IACzC,cAAc,CAAC,EAAE,OAAO,cAAc,CAAC;CACxC;AAkrBD,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,EAAE,EAAE,KAAK,EACT,YAAY,GAAE,eAAoB,GACjC,OAAO,CAAC,MAAM,CAAC,CAs7BjB"}
|
package/dist/cli.js
CHANGED
|
@@ -4,12 +4,14 @@ import path from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
6
6
|
import { ContractError } from "./contracts/index.js";
|
|
7
|
-
import {
|
|
7
|
+
import { QualificationExpectedDomainSchema } from "./contracts/qualification.js";
|
|
8
|
+
import { quoteRepositoryEntry, renderDiagnostics, renderRepositoryLinkRefusal, } from "./diagnostics.js";
|
|
8
9
|
import { connectClient, disconnectClient } from "./engine/connection.js";
|
|
9
10
|
import { parseContainerRuntimeCommand } from "./engine/container.js";
|
|
10
11
|
import { runFixtureDemo } from "./engine/demo.js";
|
|
11
12
|
import { renderGitRegressionSummary, } from "./engine/git-regression.js";
|
|
12
13
|
import { setupRepository } from "./engine/setup.js";
|
|
14
|
+
import { DEFAULT_WITNESS_ENVIRONMENT_ALLOWLIST } from "./engine/witness-import.js";
|
|
13
15
|
import { AgenticCorpusError, evaluateAgenticCorpusHoldout, evaluateAgenticCorpusPublic, inspectAgenticCorpus, } from "./evaluation/agentic-corpus.js";
|
|
14
16
|
import { createAssertLedgerServer } from "./mcp/index.js";
|
|
15
17
|
import { AssertLedger } from "./sdk/index.js";
|
|
@@ -58,9 +60,21 @@ Commands:
|
|
|
58
60
|
evidence-provider-manifest|evidence-export-request|evidence-export|
|
|
59
61
|
evidence-export-replay-result>
|
|
60
62
|
Print a JSON Schema
|
|
61
|
-
verify [request.json|-] [--container-runtime JSON_ARGV | --allow-unsafe-execution
|
|
62
|
-
|
|
63
|
+
verify [request.json|-] [--container-runtime JSON_ARGV | --allow-unsafe-execution
|
|
64
|
+
| --allow-windows-native-execution] Execute a v2-v4 container, trusted-local or
|
|
65
|
+
declared Windows-native campaign
|
|
66
|
+
import-witness [request.json|-] --out DIRECTORY
|
|
67
|
+
(--container-image NAME@sha256:DIGEST [--container-runtime JSON_ARGV]
|
|
68
|
+
| --allow-unsafe-execution | --allow-windows-native-execution)
|
|
69
|
+
[--env NAME ...] [--json] Replay a recorded red/green witness
|
|
63
70
|
replay [manifest.json|-] Verify an evidence digest
|
|
71
|
+
qualification-plan [plan.json|-] Seal an operator-owned obligation inventory
|
|
72
|
+
qualify [request.json|-] --allow-unsafe-execution
|
|
73
|
+
Execute a scoped UNSANDBOXED qualification
|
|
74
|
+
qualification-replay [receipt.json|-] [--domain domain.json]
|
|
75
|
+
Replay integrity and explicit validity domain
|
|
76
|
+
qualification-ci [receipt.json|-] --observation observation.json
|
|
77
|
+
Admit an independently signed CI observation
|
|
64
78
|
export [request.json|-] Export replay-valid evidence for external consumers
|
|
65
79
|
export-replay [export.json|-] Replay an evidence export
|
|
66
80
|
provider Describe the evidence provider and its limits
|
|
@@ -106,6 +120,10 @@ function authorizeTrustedLocalExecution(request) {
|
|
|
106
120
|
},
|
|
107
121
|
};
|
|
108
122
|
}
|
|
123
|
+
/** Windows-native execution has its own operator flag; the request value alone never grants it. */
|
|
124
|
+
function authorizeWindowsNativeExecution(request) {
|
|
125
|
+
return authorizeTrustedLocalExecution(request);
|
|
126
|
+
}
|
|
109
127
|
function authorizeBenchmarkAcquisition(request) {
|
|
110
128
|
if (!isRecord(request) || !isRecord(request.verificationRequest))
|
|
111
129
|
return request;
|
|
@@ -565,6 +583,20 @@ const VALIDATION_ERROR_CODES = new Set([
|
|
|
565
583
|
"UNSUPPORTED_ADAPTER",
|
|
566
584
|
"UNSUPPORTED_ISOLATION",
|
|
567
585
|
"UNSUPPORTED_REPOSITORY_SYMLINK",
|
|
586
|
+
"REPOSITORY_LINK_IN_TEST_CLOSURE",
|
|
587
|
+
"REPOSITORY_LINK_ESCAPES_ROOT",
|
|
588
|
+
"TEST_CLOSURE_UNBOUNDED",
|
|
589
|
+
"WINDOWS_NATIVE_HOST_REQUIRED",
|
|
590
|
+
"WINDOWS_NATIVE_EXECUTION_NOT_ACKNOWLEDGED",
|
|
591
|
+
"DESIGNATED_TEST_OVERLAID",
|
|
592
|
+
"DESIGNATED_TEST_FILE_MISSING",
|
|
593
|
+
"REPOSITORY_DEPENDENCIES_EXCLUDED",
|
|
594
|
+
"WITNESS_REPOSITORY_INVALID",
|
|
595
|
+
"WITNESS_TARGET_PATH_INVALID",
|
|
596
|
+
"WITNESS_TARGET_BASE_MISMATCH",
|
|
597
|
+
"WITNESS_TARGET_UNCHANGED",
|
|
598
|
+
"WITNESS_OUTPUT_INSIDE_REPOSITORY",
|
|
599
|
+
"WITNESS_OUTPUT_EXISTS",
|
|
568
600
|
"VERIFICATION_REQUEST_REPOSITORY_MISMATCH",
|
|
569
601
|
"WORLD_BUDGET_EXCEEDED",
|
|
570
602
|
"WORLD_OVERLAY_BYTES_EXCEEDED",
|
|
@@ -598,6 +630,19 @@ function classifyError(error) {
|
|
|
598
630
|
}
|
|
599
631
|
return 5;
|
|
600
632
|
}
|
|
633
|
+
/** Names the link that refused a static diagnostic, or the computed imports that leave the closure
|
|
634
|
+
* unbounded. Repository paths are untrusted content and are quoted. */
|
|
635
|
+
function renderLinkAssessment(assessment) {
|
|
636
|
+
if (assessment === undefined)
|
|
637
|
+
return undefined;
|
|
638
|
+
if (assessment.refused !== undefined) {
|
|
639
|
+
return `Link detail: ${quoteRepositoryEntry(assessment.refused.path)} (${assessment.refused.reasonCode})`;
|
|
640
|
+
}
|
|
641
|
+
const unbounded = assessment.unbounded
|
|
642
|
+
.slice(0, 20)
|
|
643
|
+
.map((edge) => `Unbounded closure: ${quoteRepositoryEntry(edge.file)} (${edge.reason})`);
|
|
644
|
+
return [`Links outside every test closure: ${assessment.omitted.length}`, ...unbounded].join("\n");
|
|
645
|
+
}
|
|
601
646
|
export async function runCli(argv, io, dependencies = {}) {
|
|
602
647
|
const ledger = new AssertLedger();
|
|
603
648
|
const positional = argv.filter((argument) => !argument.startsWith("--"));
|
|
@@ -699,10 +744,17 @@ export async function runCli(argv, io, dependencies = {}) {
|
|
|
699
744
|
}
|
|
700
745
|
return runtimeResult.status === "READY" ? 0 : 3;
|
|
701
746
|
}
|
|
747
|
+
let linkAssessment;
|
|
702
748
|
const result = await ledger.doctor(root, {
|
|
703
749
|
...(exclude.length === 0 ? {} : { exclude }),
|
|
704
750
|
...(framework === undefined ? {} : { framework }),
|
|
751
|
+
onLinkAssessment: (assessment) => {
|
|
752
|
+
linkAssessment = assessment;
|
|
753
|
+
},
|
|
705
754
|
});
|
|
755
|
+
const linkDetail = renderLinkAssessment(linkAssessment);
|
|
756
|
+
if (linkDetail !== undefined)
|
|
757
|
+
io.writeStderr(`${linkDetail}\n`);
|
|
706
758
|
if (argv.includes("--json")) {
|
|
707
759
|
writeJson(io, result);
|
|
708
760
|
}
|
|
@@ -1029,6 +1081,75 @@ export async function runCli(argv, io, dependencies = {}) {
|
|
|
1029
1081
|
io.writeStdout(renderGitRegressionSummary(result, options));
|
|
1030
1082
|
return decisionExitCode(result);
|
|
1031
1083
|
}
|
|
1084
|
+
case "import-witness": {
|
|
1085
|
+
const valueFlags = new Set(["--out", "--container-image", "--container-runtime", "--env"]);
|
|
1086
|
+
const booleanFlags = new Set([
|
|
1087
|
+
"--allow-unsafe-execution",
|
|
1088
|
+
"--allow-windows-native-execution",
|
|
1089
|
+
"--json",
|
|
1090
|
+
]);
|
|
1091
|
+
let input;
|
|
1092
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
1093
|
+
const argument = argv[index] ?? "";
|
|
1094
|
+
if (valueFlags.has(argument)) {
|
|
1095
|
+
const value = argv[index + 1];
|
|
1096
|
+
if (value === undefined || value.startsWith("--")) {
|
|
1097
|
+
io.writeStderr(USAGE);
|
|
1098
|
+
return 64;
|
|
1099
|
+
}
|
|
1100
|
+
index += 1;
|
|
1101
|
+
}
|
|
1102
|
+
else if (!booleanFlags.has(argument)) {
|
|
1103
|
+
if (argument.startsWith("--") || input !== undefined) {
|
|
1104
|
+
io.writeStderr(USAGE);
|
|
1105
|
+
return 64;
|
|
1106
|
+
}
|
|
1107
|
+
input = argument;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
const out = optionalFlag(argv, "--out");
|
|
1111
|
+
if (out === undefined) {
|
|
1112
|
+
io.writeStderr(USAGE);
|
|
1113
|
+
return 64;
|
|
1114
|
+
}
|
|
1115
|
+
const allowUnsafeExecution = argv.includes("--allow-unsafe-execution");
|
|
1116
|
+
const allowWindowsNative = argv.includes("--allow-windows-native-execution");
|
|
1117
|
+
const containerImage = optionalFlag(argv, "--container-image");
|
|
1118
|
+
const containerRuntime = optionalFlag(argv, "--container-runtime");
|
|
1119
|
+
const environment = repeatedFlagValues(argv, "--env");
|
|
1120
|
+
const selected = [allowUnsafeExecution, allowWindowsNative, containerImage !== undefined];
|
|
1121
|
+
if (selected.filter(Boolean).length > 1 ||
|
|
1122
|
+
(containerImage === undefined && containerRuntime !== undefined) ||
|
|
1123
|
+
(containerImage !== undefined && environment.length > 0)) {
|
|
1124
|
+
throw new TypeError("ISOLATION_MODE_CONFLICT");
|
|
1125
|
+
}
|
|
1126
|
+
if (!selected.some(Boolean)) {
|
|
1127
|
+
io.writeStderr("Refusing to replay a witness without an authorized backend: pass --container-image, " +
|
|
1128
|
+
"--allow-unsafe-execution or --allow-windows-native-execution.\n");
|
|
1129
|
+
return 4;
|
|
1130
|
+
}
|
|
1131
|
+
const environmentAllowlist = environment.length === 0 ? [...DEFAULT_WITNESS_ENVIRONMENT_ALLOWLIST] : environment;
|
|
1132
|
+
const result = await ledger.importWitness(await readJsonInput(input, io), {
|
|
1133
|
+
out: path.resolve(io.cwd, out),
|
|
1134
|
+
isolation: containerImage !== undefined
|
|
1135
|
+
? {
|
|
1136
|
+
kind: "container",
|
|
1137
|
+
image: containerImage,
|
|
1138
|
+
...(containerRuntime === undefined
|
|
1139
|
+
? {}
|
|
1140
|
+
: { runtimeCommand: parseContainerRuntimeCommand(containerRuntime) }),
|
|
1141
|
+
}
|
|
1142
|
+
: allowWindowsNative
|
|
1143
|
+
? { kind: "windows-native", environmentAllowlist }
|
|
1144
|
+
: { kind: "trusted-local", environmentAllowlist },
|
|
1145
|
+
});
|
|
1146
|
+
if (argv.includes("--json"))
|
|
1147
|
+
writeJson(io, result);
|
|
1148
|
+
else {
|
|
1149
|
+
io.writeStdout(`${await readFile(path.join(path.resolve(io.cwd, out), "summary.md"), "utf8")}`);
|
|
1150
|
+
}
|
|
1151
|
+
return decisionExitCode(result);
|
|
1152
|
+
}
|
|
1032
1153
|
case "schema": {
|
|
1033
1154
|
const name = positional[1];
|
|
1034
1155
|
if (name !== "verification-request" &&
|
|
@@ -1048,6 +1169,10 @@ export async function runCli(argv, io, dependencies = {}) {
|
|
|
1048
1169
|
name !== "replay-result" &&
|
|
1049
1170
|
name !== "agentic-benchmark-request" &&
|
|
1050
1171
|
name !== "agentic-benchmark-artifact" &&
|
|
1172
|
+
name !== "qualification-plan" &&
|
|
1173
|
+
name !== "qualification-execution-request" &&
|
|
1174
|
+
name !== "qualification-receipt" &&
|
|
1175
|
+
name !== "qualification-replay-result" &&
|
|
1051
1176
|
name !== "agentic-benchmark-replay-result" &&
|
|
1052
1177
|
name !== "agentic-benchmark-acquisition-request" &&
|
|
1053
1178
|
name !== "agentic-benchmark-acquisition-result" &&
|
|
@@ -1083,22 +1208,43 @@ export async function runCli(argv, io, dependencies = {}) {
|
|
|
1083
1208
|
case "verify": {
|
|
1084
1209
|
const request = await readJsonInput(positionalArguments(argv, new Set(["--container-runtime"]))[1], io);
|
|
1085
1210
|
const allowUnsafeExecution = argv.includes("--allow-unsafe-execution");
|
|
1211
|
+
const allowWindowsNative = argv.includes("--allow-windows-native-execution");
|
|
1086
1212
|
const runtimeArgument = optionalFlag(argv, "--container-runtime");
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1213
|
+
const isolationKind = isRecord(request) && isRecord(request.isolation) ? request.isolation.kind : undefined;
|
|
1214
|
+
const containerRequest = isolationKind === "container";
|
|
1215
|
+
const windowsNativeRequest = isolationKind === "windows-native";
|
|
1216
|
+
// Each backend has exactly one authorization; another backend's flag never stands in.
|
|
1217
|
+
if ((containerRequest && (allowUnsafeExecution || allowWindowsNative)) ||
|
|
1218
|
+
(windowsNativeRequest && (allowUnsafeExecution || runtimeArgument !== undefined)) ||
|
|
1219
|
+
(!containerRequest &&
|
|
1220
|
+
!windowsNativeRequest &&
|
|
1221
|
+
(allowWindowsNative || runtimeArgument !== undefined))) {
|
|
1091
1222
|
throw new TypeError("ISOLATION_MODE_CONFLICT");
|
|
1092
1223
|
}
|
|
1093
|
-
if (
|
|
1224
|
+
if (windowsNativeRequest && !allowWindowsNative) {
|
|
1225
|
+
io.writeStderr("Refusing WINDOWS_NATIVE_UNSANDBOXED execution without --allow-windows-native-execution.\n");
|
|
1226
|
+
return 4;
|
|
1227
|
+
}
|
|
1228
|
+
if (!containerRequest && !windowsNativeRequest && !allowUnsafeExecution) {
|
|
1094
1229
|
io.writeStderr("Refusing trusted-local execution without --allow-unsafe-execution.\n");
|
|
1095
1230
|
return 4;
|
|
1096
1231
|
}
|
|
1097
1232
|
const version2 = isRecord(request) && request.schemaVersion === "2.0.0";
|
|
1098
1233
|
const version3 = isRecord(request) && request.schemaVersion === "3.0.0";
|
|
1234
|
+
const version4 = isRecord(request) && request.schemaVersion === "4.0.0";
|
|
1099
1235
|
const runtimeOptions = runtimeArgument === undefined
|
|
1100
1236
|
? {}
|
|
1101
1237
|
: { containerRuntime: { command: parseContainerRuntimeCommand(runtimeArgument) } };
|
|
1238
|
+
if (version4) {
|
|
1239
|
+
const authorized = containerRequest
|
|
1240
|
+
? request
|
|
1241
|
+
: windowsNativeRequest
|
|
1242
|
+
? authorizeWindowsNativeExecution(request)
|
|
1243
|
+
: authorizeTrustedLocalExecution(request);
|
|
1244
|
+
const manifest = await ledger.verifyV4(authorized, runtimeOptions);
|
|
1245
|
+
writeJson(io, manifest);
|
|
1246
|
+
return decisionExitCode(manifest);
|
|
1247
|
+
}
|
|
1102
1248
|
const result = version3
|
|
1103
1249
|
? await ledger.verifyV3(containerRequest ? request : authorizeTrustedLocalExecution(request), runtimeOptions)
|
|
1104
1250
|
: containerRequest || version2
|
|
@@ -1107,6 +1253,65 @@ export async function runCli(argv, io, dependencies = {}) {
|
|
|
1107
1253
|
writeJson(io, result);
|
|
1108
1254
|
return decisionExitCode(result);
|
|
1109
1255
|
}
|
|
1256
|
+
case "qualification-plan": {
|
|
1257
|
+
if (argv.slice(1).filter((item) => item !== "--json").length > 1 ||
|
|
1258
|
+
argv.slice(1).some((item) => item.startsWith("--") && item !== "--json")) {
|
|
1259
|
+
io.writeStderr(USAGE);
|
|
1260
|
+
return 64;
|
|
1261
|
+
}
|
|
1262
|
+
writeJson(io, ledger.sealQualificationPlan(await readJsonInput(positional[1], io)));
|
|
1263
|
+
return 0;
|
|
1264
|
+
}
|
|
1265
|
+
case "qualify": {
|
|
1266
|
+
if (argv
|
|
1267
|
+
.slice(1)
|
|
1268
|
+
.some((item) => item.startsWith("--") && item !== "--allow-unsafe-execution" && item !== "--json") ||
|
|
1269
|
+
positional.length > 2) {
|
|
1270
|
+
io.writeStderr(USAGE);
|
|
1271
|
+
return 64;
|
|
1272
|
+
}
|
|
1273
|
+
if (!argv.includes("--allow-unsafe-execution")) {
|
|
1274
|
+
io.writeStderr("Refusing UNSANDBOXED qualification without --allow-unsafe-execution.\n");
|
|
1275
|
+
return 4;
|
|
1276
|
+
}
|
|
1277
|
+
const result = await ledger.qualifyOrchestration(await readJsonInput(positional[1], io), {
|
|
1278
|
+
allowUnsafeExecution: true,
|
|
1279
|
+
});
|
|
1280
|
+
writeJson(io, result);
|
|
1281
|
+
return result.decision === "QUALIFIED" ? 0 : result.decision === "OPEN" ? 3 : 2;
|
|
1282
|
+
}
|
|
1283
|
+
case "qualification-replay": {
|
|
1284
|
+
const domainPath = optionalFlag(argv, "--domain");
|
|
1285
|
+
const args = positionalArguments(argv, new Set(["--domain"]));
|
|
1286
|
+
if (args.length > 2 ||
|
|
1287
|
+
argv
|
|
1288
|
+
.slice(1)
|
|
1289
|
+
.some((item) => item.startsWith("--") && item !== "--domain" && item !== "--json")) {
|
|
1290
|
+
io.writeStderr(USAGE);
|
|
1291
|
+
return 64;
|
|
1292
|
+
}
|
|
1293
|
+
const domain = domainPath === undefined
|
|
1294
|
+
? undefined
|
|
1295
|
+
: QualificationExpectedDomainSchema.parse(await readJsonInput(domainPath, io));
|
|
1296
|
+
const result = ledger.replayQualification(await readJsonInput(args[1], io), domain);
|
|
1297
|
+
writeJson(io, result);
|
|
1298
|
+
return result.valid ? 0 : 4;
|
|
1299
|
+
}
|
|
1300
|
+
case "qualification-ci": {
|
|
1301
|
+
const observationPath = optionalFlag(argv, "--observation");
|
|
1302
|
+
const args = positionalArguments(argv, new Set(["--observation"]));
|
|
1303
|
+
if (observationPath === undefined ||
|
|
1304
|
+
args.length > 2 ||
|
|
1305
|
+
argv
|
|
1306
|
+
.slice(1)
|
|
1307
|
+
.some((item) => item.startsWith("--") && item !== "--observation" && item !== "--json")) {
|
|
1308
|
+
io.writeStderr(USAGE);
|
|
1309
|
+
return 64;
|
|
1310
|
+
}
|
|
1311
|
+
const result = ledger.admitQualificationCi(await readJsonInput(args[1], io), await readJsonInput(observationPath, io));
|
|
1312
|
+
writeJson(io, result);
|
|
1313
|
+
return result.decision === "QUALIFIED" ? 0 : result.decision === "OPEN" ? 3 : 2;
|
|
1314
|
+
}
|
|
1110
1315
|
case "replay": {
|
|
1111
1316
|
const result = ledger.replay(await readJsonInput(positional[1], io));
|
|
1112
1317
|
writeJson(io, result);
|