auditor-lambda 0.2.5 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -7
- package/audit-code-wrapper-lib.mjs +1612 -331
- package/dist/cli.js +397 -38
- package/dist/coverage.d.ts +2 -2
- package/dist/coverage.js +5 -5
- package/dist/extractors/disposition.js +10 -1
- package/dist/extractors/flows.js +7 -1
- package/dist/extractors/pathPatterns.d.ts +3 -0
- package/dist/extractors/pathPatterns.js +15 -0
- package/dist/extractors/risk.js +7 -1
- package/dist/io/artifacts.d.ts +6 -6
- package/dist/io/artifacts.js +14 -17
- package/dist/io/json.d.ts +2 -0
- package/dist/io/json.js +15 -0
- package/dist/io/runArtifacts.d.ts +3 -1
- package/dist/io/runArtifacts.js +20 -5
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +579 -0
- package/dist/orchestrator/advance.js +9 -2
- package/dist/orchestrator/dependencyMap.js +9 -13
- package/dist/orchestrator/executors.js +7 -2
- package/dist/orchestrator/flowRequeue.d.ts +2 -2
- package/dist/orchestrator/flowRequeue.js +16 -3
- package/dist/orchestrator/internalExecutors.d.ts +2 -1
- package/dist/orchestrator/internalExecutors.js +129 -48
- package/dist/orchestrator/requeue.js +10 -4
- package/dist/orchestrator/requeueCommand.js +15 -2
- package/dist/orchestrator/resultIngestion.d.ts +2 -1
- package/dist/orchestrator/resultIngestion.js +26 -6
- package/dist/orchestrator/runtimeValidation.d.ts +7 -2
- package/dist/orchestrator/runtimeValidation.js +61 -49
- package/dist/orchestrator/runtimeValidationUpdate.js +2 -4
- package/dist/orchestrator/state.js +28 -14
- package/dist/orchestrator/taskBuilder.js +4 -2
- package/dist/orchestrator/trivialAudit.d.ts +4 -0
- package/dist/orchestrator/trivialAudit.js +49 -0
- package/dist/prompts/renderWorkerPrompt.js +6 -2
- package/dist/providers/spawnLoggedCommand.js +17 -0
- package/dist/reporting/mergeFindings.js +3 -11
- package/dist/reporting/rootCause.js +92 -9
- package/dist/reporting/synthesis.d.ts +25 -22
- package/dist/reporting/synthesis.js +92 -59
- package/dist/reporting/workBlocks.d.ts +12 -3
- package/dist/reporting/workBlocks.js +124 -70
- package/dist/supervisor/sessionConfig.js +4 -2
- package/dist/types/flows.d.ts +2 -0
- package/dist/types/runtimeValidation.d.ts +2 -1
- package/dist/types.d.ts +8 -6
- package/dist/validation/auditResults.d.ts +5 -2
- package/dist/validation/auditResults.js +335 -43
- package/docs/agent-integrations.md +38 -29
- package/docs/artifacts.md +18 -51
- package/docs/bootstrap-install.md +60 -30
- package/docs/contract.md +25 -117
- package/docs/field-trial-bug-report.md +237 -0
- package/docs/next-steps.md +59 -44
- package/docs/packaging.md +13 -3
- package/docs/production-launch-bar.md +2 -2
- package/docs/production-readiness.md +9 -5
- package/docs/releasing.md +81 -0
- package/docs/session-config.md +20 -1
- package/docs/usage.md +22 -0
- package/package.json +4 -1
- package/schemas/audit_result.schema.json +4 -5
- package/schemas/audit_task.schema.json +10 -0
- package/schemas/runtime_validation_report.schema.json +1 -1
- package/skills/audit-code/SKILL.md +11 -2
- package/skills/audit-code/audit-code.prompt.md +11 -10
- package/schemas/merged_findings.schema.json +0 -19
- package/schemas/root_cause_clusters.schema.json +0 -28
- package/schemas/synthesis_report.schema.json +0 -61
package/dist/cli.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { access, mkdir } from "node:fs/promises";
|
|
1
|
+
import { access, mkdir, readdir, rename } from "node:fs/promises";
|
|
2
2
|
import { createReadStream } from "node:fs";
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
3
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { buildRepoManifest } from "./extractors/fileInventory.js";
|
|
5
5
|
import { buildFileDisposition } from "./extractors/disposition.js";
|
|
6
6
|
import { buildCriticalFlowManifest } from "./extractors/flows.js";
|
|
7
7
|
import { buildSurfaceManifest } from "./extractors/surfaces.js";
|
|
8
8
|
import { buildUnitManifest } from "./orchestrator/unitBuilder.js";
|
|
9
9
|
import { buildFlowCoverage } from "./orchestrator/flowCoverage.js";
|
|
10
|
-
import { buildRuntimeValidationTasks,
|
|
10
|
+
import { buildRuntimeValidationTasks, } from "./orchestrator/runtimeValidation.js";
|
|
11
11
|
import { initializeCoverageFromPlan } from "./orchestrator/planning.js";
|
|
12
|
-
import { loadArtifactBundle, writeCoreArtifacts,
|
|
12
|
+
import { loadArtifactBundle, writeCoreArtifacts, promoteFinalAuditReport, } from "./io/artifacts.js";
|
|
13
13
|
import { readJsonFile, writeJsonFile } from "./io/json.js";
|
|
14
14
|
import { validateArtifactBundle } from "./validation/artifacts.js";
|
|
15
15
|
import { validateAuditResults, formatAuditResultIssues, } from "./validation/auditResults.js";
|
|
16
16
|
import { validateConfiguredProviderEnvironment, validateSessionConfig, } from "./validation/sessionConfig.js";
|
|
17
|
-
import {
|
|
17
|
+
import { buildAuditReportModel, renderAuditReportMarkdown, } from "./reporting/synthesis.js";
|
|
18
18
|
import { deriveAuditState } from "./orchestrator/state.js";
|
|
19
19
|
import { advanceAudit } from "./orchestrator/advance.js";
|
|
20
20
|
import { decideNextStep } from "./orchestrator/nextStep.js";
|
|
@@ -22,9 +22,10 @@ import { createFreshSessionProvider, resolveFreshSessionProviderName, } from "./
|
|
|
22
22
|
import { appendRunLedgerEntry } from "./supervisor/runLedger.js";
|
|
23
23
|
import { buildAuditCodeHandoff, writeAuditCodeHandoffArtifacts, } from "./supervisor/operatorHandoff.js";
|
|
24
24
|
import { getSessionConfigPath, loadSessionConfig, readSessionConfigFile, } from "./supervisor/sessionConfig.js";
|
|
25
|
-
import { buildRunId, ensureSupervisorDirs, getRunPaths, writeWorkerTaskFiles, } from "./io/runArtifacts.js";
|
|
25
|
+
import { clearDispatchFiles, buildRunId, ensureSupervisorDirs, getRunPaths, writeWorkerTaskFiles, } from "./io/runArtifacts.js";
|
|
26
26
|
import { renderWorkerPrompt } from "./prompts/renderWorkerPrompt.js";
|
|
27
27
|
import { LOCAL_SUBPROCESS_PROVIDER_NAME } from "./providers/constants.js";
|
|
28
|
+
import { runAuditCodeMcpServer } from "./mcp/server.js";
|
|
28
29
|
const ADVANCE_AUDIT_CONTRACT_VERSION = "audit-code/v1alpha1";
|
|
29
30
|
const WORKER_RESULT_CONTRACT_VERSION = "audit-code-worker-result/v1alpha1";
|
|
30
31
|
const DEFAULT_MAX_RUNS = 1000;
|
|
@@ -50,6 +51,10 @@ function getArtifactsDir(argv) {
|
|
|
50
51
|
function getRootDir(argv) {
|
|
51
52
|
return resolve(getFlag(argv, "--root", "."));
|
|
52
53
|
}
|
|
54
|
+
function getBatchResultsDir(argv) {
|
|
55
|
+
const value = getFlag(argv, "--batch-results");
|
|
56
|
+
return value ? resolve(value) : undefined;
|
|
57
|
+
}
|
|
53
58
|
function getMaxRuns(argv) {
|
|
54
59
|
const raw = Number(getFlag(argv, "--max-runs", String(DEFAULT_MAX_RUNS)));
|
|
55
60
|
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : DEFAULT_MAX_RUNS;
|
|
@@ -78,6 +83,15 @@ function getParallelWorkers(argv, sessionConfig) {
|
|
|
78
83
|
}
|
|
79
84
|
return 1;
|
|
80
85
|
}
|
|
86
|
+
function getTimeoutMs(argv, sessionConfig) {
|
|
87
|
+
const fromArg = getFlag(argv, "--timeout");
|
|
88
|
+
if (fromArg !== undefined) {
|
|
89
|
+
const parsed = Number(fromArg);
|
|
90
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
91
|
+
return Math.floor(parsed);
|
|
92
|
+
}
|
|
93
|
+
return sessionConfig.timeout_ms ?? DEFAULT_TIMEOUT_MS;
|
|
94
|
+
}
|
|
81
95
|
function chunkArray(arr, size) {
|
|
82
96
|
const chunks = [];
|
|
83
97
|
for (let i = 0; i < arr.length; i += size) {
|
|
@@ -203,6 +217,29 @@ async function buildLineIndex(root, repoManifest) {
|
|
|
203
217
|
}
|
|
204
218
|
return Object.fromEntries(entries);
|
|
205
219
|
}
|
|
220
|
+
async function buildLineIndexForPaths(root, paths) {
|
|
221
|
+
const uniquePaths = [...new Set(paths)].sort();
|
|
222
|
+
const entries = await Promise.all(uniquePaths.map(async (path) => {
|
|
223
|
+
try {
|
|
224
|
+
return [path, await countLines(resolve(root, path))];
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return [path, 0];
|
|
228
|
+
}
|
|
229
|
+
}));
|
|
230
|
+
return Object.fromEntries(entries);
|
|
231
|
+
}
|
|
232
|
+
async function listBatchResultFiles(batchDir) {
|
|
233
|
+
const entries = await readdir(batchDir, { withFileTypes: true });
|
|
234
|
+
const files = entries
|
|
235
|
+
.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".json"))
|
|
236
|
+
.map((entry) => join(batchDir, entry.name))
|
|
237
|
+
.sort((a, b) => a.localeCompare(b));
|
|
238
|
+
if (files.length === 0) {
|
|
239
|
+
throw new Error(`No JSON audit result files found in ${batchDir}.`);
|
|
240
|
+
}
|
|
241
|
+
return files;
|
|
242
|
+
}
|
|
206
243
|
const PROJECT_SIGNALS = [
|
|
207
244
|
"package.json",
|
|
208
245
|
"go.mod",
|
|
@@ -229,33 +266,122 @@ async function detectProjectRoot(root) {
|
|
|
229
266
|
}
|
|
230
267
|
function buildPendingAuditTasks(bundle) {
|
|
231
268
|
const completedTaskIds = new Set((bundle.audit_results ?? []).map((result) => result.task_id));
|
|
232
|
-
return (bundle.audit_tasks ?? []).filter((task) => !completedTaskIds.has(task.task_id));
|
|
269
|
+
return (bundle.audit_tasks ?? []).filter((task) => task.status !== "complete" && !completedTaskIds.has(task.task_id));
|
|
270
|
+
}
|
|
271
|
+
function formatAuditResultValidationError(issues) {
|
|
272
|
+
return (`audit-results validation failed with ${issues.length} error(s):\n` +
|
|
273
|
+
formatAuditResultIssues(issues));
|
|
274
|
+
}
|
|
275
|
+
function buildWorkerFailureBlocker(workerResult) {
|
|
276
|
+
const details = workerResult.errors.filter((error) => error.trim().length > 0);
|
|
277
|
+
return details.length > 0
|
|
278
|
+
? `${workerResult.summary} ${details.join(" ")}`
|
|
279
|
+
: workerResult.summary;
|
|
280
|
+
}
|
|
281
|
+
function looksLikeCliFlag(value) {
|
|
282
|
+
return typeof value === "string" && value.startsWith("--");
|
|
283
|
+
}
|
|
284
|
+
async function maybeArchiveLegacyPendingResults(auditResultsPath) {
|
|
285
|
+
if (!auditResultsPath || basename(auditResultsPath) !== "worker_results_pending.json") {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
const archivedPath = join(dirname(auditResultsPath), `worker_results_submitted_${new Date().toISOString().replace(/[:.]/g, "-")}.json`);
|
|
289
|
+
try {
|
|
290
|
+
await rename(auditResultsPath, archivedPath);
|
|
291
|
+
return archivedPath;
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
process.stderr.write(`[audit-results cleanup] failed to archive ${auditResultsPath}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
233
297
|
}
|
|
234
298
|
async function runAuditStep(options) {
|
|
235
299
|
const bundle = await loadArtifactBundle(options.artifactsDir);
|
|
300
|
+
const lineIndex = bundle.repo_manifest
|
|
301
|
+
? await buildLineIndex(options.root, bundle.repo_manifest)
|
|
302
|
+
: undefined;
|
|
303
|
+
if (looksLikeCliFlag(options.auditResultsPath)) {
|
|
304
|
+
throw new Error(`Invalid audit results path '${options.auditResultsPath}'. This looks like a CLI flag rather than a file path.`);
|
|
305
|
+
}
|
|
236
306
|
const auditResults = options.auditResultsPath
|
|
237
307
|
? await readJsonFile(options.auditResultsPath)
|
|
238
308
|
: undefined;
|
|
309
|
+
if (auditResults !== undefined) {
|
|
310
|
+
const issues = validateAuditResults(auditResults, bundle.audit_tasks ?? [], {
|
|
311
|
+
lineIndex,
|
|
312
|
+
});
|
|
313
|
+
const errors = issues.filter((issue) => issue.severity === "error");
|
|
314
|
+
const warnings = issues.filter((issue) => issue.severity === "warning");
|
|
315
|
+
if (warnings.length > 0) {
|
|
316
|
+
process.stderr.write(`audit-results validation: ${warnings.length} warning(s):\n` +
|
|
317
|
+
formatAuditResultIssues(warnings) +
|
|
318
|
+
"\n");
|
|
319
|
+
}
|
|
320
|
+
if (errors.length > 0) {
|
|
321
|
+
throw new Error(formatAuditResultValidationError(errors));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
239
324
|
const runtimeValidationUpdates = options.runtimeUpdatesPath
|
|
240
325
|
? await readJsonFile(options.runtimeUpdatesPath)
|
|
241
326
|
: undefined;
|
|
242
327
|
const externalAnalyzerResults = options.externalAnalyzerPath
|
|
243
328
|
? await readJsonFile(options.externalAnalyzerPath)
|
|
244
329
|
: undefined;
|
|
245
|
-
const lineIndex = bundle.repo_manifest
|
|
246
|
-
? await buildLineIndex(options.root, bundle.repo_manifest)
|
|
247
|
-
: undefined;
|
|
248
330
|
const result = await advanceAudit(bundle, {
|
|
249
331
|
root: options.root,
|
|
250
332
|
lineIndex,
|
|
251
|
-
auditResults,
|
|
333
|
+
auditResults: auditResults,
|
|
252
334
|
runtimeValidationUpdates,
|
|
253
335
|
externalAnalyzerResults,
|
|
254
336
|
preferredExecutor: options.preferredExecutor,
|
|
255
337
|
});
|
|
256
338
|
await writeCoreArtifacts(options.artifactsDir, result.updated_bundle);
|
|
339
|
+
const archivedPendingResults = await maybeArchiveLegacyPendingResults(options.auditResultsPath);
|
|
340
|
+
if (archivedPendingResults) {
|
|
341
|
+
result.progress_summary +=
|
|
342
|
+
` Archived legacy staging file to ${archivedPendingResults}.`;
|
|
343
|
+
}
|
|
257
344
|
return result;
|
|
258
345
|
}
|
|
346
|
+
async function ingestBatchAuditResults(options) {
|
|
347
|
+
const batchFiles = await listBatchResultFiles(options.batchDir);
|
|
348
|
+
const artifactsWritten = new Set();
|
|
349
|
+
const progressSummaries = [];
|
|
350
|
+
let lastStep = null;
|
|
351
|
+
let anyProgress = false;
|
|
352
|
+
for (const batchFile of batchFiles) {
|
|
353
|
+
const step = await runAuditStep({
|
|
354
|
+
root: options.root,
|
|
355
|
+
artifactsDir: options.artifactsDir,
|
|
356
|
+
preferredExecutor: "result_ingestion_executor",
|
|
357
|
+
auditResultsPath: batchFile,
|
|
358
|
+
});
|
|
359
|
+
lastStep = step;
|
|
360
|
+
anyProgress ||= step.progress_made;
|
|
361
|
+
for (const artifact of step.artifacts_written) {
|
|
362
|
+
artifactsWritten.add(artifact);
|
|
363
|
+
}
|
|
364
|
+
progressSummaries.push(`${basename(batchFile)}: ${step.progress_summary}`);
|
|
365
|
+
}
|
|
366
|
+
const bundle = lastStep?.updated_bundle ??
|
|
367
|
+
(await loadArtifactBundle(options.artifactsDir));
|
|
368
|
+
const state = lastStep?.audit_state ?? deriveAuditState(bundle);
|
|
369
|
+
const decision = decideNextStep(bundle);
|
|
370
|
+
return {
|
|
371
|
+
batchFiles,
|
|
372
|
+
bundle,
|
|
373
|
+
audit_state: state,
|
|
374
|
+
selected_obligation: lastStep?.selected_obligation ?? decision.selected_obligation,
|
|
375
|
+
selected_executor: lastStep?.selected_executor ?? "result_ingestion_executor",
|
|
376
|
+
progress_made: anyProgress,
|
|
377
|
+
artifacts_written: Array.from(artifactsWritten),
|
|
378
|
+
progress_summary: `Imported ${batchFiles.length} batch result file${batchFiles.length === 1 ? "" : "s"} from ${options.batchDir}.` +
|
|
379
|
+
(progressSummaries.length > 0
|
|
380
|
+
? `\n${progressSummaries.join("\n")}`
|
|
381
|
+
: ""),
|
|
382
|
+
next_likely_step: state.status === "complete" ? null : decision.selected_obligation,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
259
385
|
function isWorkerResult(value) {
|
|
260
386
|
return (typeof value === "object" &&
|
|
261
387
|
value !== null &&
|
|
@@ -276,16 +402,35 @@ export async function runSample() {
|
|
|
276
402
|
pass_id: "pass:security",
|
|
277
403
|
lens: "security",
|
|
278
404
|
agent_role: "security-auditor",
|
|
279
|
-
|
|
405
|
+
file_coverage: [{ path: "src/api/auth.ts", total_lines: 100 }],
|
|
280
406
|
findings: [],
|
|
281
407
|
notes: ["Sample result ingestion path."],
|
|
282
408
|
requires_followup: false,
|
|
283
409
|
},
|
|
284
410
|
];
|
|
285
411
|
const flowCoverage = buildFlowCoverage(criticalFlows, coverage);
|
|
286
|
-
const runtimeValidationTasks = buildRuntimeValidationTasks(
|
|
287
|
-
|
|
288
|
-
|
|
412
|
+
const runtimeValidationTasks = buildRuntimeValidationTasks({
|
|
413
|
+
unitManifest,
|
|
414
|
+
criticalFlows,
|
|
415
|
+
flowCoverage,
|
|
416
|
+
command: ["npm", "test"],
|
|
417
|
+
});
|
|
418
|
+
const runtimeValidationReport = {
|
|
419
|
+
results: runtimeValidationTasks.tasks.map((task) => ({
|
|
420
|
+
task_id: task.id,
|
|
421
|
+
status: "confirmed",
|
|
422
|
+
summary: "Sample runtime validation completed.",
|
|
423
|
+
evidence: [],
|
|
424
|
+
notes: [],
|
|
425
|
+
})),
|
|
426
|
+
};
|
|
427
|
+
const auditReport = renderAuditReportMarkdown(buildAuditReportModel({
|
|
428
|
+
results: sampleResults,
|
|
429
|
+
unitManifest,
|
|
430
|
+
criticalFlows,
|
|
431
|
+
coverageMatrix: coverage,
|
|
432
|
+
runtimeValidationReport,
|
|
433
|
+
}));
|
|
289
434
|
const auditState = deriveAuditState({
|
|
290
435
|
repo_manifest: repoManifest,
|
|
291
436
|
file_disposition: disposition,
|
|
@@ -297,7 +442,7 @@ export async function runSample() {
|
|
|
297
442
|
runtime_validation_tasks: runtimeValidationTasks,
|
|
298
443
|
runtime_validation_report: runtimeValidationReport,
|
|
299
444
|
audit_results: sampleResults,
|
|
300
|
-
|
|
445
|
+
audit_report: auditReport,
|
|
301
446
|
});
|
|
302
447
|
const artifactsDir = getArtifactsDir(process.argv);
|
|
303
448
|
await mkdir(artifactsDir, { recursive: true });
|
|
@@ -312,7 +457,7 @@ export async function runSample() {
|
|
|
312
457
|
runtime_validation_tasks: runtimeValidationTasks,
|
|
313
458
|
runtime_validation_report: runtimeValidationReport,
|
|
314
459
|
audit_results: sampleResults,
|
|
315
|
-
|
|
460
|
+
audit_report: auditReport,
|
|
316
461
|
audit_state: auditState,
|
|
317
462
|
});
|
|
318
463
|
console.log(JSON.stringify({ audit_state: auditState, artifacts_dir: artifactsDir }, null, 2));
|
|
@@ -322,6 +467,37 @@ async function cmdAdvanceAudit(argv) {
|
|
|
322
467
|
const artifactsDir = getArtifactsDir(argv);
|
|
323
468
|
const sessionConfig = await loadSessionConfig(artifactsDir);
|
|
324
469
|
const providerName = resolveFreshSessionProviderName(getFlag(argv, "--provider"), sessionConfig);
|
|
470
|
+
const batchResultsDir = getBatchResultsDir(argv);
|
|
471
|
+
if (batchResultsDir && getFlag(argv, "--results")) {
|
|
472
|
+
throw new Error("Use either --results <file> or --batch-results <dir>, not both.");
|
|
473
|
+
}
|
|
474
|
+
if (batchResultsDir) {
|
|
475
|
+
const result = await ingestBatchAuditResults({
|
|
476
|
+
root,
|
|
477
|
+
artifactsDir,
|
|
478
|
+
batchDir: batchResultsDir,
|
|
479
|
+
});
|
|
480
|
+
if (result.selected_executor !== "agent") {
|
|
481
|
+
await clearDispatchFiles(artifactsDir);
|
|
482
|
+
}
|
|
483
|
+
await emitEnvelope({
|
|
484
|
+
root,
|
|
485
|
+
artifactsDir,
|
|
486
|
+
bundle: result.bundle,
|
|
487
|
+
audit_state: result.audit_state,
|
|
488
|
+
selected_obligation: result.selected_obligation,
|
|
489
|
+
selected_executor: result.selected_executor,
|
|
490
|
+
progress_made: result.progress_made,
|
|
491
|
+
artifacts_written: result.artifacts_written,
|
|
492
|
+
progress_summary: result.progress_summary,
|
|
493
|
+
next_likely_step: result.next_likely_step,
|
|
494
|
+
providerName,
|
|
495
|
+
});
|
|
496
|
+
if (result.audit_state.status === "complete") {
|
|
497
|
+
await promoteFinalAuditReport({ artifactsDir, repoRoot: root });
|
|
498
|
+
}
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
325
501
|
const externalAnalyzerPath = getFlag(argv, "--external-analyzer-results");
|
|
326
502
|
const result = await runAuditStep({
|
|
327
503
|
root,
|
|
@@ -332,6 +508,9 @@ async function cmdAdvanceAudit(argv) {
|
|
|
332
508
|
runtimeUpdatesPath: getFlag(argv, "--updates"),
|
|
333
509
|
externalAnalyzerPath,
|
|
334
510
|
});
|
|
511
|
+
if (result.selected_executor !== "agent") {
|
|
512
|
+
await clearDispatchFiles(artifactsDir);
|
|
513
|
+
}
|
|
335
514
|
await emitEnvelope({
|
|
336
515
|
root,
|
|
337
516
|
artifactsDir,
|
|
@@ -346,7 +525,7 @@ async function cmdAdvanceAudit(argv) {
|
|
|
346
525
|
providerName,
|
|
347
526
|
});
|
|
348
527
|
if (result.audit_state.status === "complete") {
|
|
349
|
-
await
|
|
528
|
+
await promoteFinalAuditReport({ artifactsDir, repoRoot: root });
|
|
350
529
|
}
|
|
351
530
|
}
|
|
352
531
|
async function cmdRunToCompletion(argv) {
|
|
@@ -358,10 +537,17 @@ async function cmdRunToCompletion(argv) {
|
|
|
358
537
|
const maxRuns = getMaxRuns(argv);
|
|
359
538
|
const agentBatchSize = getAgentBatchSize(argv, sessionConfig);
|
|
360
539
|
const parallelWorkers = getParallelWorkers(argv, sessionConfig);
|
|
361
|
-
const timeoutMs = sessionConfig
|
|
540
|
+
const timeoutMs = getTimeoutMs(argv, sessionConfig);
|
|
362
541
|
const selfCliPath = resolve(process.argv[1] ?? "");
|
|
363
542
|
await mkdir(artifactsDir, { recursive: true });
|
|
364
543
|
await ensureSupervisorDirs(artifactsDir);
|
|
544
|
+
const batchResultsDir = getBatchResultsDir(argv);
|
|
545
|
+
if (batchResultsDir && getFlag(argv, "--results")) {
|
|
546
|
+
throw new Error("Use either --results <file> or --batch-results <dir>, not both.");
|
|
547
|
+
}
|
|
548
|
+
let pendingBatchAuditResults = batchResultsDir
|
|
549
|
+
? await listBatchResultFiles(batchResultsDir)
|
|
550
|
+
: [];
|
|
365
551
|
const earlyBundle = await loadArtifactBundle(artifactsDir);
|
|
366
552
|
if (!earlyBundle.unit_manifest) {
|
|
367
553
|
const foundSignal = await detectProjectRoot(root);
|
|
@@ -411,6 +597,11 @@ async function cmdRunToCompletion(argv) {
|
|
|
411
597
|
obligationId = "external_analyzer_import";
|
|
412
598
|
externalAnalyzerPath = pendingExternalAnalyzerPath;
|
|
413
599
|
}
|
|
600
|
+
else if (pendingBatchAuditResults.length > 0 && bundle.coverage_matrix) {
|
|
601
|
+
preferredExecutor = "result_ingestion_executor";
|
|
602
|
+
obligationId = "audit_results_ingested";
|
|
603
|
+
auditResultsPath = pendingBatchAuditResults[0];
|
|
604
|
+
}
|
|
414
605
|
else if (pendingAuditResultsPath && bundle.coverage_matrix) {
|
|
415
606
|
preferredExecutor = "result_ingestion_executor";
|
|
416
607
|
obligationId = "audit_results_ingested";
|
|
@@ -457,7 +648,7 @@ async function cmdRunToCompletion(argv) {
|
|
|
457
648
|
pending_audit_tasks_path: blockPendingTasksPath,
|
|
458
649
|
};
|
|
459
650
|
const blockPrompt = renderWorkerPrompt(blockTask);
|
|
460
|
-
await writeWorkerTaskFiles(blockTask, blockPrompt, blockPaths, artifactsDir);
|
|
651
|
+
await writeWorkerTaskFiles(blockTask, blockPrompt, blockPaths, artifactsDir, blockPendingTasks);
|
|
461
652
|
await writeJsonFile(blockPendingTasksPath, blockPendingTasks);
|
|
462
653
|
await emitEnvelope({
|
|
463
654
|
root,
|
|
@@ -479,6 +670,7 @@ async function cmdRunToCompletion(argv) {
|
|
|
479
670
|
}
|
|
480
671
|
if (!preferredExecutor) {
|
|
481
672
|
const state = bundle.audit_state ?? decision.state;
|
|
673
|
+
await clearDispatchFiles(artifactsDir);
|
|
482
674
|
await emitEnvelope({
|
|
483
675
|
root,
|
|
484
676
|
artifactsDir,
|
|
@@ -499,7 +691,7 @@ async function cmdRunToCompletion(argv) {
|
|
|
499
691
|
providerName: provider.name,
|
|
500
692
|
});
|
|
501
693
|
if (state.status === "complete") {
|
|
502
|
-
await
|
|
694
|
+
await promoteFinalAuditReport({ artifactsDir, repoRoot: root });
|
|
503
695
|
}
|
|
504
696
|
return;
|
|
505
697
|
}
|
|
@@ -527,12 +719,12 @@ async function cmdRunToCompletion(argv) {
|
|
|
527
719
|
skip_worker_command: true,
|
|
528
720
|
};
|
|
529
721
|
const slotPrompt = renderWorkerPrompt(slotTask);
|
|
530
|
-
await writeWorkerTaskFiles(slotTask, slotPrompt, slotPaths, artifactsDir);
|
|
722
|
+
await writeWorkerTaskFiles(slotTask, slotPrompt, slotPaths, artifactsDir, group);
|
|
531
723
|
await writeJsonFile(slotPendingTasksPath, group);
|
|
532
724
|
workerSlots.push({ runId: slotRunId, paths: slotPaths, auditResultsPath: slotAuditResultsPath, pendingTasksPath: slotPendingTasksPath, group });
|
|
533
725
|
}
|
|
534
726
|
const parallelStartedAt = new Date().toISOString();
|
|
535
|
-
await Promise.allSettled(workerSlots.map((slot) => provider.launch({
|
|
727
|
+
const launchResults = await Promise.allSettled(workerSlots.map((slot) => provider.launch({
|
|
536
728
|
repoRoot: root,
|
|
537
729
|
runId: slot.runId,
|
|
538
730
|
obligationId,
|
|
@@ -544,21 +736,37 @@ async function cmdRunToCompletion(argv) {
|
|
|
544
736
|
uiMode,
|
|
545
737
|
timeoutMs,
|
|
546
738
|
})));
|
|
739
|
+
const launchErrorsByRunId = new Map();
|
|
740
|
+
for (let index = 0; index < launchResults.length; index++) {
|
|
741
|
+
const outcome = launchResults[index];
|
|
742
|
+
if (outcome?.status === "rejected") {
|
|
743
|
+
launchErrorsByRunId.set(workerSlots[index].runId, outcome.reason instanceof Error
|
|
744
|
+
? outcome.reason.message
|
|
745
|
+
: String(outcome.reason));
|
|
746
|
+
}
|
|
747
|
+
}
|
|
547
748
|
// Result ingestion is intentionally sequential even though agent launch
|
|
548
749
|
// was parallel. Writing to coverage_matrix.json is not atomic, so
|
|
549
750
|
// concurrent ingest calls would race and corrupt coverage state.
|
|
550
751
|
let batchProgress = false;
|
|
752
|
+
const batchErrors = [];
|
|
551
753
|
for (const slot of workerSlots) {
|
|
552
754
|
const parallelEndedAt = new Date().toISOString();
|
|
553
755
|
let slotStatus = "no_progress";
|
|
554
756
|
try {
|
|
757
|
+
const launchError = launchErrorsByRunId.get(slot.runId);
|
|
758
|
+
if (launchError) {
|
|
759
|
+
throw new Error(`Worker launch failed: ${launchError}`);
|
|
760
|
+
}
|
|
555
761
|
const auditResults = await readJsonFile(slot.auditResultsPath);
|
|
556
762
|
const pendingTaskIds = new Set(slot.group.map((t) => t.task_id));
|
|
557
763
|
const matchedCount = auditResults.filter((r) => pendingTaskIds.has(r.task_id)).length;
|
|
558
764
|
if (slot.group.length > 0 && matchedCount === 0) {
|
|
559
765
|
throw new Error("Worker did not emit any audit results for the assigned tasks.");
|
|
560
766
|
}
|
|
561
|
-
const issues = validateAuditResults(auditResults, slot.group
|
|
767
|
+
const issues = validateAuditResults(auditResults, slot.group, {
|
|
768
|
+
lineIndex: await buildLineIndexForPaths(root, slot.group.flatMap((task) => task.file_paths)),
|
|
769
|
+
});
|
|
562
770
|
const errors = issues.filter((issue) => issue.severity === "error");
|
|
563
771
|
const warnings = issues.filter((issue) => issue.severity === "warning");
|
|
564
772
|
if (warnings.length > 0) {
|
|
@@ -582,8 +790,11 @@ async function cmdRunToCompletion(argv) {
|
|
|
582
790
|
for (const a of stepResult.artifacts_written)
|
|
583
791
|
artifactsWritten.add(a);
|
|
584
792
|
}
|
|
585
|
-
catch {
|
|
793
|
+
catch (error) {
|
|
586
794
|
slotStatus = "failed";
|
|
795
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
796
|
+
batchErrors.push(`${slot.runId}: ${message}`);
|
|
797
|
+
process.stderr.write(`[agent-batch] ${slot.runId} failed: ${message}\n`);
|
|
587
798
|
}
|
|
588
799
|
await appendRunLedgerEntry(artifactsDir, {
|
|
589
800
|
run_id: slot.runId,
|
|
@@ -597,6 +808,35 @@ async function cmdRunToCompletion(argv) {
|
|
|
597
808
|
});
|
|
598
809
|
artifactsWritten.add("run-ledger.json");
|
|
599
810
|
}
|
|
811
|
+
if (batchErrors.length > 0) {
|
|
812
|
+
const bundleAfter = await loadArtifactBundle(artifactsDir);
|
|
813
|
+
const blockedState = buildBlockedAuditState({
|
|
814
|
+
state: bundleAfter.audit_state ?? deriveAuditState(bundleAfter),
|
|
815
|
+
obligationId,
|
|
816
|
+
executor: "agent",
|
|
817
|
+
blocker: `Parallel worker batch failed for ${batchErrors.length} run(s). ` +
|
|
818
|
+
batchErrors.slice(0, 3).join(" | "),
|
|
819
|
+
});
|
|
820
|
+
await writeCoreArtifacts(artifactsDir, {
|
|
821
|
+
...bundleAfter,
|
|
822
|
+
audit_state: blockedState,
|
|
823
|
+
});
|
|
824
|
+
await emitEnvelope({
|
|
825
|
+
root,
|
|
826
|
+
artifactsDir,
|
|
827
|
+
bundle: { ...bundleAfter, audit_state: blockedState },
|
|
828
|
+
audit_state: blockedState,
|
|
829
|
+
selected_obligation: obligationId,
|
|
830
|
+
selected_executor: "agent",
|
|
831
|
+
progress_made: anyProgress,
|
|
832
|
+
artifacts_written: Array.from(new Set([...artifactsWritten, "audit_state.json"])),
|
|
833
|
+
progress_summary: `Parallel worker batch failed for ${batchErrors.length} run(s).\n` +
|
|
834
|
+
batchErrors.join("\n"),
|
|
835
|
+
next_likely_step: null,
|
|
836
|
+
providerName: provider.name,
|
|
837
|
+
});
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
600
840
|
if (!batchProgress) {
|
|
601
841
|
const bundleAfter = await loadArtifactBundle(artifactsDir);
|
|
602
842
|
const state = bundleAfter.audit_state ?? deriveAuditState(bundleAfter);
|
|
@@ -650,7 +890,7 @@ async function cmdRunToCompletion(argv) {
|
|
|
650
890
|
external_analyzer_results_path: externalAnalyzerPath,
|
|
651
891
|
};
|
|
652
892
|
const prompt = renderWorkerPrompt(task);
|
|
653
|
-
await writeWorkerTaskFiles(task, prompt, paths, artifactsDir);
|
|
893
|
+
await writeWorkerTaskFiles(task, prompt, paths, artifactsDir, pendingAuditTasks);
|
|
654
894
|
if (pendingAuditTasksPath && pendingAuditTasks) {
|
|
655
895
|
await writeJsonFile(pendingAuditTasksPath, pendingAuditTasks);
|
|
656
896
|
}
|
|
@@ -686,6 +926,7 @@ async function cmdRunToCompletion(argv) {
|
|
|
686
926
|
};
|
|
687
927
|
}
|
|
688
928
|
catch (error) {
|
|
929
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
689
930
|
workerResult = {
|
|
690
931
|
contract_version: WORKER_RESULT_CONTRACT_VERSION,
|
|
691
932
|
run_id: runId,
|
|
@@ -694,9 +935,9 @@ async function cmdRunToCompletion(argv) {
|
|
|
694
935
|
progress_made: false,
|
|
695
936
|
selected_executor: preferredExecutor,
|
|
696
937
|
artifacts_written: [],
|
|
697
|
-
summary: `Worker launch failed for ${preferredExecutor}
|
|
938
|
+
summary: `Worker launch failed for ${preferredExecutor}: ${message}`,
|
|
698
939
|
next_likely_step: decision.selected_obligation,
|
|
699
|
-
errors: [
|
|
940
|
+
errors: [message],
|
|
700
941
|
};
|
|
701
942
|
await writeJsonFile(paths.resultPath, workerResult);
|
|
702
943
|
}
|
|
@@ -720,6 +961,13 @@ async function cmdRunToCompletion(argv) {
|
|
|
720
961
|
artifactsWritten.add("run-ledger.json");
|
|
721
962
|
if (externalAnalyzerPath)
|
|
722
963
|
pendingExternalAnalyzerPath = undefined;
|
|
964
|
+
if (auditResultsPath &&
|
|
965
|
+
pendingBatchAuditResults[0] === auditResultsPath &&
|
|
966
|
+
preferredExecutor === "result_ingestion_executor" &&
|
|
967
|
+
workerResult.status !== "failed" &&
|
|
968
|
+
workerResult.status !== "blocked") {
|
|
969
|
+
pendingBatchAuditResults.shift();
|
|
970
|
+
}
|
|
723
971
|
if (providerAuditResultsPath)
|
|
724
972
|
pendingAuditResultsPath = undefined;
|
|
725
973
|
if (runtimeUpdatesPath)
|
|
@@ -728,18 +976,36 @@ async function cmdRunToCompletion(argv) {
|
|
|
728
976
|
workerResult.status === "blocked" ||
|
|
729
977
|
workerResult.status === "no_progress") {
|
|
730
978
|
const bundleAfter = await loadArtifactBundle(artifactsDir);
|
|
731
|
-
const
|
|
979
|
+
const shouldBlock = workerResult.status === "failed" || workerResult.status === "blocked";
|
|
980
|
+
const state = shouldBlock
|
|
981
|
+
? buildBlockedAuditState({
|
|
982
|
+
state: bundleAfter.audit_state ?? deriveAuditState(bundleAfter),
|
|
983
|
+
obligationId: workerResult.obligation_id,
|
|
984
|
+
executor: workerResult.selected_executor,
|
|
985
|
+
blocker: buildWorkerFailureBlocker(workerResult),
|
|
986
|
+
})
|
|
987
|
+
: bundleAfter.audit_state ?? deriveAuditState(bundleAfter);
|
|
988
|
+
if (shouldBlock) {
|
|
989
|
+
await writeCoreArtifacts(artifactsDir, {
|
|
990
|
+
...bundleAfter,
|
|
991
|
+
audit_state: state,
|
|
992
|
+
});
|
|
993
|
+
}
|
|
732
994
|
await emitEnvelope({
|
|
733
995
|
root,
|
|
734
996
|
artifactsDir,
|
|
735
|
-
bundle:
|
|
997
|
+
bundle: shouldBlock
|
|
998
|
+
? { ...bundleAfter, audit_state: state }
|
|
999
|
+
: bundleAfter,
|
|
736
1000
|
audit_state: state,
|
|
737
1001
|
selected_obligation: workerResult.obligation_id,
|
|
738
1002
|
selected_executor: workerResult.selected_executor,
|
|
739
1003
|
progress_made: anyProgress,
|
|
740
|
-
artifacts_written: Array.from(
|
|
741
|
-
|
|
742
|
-
|
|
1004
|
+
artifacts_written: Array.from(shouldBlock
|
|
1005
|
+
? new Set([...artifactsWritten, "audit_state.json"])
|
|
1006
|
+
: artifactsWritten),
|
|
1007
|
+
progress_summary: buildWorkerFailureBlocker(workerResult),
|
|
1008
|
+
next_likely_step: shouldBlock ? null : workerResult.next_likely_step,
|
|
743
1009
|
providerName: provider.name,
|
|
744
1010
|
});
|
|
745
1011
|
return;
|
|
@@ -748,6 +1014,9 @@ async function cmdRunToCompletion(argv) {
|
|
|
748
1014
|
const bundle = await loadArtifactBundle(artifactsDir);
|
|
749
1015
|
const decision = decideNextStep(bundle);
|
|
750
1016
|
const state = bundle.audit_state ?? decision.state;
|
|
1017
|
+
if (state.status === "complete") {
|
|
1018
|
+
await clearDispatchFiles(artifactsDir);
|
|
1019
|
+
}
|
|
751
1020
|
await emitEnvelope({
|
|
752
1021
|
root,
|
|
753
1022
|
artifactsDir,
|
|
@@ -770,6 +1039,9 @@ async function cmdWorkerRun(argv) {
|
|
|
770
1039
|
const task = await readJsonFile(taskPath);
|
|
771
1040
|
let workerResult;
|
|
772
1041
|
try {
|
|
1042
|
+
if (looksLikeCliFlag(task.audit_results_path)) {
|
|
1043
|
+
throw new Error(`task.audit_results_path resolved to '${task.audit_results_path}', which looks like a CLI flag instead of a file path.`);
|
|
1044
|
+
}
|
|
773
1045
|
if (task.preferred_executor === "agent" && !task.audit_results_path) {
|
|
774
1046
|
throw new Error("agent worker-run requires audit_results_path so provider-assisted review can be ingested.");
|
|
775
1047
|
}
|
|
@@ -783,7 +1055,9 @@ async function cmdWorkerRun(argv) {
|
|
|
783
1055
|
if (pendingTasks.length > 0 && matchedResultCount === 0) {
|
|
784
1056
|
throw new Error("Provider-assisted review did not emit any audit results for the pending audit tasks.");
|
|
785
1057
|
}
|
|
786
|
-
const issues = validateAuditResults(auditResults, pendingTasks
|
|
1058
|
+
const issues = validateAuditResults(auditResults, pendingTasks, {
|
|
1059
|
+
lineIndex: await buildLineIndexForPaths(task.repo_root, pendingTasks.flatMap((item) => item.file_paths)),
|
|
1060
|
+
});
|
|
787
1061
|
const errors = issues.filter((issue) => issue.severity === "error");
|
|
788
1062
|
const warnings = issues.filter((issue) => issue.severity === "warning");
|
|
789
1063
|
if (warnings.length > 0) {
|
|
@@ -792,8 +1066,7 @@ async function cmdWorkerRun(argv) {
|
|
|
792
1066
|
"\n");
|
|
793
1067
|
}
|
|
794
1068
|
if (errors.length > 0) {
|
|
795
|
-
throw new Error(
|
|
796
|
-
formatAuditResultIssues(errors));
|
|
1069
|
+
throw new Error(formatAuditResultValidationError(errors));
|
|
797
1070
|
}
|
|
798
1071
|
}
|
|
799
1072
|
const preferredExecutor = task.preferred_executor === "agent"
|
|
@@ -829,7 +1102,7 @@ async function cmdWorkerRun(argv) {
|
|
|
829
1102
|
progress_made: false,
|
|
830
1103
|
selected_executor: task.preferred_executor,
|
|
831
1104
|
artifacts_written: [],
|
|
832
|
-
summary: `Worker failed for executor ${task.preferred_executor}
|
|
1105
|
+
summary: `Worker failed for executor ${task.preferred_executor}: ${error instanceof Error ? error.message : String(error)}`,
|
|
833
1106
|
next_likely_step: task.obligation_id,
|
|
834
1107
|
errors: [error instanceof Error ? error.message : String(error)],
|
|
835
1108
|
};
|
|
@@ -882,6 +1155,24 @@ async function cmdPlan(argv) {
|
|
|
882
1155
|
}
|
|
883
1156
|
async function cmdIngestResults(argv) {
|
|
884
1157
|
const artifactsDir = getArtifactsDir(argv);
|
|
1158
|
+
const batchResultsDir = getBatchResultsDir(argv);
|
|
1159
|
+
if (batchResultsDir && getFlag(argv, "--results")) {
|
|
1160
|
+
throw new Error("Use either --results <file> or --batch-results <dir>, not both.");
|
|
1161
|
+
}
|
|
1162
|
+
if (batchResultsDir) {
|
|
1163
|
+
const result = await ingestBatchAuditResults({
|
|
1164
|
+
root: getRootDir(argv),
|
|
1165
|
+
artifactsDir,
|
|
1166
|
+
batchDir: batchResultsDir,
|
|
1167
|
+
});
|
|
1168
|
+
console.log(JSON.stringify({
|
|
1169
|
+
artifacts_dir: artifactsDir,
|
|
1170
|
+
imported_files: result.batchFiles,
|
|
1171
|
+
selected_executor: result.selected_executor,
|
|
1172
|
+
progress_summary: result.progress_summary,
|
|
1173
|
+
}, null, 2));
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
885
1176
|
const result = await runAuditStep({
|
|
886
1177
|
root: getRootDir(argv),
|
|
887
1178
|
artifactsDir,
|
|
@@ -894,6 +1185,37 @@ async function cmdIngestResults(argv) {
|
|
|
894
1185
|
progress_summary: result.progress_summary,
|
|
895
1186
|
}, null, 2));
|
|
896
1187
|
}
|
|
1188
|
+
async function cmdExplainTask(argv) {
|
|
1189
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
1190
|
+
const taskId = getFlag(argv, "--task-id") ?? argv[3];
|
|
1191
|
+
if (!taskId) {
|
|
1192
|
+
throw new Error("explain-task requires <task_id> or --task-id <task_id>");
|
|
1193
|
+
}
|
|
1194
|
+
const bundle = await loadArtifactBundle(artifactsDir);
|
|
1195
|
+
const task = [...(bundle.audit_tasks ?? []), ...(bundle.requeue_tasks ?? [])].find((item) => item.task_id === taskId);
|
|
1196
|
+
if (!task) {
|
|
1197
|
+
throw new Error(`Unknown task_id '${taskId}'.`);
|
|
1198
|
+
}
|
|
1199
|
+
const coverageEntries = (bundle.coverage_matrix?.files ?? [])
|
|
1200
|
+
.filter((file) => task.file_paths.includes(file.path))
|
|
1201
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
1202
|
+
const matchingResults = (bundle.audit_results ?? []).filter((result) => result.task_id === task.task_id);
|
|
1203
|
+
console.log(JSON.stringify({
|
|
1204
|
+
artifacts_dir: artifactsDir,
|
|
1205
|
+
task_id: task.task_id,
|
|
1206
|
+
task,
|
|
1207
|
+
file_count: task.file_paths.length,
|
|
1208
|
+
coverage_entries: coverageEntries,
|
|
1209
|
+
pending_coverage: coverageEntries
|
|
1210
|
+
.map((file) => ({
|
|
1211
|
+
path: file.path,
|
|
1212
|
+
missing_lenses: file.required_lenses.filter((lens) => !file.completed_lenses.includes(lens)),
|
|
1213
|
+
}))
|
|
1214
|
+
.filter((file) => file.missing_lenses.length > 0),
|
|
1215
|
+
matching_result_count: matchingResults.length,
|
|
1216
|
+
matching_finding_ids: matchingResults.flatMap((result) => result.findings.map((finding) => finding.id)),
|
|
1217
|
+
}, null, 2));
|
|
1218
|
+
}
|
|
897
1219
|
async function cmdUpdateRuntimeValidation(argv) {
|
|
898
1220
|
const artifactsDir = getArtifactsDir(argv);
|
|
899
1221
|
const result = await runAuditStep({
|
|
@@ -942,6 +1264,31 @@ async function cmdValidate(argv) {
|
|
|
942
1264
|
}, null, 2));
|
|
943
1265
|
process.exitCode = issues.length > 0 ? 1 : 0;
|
|
944
1266
|
}
|
|
1267
|
+
async function cmdValidateResults(argv) {
|
|
1268
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
1269
|
+
const resultsPath = getFlag(argv, "--results");
|
|
1270
|
+
if (!resultsPath) {
|
|
1271
|
+
throw new Error("validate-results requires --results <file>");
|
|
1272
|
+
}
|
|
1273
|
+
const bundle = await loadArtifactBundle(artifactsDir);
|
|
1274
|
+
const lineIndex = bundle.repo_manifest
|
|
1275
|
+
? await buildLineIndex(getRootDir(argv), bundle.repo_manifest)
|
|
1276
|
+
: undefined;
|
|
1277
|
+
const auditResults = await readJsonFile(resultsPath);
|
|
1278
|
+
const issues = validateAuditResults(auditResults, bundle.audit_tasks ?? [], {
|
|
1279
|
+
lineIndex,
|
|
1280
|
+
});
|
|
1281
|
+
const errors = issues.filter((issue) => issue.severity === "error");
|
|
1282
|
+
const warnings = issues.filter((issue) => issue.severity === "warning");
|
|
1283
|
+
console.log(JSON.stringify({
|
|
1284
|
+
artifacts_dir: artifactsDir,
|
|
1285
|
+
results_path: resolve(resultsPath),
|
|
1286
|
+
warning_count: warnings.length,
|
|
1287
|
+
error_count: errors.length,
|
|
1288
|
+
issues,
|
|
1289
|
+
}, null, 2));
|
|
1290
|
+
process.exitCode = errors.length > 0 ? 1 : 0;
|
|
1291
|
+
}
|
|
945
1292
|
async function cmdRequeue(argv) {
|
|
946
1293
|
const artifactsDir = getArtifactsDir(argv);
|
|
947
1294
|
const bundle = await loadArtifactBundle(artifactsDir);
|
|
@@ -963,6 +1310,9 @@ async function cmdSynthesize(argv) {
|
|
|
963
1310
|
progress_summary: result.progress_summary,
|
|
964
1311
|
}, null, 2));
|
|
965
1312
|
}
|
|
1313
|
+
async function cmdMcp(argv) {
|
|
1314
|
+
await runAuditCodeMcpServer(argv.slice(3));
|
|
1315
|
+
}
|
|
966
1316
|
async function main(argv) {
|
|
967
1317
|
const command = argv[2] ?? "sample-run";
|
|
968
1318
|
switch (command) {
|
|
@@ -990,21 +1340,30 @@ async function main(argv) {
|
|
|
990
1340
|
case "ingest-results":
|
|
991
1341
|
await cmdIngestResults(argv);
|
|
992
1342
|
return;
|
|
1343
|
+
case "explain-task":
|
|
1344
|
+
await cmdExplainTask(argv);
|
|
1345
|
+
return;
|
|
993
1346
|
case "update-runtime-validation":
|
|
994
1347
|
await cmdUpdateRuntimeValidation(argv);
|
|
995
1348
|
return;
|
|
996
1349
|
case "validate":
|
|
997
1350
|
await cmdValidate(argv);
|
|
998
1351
|
return;
|
|
1352
|
+
case "validate-results":
|
|
1353
|
+
await cmdValidateResults(argv);
|
|
1354
|
+
return;
|
|
999
1355
|
case "requeue":
|
|
1000
1356
|
await cmdRequeue(argv);
|
|
1001
1357
|
return;
|
|
1002
1358
|
case "synthesize":
|
|
1003
1359
|
await cmdSynthesize(argv);
|
|
1004
1360
|
return;
|
|
1361
|
+
case "mcp":
|
|
1362
|
+
await cmdMcp(argv);
|
|
1363
|
+
return;
|
|
1005
1364
|
default:
|
|
1006
1365
|
console.error(`Unknown command: ${command}`);
|
|
1007
|
-
console.error("Available commands: sample-run, advance-audit, run-to-completion, worker-run, import-external-analyzer, intake, plan, ingest-results, update-runtime-validation, validate, requeue, synthesize");
|
|
1366
|
+
console.error("Available commands: sample-run, advance-audit, run-to-completion, worker-run, import-external-analyzer, intake, plan, ingest-results, explain-task, update-runtime-validation, validate, validate-results, requeue, synthesize, mcp");
|
|
1008
1367
|
process.exitCode = 1;
|
|
1009
1368
|
}
|
|
1010
1369
|
}
|