witnora 0.8.1 → 0.9.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.
Files changed (87) hide show
  1. package/README.md +275 -12
  2. package/dist/artifact-manifest.js +37 -0
  3. package/dist/artifact-validation.js +102 -0
  4. package/dist/badge.js +31 -0
  5. package/dist/browser-adapter.js +156 -0
  6. package/dist/bundle.js +100 -0
  7. package/dist/cli.js +971 -0
  8. package/dist/command-help.js +71 -0
  9. package/dist/companion-artifacts.js +170 -0
  10. package/dist/conformance.js +115 -0
  11. package/dist/control-plane.js +409 -0
  12. package/dist/corpus-governance.js +126 -0
  13. package/dist/corpus-store.js +239 -0
  14. package/dist/corpus.js +438 -0
  15. package/dist/credentials.js +127 -0
  16. package/dist/evidence-signing.js +85 -0
  17. package/dist/failure-review.js +188 -0
  18. package/dist/guided-setup.js +101 -0
  19. package/dist/index.js +16 -0
  20. package/dist/lab.js +323 -0
  21. package/dist/local-server.js +491 -0
  22. package/dist/monitor.js +100 -0
  23. package/dist/normalizers.js +265 -0
  24. package/dist/onboarding-templates.js +158 -0
  25. package/dist/release-gate.js +457 -0
  26. package/dist/report.js +165 -0
  27. package/dist/runner.js +349 -0
  28. package/dist/sandbox.js +424 -0
  29. package/dist/schema-validator.js +503 -0
  30. package/dist/try.js +135 -0
  31. package/dist/types.js +3 -0
  32. package/dist/vendor/onegent-runtime/browser-adapter-kit.d.ts +107 -0
  33. package/dist/vendor/onegent-runtime/browser-adapter-kit.d.ts.map +1 -0
  34. package/dist/vendor/onegent-runtime/browser-adapter-kit.js +187 -0
  35. package/dist/vendor/onegent-runtime/browser-enforcement-runtime.d.ts +183 -0
  36. package/dist/vendor/onegent-runtime/browser-enforcement-runtime.d.ts.map +1 -0
  37. package/dist/vendor/onegent-runtime/browser-enforcement-runtime.js +344 -0
  38. package/dist/vendor/onegent-runtime/controlled-adapter.d.ts +6 -0
  39. package/dist/vendor/onegent-runtime/controlled-adapter.d.ts.map +1 -0
  40. package/dist/vendor/onegent-runtime/controlled-adapter.js +40 -0
  41. package/dist/vendor/onegent-runtime/mock-procurement.d.ts +6 -0
  42. package/dist/vendor/onegent-runtime/mock-procurement.d.ts.map +1 -0
  43. package/dist/vendor/onegent-runtime/mock-procurement.js +33 -0
  44. package/dist/vendor/onegent-runtime/policies.d.ts +6 -0
  45. package/dist/vendor/onegent-runtime/policies.d.ts.map +1 -0
  46. package/dist/vendor/onegent-runtime/policies.js +109 -0
  47. package/dist/vendor/onegent-runtime/risk.d.ts +3 -0
  48. package/dist/vendor/onegent-runtime/risk.d.ts.map +1 -0
  49. package/dist/vendor/onegent-runtime/risk.js +54 -0
  50. package/dist/vendor/onegent-runtime/sandbox-adapter-kit.d.ts +45 -0
  51. package/dist/vendor/onegent-runtime/sandbox-adapter-kit.d.ts.map +1 -0
  52. package/dist/vendor/onegent-runtime/sandbox-adapter-kit.js +148 -0
  53. package/dist/vendor/onegent-runtime/sandbox-harness.d.ts +167 -0
  54. package/dist/vendor/onegent-runtime/sandbox-harness.d.ts.map +1 -0
  55. package/dist/vendor/onegent-runtime/sandbox-harness.js +729 -0
  56. package/dist/vendor/onegent-runtime/sandbox-hosted.d.ts +77 -0
  57. package/dist/vendor/onegent-runtime/sandbox-hosted.d.ts.map +1 -0
  58. package/dist/vendor/onegent-runtime/sandbox-hosted.js +152 -0
  59. package/dist/vendor/onegent-runtime/sdk.d.ts +29 -0
  60. package/dist/vendor/onegent-runtime/sdk.d.ts.map +1 -0
  61. package/dist/vendor/onegent-runtime/sdk.js +140 -0
  62. package/dist/vendor/onegent-runtime/service.d.ts +27 -0
  63. package/dist/vendor/onegent-runtime/service.d.ts.map +1 -0
  64. package/dist/vendor/onegent-runtime/service.js +423 -0
  65. package/dist/vendor/onegent-runtime/store.d.ts +16 -0
  66. package/dist/vendor/onegent-runtime/store.d.ts.map +1 -0
  67. package/dist/vendor/onegent-runtime/store.js +29 -0
  68. package/dist/vendor/onegent-runtime/stripe-test-readonly.d.ts +79 -0
  69. package/dist/vendor/onegent-runtime/stripe-test-readonly.d.ts.map +1 -0
  70. package/dist/vendor/onegent-runtime/stripe-test-readonly.js +163 -0
  71. package/dist/vendor/onegent-runtime/trust-crypto.d.ts +11 -0
  72. package/dist/vendor/onegent-runtime/trust-crypto.d.ts.map +1 -0
  73. package/dist/vendor/onegent-runtime/trust-crypto.js +70 -0
  74. package/dist/vendor/onegent-runtime/trust-types.d.ts +163 -0
  75. package/dist/vendor/onegent-runtime/trust-types.d.ts.map +1 -0
  76. package/dist/vendor/onegent-runtime/trust-types.js +7 -0
  77. package/dist/vendor/onegent-runtime/trusted-recorder.d.ts +49 -0
  78. package/dist/vendor/onegent-runtime/trusted-recorder.d.ts.map +1 -0
  79. package/dist/vendor/onegent-runtime/trusted-recorder.js +347 -0
  80. package/dist/vendor/onegent-runtime/types.d.ts +287 -0
  81. package/dist/vendor/onegent-runtime/types.d.ts.map +1 -0
  82. package/dist/vendor/onegent-runtime/types.js +1 -0
  83. package/dist/vendor/onegent-runtime/vendor-sandbox-egress.d.ts +49 -0
  84. package/dist/vendor/onegent-runtime/vendor-sandbox-egress.d.ts.map +1 -0
  85. package/dist/vendor/onegent-runtime/vendor-sandbox-egress.js +152 -0
  86. package/package.json +33 -13
  87. package/bin/witnora.js +0 -3
package/dist/cli.js ADDED
@@ -0,0 +1,971 @@
1
+ #!/usr/bin/env node
2
+ import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
+ import { createInterface } from "node:readline/promises";
5
+ import { validateEvidenceArtifacts } from "./artifact-validation.js";
6
+ import { renderAgentCertBadge } from "./badge.js";
7
+ import { buildEvidenceBundle } from "./bundle.js";
8
+ import { collectCompanionArtifacts, MAX_REPORTED_COMPANION_ARTIFACT_SKIPS } from "./companion-artifacts.js";
9
+ import { recordsFromAgentCertResult, evaluateFailureClassifier, renderCorpusSummary, summarizeCorpus, writeReviewedFailureDataset, } from "./corpus.js";
10
+ import { openCorpusStore, parseCorpusStoreKind } from "./corpus-store.js";
11
+ import { deleteCorpusRecords, exportGovernedCorpus, governCorpusRecords, parseCorpusConsent } from "./corpus-governance.js";
12
+ import { pushEvidenceToControlPlane, requireContinuousAssuranceCurrent, verifyControlPlaneConnection } from "./control-plane.js";
13
+ import { runEvidenceConformance } from "./conformance.js";
14
+ import { DEFAULT_WITNORA_SERVER, resolveConnection, saveConnection } from "./credentials.js";
15
+ import { applyFailureReviews, appendFailureReview, createFailureReview, findFailurePattern, parseFailureReviewStatus, parseFailureType, parseReviewConfidence, readFailureReviews, } from "./failure-review.js";
16
+ import { buildMonitorSnapshot, writeMonitorSnapshot } from "./monitor.js";
17
+ import { normalizeMcpBenchResult, normalizeOnegentAuditPacket, normalizeTripwireResult } from "./normalizers.js";
18
+ import { renderHtmlReport, renderMarkdownReport } from "./report.js";
19
+ import { generateEvidenceSigningKeyPair, signEvidenceFile, verifyEvidenceFile, } from "./evidence-signing.js";
20
+ import { buildReleaseGateReport, renderReleaseGateSummary, writeReleaseGateArtifacts, } from "./release-gate.js";
21
+ import { serveAgentCertMonitor } from "./local-server.js";
22
+ import { parseSchemaId, validateAgentCertSchema } from "./schema-validator.js";
23
+ import { applyRunOverrides, loadRunProfile, profileFromArtifactFlags, renderRunSummary, runAgentCertProfile, } from "./runner.js";
24
+ import { buildRobustnessLabSnapshot, readRobustnessLabConfig, renderRobustnessLabSummary, writeRobustnessLabSnapshot } from "./lab.js";
25
+ import { renderCommandHelp } from "./command-help.js";
26
+ import { runSandboxCommand } from "./sandbox.js";
27
+ import { runBrowserAdapterCommand } from "./browser-adapter.js";
28
+ import { parseAgentTemplate, starterAdapter, starterGitHubActionWorkflow, starterInstructions, starterProfile, starterTripwireConfig, } from "./onboarding-templates.js";
29
+ import { renderTrySummary, writeTryEvidence } from "./try.js";
30
+ import { applyGuidedSetup } from "./guided-setup.js";
31
+ process.on("uncaughtException", reportFatalError);
32
+ process.on("unhandledRejection", reportFatalError);
33
+ const command = process.argv[2] ?? "help";
34
+ const commandHelp = process.argv.some((argument) => argument === "--help" || argument === "-h")
35
+ ? renderCommandHelp(command)
36
+ : undefined;
37
+ if (commandHelp) {
38
+ process.stdout.write(commandHelp);
39
+ }
40
+ else if (command === "try") {
41
+ const template = parseAgentTemplate(readFlag("--template") ?? "workflow");
42
+ const subject = readFlag("--subject") ?? `witnora-sample-${template}`;
43
+ const outcome = await writeTryEvidence({
44
+ template,
45
+ subject,
46
+ outDir: readFlag("--out") ?? ".witnora/try",
47
+ });
48
+ process.stdout.write(renderTrySummary(outcome));
49
+ if (readBoolFlag("--push")) {
50
+ await pushHostedEvidence(outcome.bundle, Buffer.from(`${JSON.stringify(outcome.bundle, null, 2)}\n`), "agentcert-evidence.json");
51
+ }
52
+ }
53
+ else if (command === "init") {
54
+ const outPath = resolve(readFlag("--out") ?? "witnora.config.json");
55
+ const tripwireConfigPath = resolve(readFlag("--tripwire-config") ?? "tripwire.yml");
56
+ const githubWorkflowPath = resolve(readFlag("--github-action-out") ?? ".github/workflows/witnora-tripwire.yml");
57
+ const adapterPath = resolve(readFlag("--adapter-out") ?? "witnora.adapter.mjs");
58
+ const template = parseAgentTemplate(readFlag("--template"));
59
+ const subject = readFlag("--subject") ?? `my-${template}-agent`;
60
+ const force = readBoolFlag("--force");
61
+ const writeGitHubAction = (readBoolFlag("--github-action") || process.argv.includes("--github-action-out")) && !readBoolFlag("--skip-github-action");
62
+ const config = starterProfile(template, subject);
63
+ await writeStarterFile(outPath, `${JSON.stringify(config, null, 2)}\n`, force);
64
+ process.stdout.write(`Wrote ${outPath}\n`);
65
+ if (template === "browser" && !readBoolFlag("--skip-tripwire")) {
66
+ await writeStarterFile(tripwireConfigPath, starterTripwireConfig(subject), force);
67
+ process.stdout.write(`Wrote ${tripwireConfigPath}\n`);
68
+ }
69
+ if (template === "browser" && writeGitHubAction) {
70
+ await writeStarterFile(githubWorkflowPath, starterGitHubActionWorkflow(subject), force);
71
+ process.stdout.write(`Wrote ${githubWorkflowPath}\n`);
72
+ }
73
+ if (template === "coding" || template === "workflow" || template === "data") {
74
+ await writeStarterFile(adapterPath, starterAdapter(template, subject), force);
75
+ process.stdout.write(`Wrote ${adapterPath}\n`);
76
+ }
77
+ process.stdout.write(starterInstructions(template, subject));
78
+ }
79
+ else if (command === "connect") {
80
+ if (readBoolFlag("--help")) {
81
+ process.stdout.write(`Usage:
82
+ witnora connect --server https://witnora.com --project <project-id>
83
+ witnora connect --name staging --server https://staging.example.com --project <project-id>
84
+
85
+ The API key is read from WITNORA_API_KEY (or legacy AGENTCERT_API_KEY) or requested with hidden input.
86
+ Saved connections are reused by witnora push and witnora run --push.
87
+ `);
88
+ }
89
+ else {
90
+ const name = readFlag("--name") ?? "default";
91
+ const server = readFlag("--server") ?? brandedEnvironment("BASE_URL") ?? DEFAULT_WITNORA_SERVER;
92
+ const projectId = readFlag("--project") ?? brandedEnvironment("PROJECT_ID") ?? await promptValue("Project ID: ");
93
+ const apiKey = readFlag("--api-key") ?? brandedEnvironment("API_KEY") ?? await promptSecret("Project API key: ");
94
+ const connection = await resolveConnection({ server, projectId, apiKey, env: {} });
95
+ const verified = await verifyControlPlaneConnection({
96
+ baseUrl: connection.server,
97
+ projectId: connection.projectId,
98
+ apiKey: connection.apiKey,
99
+ });
100
+ const path = await saveConnection(name, connection);
101
+ process.stdout.write(`Connected ${JSON.stringify(name)} to ${connection.server}\n`);
102
+ process.stdout.write(`Project: ${verified.projectId} (${verified.runs} runs, ${verified.evidence} evidence objects)\n`);
103
+ process.stdout.write(`Credentials: ${path}\n`);
104
+ }
105
+ }
106
+ else if (command === "setup") {
107
+ const action = process.argv[3] ?? "help";
108
+ if (action !== "apply")
109
+ throw new Error("Use witnora setup apply --plan <setup-plan.json> --repo <directory>.");
110
+ const planPath = readFlag("--plan");
111
+ if (!planPath)
112
+ throw new Error("--plan is required.");
113
+ const result = await applyGuidedSetup({
114
+ planPath,
115
+ repository: readFlag("--repo") ?? process.cwd(),
116
+ force: readBoolFlag("--force"),
117
+ githubPullRequest: readBoolFlag("--github-pr"),
118
+ configureCi: readBoolFlag("--configure-ci"),
119
+ });
120
+ process.stdout.write(`Applied ${result.template} setup plan ${result.planId}.\n`);
121
+ for (const file of result.files)
122
+ process.stdout.write(`Wrote ${file}\n`);
123
+ if (result.pullRequestUrl)
124
+ process.stdout.write(`Pull request: ${result.pullRequestUrl}\n`);
125
+ if (result.ciConfigured)
126
+ process.stdout.write("Configured WITNORA_PROJECT_ID and WITNORA_API_KEY for GitHub Actions.\n");
127
+ else
128
+ process.stdout.write("Review the generated files, store WITNORA_API_KEY in your secret manager, then run the generated workflow. Legacy AGENTCERT_* names remain supported.\n");
129
+ }
130
+ else if (command === "sandbox") {
131
+ const result = await runSandboxCommand(process.argv.slice(3));
132
+ process.exitCode = result.exitCode;
133
+ }
134
+ else if (command === "browser-adapter") {
135
+ const result = await runBrowserAdapterCommand(process.argv.slice(3));
136
+ process.exitCode = result.exitCode;
137
+ }
138
+ else if (command === "report") {
139
+ const config = await loadConfig(readFlag("--config"));
140
+ const subject = readFlag("--subject") ?? config?.subject.name ?? "agentcert-subject";
141
+ const subjectType = readFlag("--subject-type") ?? config?.subject.type ?? "agent";
142
+ const outDir = resolve(readFlag("--out") ?? config?.outputDir ?? ".witnora/latest");
143
+ const loaded = await loadArtifactResults(config);
144
+ const results = loaded.map((artifact) => artifact.result);
145
+ if (results.length === 0) {
146
+ throw new Error("No input artifacts were provided. Use --mcpbench, --tripwire, --onegent, or --config.");
147
+ }
148
+ const bundle = buildEvidenceBundle(results, subject, subjectType);
149
+ await mkdir(outDir, { recursive: true });
150
+ await writeFile(`${outDir}/agentcert-evidence.json`, `${JSON.stringify(bundle, null, 2)}\n`);
151
+ await writeFile(`${outDir}/agentcert-report.md`, renderMarkdownReport(bundle));
152
+ await writeFile(`${outDir}/agentcert-report.html`, renderHtmlReport(bundle));
153
+ await writeFile(`${outDir}/badge.svg`, renderAgentCertBadge(bundle));
154
+ process.stdout.write(`Wrote ${outDir}\\agentcert-evidence.json\n`);
155
+ process.stdout.write(`Wrote ${outDir}\\agentcert-report.md\n`);
156
+ process.stdout.write(`Wrote ${outDir}\\agentcert-report.html\n`);
157
+ process.stdout.write(`Wrote ${outDir}\\badge.svg\n`);
158
+ process.exitCode = bundle.verdict.passed ? 0 : 1;
159
+ }
160
+ else if (command === "corpus") {
161
+ const action = process.argv[3] ?? "help";
162
+ if (action === "ingest") {
163
+ const config = await loadConfig(readFlag("--config"));
164
+ const subject = readFlag("--subject") ?? config?.subject.name ?? "agentcert-subject";
165
+ const storeOptions = readCorpusStoreOptions(".witnora/corpus/corpus.jsonl", readFlag("--out"));
166
+ const replace = readBoolFlag("--replace");
167
+ const loaded = await loadArtifactResults(config);
168
+ if (loaded.length === 0) {
169
+ throw new Error("No input artifacts were provided. Use --mcpbench, --tripwire, --onegent, or --config.");
170
+ }
171
+ const reviews = await readFailureReviews(readReviewsPath());
172
+ const records = governCorpusRecords(applyFailureReviews(loaded.flatMap(({ result, path, raw }) => recordsFromAgentCertResult(result, path, subject, raw)), reviews), { consent: parseCorpusConsent(readFlag("--consent")), consentSource: readFlag("--consent-source") ?? "local-default" });
173
+ const store = await openCorpusStore(storeOptions);
174
+ try {
175
+ await store.append(records, { replace });
176
+ process.stdout.write(`Wrote ${records.length} corpus records to ${store.description}\n`);
177
+ }
178
+ finally {
179
+ await store.close();
180
+ }
181
+ const summary = summarizeCorpus(records);
182
+ process.stdout.write(renderCorpusSummary(summary));
183
+ }
184
+ else if (action === "summary") {
185
+ const store = await openCorpusStore(readCorpusStoreOptions(".witnora/corpus/corpus.jsonl"));
186
+ try {
187
+ const records = applyFailureReviews(await store.readAll(), await readFailureReviews(readReviewsPath()));
188
+ process.stdout.write(renderCorpusSummary(summarizeCorpus(records)));
189
+ }
190
+ finally {
191
+ await store.close();
192
+ }
193
+ }
194
+ else if (action === "metrics") {
195
+ const store = await openCorpusStore(readCorpusStoreOptions(".witnora/corpus/corpus.jsonl"));
196
+ try {
197
+ const records = applyFailureReviews(await store.readAll(), await readFailureReviews(readReviewsPath()));
198
+ const summary = summarizeCorpus(records);
199
+ process.stdout.write(`${JSON.stringify(summary.taxonomy, null, 2)}\n`);
200
+ }
201
+ finally {
202
+ await store.close();
203
+ }
204
+ }
205
+ else if (action === "export-reviewed") {
206
+ const outPath = readFlag("--out") ?? ".witnora/latest/reviewed-failure-dataset.jsonl";
207
+ const store = await openCorpusStore(readCorpusStoreOptions(".witnora/corpus/corpus.jsonl"));
208
+ try {
209
+ const records = applyFailureReviews(await store.readAll(), await readFailureReviews(readReviewsPath()));
210
+ const rows = await writeReviewedFailureDataset(outPath, records);
211
+ process.stdout.write(`Wrote ${rows.length} reviewed failure rows to ${resolve(outPath)}\n`);
212
+ }
213
+ finally {
214
+ await store.close();
215
+ }
216
+ }
217
+ else if (action === "export") {
218
+ const outPath = readFlag("--out") ?? ".witnora/latest/governed-corpus.jsonl";
219
+ const allowed = (readFlag("--consent") ?? "public,anonymous").split(",").map((item) => parseCorpusConsent(item.trim()));
220
+ const store = await openCorpusStore(readCorpusStoreOptions(".witnora/corpus/corpus.jsonl"));
221
+ try {
222
+ const records = exportGovernedCorpus(await store.readAll(), allowed);
223
+ await mkdir(dirname(resolve(outPath)), { recursive: true });
224
+ await writeFile(outPath, records.map((record) => JSON.stringify(record)).join("\n") + (records.length ? "\n" : ""));
225
+ process.stdout.write(`Exported ${records.length} consented corpus records to ${resolve(outPath)}\n`);
226
+ }
227
+ finally {
228
+ await store.close();
229
+ }
230
+ }
231
+ else if (action === "delete") {
232
+ const ids = requiredFlag("--record-id").split(",").map((item) => item.trim()).filter(Boolean);
233
+ const reason = requiredFlag("--reason");
234
+ const journalPath = readFlag("--journal") ?? ".witnora/corpus/deletion-journal.jsonl";
235
+ const store = await openCorpusStore(readCorpusStoreOptions(".witnora/corpus/corpus.jsonl"));
236
+ try {
237
+ const result = deleteCorpusRecords(await store.readAll(), ids, reason);
238
+ if (!result.tombstones.length)
239
+ throw new Error("No corpus records matched --record-id; nothing was deleted.");
240
+ await store.append(result.retained, { replace: true });
241
+ await mkdir(dirname(resolve(journalPath)), { recursive: true });
242
+ await appendFile(journalPath, result.tombstones.map((item) => JSON.stringify(item)).join("\n") + "\n");
243
+ process.stdout.write(`Deleted ${result.tombstones.length} corpus records and wrote non-identifying tombstones to ${resolve(journalPath)}\n`);
244
+ }
245
+ finally {
246
+ await store.close();
247
+ }
248
+ }
249
+ else if (action === "classifier-eval") {
250
+ const outPath = readFlag("--out");
251
+ const store = await openCorpusStore(readCorpusStoreOptions(".witnora/corpus/corpus.jsonl"));
252
+ try {
253
+ const records = applyFailureReviews(await store.readAll(), await readFailureReviews(readReviewsPath()));
254
+ const evaluation = `${JSON.stringify(evaluateFailureClassifier(records), null, 2)}\n`;
255
+ if (outPath) {
256
+ await mkdir(dirname(resolve(outPath)), { recursive: true });
257
+ await writeFile(outPath, evaluation);
258
+ process.stdout.write(`Wrote classifier evaluation to ${resolve(outPath)}\n`);
259
+ }
260
+ else {
261
+ process.stdout.write(evaluation);
262
+ }
263
+ }
264
+ finally {
265
+ await store.close();
266
+ }
267
+ }
268
+ else if (action === "review") {
269
+ const storeOptions = readCorpusStoreOptions(".witnora/corpus/corpus.jsonl");
270
+ const reviewPath = readReviewsPath() ?? ".witnora/corpus/failure-reviews.jsonl";
271
+ const store = await openCorpusStore(storeOptions);
272
+ try {
273
+ const records = await store.readAll();
274
+ const target = {
275
+ patternKey: requiredFlag("--pattern-key"),
276
+ recordId: readFlag("--record-id"),
277
+ runId: readFlag("--run-id"),
278
+ product: readFlag("--product"),
279
+ scenarioName: readFlag("--scenario"),
280
+ faultName: readFlag("--fault"),
281
+ };
282
+ const matched = findFailurePattern(records, target);
283
+ const type = parseFailureType(readFlag("--type"));
284
+ const suggestedType = matched?.pattern.suggestedType ?? matched?.pattern.type;
285
+ const status = parseFailureReviewStatus(readFlag("--status"), type, suggestedType);
286
+ const review = createFailureReview({
287
+ target,
288
+ type,
289
+ status,
290
+ suggestedType,
291
+ reviewer: readFlag("--reviewer"),
292
+ note: readFlag("--note"),
293
+ confidence: parseReviewConfidence(readFlag("--confidence")),
294
+ evidenceContext: readReviewEvidenceContextFromFlags(),
295
+ taxonomyRationale: readReviewTaxonomyRationaleFromFlags(),
296
+ });
297
+ await appendFailureReview(reviewPath, review);
298
+ const updated = applyFailureReviews(records, await readFailureReviews(reviewPath));
299
+ await store.append(updated, { replace: true });
300
+ process.stdout.write(`Wrote failure review ${review.id} to ${resolve(reviewPath)}\n`);
301
+ process.stdout.write(`Updated ${updated.length} corpus records in ${store.description}\n`);
302
+ }
303
+ finally {
304
+ await store.close();
305
+ }
306
+ }
307
+ else {
308
+ process.stdout.write(`Usage:
309
+ witnora corpus ingest --tripwire .tripwire/latest/tripwire-result.json --out .witnora/corpus/corpus.jsonl --subject my-agent
310
+ witnora corpus ingest --store sqlite --sqlite .witnora/corpus/agentcert.sqlite --tripwire .tripwire/latest/tripwire-result.json --subject my-agent
311
+ witnora corpus ingest --tripwire result.json --consent anonymous --consent-source "pilot agreement 2026-07"
312
+ witnora corpus review --corpus .witnora/corpus/corpus.jsonl --reviews .witnora/corpus/failure-reviews.jsonl --pattern-key tripwire:ui_drift:modal-overlay:url_contains --type ui_drift --status confirmed --reviewer you@example.com --confidence 0.8 --why "Visible evidence supports the ui_drift label."
313
+ witnora corpus metrics --corpus .witnora/corpus/corpus.jsonl
314
+ witnora corpus export-reviewed --corpus .witnora/corpus/corpus.jsonl --out .witnora/latest/reviewed-failure-dataset.jsonl
315
+ witnora corpus export --corpus .witnora/corpus/corpus.jsonl --consent public,anonymous --out .witnora/latest/governed-corpus.jsonl
316
+ witnora corpus delete --corpus .witnora/corpus/corpus.jsonl --record-id <id> --reason "participant withdrawal"
317
+ witnora corpus classifier-eval --corpus .witnora/corpus/corpus.jsonl --out .witnora/latest/failure-classifier-evaluation.json
318
+ witnora corpus summary --corpus .witnora/corpus/corpus.jsonl
319
+ witnora corpus summary --store postgres --database-url "$DATABASE_URL"
320
+ `);
321
+ }
322
+ }
323
+ else if (command === "monitor") {
324
+ const action = process.argv[3] ?? "help";
325
+ if (action === "build") {
326
+ const outPath = readFlag("--out") ?? ".witnora/latest/monitor.json";
327
+ const subject = readFlag("--subject") ?? "agentcert-subject";
328
+ const detailUrl = readFlag("--detail-url");
329
+ const store = await openCorpusStore(readCorpusStoreOptions(".witnora/corpus/corpus.jsonl"));
330
+ try {
331
+ const records = applyFailureReviews(await store.readAll(), await readFailureReviews(readReviewsPath()));
332
+ const snapshot = buildMonitorSnapshot(records, { subject, detailUrl });
333
+ await writeMonitorSnapshot(outPath, snapshot);
334
+ process.stdout.write(`Wrote ${resolve(outPath)}\n`);
335
+ process.stdout.write(`Monitor snapshot: ${snapshot.summary.totalRecords} records, ${(snapshot.summary.passRate * 100).toFixed(1)}% pass rate\n`);
336
+ }
337
+ finally {
338
+ await store.close();
339
+ }
340
+ }
341
+ else {
342
+ process.stdout.write(`Usage:
343
+ witnora monitor build --corpus .witnora/corpus/corpus.jsonl --out packages/agentcert-dashboard/public/data/monitor.json --subject my-agent
344
+ witnora monitor build --store sqlite --sqlite .witnora/corpus/agentcert.sqlite --out packages/agentcert-dashboard/public/data/monitor.json --subject my-agent
345
+ `);
346
+ }
347
+ }
348
+ else if (command === "run") {
349
+ const overrides = readRunOverrides();
350
+ const profileName = readFlag("--profile");
351
+ const configPath = readFlag("--config");
352
+ const hasArtifactFlags = Boolean(overrides.mcpbench || overrides.tripwire || overrides.onegent);
353
+ const profile = hasArtifactFlags && !profileName && !configPath
354
+ ? profileFromArtifactFlags(overrides)
355
+ : await loadRunProfile(profileName, configPath);
356
+ const outcome = await runAgentCertProfile(profile, {
357
+ runCommands: !readBoolFlag("--skip-commands"),
358
+ overrides,
359
+ });
360
+ process.stdout.write(renderRunSummary(outcome));
361
+ if (readBoolFlag("--push")) {
362
+ await pushHostedEvidence(outcome.bundle, Buffer.from(`${JSON.stringify(outcome.bundle, null, 2)}\n`), "agentcert-evidence.json");
363
+ }
364
+ process.exitCode = outcome.exitCode;
365
+ }
366
+ else if (command === "push") {
367
+ const evidencePath = resolve(readFlag("--evidence") ?? ".witnora/latest/agentcert-evidence.json");
368
+ const evidenceBytes = await readFile(evidencePath);
369
+ let bundle;
370
+ try {
371
+ bundle = JSON.parse(evidenceBytes.toString("utf8"));
372
+ }
373
+ catch {
374
+ throw new Error(`Evidence file is not valid JSON: ${evidencePath}`);
375
+ }
376
+ const validation = validateAgentCertSchema("evidence-bundle", bundle);
377
+ if (!validation.valid) {
378
+ throw new Error(`Evidence bundle is invalid:\n${validation.errors.map((error) => `- ${error}`).join("\n")}`);
379
+ }
380
+ await pushHostedEvidence(bundle, evidenceBytes, basename(evidencePath));
381
+ }
382
+ else if (command === "release-gate") {
383
+ const overrides = readRunOverrides();
384
+ const profileName = readFlag("--profile");
385
+ const configPath = readFlag("--config");
386
+ const evidenceInput = readFlag("--evidence");
387
+ let profile;
388
+ let bundle;
389
+ let evidenceBundlePath;
390
+ let sourceArtifacts;
391
+ if (evidenceInput) {
392
+ profile = configPath ? applyRunOverrides(await loadRunProfile(profileName, configPath), overrides) : undefined;
393
+ bundle = (await readJson(evidenceInput));
394
+ evidenceBundlePath = resolve(evidenceInput);
395
+ sourceArtifacts = bundle.artifacts;
396
+ }
397
+ else {
398
+ const hasArtifactFlags = Boolean(overrides.mcpbench || overrides.tripwire || overrides.onegent);
399
+ const loadedProfile = hasArtifactFlags && !profileName && !configPath
400
+ ? profileFromArtifactFlags(overrides)
401
+ : await loadRunProfile(profileName, configPath);
402
+ profile = applyRunOverrides(loadedProfile, overrides);
403
+ const outcome = await runAgentCertProfile(profile, {
404
+ runCommands: !readBoolFlag("--skip-commands"),
405
+ });
406
+ bundle = outcome.bundle;
407
+ evidenceBundlePath = resolve(outcome.manifest.outputs.evidenceBundle ?? join(profile.outputDir, "agentcert-evidence.json"));
408
+ sourceArtifacts = profile.artifacts;
409
+ if (!outcome.manifest.outputs.evidenceBundle) {
410
+ await mkdir(dirname(evidenceBundlePath), { recursive: true });
411
+ await writeFile(evidenceBundlePath, `${JSON.stringify(outcome.bundle, null, 2)}\n`);
412
+ }
413
+ }
414
+ const outDir = resolve(readFlag("--out") ?? profile?.run?.gate?.outDir ?? profile?.outputDir ?? dirname(evidenceBundlePath));
415
+ await mkdir(outDir, { recursive: true });
416
+ const baselinePath = readFlag("--baseline") ?? profile?.run?.gate?.baseline;
417
+ const baseline = baselinePath ? (await readJson(baselinePath)) : undefined;
418
+ const report = await buildReleaseGateReport(bundle, {
419
+ strict: readBoolFlag("--strict") || profile?.run?.gate?.strict,
420
+ requireBaseline: readBoolFlag("--require-baseline") || profile?.run?.gate?.requireBaseline,
421
+ maxScoreDrop: parseOptionalNonNegativeNumber(readFlag("--max-score-drop"), "--max-score-drop") ?? profile?.run?.gate?.maxScoreDrop,
422
+ baseline,
423
+ attestations: profile?.run?.gate?.controls,
424
+ sourceArtifacts,
425
+ evidenceBundlePath,
426
+ });
427
+ const paths = await writeReleaseGateArtifacts(outDir, report);
428
+ const saveBaselinePath = readFlag("--save-baseline");
429
+ if (saveBaselinePath) {
430
+ if (report.verdict.passed) {
431
+ await mkdir(dirname(resolve(saveBaselinePath)), { recursive: true });
432
+ await writeFile(resolve(saveBaselinePath), `${JSON.stringify(bundle, null, 2)}\n`);
433
+ process.stdout.write(`Saved passing baseline ${resolve(saveBaselinePath)}\n`);
434
+ }
435
+ else {
436
+ process.stdout.write(`Baseline not saved because the release gate failed: ${resolve(saveBaselinePath)}\n`);
437
+ }
438
+ }
439
+ const privateKey = readFlag("--sign-private-key") ?? profile?.run?.gate?.signing?.privateKey;
440
+ if (privateKey) {
441
+ await signEvidenceFile(evidenceBundlePath, privateKey);
442
+ await signEvidenceFile(paths.json, privateKey);
443
+ process.stdout.write(`Signed ${evidenceBundlePath}\nSigned ${paths.json}\n`);
444
+ }
445
+ process.stdout.write(renderReleaseGateSummary(report));
446
+ process.stdout.write(`Release gate JSON: ${paths.json}\nRelease gate HTML: ${paths.html}\nRelease gate JUnit: ${paths.junit}\n`);
447
+ process.exitCode = report.verdict.passed ? 0 : 1;
448
+ }
449
+ else if (command === "evidence") {
450
+ const action = process.argv[3] ?? "help";
451
+ if (action === "keygen") {
452
+ const privateKeyPath = readFlag("--private-key") ?? ".witnora/keys/evidence-private.pem";
453
+ const publicKeyPath = readFlag("--public-key") ?? ".witnora/keys/evidence-public.pem";
454
+ await generateEvidenceSigningKeyPair(privateKeyPath, publicKeyPath);
455
+ process.stdout.write(`Wrote private key ${resolve(privateKeyPath)}\nWrote public key ${resolve(publicKeyPath)}\n`);
456
+ }
457
+ else if (action === "sign") {
458
+ const file = readFlag("--file") ?? readPositional(4);
459
+ if (!file)
460
+ throw new Error("Missing evidence file. Usage: agentcert evidence sign <file> --private-key <path>.");
461
+ const privateKeyPath = requiredFlag("--private-key");
462
+ const signaturePath = readFlag("--out") ?? `${file}.sig.json`;
463
+ const signature = await signEvidenceFile(file, privateKeyPath, signaturePath);
464
+ process.stdout.write(`Signed ${resolve(file)}\nSignature: ${resolve(signaturePath)}\nKey id: ${signature.keyId}\n`);
465
+ }
466
+ else if (action === "verify") {
467
+ const file = readFlag("--file") ?? readPositional(4);
468
+ if (!file)
469
+ throw new Error("Missing evidence file. Usage: agentcert evidence verify <file> --signature <path> --public-key <path>.");
470
+ const result = await verifyEvidenceFile(file, requiredFlag("--signature"), requiredFlag("--public-key"));
471
+ process.stdout.write(`${result.valid ? "Valid" : "Invalid"} evidence signature: ${resolve(file)}\nKey id: ${result.keyId}\nSHA-256: ${result.artifactSha256}\n`);
472
+ process.exitCode = result.valid ? 0 : 1;
473
+ }
474
+ else {
475
+ process.stdout.write(`Usage:
476
+ witnora evidence keygen --private-key .witnora/keys/evidence-private.pem --public-key .witnora/keys/evidence-public.pem
477
+ witnora evidence sign .witnora/latest/agentcert-evidence.json --private-key .witnora/keys/evidence-private.pem
478
+ witnora evidence verify .witnora/latest/agentcert-evidence.json --signature .witnora/latest/agentcert-evidence.json.sig.json --public-key .witnora/keys/evidence-public.pem
479
+ `);
480
+ }
481
+ }
482
+ else if (command === "lab") {
483
+ const action = process.argv[3] ?? "help";
484
+ if (action === "build") {
485
+ const configPath = readFlag("--config") ?? "examples/real-agents/robustness-lab/lab.config.json";
486
+ const outPath = readFlag("--out") ?? "public-demo/real-agent-robustness/evidence/lab-snapshot.json";
487
+ const config = await readRobustnessLabConfig(configPath);
488
+ const snapshot = await buildRobustnessLabSnapshot(config);
489
+ await writeRobustnessLabSnapshot(outPath, snapshot);
490
+ process.stdout.write(`Wrote ${resolve(outPath)}\n`);
491
+ process.stdout.write(renderRobustnessLabSummary(snapshot));
492
+ }
493
+ else {
494
+ process.stdout.write(`Usage:
495
+ witnora lab build --config examples/real-agents/robustness-lab/lab.config.json --out public-demo/real-agent-robustness/evidence/lab-snapshot.json
496
+ `);
497
+ }
498
+ }
499
+ else if (command === "serve") {
500
+ await serveAgentCertMonitor({
501
+ host: readFlag("--host") ?? "127.0.0.1",
502
+ port: Number(readFlag("--port") ?? process.env.PORT ?? 8765),
503
+ subject: readFlag("--subject") ?? "agentcert-local",
504
+ detailUrl: readFlag("--detail-url") ?? "../browser-agent-robustness/",
505
+ staticDir: readFlag("--static") ?? "public-demo/agentcert-monitor",
506
+ artifactRoot: readFlag("--artifact-root") ?? "public-demo/browser-agent-robustness/evidence/tripwire-public-demo",
507
+ store: readCorpusStoreOptions("public-demo/browser-agent-robustness/evidence/agentcert-corpus.jsonl"),
508
+ reviewsPath: readReviewsPath() ?? "public-demo/browser-agent-robustness/evidence/failure-reviews.jsonl",
509
+ });
510
+ }
511
+ else if (command === "schema") {
512
+ const action = process.argv[3] ?? "help";
513
+ if (action === "validate") {
514
+ const schema = parseSchemaId(readFlag("--schema"));
515
+ const file = requiredFlag("--file");
516
+ const result = validateAgentCertSchema(schema, await readJson(file));
517
+ if (result.valid) {
518
+ process.stdout.write(`Valid ${schema}: ${resolve(file)}\n`);
519
+ }
520
+ else {
521
+ process.stdout.write(`Invalid ${schema}: ${resolve(file)}\n`);
522
+ for (const error of result.errors) {
523
+ process.stdout.write(`- ${error}\n`);
524
+ }
525
+ process.exitCode = 1;
526
+ }
527
+ }
528
+ else {
529
+ process.stdout.write(`Usage:
530
+ witnora schema validate --schema evidence-bundle --file .witnora/latest/agentcert-evidence.json
531
+ witnora schema validate --schema monitor-snapshot --file .witnora/latest/monitor.json
532
+ witnora schema validate --schema corpus-record --file examples/agentcert/corpus-record.example.json
533
+ witnora schema validate --schema classifier-eval --file examples/agentcert/classifier-eval.example.json
534
+ witnora schema validate --schema release-gate --file .witnora/latest/agentcert-release-gate.json
535
+ witnora schema validate --schema assurance-report --file assurance-report.json
536
+ witnora schema validate --schema assurance-delivery --file assurance-delivery.json
537
+ witnora schema validate --schema evidence-signature --file .witnora/latest/agentcert-evidence.json.sig.json
538
+ `);
539
+ }
540
+ }
541
+ else if (command === "validate") {
542
+ const schema = parseSchemaId(readFlag("--schema") ?? "evidence-bundle");
543
+ const file = readFlag("--file") ?? readFirstPositionalAfterCommand();
544
+ if (!file) {
545
+ throw new Error("Missing evidence file. Usage: agentcert validate <file> [--schema evidence-bundle].");
546
+ }
547
+ const input = await readJson(file);
548
+ const result = validateAgentCertSchema(schema, input);
549
+ if (result.valid) {
550
+ process.stdout.write(`Valid ${schema}: ${resolve(file)}\n`);
551
+ }
552
+ else {
553
+ process.stdout.write(`Invalid ${schema}: ${resolve(file)}\n`);
554
+ for (const error of result.errors) {
555
+ process.stdout.write(`- ${error}\n`);
556
+ }
557
+ process.exitCode = 1;
558
+ }
559
+ if (result.valid && schema === "evidence-bundle" && readBoolFlag("--check-artifacts")) {
560
+ const artifactResult = await validateEvidenceArtifacts(input, resolve(readFlag("--artifact-root") ?? process.cwd()));
561
+ process.stdout.write(`Artifact paths checked: ${artifactResult.checked}\n`);
562
+ if (artifactResult.missing.length > 0) {
563
+ process.stdout.write("Missing artifact paths:\n");
564
+ for (const missing of artifactResult.missing) {
565
+ process.stdout.write(`- ${missing}\n`);
566
+ }
567
+ process.exitCode = 1;
568
+ }
569
+ }
570
+ }
571
+ else if (command === "conformance") {
572
+ const file = readFlag("--file") ?? readFirstPositionalAfterCommand();
573
+ if (!file)
574
+ throw new Error("Missing evidence file. Usage: agentcert conformance <file> --artifact-root <directory>.");
575
+ const artifactRoot = resolve(readFlag("--artifact-root") ?? dirname(resolve(file)));
576
+ const report = await runEvidenceConformance(await readJson(file), {
577
+ evidenceFile: file,
578
+ artifactRoot,
579
+ implementation: readFlag("--implementation"),
580
+ });
581
+ const output = `${JSON.stringify(report, null, 2)}\n`;
582
+ const outPath = readFlag("--out");
583
+ if (outPath) {
584
+ await mkdir(dirname(resolve(outPath)), { recursive: true });
585
+ await writeFile(outPath, output);
586
+ process.stdout.write(`Wrote conformance report: ${resolve(outPath)}\n`);
587
+ }
588
+ process.stdout.write(`${report.valid ? "Conformant" : "Non-conformant"} evidence implementation: ${report.implementation}\n`);
589
+ for (const item of report.checks) {
590
+ process.stdout.write(`- ${item.status === "passed" ? "PASS" : "FAIL"} ${item.id}: ${item.message}\n`);
591
+ for (const error of item.errors)
592
+ process.stdout.write(` - ${error}\n`);
593
+ }
594
+ if (!report.valid)
595
+ process.exitCode = 1;
596
+ }
597
+ else {
598
+ process.stdout.write(`Usage:
599
+ witnora try --template workflow [--push]
600
+ witnora init --subject my-browser-agent
601
+ witnora connect --server https://witnora.com --project <project-id>
602
+ witnora sandbox init
603
+ witnora sandbox certify --adapter ./witnora.sandbox.mjs
604
+ witnora sandbox push --adapter ./witnora.sandbox.mjs
605
+ witnora browser-adapter init
606
+ witnora browser-adapter certify --adapter ./witnora.browser-adapter.mjs
607
+ witnora init --out witnora.config.json --tripwire-config tripwire.yml --force
608
+ witnora init --subject my-browser-agent --github-action
609
+ witnora report --mcpbench .mcpbench/latest/results.json --tripwire .tripwire/latest/tripwire-result.json --onegent .onegent/procurement/audit-packet.json --out .witnora/latest --subject my-agent
610
+ witnora corpus ingest --tripwire .tripwire/latest/tripwire-result.json --out .witnora/corpus/corpus.jsonl --subject my-agent
611
+ witnora corpus review --corpus .witnora/corpus/corpus.jsonl --reviews .witnora/corpus/failure-reviews.jsonl --pattern-key <failure-key> --type wrong_click --status corrected
612
+ witnora corpus summary --corpus .witnora/corpus/corpus.jsonl
613
+ witnora corpus classifier-eval --corpus .witnora/corpus/corpus.jsonl --out .witnora/latest/failure-classifier-evaluation.json
614
+ witnora monitor build --corpus .witnora/corpus/corpus.jsonl --out .witnora/latest/monitor.json --subject my-agent
615
+ witnora monitor build --store sqlite --sqlite .witnora/corpus/agentcert.sqlite --out .witnora/latest/monitor.json --subject my-agent
616
+ witnora run --profile public-demo
617
+ witnora push --evidence .witnora/latest/agentcert-evidence.json --server https://witnora.example.com --project <project-id> [--artifact-root .] [--no-artifacts]
618
+ witnora run --tripwire .tripwire/latest/tripwire-result.json --push
619
+ witnora run --mcpbench .mcpbench/latest/results.json --tripwire .tripwire/latest/tripwire-result.json --onegent .onegent/procurement/audit-packet.json --out .witnora/latest --corpus .witnora/corpus/corpus.jsonl --monitor-out .witnora/latest/monitor.json --reviewed-dataset-out .witnora/latest/reviewed-failure-dataset.jsonl
620
+ witnora release-gate --config witnora.config.json --strict
621
+ witnora release-gate --evidence .witnora/latest/agentcert-evidence.json --baseline .witnora/baselines/main.json
622
+ witnora release-gate --evidence .witnora/latest/agentcert-evidence.json --save-baseline .witnora/baselines/main.json
623
+ witnora release-gate --tripwire .tripwire/latest/tripwire-result.json --baseline .witnora/baselines/main.json
624
+ witnora evidence keygen --private-key .witnora/keys/evidence-private.pem --public-key .witnora/keys/evidence-public.pem
625
+ witnora evidence sign .witnora/latest/agentcert-evidence.json --private-key .witnora/keys/evidence-private.pem
626
+ witnora evidence verify .witnora/latest/agentcert-evidence.json --signature .witnora/latest/agentcert-evidence.json.sig.json --public-key .witnora/keys/evidence-public.pem
627
+ witnora lab build --config examples/real-agents/robustness-lab/lab.config.json --out public-demo/real-agent-robustness/evidence/lab-snapshot.json
628
+ witnora serve --corpus .witnora/corpus/corpus.jsonl --static public-demo/agentcert-monitor --artifact-root public-demo/browser-agent-robustness/evidence/tripwire-public-demo
629
+ witnora validate .witnora/latest/agentcert-evidence.json
630
+ witnora validate .witnora/latest/agentcert-evidence.json --check-artifacts
631
+ witnora conformance .witnora/latest/agentcert-evidence.json --artifact-root . --implementation my-agent
632
+ witnora schema validate --schema evidence-bundle --file .witnora/latest/agentcert-evidence.json
633
+ `);
634
+ }
635
+ async function loadConfig(path) {
636
+ if (!path) {
637
+ return undefined;
638
+ }
639
+ return (await readJson(path));
640
+ }
641
+ async function pushHostedEvidence(bundle, bytes, fileName) {
642
+ const connection = await resolveConnection({
643
+ name: readFlag("--connection"),
644
+ server: readFlag("--server"),
645
+ projectId: readFlag("--project"),
646
+ apiKey: readFlag("--api-key"),
647
+ });
648
+ const companions = readBoolFlag("--no-artifacts")
649
+ ? undefined
650
+ : await collectCompanionArtifacts(bundle, resolve(readFlag("--artifact-root") ?? process.cwd()));
651
+ const assurance = await readContinuousAssuranceBinding();
652
+ const healthOut = readFlag("--continuous-health-out");
653
+ const requireCurrent = readBoolFlag("--require-current");
654
+ if (requireCurrent && !assurance)
655
+ throw new Error("--require-current needs --assurance-case and --assurance-scope.");
656
+ const result = await pushEvidenceToControlPlane({
657
+ baseUrl: connection.server,
658
+ projectId: connection.projectId,
659
+ apiKey: connection.apiKey,
660
+ bundle,
661
+ evidenceBytes: bytes,
662
+ fileName,
663
+ externalId: readFlag("--external-id"),
664
+ companionArtifacts: companions?.artifacts,
665
+ skippedCompanionArtifacts: companions?.skipped,
666
+ assurance,
667
+ verifyContinuousAssurance: Boolean(assurance && (healthOut || requireCurrent)),
668
+ });
669
+ process.stdout.write(`Hosted run: ${result.runId}\nHosted evidence: ${result.evidenceId}\n`);
670
+ if (companions) {
671
+ process.stdout.write(`Hosted companion artifacts: ${result.artifactsUploaded} uploaded, ${result.artifactsSkipped} skipped, ${result.artifactBytesUploaded} bytes.\n`);
672
+ for (const skipped of companions.skipped.slice(0, MAX_REPORTED_COMPANION_ARTIFACT_SKIPS)) {
673
+ process.stderr.write(`Skipped companion artifact ${skipped.sourcePath}: ${skipped.reason}${skipped.detail ? ` (${skipped.detail})` : ""}.\n`);
674
+ }
675
+ if (companions.skipped.length > MAX_REPORTED_COMPANION_ARTIFACT_SKIPS) {
676
+ process.stderr.write(`${companions.skipped.length - MAX_REPORTED_COMPANION_ARTIFACT_SKIPS} additional skipped artifacts omitted.\n`);
677
+ }
678
+ }
679
+ if (result.continuousAssuranceHealth) {
680
+ process.stdout.write(`Hosted assurance: ${result.continuousAssuranceHealth.status} (${result.continuousAssuranceHealth.healthy ? "healthy" : "attention required"})\n`);
681
+ if (healthOut) {
682
+ const outputPath = resolve(healthOut);
683
+ await mkdir(dirname(outputPath), { recursive: true });
684
+ await writeFile(outputPath, `${JSON.stringify(result.continuousAssuranceHealth, null, 2)}\n`, "utf8");
685
+ process.stdout.write(`Continuous assurance health: ${outputPath}\n`);
686
+ }
687
+ if (requireCurrent)
688
+ requireContinuousAssuranceCurrent(result.continuousAssuranceHealth);
689
+ }
690
+ }
691
+ async function readContinuousAssuranceBinding() {
692
+ const caseId = readFlag("--assurance-case");
693
+ const scopePath = readFlag("--assurance-scope");
694
+ if (!caseId && !scopePath)
695
+ return undefined;
696
+ if (!caseId || !scopePath)
697
+ throw new Error("--assurance-case and --assurance-scope must be provided together.");
698
+ const scope = await readJson(scopePath);
699
+ const validation = validateAgentCertSchema("assurance-scope", scope);
700
+ if (!validation.valid)
701
+ throw new Error(`Assurance scope is invalid:\n${validation.errors.map((error) => `- ${error}`).join("\n")}`);
702
+ return { caseId, trigger: assuranceTriggerFromEnvironment(readFlag("--assurance-trigger") ?? "auto"), scope: scope };
703
+ }
704
+ function assuranceTriggerFromEnvironment(value) {
705
+ if (value === "pull_request" || value === "release" || value === "nightly")
706
+ return value;
707
+ if (value !== "auto")
708
+ throw new Error("--assurance-trigger must be auto, pull_request, release, or nightly.");
709
+ const event = process.env.GITHUB_EVENT_NAME;
710
+ if (event === "pull_request" || event === "pull_request_target")
711
+ return "pull_request";
712
+ if (event === "schedule")
713
+ return "nightly";
714
+ return "release";
715
+ }
716
+ async function promptValue(label) {
717
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
718
+ throw new Error(`Missing ${label.trim().replace(/:$/, "").toLowerCase()}. Pass it as a flag or environment variable.`);
719
+ }
720
+ const prompt = createInterface({ input: process.stdin, output: process.stdout });
721
+ try {
722
+ return (await prompt.question(label)).trim();
723
+ }
724
+ finally {
725
+ prompt.close();
726
+ }
727
+ }
728
+ async function promptSecret(label) {
729
+ if (!process.stdin.isTTY || !process.stdout.isTTY || !process.stdin.setRawMode) {
730
+ throw new Error("Missing project API key. Set WITNORA_API_KEY (or legacy AGENTCERT_API_KEY) or pass --api-key in a trusted environment.");
731
+ }
732
+ process.stdout.write(label);
733
+ process.stdin.setRawMode(true);
734
+ process.stdin.resume();
735
+ process.stdin.setEncoding("utf8");
736
+ return new Promise((resolveSecret, rejectSecret) => {
737
+ let value = "";
738
+ const cleanup = () => {
739
+ process.stdin.off("data", onData);
740
+ process.stdin.setRawMode?.(false);
741
+ process.stdin.pause();
742
+ process.stdout.write("\n");
743
+ };
744
+ const onData = (chunk) => {
745
+ for (const character of String(chunk)) {
746
+ if (character === "\u0003") {
747
+ cleanup();
748
+ rejectSecret(new Error("Witnora connection cancelled."));
749
+ return;
750
+ }
751
+ if (character === "\r" || character === "\n") {
752
+ cleanup();
753
+ resolveSecret(value.trim());
754
+ return;
755
+ }
756
+ if (character === "\u007f" || character === "\b") {
757
+ if (value) {
758
+ value = value.slice(0, -1);
759
+ process.stdout.write("\b \b");
760
+ }
761
+ continue;
762
+ }
763
+ value += character;
764
+ process.stdout.write("*");
765
+ }
766
+ };
767
+ process.stdin.on("data", onData);
768
+ });
769
+ }
770
+ async function readJson(path) {
771
+ const raw = await readFile(resolve(path), "utf8");
772
+ return JSON.parse(raw);
773
+ }
774
+ async function loadArtifactResults(config) {
775
+ const loaded = [];
776
+ const mcpbenchPath = readFlag("--mcpbench") ?? config?.artifacts.mcpbench;
777
+ const tripwirePath = readFlag("--tripwire") ?? config?.artifacts.tripwire;
778
+ const onegentPath = readFlag("--onegent") ?? config?.artifacts.onegent;
779
+ if (mcpbenchPath) {
780
+ const raw = await readJson(mcpbenchPath);
781
+ loaded.push({ result: normalizeMcpBenchResult(raw, mcpbenchPath), path: mcpbenchPath, raw });
782
+ }
783
+ if (tripwirePath) {
784
+ const raw = await readJson(tripwirePath);
785
+ loaded.push({ result: normalizeTripwireResult(raw, tripwirePath), path: tripwirePath, raw });
786
+ }
787
+ if (onegentPath) {
788
+ const raw = await readJson(onegentPath);
789
+ loaded.push({ result: normalizeOnegentAuditPacket(raw, onegentPath), path: onegentPath, raw });
790
+ }
791
+ return loaded;
792
+ }
793
+ function readFlag(name) {
794
+ const index = process.argv.indexOf(name);
795
+ return index >= 0 ? process.argv[index + 1] : undefined;
796
+ }
797
+ function brandedEnvironment(name) {
798
+ return process.env[`WITNORA_${name}`] ?? process.env[`AGENTCERT_${name}`];
799
+ }
800
+ function readFirstPositionalAfterCommand() {
801
+ for (let index = 3; index < process.argv.length; index += 1) {
802
+ const value = process.argv[index];
803
+ if (!value)
804
+ continue;
805
+ if (value.startsWith("--")) {
806
+ index += 1;
807
+ continue;
808
+ }
809
+ return value;
810
+ }
811
+ return undefined;
812
+ }
813
+ function readPositional(startIndex) {
814
+ for (let index = startIndex; index < process.argv.length; index += 1) {
815
+ const value = process.argv[index];
816
+ if (!value)
817
+ continue;
818
+ if (value.startsWith("--")) {
819
+ index += 1;
820
+ continue;
821
+ }
822
+ return value;
823
+ }
824
+ return undefined;
825
+ }
826
+ function readBoolFlag(name) {
827
+ return process.argv.includes(name);
828
+ }
829
+ function readRepeatedFlag(name) {
830
+ const values = [];
831
+ for (let index = 0; index < process.argv.length; index += 1) {
832
+ if (process.argv[index] === name && process.argv[index + 1]) {
833
+ values.push(process.argv[index + 1]);
834
+ }
835
+ }
836
+ return values;
837
+ }
838
+ function readReviewEvidenceContextFromFlags() {
839
+ const firstDivergenceSnippet = readFlag("--first-divergence") ?? readFlag("--first-divergence-snippet");
840
+ const screenshotPath = readFlag("--screenshot") ?? readFlag("--screenshot-path");
841
+ const screenshotUrl = readFlag("--screenshot-url");
842
+ const tracePath = readFlag("--trace") ?? readFlag("--trace-path");
843
+ const stepIndex = parseOptionalNonNegativeInteger(readFlag("--step-index"), "--step-index");
844
+ if (!firstDivergenceSnippet && !screenshotPath && !screenshotUrl && !tracePath && stepIndex === undefined) {
845
+ return undefined;
846
+ }
847
+ return {
848
+ firstDivergenceSnippet,
849
+ screenshotPath,
850
+ screenshotUrl,
851
+ tracePath,
852
+ stepIndex,
853
+ };
854
+ }
855
+ function readReviewTaxonomyRationaleFromFlags() {
856
+ const primaryReason = readFlag("--why") ?? readFlag("--taxonomy-reason");
857
+ const supportingSignals = readRepeatedFlag("--signal");
858
+ const contradictingSignals = readRepeatedFlag("--contradiction");
859
+ const classifierLimitation = readFlag("--classifier-limitation");
860
+ if (!primaryReason) {
861
+ return undefined;
862
+ }
863
+ return {
864
+ primaryReason,
865
+ supportingSignals: supportingSignals.length > 0 ? supportingSignals : undefined,
866
+ contradictingSignals: contradictingSignals.length > 0 ? contradictingSignals : undefined,
867
+ classifierLimitation,
868
+ };
869
+ }
870
+ function readRunOverrides() {
871
+ return {
872
+ subject: readFlag("--subject"),
873
+ mcpbench: readFlag("--mcpbench"),
874
+ tripwire: readFlag("--tripwire"),
875
+ onegent: readFlag("--onegent"),
876
+ outDir: readFlag("--out"),
877
+ corpusPath: readFlag("--corpus"),
878
+ monitorOut: readFlag("--monitor-out"),
879
+ reviewedDatasetOut: readFlag("--reviewed-dataset-out"),
880
+ reviewsPath: readReviewsPath(),
881
+ replaceCorpus: readBoolFlag("--replace") ? true : undefined,
882
+ failOnVerdict: readBoolFlag("--fail-on-verdict") ? true : undefined,
883
+ };
884
+ }
885
+ function requiredFlag(name) {
886
+ const value = readFlag(name);
887
+ if (!value) {
888
+ throw new Error(`Missing required flag ${name}.`);
889
+ }
890
+ return value;
891
+ }
892
+ function parseOptionalNonNegativeInteger(input, flagName) {
893
+ if (input === undefined)
894
+ return undefined;
895
+ const value = Number(input);
896
+ if (!Number.isInteger(value) || value < 0) {
897
+ throw new Error(`${flagName} must be a non-negative integer.`);
898
+ }
899
+ return value;
900
+ }
901
+ function parseOptionalNonNegativeNumber(input, flagName) {
902
+ if (input === undefined)
903
+ return undefined;
904
+ const value = Number(input);
905
+ if (!Number.isFinite(value) || value < 0) {
906
+ throw new Error(`${flagName} must be a non-negative number.`);
907
+ }
908
+ return value;
909
+ }
910
+ function readReviewsPath() {
911
+ return readFlag("--reviews") ?? brandedEnvironment("FAILURE_REVIEWS");
912
+ }
913
+ function readCorpusStoreOptions(defaultJsonlPath, outPath) {
914
+ return {
915
+ kind: parseCorpusStoreKind(readFlag("--store") ?? brandedEnvironment("CORPUS_STORE")),
916
+ jsonlPath: readFlag("--corpus") ?? outPath ?? defaultJsonlPath,
917
+ sqlitePath: readFlag("--sqlite") ?? brandedEnvironment("SQLITE_PATH") ?? ".witnora/corpus/witnora.sqlite",
918
+ databaseUrl: readFlag("--database-url") ?? brandedEnvironment("DATABASE_URL") ?? process.env.DATABASE_URL,
919
+ tableName: readFlag("--table") ?? brandedEnvironment("CORPUS_TABLE"),
920
+ };
921
+ }
922
+ async function writeStarterFile(path, content, force) {
923
+ await mkdir(dirname(path), { recursive: true });
924
+ try {
925
+ await writeFile(path, content, { flag: force ? "w" : "wx" });
926
+ }
927
+ catch (error) {
928
+ if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
929
+ throw new Error(`${path} already exists. Re-run with --force to overwrite it.`);
930
+ }
931
+ throw error;
932
+ }
933
+ }
934
+ function reportFatalError(error) {
935
+ process.stderr.write(formatCliError(error));
936
+ process.exit(1);
937
+ }
938
+ function formatCliError(error) {
939
+ const message = error instanceof Error ? error.message : String(error);
940
+ const code = error && typeof error === "object" && "code" in error ? String(error.code) : undefined;
941
+ const detail = [`Witnora error: ${message}`];
942
+ const hint = errorHint(message, code);
943
+ if (hint) {
944
+ detail.push("", hint);
945
+ }
946
+ return `${detail.join("\n")}\n`;
947
+ }
948
+ function errorHint(message, code) {
949
+ if (code === "ENOENT") {
950
+ return "Hint: check the artifact path. Run `witnora init` for starter config, then run Tripwire before `witnora run`.";
951
+ }
952
+ if (message.includes("witnora run requires") || message.includes("agentcert run requires")) {
953
+ return "Hint: run `witnora init`, pass `--tripwire .tripwire/latest/tripwire-result.json`, or use `--config witnora.config.json`.";
954
+ }
955
+ if (message.includes("No AgentCert artifacts were loaded") || message.includes("No input artifacts were provided")) {
956
+ return "Hint: provide at least one artifact with `--tripwire`, `--mcpbench`, `--onegent`, or configure `witnora.config.json`.";
957
+ }
958
+ if (message.includes("Unexpected token") || message.includes("JSON")) {
959
+ return "Hint: Witnora expected JSON. Check that the artifact file exists and was not replaced by logs or HTML.";
960
+ }
961
+ if (message.includes("already exists")) {
962
+ return "Hint: re-run with `--force` to overwrite starter files, or choose a different `--out` path.";
963
+ }
964
+ if (message.includes("Hosted push requires")) {
965
+ return "Hint: create a project API key in Witnora Setup, then set WITNORA_BASE_URL, WITNORA_PROJECT_ID, and WITNORA_API_KEY. Legacy AGENTCERT_* names remain supported.";
966
+ }
967
+ if (message.includes("Authentication required") || message.includes("API key")) {
968
+ return "Hint: check WITNORA_API_KEY and confirm the key belongs to WITNORA_PROJECT_ID. Legacy AGENTCERT_* names remain supported.";
969
+ }
970
+ return undefined;
971
+ }