auditor-lambda 0.6.10 → 0.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/cleanup.d.ts +1 -0
- package/dist/cli/cleanup.js +23 -0
- package/dist/cli/nextStepCommand.d.ts +1 -0
- package/dist/cli/nextStepCommand.js +553 -0
- package/dist/cli/paths.d.ts +1 -0
- package/dist/cli/paths.js +7 -0
- package/dist/cli/reviewRun.d.ts +5 -0
- package/dist/cli/reviewRun.js +24 -1
- package/dist/cli/runToCompletion.d.ts +1 -0
- package/dist/cli/runToCompletion.js +878 -0
- package/dist/cli/semanticReviewStep.d.ts +11 -0
- package/dist/cli/semanticReviewStep.js +103 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +16 -1543
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { mkdir, readFile, readdir, rm,
|
|
1
|
+
import { mkdir, readFile, readdir, rm, } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import {
|
|
3
|
+
import { join, resolve, } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { buildRepoManifest } from "./extractors/fileInventory.js";
|
|
6
6
|
import { buildFileDisposition } from "./extractors/disposition.js";
|
|
@@ -10,46 +10,33 @@ import { buildUnitManifest } from "./orchestrator/unitBuilder.js";
|
|
|
10
10
|
import { buildFlowCoverage } from "./orchestrator/flowCoverage.js";
|
|
11
11
|
import { buildRuntimeValidationTasks, } from "./orchestrator/runtimeValidation.js";
|
|
12
12
|
import { initializeCoverageFromPlan } from "./orchestrator/planning.js";
|
|
13
|
-
import { loadArtifactBundle, writeCoreArtifacts, promoteFinalAuditReport,
|
|
14
|
-
import { isFileMissingError, readJsonFile, writeJsonFile, prefixValidationIssues } from "@audit-tools/shared";
|
|
13
|
+
import { loadArtifactBundle, writeCoreArtifacts, promoteFinalAuditReport, } from "./io/artifacts.js";
|
|
14
|
+
import { isFileMissingError, readJsonFile, writeJsonFile, prefixValidationIssues, } from "@audit-tools/shared";
|
|
15
15
|
import { buildQuotaSource } from "@audit-tools/shared/quota/compositeQuotaSource";
|
|
16
16
|
import { validateArtifactBundle } from "./validation/artifacts.js";
|
|
17
17
|
import { validateAuditResults, formatAuditResultIssues, } from "./validation/auditResults.js";
|
|
18
18
|
import { validateConfiguredProviderEnvironment, validateSessionConfig, } from "./validation/sessionConfig.js";
|
|
19
19
|
import { buildAuditReportModel, renderAuditReportMarkdown, } from "./reporting/synthesis.js";
|
|
20
20
|
import { deriveAuditState } from "./orchestrator/state.js";
|
|
21
|
-
import { advanceAudit } from "./orchestrator/advance.js";
|
|
22
|
-
import { checkFileIntegrity } from "./orchestrator/fileIntegrity.js";
|
|
23
|
-
import { decideNextStep } from "./orchestrator/nextStep.js";
|
|
24
|
-
import { collectLowConfidenceEdges, buildEdgeReasoningPrompt, edgeReasoningContentHash, } from "./orchestrator/edgeReasoning.js";
|
|
25
|
-
import { renderDesignReviewPrompt } from "./orchestrator/designReviewPrompt.js";
|
|
26
|
-
import { renderSynthesisNarrativePrompt } from "./reporting/synthesisNarrativePrompt.js";
|
|
27
21
|
import { createFreshSessionProvider, resolveFreshSessionProviderName, } from "./providers/index.js";
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import { resolveAnalyzerPlan, needsInstallDecision, } from "./extractors/analyzers/registry.js";
|
|
32
|
-
import { buildPathLookup } from "./extractors/graph.js";
|
|
33
|
-
import { buildDispositionMap } from "./extractors/disposition.js";
|
|
34
|
-
import { clearDispatchFiles, buildRunId, ensureSupervisorDirs, getRunPaths, writeDispatchBatchFiles, writeWorkerTaskFiles, } from "./io/runArtifacts.js";
|
|
35
|
-
import { renderWorkerPrompt } from "./prompts/renderWorkerPrompt.js";
|
|
36
|
-
import { estimateTaskGroupTokens, sizeIndexFromManifest, } from "./orchestrator/reviewPackets.js";
|
|
37
|
-
import { LOCAL_SUBPROCESS_PROVIDER_NAME } from "./providers/constants.js";
|
|
22
|
+
import { loadRunLedger, } from "./supervisor/runLedger.js";
|
|
23
|
+
import { getSessionConfigPath, loadSessionConfig, readSessionConfigFile, } from "./supervisor/sessionConfig.js";
|
|
24
|
+
import { clearDispatchFiles, ensureSupervisorDirs, } from "./io/runArtifacts.js";
|
|
38
25
|
import { runAuditCodeMcpServer } from "./mcp/server.js";
|
|
39
|
-
import { scheduleWave, buildProviderModelKey, readQuotaState,
|
|
26
|
+
import { scheduleWave, buildProviderModelKey, readQuotaState, resolveLimits, resolveHostActiveSubagentLimit, probeProvider, computeMaxSafeConcurrency, getQuotaStatePath, lookupDiscoveredLimits, setQuotaStateDir, } from "./quota/index.js";
|
|
40
27
|
// Re-exports from extracted modules
|
|
41
28
|
export { resolveHostDispatchCapability, DIRECT_CLI_DEFAULTS, getFlag, hasFlag, getOptionalBooleanFlag, getArtifactsDir, getRootDir, getBatchResultsDir, getMaxRuns, getAgentBatchSize, getParallelWorkers, getTimeoutMs, chunkArray, getUiMode, looksLikeCliFlag, countLines, warnIfNotGitRepo, } from "./cli/args.js";
|
|
42
|
-
import { DIRECT_CLI_DEFAULTS, getFlag, hasFlag,
|
|
43
|
-
import {
|
|
44
|
-
import { writeCurrentStep, } from "./cli/steps.js";
|
|
45
|
-
import { WORKER_RESULT_CONTRACT_VERSION, buildWorkerResult, persistWorkerRunArtifacts, isWorkerResult, buildWorkerFailureBlocker, formatAuditResultValidationError, } from "./cli/workerResult.js";
|
|
29
|
+
import { DIRECT_CLI_DEFAULTS, getFlag, hasFlag, fromBase64Url, taskResultPath, readStdinText, getArtifactsDir, getRootDir, warnIfNotGitRepo, getBatchResultsDir, getMaxRuns, getAgentBatchSize, getParallelWorkers, getTimeoutMs, getExplicitProvider, getHostModel, getHostMaxActiveSubagents, getQuotaProbeMode, resolveRunProviderName, chunkArray, getUiMode, looksLikeCliFlag, countLines, } from "./cli/args.js";
|
|
30
|
+
import { WORKER_RESULT_CONTRACT_VERSION, buildWorkerResult, formatAuditResultValidationError, } from "./cli/workerResult.js";
|
|
46
31
|
import { DISPATCH_RESULT_MAP_FILENAME, ACTIVE_DISPATCH_FILENAME, resolveRunScopedArg, loadDispatchResultMap, entriesByTaskId, buildPendingAuditTasks, prepareDispatchArtifacts, } from "./cli/dispatch.js";
|
|
47
|
-
import { readWaveManifest, writeWaveManifest, removeWaveManifest, buildWaveSlotEntry, } from "./cli/waveManifest.js";
|
|
48
32
|
import { buildLineIndex, buildLineIndexForPaths, addFileLineCountHints, } from "./cli/lineIndex.js";
|
|
49
|
-
import { emitEnvelope,
|
|
50
|
-
import {
|
|
33
|
+
import { emitEnvelope, } from "./cli/envelope.js";
|
|
34
|
+
import { persistConfigErrorHandoff } from "./cli/reviewRun.js";
|
|
51
35
|
import { runAuditStep, ingestBatchAuditResults, } from "./cli/auditStep.js";
|
|
52
|
-
|
|
36
|
+
import { packageRoot } from "./cli/paths.js";
|
|
37
|
+
import { cmdNextStep } from "./cli/nextStepCommand.js";
|
|
38
|
+
import { cmdRunToCompletion } from "./cli/runToCompletion.js";
|
|
39
|
+
import { cleanupStaleArtifactsDir } from "./cli/cleanup.js";
|
|
53
40
|
const SAMPLE_REPO_FILES = [
|
|
54
41
|
{ path: "src/api/auth.ts", size_bytes: 1240, hash: "abc123" },
|
|
55
42
|
{ path: "src/lib/session.ts", size_bytes: 980, hash: "def456" },
|
|
@@ -73,28 +60,6 @@ export const cliTestUtils = {
|
|
|
73
60
|
countLines,
|
|
74
61
|
warnIfNotGitRepo,
|
|
75
62
|
};
|
|
76
|
-
async function persistConfigErrorHandoff(params) {
|
|
77
|
-
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
78
|
-
const blockedState = buildBlockedAuditState({
|
|
79
|
-
state: bundle.audit_state ?? deriveAuditState(bundle),
|
|
80
|
-
obligationId: null,
|
|
81
|
-
executor: null,
|
|
82
|
-
blocker: params.progressSummary,
|
|
83
|
-
});
|
|
84
|
-
await writeCoreArtifacts(params.artifactsDir, {
|
|
85
|
-
...bundle,
|
|
86
|
-
audit_state: blockedState,
|
|
87
|
-
});
|
|
88
|
-
const handoff = buildAuditCodeHandoff({
|
|
89
|
-
root: params.root,
|
|
90
|
-
artifactsDir: params.artifactsDir,
|
|
91
|
-
state: blockedState,
|
|
92
|
-
bundle: { ...bundle, audit_state: blockedState },
|
|
93
|
-
progressSummary: params.progressSummary,
|
|
94
|
-
isConfigError: true,
|
|
95
|
-
});
|
|
96
|
-
await writeAuditCodeHandoffArtifacts(handoff);
|
|
97
|
-
}
|
|
98
63
|
export async function runSample(argv = process.argv) {
|
|
99
64
|
const repoManifest = buildRepoManifest("sample-repo", SAMPLE_REPO_FILES);
|
|
100
65
|
const disposition = buildFileDisposition(repoManifest);
|
|
@@ -255,1482 +220,6 @@ async function cmdAdvanceAudit(argv) {
|
|
|
255
220
|
await promoteFinalAuditReport({ artifactsDir, repoRoot: root });
|
|
256
221
|
}
|
|
257
222
|
}
|
|
258
|
-
async function runDeterministicForNextStep(params) {
|
|
259
|
-
let lastSummary = "";
|
|
260
|
-
let analyzers = params.analyzers;
|
|
261
|
-
for (let index = 0; index < params.maxRuns; index++) {
|
|
262
|
-
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
263
|
-
const decision = decideNextStep(bundle);
|
|
264
|
-
const state = decision.state;
|
|
265
|
-
if (state.status === "complete") {
|
|
266
|
-
await writeHandoffOnly({
|
|
267
|
-
root: params.root,
|
|
268
|
-
artifactsDir: params.artifactsDir,
|
|
269
|
-
bundle,
|
|
270
|
-
audit_state: state,
|
|
271
|
-
progress_summary: decision.reason,
|
|
272
|
-
providerName: LOCAL_SUBPROCESS_PROVIDER_NAME,
|
|
273
|
-
});
|
|
274
|
-
const promoted = await promoteFinalAuditReport({
|
|
275
|
-
artifactsDir: params.artifactsDir,
|
|
276
|
-
repoRoot: params.root,
|
|
277
|
-
});
|
|
278
|
-
return {
|
|
279
|
-
kind: "complete",
|
|
280
|
-
state,
|
|
281
|
-
bundle,
|
|
282
|
-
finalReportPath: promoted.promoted
|
|
283
|
-
? join(params.root, AUDIT_REPORT_FILENAME)
|
|
284
|
-
: join(params.artifactsDir, AUDIT_REPORT_FILENAME),
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
if (index === 0 && bundle.repo_manifest) {
|
|
288
|
-
const pendingTasks = buildPendingAuditTasks(bundle);
|
|
289
|
-
const taskFiles = new Set();
|
|
290
|
-
for (const task of pendingTasks) {
|
|
291
|
-
for (const fp of Object.keys(task.file_line_counts ?? {}))
|
|
292
|
-
taskFiles.add(fp);
|
|
293
|
-
}
|
|
294
|
-
if (taskFiles.size > 0) {
|
|
295
|
-
const integrity = await checkFileIntegrity(params.root, bundle.repo_manifest, [...taskFiles]);
|
|
296
|
-
if (!integrity.is_clean) {
|
|
297
|
-
console.log(`File integrity check: ${integrity.changed_files.length} changed, ${integrity.missing_files.length} missing — re-running intake.`);
|
|
298
|
-
await advanceAudit(bundle, { root: params.root, preferredExecutor: "intake_executor", opentoken: params.opentoken });
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
if (decision.selected_executor === "graph_enrichment_executor") {
|
|
304
|
-
const includedFiles = bundle.repo_manifest
|
|
305
|
-
? [
|
|
306
|
-
...new Set(buildPathLookup(bundle.repo_manifest, buildDispositionMap(bundle.file_disposition)).values()),
|
|
307
|
-
]
|
|
308
|
-
: [];
|
|
309
|
-
const plan = resolveAnalyzerPlan(params.root, analyzers, includedFiles);
|
|
310
|
-
const unresolved = plan.filter(needsInstallDecision);
|
|
311
|
-
if (unresolved.length > 0) {
|
|
312
|
-
const decisionsPath = join(params.artifactsDir, "incoming", "analyzer-decisions.json");
|
|
313
|
-
let decisions;
|
|
314
|
-
try {
|
|
315
|
-
decisions = await readJsonFile(decisionsPath);
|
|
316
|
-
}
|
|
317
|
-
catch (error) {
|
|
318
|
-
if (!isFileMissingError(error))
|
|
319
|
-
throw error;
|
|
320
|
-
}
|
|
321
|
-
if (decisions && typeof decisions === "object") {
|
|
322
|
-
const settings = {};
|
|
323
|
-
for (const [id, value] of Object.entries(decisions)) {
|
|
324
|
-
if (value === "ephemeral" ||
|
|
325
|
-
value === "permanent" ||
|
|
326
|
-
value === "skip" ||
|
|
327
|
-
value === "repo" ||
|
|
328
|
-
value === "auto") {
|
|
329
|
-
settings[id] = value;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
if (Object.keys(settings).length > 0) {
|
|
333
|
-
const merged = await persistAnalyzerSettings(params.artifactsDir, settings);
|
|
334
|
-
analyzers = merged.analyzers;
|
|
335
|
-
}
|
|
336
|
-
await unlink(decisionsPath).catch(() => { });
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
return {
|
|
340
|
-
kind: "analyzer_install",
|
|
341
|
-
state,
|
|
342
|
-
bundle,
|
|
343
|
-
unresolved,
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
// Phase 4B — optional edge-reasoning producing turn. Once analyzer installs
|
|
347
|
-
// are resolved, if the flag is on and the floor carries low-confidence
|
|
348
|
-
// (< 0.65) edges, emit one bounded host turn (subagent dispatch or a single
|
|
349
|
-
// host step) to produce reason rewrites, then re-run. The enrichment
|
|
350
|
-
// executor applies the host-supplied rewrites in the SAME advanceAudit call
|
|
351
|
-
// that merges analyzer edges and writes analyzer_capability, so graph_bundle
|
|
352
|
-
// and its marker stay revision-consistent (no staleness loop). Flag off or
|
|
353
|
-
// no candidates → fall through and run the executor with no rewrites.
|
|
354
|
-
if (params.graphLlmEdgeReasoning === true && bundle.graph_bundle) {
|
|
355
|
-
const candidates = collectLowConfidenceEdges(bundle.graph_bundle);
|
|
356
|
-
if (candidates.length > 0) {
|
|
357
|
-
const edgeReasoningResultsPath = join(params.artifactsDir, "incoming", "edge-reasoning.json");
|
|
358
|
-
let edgeReasoningResults;
|
|
359
|
-
try {
|
|
360
|
-
edgeReasoningResults = await readJsonFile(edgeReasoningResultsPath);
|
|
361
|
-
}
|
|
362
|
-
catch (error) {
|
|
363
|
-
if (!isFileMissingError(error))
|
|
364
|
-
throw error;
|
|
365
|
-
}
|
|
366
|
-
if (edgeReasoningResults) {
|
|
367
|
-
await runAuditStep({
|
|
368
|
-
root: params.root,
|
|
369
|
-
artifactsDir: params.artifactsDir,
|
|
370
|
-
analyzers,
|
|
371
|
-
graphLlmEdgeReasoning: true,
|
|
372
|
-
edgeReasoningResultsPath,
|
|
373
|
-
since: params.since,
|
|
374
|
-
opentoken: params.opentoken,
|
|
375
|
-
});
|
|
376
|
-
await unlink(edgeReasoningResultsPath).catch(() => { });
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
return { kind: "edge_reasoning", state, bundle, candidates };
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
// No undecided installs (and no pending edge reasoning): fall through to run
|
|
383
|
-
// the executor below (it installs for ephemeral/permanent, uses repo/cache,
|
|
384
|
-
// skips the rest).
|
|
385
|
-
}
|
|
386
|
-
if (decision.selected_executor === "design_review") {
|
|
387
|
-
const findingsPath = join(params.artifactsDir, "incoming", "design-review-findings.json");
|
|
388
|
-
let reviewFindings;
|
|
389
|
-
try {
|
|
390
|
-
reviewFindings = await readJsonFile(findingsPath);
|
|
391
|
-
}
|
|
392
|
-
catch (error) {
|
|
393
|
-
if (!isFileMissingError(error))
|
|
394
|
-
throw error;
|
|
395
|
-
}
|
|
396
|
-
if (reviewFindings && Array.isArray(reviewFindings)) {
|
|
397
|
-
const existing = bundle.design_assessment;
|
|
398
|
-
if (existing) {
|
|
399
|
-
existing.review_findings = reviewFindings;
|
|
400
|
-
existing.reviewed = true;
|
|
401
|
-
await writeJsonFile(join(params.artifactsDir, "design_assessment.json"), existing);
|
|
402
|
-
await unlink(findingsPath).catch(() => { });
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
return {
|
|
407
|
-
kind: "design_review",
|
|
408
|
-
state,
|
|
409
|
-
bundle,
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
if (decision.selected_executor === "synthesis_narrative_executor") {
|
|
413
|
-
const narrativePath = join(params.artifactsDir, "incoming", "synthesis-narrative.json");
|
|
414
|
-
let narrativeResults;
|
|
415
|
-
try {
|
|
416
|
-
narrativeResults = await readJsonFile(narrativePath);
|
|
417
|
-
}
|
|
418
|
-
catch (error) {
|
|
419
|
-
if (!isFileMissingError(error))
|
|
420
|
-
throw error;
|
|
421
|
-
}
|
|
422
|
-
if (narrativeResults) {
|
|
423
|
-
await runAuditStep({
|
|
424
|
-
root: params.root,
|
|
425
|
-
artifactsDir: params.artifactsDir,
|
|
426
|
-
preferredExecutor: "synthesis_narrative_executor",
|
|
427
|
-
narrativeResultsPath: narrativePath,
|
|
428
|
-
opentoken: params.opentoken,
|
|
429
|
-
});
|
|
430
|
-
await unlink(narrativePath).catch(() => { });
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
if (params.narrativeEnabled) {
|
|
434
|
-
return {
|
|
435
|
-
kind: "synthesis_narrative",
|
|
436
|
-
state,
|
|
437
|
-
bundle,
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
// Narrative disabled: fall through so the deterministic omit runs below.
|
|
441
|
-
}
|
|
442
|
-
if (decision.selected_executor === "agent") {
|
|
443
|
-
return {
|
|
444
|
-
kind: "semantic_review",
|
|
445
|
-
...(await ensureSemanticReviewRun({
|
|
446
|
-
root: params.root,
|
|
447
|
-
artifactsDir: params.artifactsDir,
|
|
448
|
-
bundle,
|
|
449
|
-
state,
|
|
450
|
-
obligationId: decision.selected_obligation,
|
|
451
|
-
selfCliPath: params.selfCliPath,
|
|
452
|
-
timeoutMs: params.timeoutMs,
|
|
453
|
-
})),
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
if (!decision.selected_executor) {
|
|
457
|
-
await writeHandoffOnly({
|
|
458
|
-
root: params.root,
|
|
459
|
-
artifactsDir: params.artifactsDir,
|
|
460
|
-
bundle,
|
|
461
|
-
audit_state: state,
|
|
462
|
-
progress_summary: lastSummary || decision.reason,
|
|
463
|
-
providerName: LOCAL_SUBPROCESS_PROVIDER_NAME,
|
|
464
|
-
});
|
|
465
|
-
return {
|
|
466
|
-
kind: "blocked",
|
|
467
|
-
state,
|
|
468
|
-
bundle,
|
|
469
|
-
reason: lastSummary || decision.reason,
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
let result;
|
|
473
|
-
try {
|
|
474
|
-
result = await runAuditStep({
|
|
475
|
-
root: params.root,
|
|
476
|
-
artifactsDir: params.artifactsDir,
|
|
477
|
-
analyzers,
|
|
478
|
-
graphLlmEdgeReasoning: params.graphLlmEdgeReasoning,
|
|
479
|
-
since: params.since,
|
|
480
|
-
opentoken: params.opentoken,
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
catch (error) {
|
|
484
|
-
const current = await loadArtifactBundle(params.artifactsDir);
|
|
485
|
-
const currentState = deriveAuditState(current);
|
|
486
|
-
currentState.last_executor = decision.selected_executor ?? undefined;
|
|
487
|
-
currentState.last_obligation = decision.selected_obligation ?? undefined;
|
|
488
|
-
await writeCoreArtifacts(params.artifactsDir, { ...current, audit_state: currentState });
|
|
489
|
-
await writeJsonFile(join(params.artifactsDir, "steps", "deterministic-progress.json"), {
|
|
490
|
-
iteration: index + 1,
|
|
491
|
-
max_runs: params.maxRuns,
|
|
492
|
-
last_executor: decision.selected_executor,
|
|
493
|
-
last_obligation: decision.selected_obligation,
|
|
494
|
-
prior_summary: lastSummary || null,
|
|
495
|
-
error: error instanceof Error ? error.message : String(error),
|
|
496
|
-
timestamp: new Date().toISOString(),
|
|
497
|
-
});
|
|
498
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
499
|
-
throw new Error(`Deterministic executor ${decision.selected_executor} failed on obligation ${decision.selected_obligation} (iteration ${index + 1}/${params.maxRuns}, prior progress: ${lastSummary || "none"}): ${detail}`, { cause: error instanceof Error ? error : undefined });
|
|
500
|
-
}
|
|
501
|
-
lastSummary = result.progress_summary;
|
|
502
|
-
await writeJsonFile(join(params.artifactsDir, "steps", "deterministic-progress.json"), {
|
|
503
|
-
iteration: index + 1,
|
|
504
|
-
max_runs: params.maxRuns,
|
|
505
|
-
last_executor: result.selected_executor,
|
|
506
|
-
last_obligation: decision.selected_obligation,
|
|
507
|
-
progress_made: result.progress_made,
|
|
508
|
-
summary: result.progress_summary,
|
|
509
|
-
timestamp: new Date().toISOString(),
|
|
510
|
-
});
|
|
511
|
-
if (result.selected_executor !== "agent") {
|
|
512
|
-
await clearDispatchFiles(params.artifactsDir);
|
|
513
|
-
}
|
|
514
|
-
if (!result.progress_made) {
|
|
515
|
-
return {
|
|
516
|
-
kind: "blocked",
|
|
517
|
-
state: result.audit_state,
|
|
518
|
-
bundle: result.updated_bundle,
|
|
519
|
-
reason: result.progress_summary,
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
524
|
-
const state = deriveAuditState(bundle);
|
|
525
|
-
return {
|
|
526
|
-
kind: "blocked",
|
|
527
|
-
state,
|
|
528
|
-
bundle,
|
|
529
|
-
reason: `Reached max run limit (${params.maxRuns}) before a review, report, or blocker step was ready.`,
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
// Renders the actionable semantic-review step (packet dispatch or single-task
|
|
533
|
-
// fallback) and writes steps/current-step.json. Shared by next-step and
|
|
534
|
-
// run-to-completion so the backend produces the actionable step itself rather
|
|
535
|
-
// than handing the host a second command. Host dispatch capability is resolved
|
|
536
|
-
// by the caller (flag -> session config -> env -> default true) and is never
|
|
537
|
-
// required from the host to make progress.
|
|
538
|
-
async function renderSemanticReviewStep(params) {
|
|
539
|
-
const { root, artifactsDir, activeReviewRun } = params;
|
|
540
|
-
if (!params.hostCanDispatch) {
|
|
541
|
-
const singleTaskPromptPath = join(artifactsDir, "dispatch", "current-single-task-prompt.md");
|
|
542
|
-
const workerCommand = renderCommand(activeReviewRun.worker_command);
|
|
543
|
-
return writeCurrentStep({
|
|
544
|
-
artifactsDir,
|
|
545
|
-
stepKind: "single_task_fallback",
|
|
546
|
-
status: "ready",
|
|
547
|
-
runId: activeReviewRun.run_id,
|
|
548
|
-
allowedCommands: [workerCommand],
|
|
549
|
-
stopCondition: "Run the exact worker_command after one result, then stop without looping.",
|
|
550
|
-
repoRoot: root,
|
|
551
|
-
artifactPaths: {
|
|
552
|
-
active_review_task: activeReviewRun.task_path,
|
|
553
|
-
active_review_prompt: activeReviewRun.prompt_path,
|
|
554
|
-
pending_audit_tasks: activeReviewRun.pending_audit_tasks_path ?? null,
|
|
555
|
-
audit_results: activeReviewRun.audit_results_path,
|
|
556
|
-
single_task_prompt: singleTaskPromptPath,
|
|
557
|
-
},
|
|
558
|
-
prompt: renderSingleTaskFallbackStepPrompt({
|
|
559
|
-
singleTaskPromptPath,
|
|
560
|
-
activeReviewRun,
|
|
561
|
-
}),
|
|
562
|
-
access: {
|
|
563
|
-
read_paths: [singleTaskPromptPath],
|
|
564
|
-
write_paths: [activeReviewRun.audit_results_path],
|
|
565
|
-
},
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
const sessionConfig = await loadSessionConfig(artifactsDir).catch(() => ({}));
|
|
569
|
-
const provider = createFreshSessionProvider(undefined, sessionConfig);
|
|
570
|
-
const dispatch = await prepareDispatchArtifacts({
|
|
571
|
-
packageRoot,
|
|
572
|
-
runId: activeReviewRun.run_id,
|
|
573
|
-
artifactsDir,
|
|
574
|
-
root,
|
|
575
|
-
sessionConfig,
|
|
576
|
-
hostModel: sessionConfig.block_quota?.host_model ?? null,
|
|
577
|
-
queryLimits: provider.queryLimits?.bind(provider),
|
|
578
|
-
hostActiveSubagentLimit: params.hostMaxActiveSubagents,
|
|
579
|
-
});
|
|
580
|
-
const mergeCommand = mergeAndIngestCommand(artifactsDir, activeReviewRun.run_id);
|
|
581
|
-
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
582
|
-
return writeCurrentStep({
|
|
583
|
-
artifactsDir,
|
|
584
|
-
stepKind: "dispatch_review",
|
|
585
|
-
status: "ready",
|
|
586
|
-
runId: activeReviewRun.run_id,
|
|
587
|
-
allowedCommands: [mergeCommand, continueCommand],
|
|
588
|
-
allowedMcpTools: ["auditor_merge_and_ingest", "auditor_continue_audit"],
|
|
589
|
-
progress: {
|
|
590
|
-
summary: `Dispatching ${dispatch.packet_count} review packet(s) covering ` +
|
|
591
|
-
`${dispatch.task_count} task(s) in waves of ${dispatch.wave_size}` +
|
|
592
|
-
(dispatch.skipped_task_count > 0
|
|
593
|
-
? `; ${dispatch.skipped_task_count} task(s) already completed.`
|
|
594
|
-
: "."),
|
|
595
|
-
pending_packets: dispatch.packet_count,
|
|
596
|
-
pending_tasks: dispatch.task_count,
|
|
597
|
-
completed_tasks: dispatch.skipped_task_count,
|
|
598
|
-
wave_size: dispatch.wave_size,
|
|
599
|
-
},
|
|
600
|
-
stopCondition: "Dispatch every packet, run merge-and-ingest once, then run next-step.",
|
|
601
|
-
repoRoot: root,
|
|
602
|
-
artifactPaths: {
|
|
603
|
-
dispatch_plan: dispatch.dispatch_plan_path,
|
|
604
|
-
dispatch_quota: dispatch.dispatch_quota_path,
|
|
605
|
-
dispatch_warnings: dispatch.dispatch_warnings_path,
|
|
606
|
-
active_review_task: activeReviewRun.task_path,
|
|
607
|
-
pending_audit_tasks: activeReviewRun.pending_audit_tasks_path ?? null,
|
|
608
|
-
},
|
|
609
|
-
prompt: renderDispatchReviewPrompt({
|
|
610
|
-
root,
|
|
611
|
-
artifactsDir,
|
|
612
|
-
activeReviewRun,
|
|
613
|
-
dispatchPlanPath: dispatch.dispatch_plan_path,
|
|
614
|
-
dispatchQuotaPath: dispatch.dispatch_quota_path,
|
|
615
|
-
hostCanRestrictSubagentTools: params.hostCanRestrictSubagentTools,
|
|
616
|
-
hostCanSelectSubagentModel: params.hostCanSelectSubagentModel,
|
|
617
|
-
}),
|
|
618
|
-
access: {
|
|
619
|
-
read_paths: [
|
|
620
|
-
dispatch.dispatch_plan_path,
|
|
621
|
-
...(dispatch.dispatch_quota_path ? [dispatch.dispatch_quota_path] : []),
|
|
622
|
-
],
|
|
623
|
-
write_paths: [],
|
|
624
|
-
},
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
async function cmdNextStep(argv) {
|
|
628
|
-
const root = getRootDir(argv);
|
|
629
|
-
warnIfNotGitRepo(root);
|
|
630
|
-
const artifactsDir = getArtifactsDir(argv);
|
|
631
|
-
await mkdir(artifactsDir, { recursive: true });
|
|
632
|
-
await ensureSupervisorDirs(artifactsDir);
|
|
633
|
-
const hostCanDispatchSubagents = getOptionalBooleanFlag(argv, "--host-can-dispatch-subagents");
|
|
634
|
-
const hostCanRestrictSubagentTools = getOptionalBooleanFlag(argv, "--host-can-restrict-subagent-tools") ??
|
|
635
|
-
false;
|
|
636
|
-
const hostCanSelectSubagentModel = getOptionalBooleanFlag(argv, "--host-can-select-subagent-model") ?? false;
|
|
637
|
-
const hostMaxActiveSubagents = getHostMaxActiveSubagents(argv);
|
|
638
|
-
let sessionConfig;
|
|
639
|
-
try {
|
|
640
|
-
sessionConfig = await loadSessionConfig(artifactsDir);
|
|
641
|
-
}
|
|
642
|
-
catch (error) {
|
|
643
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
644
|
-
await persistConfigErrorHandoff({
|
|
645
|
-
root,
|
|
646
|
-
artifactsDir,
|
|
647
|
-
progressSummary: reason,
|
|
648
|
-
});
|
|
649
|
-
const step = await writeCurrentStep({
|
|
650
|
-
artifactsDir,
|
|
651
|
-
stepKind: "blocked",
|
|
652
|
-
status: "blocked",
|
|
653
|
-
runId: null,
|
|
654
|
-
allowedCommands: [],
|
|
655
|
-
stopCondition: "Report the configuration blocker and stop.",
|
|
656
|
-
repoRoot: root,
|
|
657
|
-
artifactPaths: {
|
|
658
|
-
operator_handoff: join(artifactsDir, "operator-handoff.json"),
|
|
659
|
-
},
|
|
660
|
-
prompt: renderBlockedStepPrompt(reason),
|
|
661
|
-
});
|
|
662
|
-
console.log(JSON.stringify(step, null, 2));
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
const hostCanDispatch = resolveHostDispatchCapability({
|
|
666
|
-
explicit: hostCanDispatchSubagents,
|
|
667
|
-
sessionConfig,
|
|
668
|
-
});
|
|
669
|
-
const result = await runDeterministicForNextStep({
|
|
670
|
-
root,
|
|
671
|
-
artifactsDir,
|
|
672
|
-
selfCliPath: resolve(argv[1] ?? process.argv[1] ?? ""),
|
|
673
|
-
timeoutMs: getTimeoutMs(argv, sessionConfig),
|
|
674
|
-
maxRuns: getMaxRuns(argv),
|
|
675
|
-
opentoken: sessionConfig.opentoken?.enabled,
|
|
676
|
-
narrativeEnabled: sessionConfig.synthesis?.narrative !== false,
|
|
677
|
-
analyzers: sessionConfig.analyzers,
|
|
678
|
-
graphLlmEdgeReasoning: sessionConfig.graph?.llm_edge_reasoning,
|
|
679
|
-
since: getFlag(argv, "--since"),
|
|
680
|
-
});
|
|
681
|
-
if (result.kind === "complete") {
|
|
682
|
-
const step = await writeCurrentStep({
|
|
683
|
-
artifactsDir,
|
|
684
|
-
stepKind: "present_report",
|
|
685
|
-
status: "complete",
|
|
686
|
-
runId: null,
|
|
687
|
-
allowedCommands: [],
|
|
688
|
-
stopCondition: "Present the final report and stop.",
|
|
689
|
-
repoRoot: root,
|
|
690
|
-
artifactPaths: {
|
|
691
|
-
final_report: result.finalReportPath,
|
|
692
|
-
},
|
|
693
|
-
prompt: renderPresentReportPrompt(result.finalReportPath),
|
|
694
|
-
});
|
|
695
|
-
console.log(JSON.stringify(step, null, 2));
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
if (result.kind === "blocked") {
|
|
699
|
-
const step = await writeCurrentStep({
|
|
700
|
-
artifactsDir,
|
|
701
|
-
stepKind: "blocked",
|
|
702
|
-
status: "blocked",
|
|
703
|
-
runId: null,
|
|
704
|
-
allowedCommands: [],
|
|
705
|
-
stopCondition: "Report the blocker and stop.",
|
|
706
|
-
repoRoot: root,
|
|
707
|
-
artifactPaths: {
|
|
708
|
-
operator_handoff: join(artifactsDir, "operator-handoff.json"),
|
|
709
|
-
},
|
|
710
|
-
prompt: renderBlockedStepPrompt(result.reason),
|
|
711
|
-
});
|
|
712
|
-
console.log(JSON.stringify(step, null, 2));
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
if (result.kind === "design_review") {
|
|
716
|
-
const designReviewResultsPath = join(artifactsDir, "incoming", "design-review-findings.json");
|
|
717
|
-
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
718
|
-
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
719
|
-
const prompt = renderDesignReviewPrompt(result.bundle);
|
|
720
|
-
const fullPrompt = [
|
|
721
|
-
prompt,
|
|
722
|
-
"## Results path",
|
|
723
|
-
"",
|
|
724
|
-
`Write the JSON array of findings to:`,
|
|
725
|
-
"",
|
|
726
|
-
` ${designReviewResultsPath}`,
|
|
727
|
-
"",
|
|
728
|
-
`Then run: ${continueCommand}`,
|
|
729
|
-
"",
|
|
730
|
-
].join("\n");
|
|
731
|
-
const step = await writeCurrentStep({
|
|
732
|
-
artifactsDir,
|
|
733
|
-
stepKind: "design_review",
|
|
734
|
-
status: "ready",
|
|
735
|
-
runId: null,
|
|
736
|
-
allowedCommands: [continueCommand],
|
|
737
|
-
stopCondition: "Write design review findings to the results path, then run next-step.",
|
|
738
|
-
repoRoot: root,
|
|
739
|
-
artifactPaths: {
|
|
740
|
-
design_review_results: designReviewResultsPath,
|
|
741
|
-
},
|
|
742
|
-
prompt: fullPrompt,
|
|
743
|
-
});
|
|
744
|
-
console.log(JSON.stringify(step, null, 2));
|
|
745
|
-
return;
|
|
746
|
-
}
|
|
747
|
-
if (result.kind === "analyzer_install") {
|
|
748
|
-
const decisionsPath = join(artifactsDir, "incoming", "analyzer-decisions.json");
|
|
749
|
-
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
750
|
-
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
751
|
-
const step = await writeCurrentStep({
|
|
752
|
-
artifactsDir,
|
|
753
|
-
stepKind: "analyzer_install",
|
|
754
|
-
status: "ready",
|
|
755
|
-
runId: null,
|
|
756
|
-
allowedCommands: [continueCommand],
|
|
757
|
-
stopCondition: "Write analyzer install decisions to the results path, then run next-step.",
|
|
758
|
-
repoRoot: root,
|
|
759
|
-
artifactPaths: {
|
|
760
|
-
analyzer_decisions: decisionsPath,
|
|
761
|
-
},
|
|
762
|
-
prompt: renderAnalyzerInstallPrompt({
|
|
763
|
-
unresolved: result.unresolved,
|
|
764
|
-
decisionsPath,
|
|
765
|
-
continueCommand,
|
|
766
|
-
}),
|
|
767
|
-
});
|
|
768
|
-
console.log(JSON.stringify(step, null, 2));
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
if (result.kind === "edge_reasoning") {
|
|
772
|
-
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
773
|
-
const edgeReasoningResultsPath = join(artifactsDir, "incoming", "edge-reasoning.json");
|
|
774
|
-
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
775
|
-
const basePrompt = buildEdgeReasoningPrompt(result.candidates);
|
|
776
|
-
const contentHash = edgeReasoningContentHash(result.candidates);
|
|
777
|
-
if (hostCanDispatch) {
|
|
778
|
-
// Dispatch path: isolate the (potentially large) edge-list prompt in a file
|
|
779
|
-
// and have the host fan it out to one subagent, mirroring the packet review
|
|
780
|
-
// dispatch contract. The subagent writes the rewrites file; next-step applies.
|
|
781
|
-
const edgeReasoningPromptPath = join(artifactsDir, "incoming", "edge-reasoning-prompt.md");
|
|
782
|
-
await writeFile(edgeReasoningPromptPath, basePrompt, "utf8");
|
|
783
|
-
const step = await writeCurrentStep({
|
|
784
|
-
artifactsDir,
|
|
785
|
-
stepKind: "edge_reasoning_dispatch",
|
|
786
|
-
status: "ready",
|
|
787
|
-
runId: null,
|
|
788
|
-
allowedCommands: [continueCommand],
|
|
789
|
-
stopCondition: "Dispatch one subagent to write the edge-reasoning rewrites, then run next-step.",
|
|
790
|
-
repoRoot: root,
|
|
791
|
-
artifactPaths: {
|
|
792
|
-
edge_reasoning_prompt: edgeReasoningPromptPath,
|
|
793
|
-
edge_reasoning_results: edgeReasoningResultsPath,
|
|
794
|
-
},
|
|
795
|
-
prompt: renderEdgeReasoningDispatchPrompt({
|
|
796
|
-
promptPath: edgeReasoningPromptPath,
|
|
797
|
-
resultsPath: edgeReasoningResultsPath,
|
|
798
|
-
continueCommand,
|
|
799
|
-
contentHash,
|
|
800
|
-
candidateCount: result.candidates.length,
|
|
801
|
-
}),
|
|
802
|
-
access: {
|
|
803
|
-
read_paths: [edgeReasoningPromptPath],
|
|
804
|
-
write_paths: [edgeReasoningResultsPath],
|
|
805
|
-
},
|
|
806
|
-
});
|
|
807
|
-
console.log(JSON.stringify(step, null, 2));
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
// One-step fallback (no callable subagent facility): the host produces the
|
|
811
|
-
// rewrites itself in a single bounded turn, mirroring the narrative step.
|
|
812
|
-
const step = await writeCurrentStep({
|
|
813
|
-
artifactsDir,
|
|
814
|
-
stepKind: "edge_reasoning",
|
|
815
|
-
status: "ready",
|
|
816
|
-
runId: null,
|
|
817
|
-
allowedCommands: [continueCommand],
|
|
818
|
-
stopCondition: "Write the edge-reasoning rewrites to the results path, then run next-step.",
|
|
819
|
-
repoRoot: root,
|
|
820
|
-
artifactPaths: {
|
|
821
|
-
edge_reasoning_results: edgeReasoningResultsPath,
|
|
822
|
-
},
|
|
823
|
-
prompt: renderEdgeReasoningStepPrompt({
|
|
824
|
-
basePrompt,
|
|
825
|
-
resultsPath: edgeReasoningResultsPath,
|
|
826
|
-
continueCommand,
|
|
827
|
-
contentHash,
|
|
828
|
-
}),
|
|
829
|
-
access: {
|
|
830
|
-
read_paths: [],
|
|
831
|
-
write_paths: [edgeReasoningResultsPath],
|
|
832
|
-
},
|
|
833
|
-
});
|
|
834
|
-
console.log(JSON.stringify(step, null, 2));
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
if (result.kind === "synthesis_narrative") {
|
|
838
|
-
const narrativeResultsPath = join(artifactsDir, "incoming", "synthesis-narrative.json");
|
|
839
|
-
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
840
|
-
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
841
|
-
const basePrompt = result.bundle.audit_findings
|
|
842
|
-
? renderSynthesisNarrativePrompt(result.bundle.audit_findings)
|
|
843
|
-
: "# Synthesis narrative\n\nNo findings report is available; write an empty themes array.";
|
|
844
|
-
const fullPrompt = [
|
|
845
|
-
basePrompt,
|
|
846
|
-
"## Results path",
|
|
847
|
-
"",
|
|
848
|
-
"Write the SynthesisNarrative JSON object to:",
|
|
849
|
-
"",
|
|
850
|
-
` ${narrativeResultsPath}`,
|
|
851
|
-
"",
|
|
852
|
-
`Then run: ${continueCommand}`,
|
|
853
|
-
"",
|
|
854
|
-
].join("\n");
|
|
855
|
-
const step = await writeCurrentStep({
|
|
856
|
-
artifactsDir,
|
|
857
|
-
stepKind: "synthesis_narrative",
|
|
858
|
-
status: "ready",
|
|
859
|
-
runId: null,
|
|
860
|
-
allowedCommands: [continueCommand],
|
|
861
|
-
stopCondition: "Write the synthesis narrative to the results path, then run next-step.",
|
|
862
|
-
repoRoot: root,
|
|
863
|
-
artifactPaths: {
|
|
864
|
-
synthesis_narrative_results: narrativeResultsPath,
|
|
865
|
-
},
|
|
866
|
-
prompt: fullPrompt,
|
|
867
|
-
});
|
|
868
|
-
console.log(JSON.stringify(step, null, 2));
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
|
-
const step = await renderSemanticReviewStep({
|
|
872
|
-
root,
|
|
873
|
-
artifactsDir,
|
|
874
|
-
activeReviewRun: result.activeReviewRun,
|
|
875
|
-
hostCanDispatch,
|
|
876
|
-
hostMaxActiveSubagents,
|
|
877
|
-
hostCanRestrictSubagentTools,
|
|
878
|
-
hostCanSelectSubagentModel,
|
|
879
|
-
});
|
|
880
|
-
console.log(JSON.stringify(step, null, 2));
|
|
881
|
-
}
|
|
882
|
-
async function cmdRunToCompletion(argv) {
|
|
883
|
-
const root = getRootDir(argv);
|
|
884
|
-
warnIfNotGitRepo(root);
|
|
885
|
-
const artifactsDir = getArtifactsDir(argv);
|
|
886
|
-
await cleanupStaleArtifactsDir(artifactsDir);
|
|
887
|
-
await mkdir(artifactsDir, { recursive: true });
|
|
888
|
-
await ensureSupervisorDirs(artifactsDir);
|
|
889
|
-
let sessionConfig;
|
|
890
|
-
try {
|
|
891
|
-
sessionConfig = await loadSessionConfig(artifactsDir);
|
|
892
|
-
}
|
|
893
|
-
catch (error) {
|
|
894
|
-
await persistConfigErrorHandoff({
|
|
895
|
-
root,
|
|
896
|
-
artifactsDir,
|
|
897
|
-
progressSummary: error instanceof Error ? error.message : String(error),
|
|
898
|
-
});
|
|
899
|
-
throw error;
|
|
900
|
-
}
|
|
901
|
-
const explicitProvider = getExplicitProvider(argv);
|
|
902
|
-
const provider = createFreshSessionProvider(explicitProvider, sessionConfig);
|
|
903
|
-
const uiMode = getUiMode(argv, sessionConfig.ui_mode ?? "headless");
|
|
904
|
-
const maxRuns = getMaxRuns(argv);
|
|
905
|
-
const agentBatchSize = getAgentBatchSize(argv, sessionConfig);
|
|
906
|
-
const parallelWorkers = getParallelWorkers(argv, sessionConfig);
|
|
907
|
-
const timeoutMs = getTimeoutMs(argv, sessionConfig);
|
|
908
|
-
const hostModel = getHostModel(argv);
|
|
909
|
-
const selfCliPath = resolve(argv[1] ?? process.argv[1] ?? "");
|
|
910
|
-
const batchResultsDir = getBatchResultsDir(argv);
|
|
911
|
-
if (batchResultsDir && getFlag(argv, "--results")) {
|
|
912
|
-
throw new Error("Use either --results <file> or --batch-results <dir>, not both.");
|
|
913
|
-
}
|
|
914
|
-
let pendingBatchAuditResults = batchResultsDir
|
|
915
|
-
? await listBatchResultFiles(batchResultsDir)
|
|
916
|
-
: [];
|
|
917
|
-
let pendingAuditResultsPath = getFlag(argv, "--results");
|
|
918
|
-
let pendingRuntimeUpdatesPath = getFlag(argv, "--updates");
|
|
919
|
-
let pendingExternalAnalyzerPath = getFlag(argv, "--external-analyzer-results");
|
|
920
|
-
let runCount = 0;
|
|
921
|
-
let deepeningCycles = 0;
|
|
922
|
-
const MAX_DEEPENING_CYCLES = 3;
|
|
923
|
-
let anyProgress = false;
|
|
924
|
-
let lastResult = null;
|
|
925
|
-
const artifactsWritten = new Set();
|
|
926
|
-
while (runCount < maxRuns) {
|
|
927
|
-
const bundle = await loadArtifactBundle(artifactsDir);
|
|
928
|
-
const decision = decideNextStep(bundle);
|
|
929
|
-
// Resume interrupted parallel wave: ingest any results that workers
|
|
930
|
-
// wrote before the previous process exited.
|
|
931
|
-
const priorWave = await readWaveManifest(artifactsDir);
|
|
932
|
-
if (priorWave) {
|
|
933
|
-
process.stderr.write(`[audit-code] Recovering interrupted wave (${priorWave.slots.length} slot(s), obligation ${priorWave.obligation_id}).\n`);
|
|
934
|
-
let recoveredProgress = false;
|
|
935
|
-
for (const entry of priorWave.slots) {
|
|
936
|
-
try {
|
|
937
|
-
const results = await readJsonFile(entry.audit_results_path);
|
|
938
|
-
if (!results || results.length === 0)
|
|
939
|
-
continue;
|
|
940
|
-
const stepResult = await runAuditStep({
|
|
941
|
-
root,
|
|
942
|
-
artifactsDir,
|
|
943
|
-
preferredExecutor: "result_ingestion_executor",
|
|
944
|
-
auditResultsPath: entry.audit_results_path,
|
|
945
|
-
});
|
|
946
|
-
if (stepResult.progress_made) {
|
|
947
|
-
recoveredProgress = true;
|
|
948
|
-
anyProgress = true;
|
|
949
|
-
for (const a of stepResult.artifacts_written)
|
|
950
|
-
artifactsWritten.add(a);
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
catch {
|
|
954
|
-
process.stderr.write(`[audit-code] Skipping unreadable results for ${entry.run_id}.\n`);
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
await removeWaveManifest(artifactsDir);
|
|
958
|
-
if (recoveredProgress)
|
|
959
|
-
continue;
|
|
960
|
-
}
|
|
961
|
-
if (decision.selected_executor === "agent" &&
|
|
962
|
-
bundle.audit_tasks?.some((t) => t.tags?.includes("selective_deepening") &&
|
|
963
|
-
t.status !== "complete") &&
|
|
964
|
-
!bundle.audit_tasks?.some((t) => !t.tags?.includes("selective_deepening") &&
|
|
965
|
-
t.status !== "complete")) {
|
|
966
|
-
deepeningCycles++;
|
|
967
|
-
if (deepeningCycles > MAX_DEEPENING_CYCLES) {
|
|
968
|
-
process.stderr.write(`[audit-code] Reached max deepening cycles (${MAX_DEEPENING_CYCLES}). Stopping to prevent churn.\n`);
|
|
969
|
-
break;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
let preferredExecutor = decision.selected_executor;
|
|
973
|
-
let obligationId = decision.selected_obligation;
|
|
974
|
-
let auditResultsPath;
|
|
975
|
-
let runtimeUpdatesPath;
|
|
976
|
-
let externalAnalyzerPath;
|
|
977
|
-
if (pendingExternalAnalyzerPath) {
|
|
978
|
-
preferredExecutor = "external_analyzer_import_executor";
|
|
979
|
-
obligationId = "external_analyzer_import";
|
|
980
|
-
externalAnalyzerPath = pendingExternalAnalyzerPath;
|
|
981
|
-
}
|
|
982
|
-
else if (pendingBatchAuditResults.length > 0 && bundle.coverage_matrix) {
|
|
983
|
-
preferredExecutor = "result_ingestion_executor";
|
|
984
|
-
obligationId = "audit_results_ingested";
|
|
985
|
-
auditResultsPath = pendingBatchAuditResults[0];
|
|
986
|
-
}
|
|
987
|
-
else if (pendingAuditResultsPath && bundle.coverage_matrix) {
|
|
988
|
-
preferredExecutor = "result_ingestion_executor";
|
|
989
|
-
obligationId = "audit_results_ingested";
|
|
990
|
-
auditResultsPath = pendingAuditResultsPath;
|
|
991
|
-
}
|
|
992
|
-
else if (pendingRuntimeUpdatesPath && bundle.runtime_validation_tasks) {
|
|
993
|
-
preferredExecutor = "runtime_validation_update_executor";
|
|
994
|
-
obligationId = "runtime_validation_current";
|
|
995
|
-
runtimeUpdatesPath = pendingRuntimeUpdatesPath;
|
|
996
|
-
}
|
|
997
|
-
if (preferredExecutor === "agent" && provider.name === LOCAL_SUBPROCESS_PROVIDER_NAME) {
|
|
998
|
-
const blocker = buildManualReviewBlocker(provider.name);
|
|
999
|
-
const blockedState = buildBlockedAuditState({
|
|
1000
|
-
state: decision.state,
|
|
1001
|
-
obligationId,
|
|
1002
|
-
executor: preferredExecutor,
|
|
1003
|
-
blocker,
|
|
1004
|
-
});
|
|
1005
|
-
await writeCoreArtifacts(artifactsDir, {
|
|
1006
|
-
...bundle,
|
|
1007
|
-
audit_state: blockedState,
|
|
1008
|
-
});
|
|
1009
|
-
const blockRunId = buildRunId(obligationId, runCount + 1);
|
|
1010
|
-
const blockPaths = getRunPaths(artifactsDir, blockRunId);
|
|
1011
|
-
const blockPendingTasks = await addFileLineCountHints(root, buildPendingAuditTasks(bundle));
|
|
1012
|
-
const blockPendingTasksPath = join(blockPaths.runDir, "pending-audit-tasks.json");
|
|
1013
|
-
const blockAuditResultsPath = join(blockPaths.runDir, "audit-results.json");
|
|
1014
|
-
const blockReadPaths = new Set();
|
|
1015
|
-
for (const pt of blockPendingTasks) {
|
|
1016
|
-
for (const fp of pt.file_paths)
|
|
1017
|
-
blockReadPaths.add(fp);
|
|
1018
|
-
}
|
|
1019
|
-
const blockTask = {
|
|
1020
|
-
contract_version: "audit-code-worker/v1alpha1",
|
|
1021
|
-
run_id: blockRunId,
|
|
1022
|
-
repo_root: root,
|
|
1023
|
-
artifacts_dir: artifactsDir,
|
|
1024
|
-
obligation_id: obligationId,
|
|
1025
|
-
preferred_executor: preferredExecutor,
|
|
1026
|
-
result_path: blockPaths.resultPath,
|
|
1027
|
-
worker_command: [
|
|
1028
|
-
process.execPath,
|
|
1029
|
-
selfCliPath,
|
|
1030
|
-
"worker-run",
|
|
1031
|
-
"--task",
|
|
1032
|
-
blockPaths.taskPath,
|
|
1033
|
-
],
|
|
1034
|
-
audit_results_path: blockAuditResultsPath,
|
|
1035
|
-
pending_audit_tasks_path: blockPendingTasksPath,
|
|
1036
|
-
timeout_ms: timeoutMs,
|
|
1037
|
-
max_retries: 0,
|
|
1038
|
-
access: {
|
|
1039
|
-
read_paths: [...blockReadPaths],
|
|
1040
|
-
write_paths: [blockAuditResultsPath, blockPaths.resultPath],
|
|
1041
|
-
},
|
|
1042
|
-
};
|
|
1043
|
-
const blockPrompt = renderWorkerPrompt(blockTask);
|
|
1044
|
-
await writeWorkerTaskFiles(blockTask, blockPrompt, blockPaths, artifactsDir, blockPendingTasks);
|
|
1045
|
-
await writeJsonFile(blockPendingTasksPath, blockPendingTasks);
|
|
1046
|
-
const reviewRun = {
|
|
1047
|
-
run_id: blockRunId,
|
|
1048
|
-
task_path: blockPaths.taskPath,
|
|
1049
|
-
prompt_path: blockPaths.promptPath,
|
|
1050
|
-
pending_audit_tasks_path: blockPendingTasksPath,
|
|
1051
|
-
audit_results_path: blockAuditResultsPath,
|
|
1052
|
-
worker_command: blockTask.worker_command,
|
|
1053
|
-
};
|
|
1054
|
-
// Render the actionable dispatch / single-task step here instead of
|
|
1055
|
-
// leaving the host to issue next-step as a second command. Capability is
|
|
1056
|
-
// resolved from flags/config/env with a sane default, so nothing is
|
|
1057
|
-
// required from the host to make progress. If rendering fails we still
|
|
1058
|
-
// emit the hand-off below — run-to-completion is never worse than before,
|
|
1059
|
-
// and next-step will re-render and surface the error loudly.
|
|
1060
|
-
try {
|
|
1061
|
-
await renderSemanticReviewStep({
|
|
1062
|
-
root,
|
|
1063
|
-
artifactsDir,
|
|
1064
|
-
activeReviewRun: reviewRun,
|
|
1065
|
-
hostCanDispatch: resolveHostDispatchCapability({
|
|
1066
|
-
explicit: getOptionalBooleanFlag(argv, "--host-can-dispatch-subagents"),
|
|
1067
|
-
sessionConfig,
|
|
1068
|
-
}),
|
|
1069
|
-
hostMaxActiveSubagents: getHostMaxActiveSubagents(argv),
|
|
1070
|
-
hostCanRestrictSubagentTools: getOptionalBooleanFlag(argv, "--host-can-restrict-subagent-tools") ?? false,
|
|
1071
|
-
hostCanSelectSubagentModel: getOptionalBooleanFlag(argv, "--host-can-select-subagent-model") ?? false,
|
|
1072
|
-
});
|
|
1073
|
-
}
|
|
1074
|
-
catch (stepError) {
|
|
1075
|
-
process.stderr.write(`[audit-code] Could not pre-render the review step; the operator hand-off points to next-step instead. ${stepError instanceof Error ? stepError.message : String(stepError)}\n`);
|
|
1076
|
-
}
|
|
1077
|
-
await emitEnvelope({
|
|
1078
|
-
root,
|
|
1079
|
-
artifactsDir,
|
|
1080
|
-
bundle: {
|
|
1081
|
-
...bundle,
|
|
1082
|
-
audit_state: blockedState,
|
|
1083
|
-
},
|
|
1084
|
-
audit_state: blockedState,
|
|
1085
|
-
selected_obligation: obligationId,
|
|
1086
|
-
selected_executor: preferredExecutor,
|
|
1087
|
-
progress_made: anyProgress,
|
|
1088
|
-
artifacts_written: Array.from(new Set([...artifactsWritten, "audit_state.json"])),
|
|
1089
|
-
progress_summary: blocker,
|
|
1090
|
-
next_likely_step: null,
|
|
1091
|
-
providerName: provider.name,
|
|
1092
|
-
activeReviewRun: reviewRun,
|
|
1093
|
-
});
|
|
1094
|
-
return;
|
|
1095
|
-
}
|
|
1096
|
-
if (!preferredExecutor) {
|
|
1097
|
-
const state = decision.state;
|
|
1098
|
-
await clearDispatchFiles(artifactsDir);
|
|
1099
|
-
await emitEnvelope({
|
|
1100
|
-
root,
|
|
1101
|
-
artifactsDir,
|
|
1102
|
-
bundle,
|
|
1103
|
-
audit_state: state,
|
|
1104
|
-
selected_obligation: anyProgress
|
|
1105
|
-
? (lastResult?.obligation_id ?? null)
|
|
1106
|
-
: null,
|
|
1107
|
-
selected_executor: anyProgress
|
|
1108
|
-
? (lastResult?.selected_executor ?? null)
|
|
1109
|
-
: null,
|
|
1110
|
-
progress_made: anyProgress,
|
|
1111
|
-
artifacts_written: Array.from(artifactsWritten),
|
|
1112
|
-
progress_summary: anyProgress && state.status === "complete"
|
|
1113
|
-
? `Completed audit in ${runCount} fresh worker runs.`
|
|
1114
|
-
: decision.reason,
|
|
1115
|
-
next_likely_step: state.status === "complete" ? null : decision.selected_obligation,
|
|
1116
|
-
providerName: provider.name,
|
|
1117
|
-
});
|
|
1118
|
-
if (state.status === "complete") {
|
|
1119
|
-
await promoteFinalAuditReport({ artifactsDir, repoRoot: root });
|
|
1120
|
-
}
|
|
1121
|
-
return;
|
|
1122
|
-
}
|
|
1123
|
-
if (preferredExecutor === "agent" && parallelWorkers > 1) {
|
|
1124
|
-
const quotaState = await readQuotaState();
|
|
1125
|
-
const providerModelKey = buildProviderModelKey(provider.name, hostModel);
|
|
1126
|
-
const quotaStateEntry = quotaState.entries[providerModelKey] ?? null;
|
|
1127
|
-
const allCandidateTasks = buildPendingAuditTasks(bundle);
|
|
1128
|
-
const candidateGroups = chunkArray(allCandidateTasks.slice(0, parallelWorkers * agentBatchSize), agentBatchSize);
|
|
1129
|
-
const candidateSizeIndex = sizeIndexFromManifest(bundle.repo_manifest);
|
|
1130
|
-
const slotTokenEstimates = candidateGroups.map((g) => estimateTaskGroupTokens(g, candidateSizeIndex));
|
|
1131
|
-
const providerLimits = await provider.queryLimits?.(hostModel)
|
|
1132
|
-
.then((r) => r ? { ...r, source: "provider_query" } : null)
|
|
1133
|
-
.catch(() => null)
|
|
1134
|
-
?? null;
|
|
1135
|
-
const cachedLimits = await lookupDiscoveredLimits(providerModelKey).catch(() => null);
|
|
1136
|
-
const discoveredLimits = mergeDiscoveredLimits(providerLimits, cachedLimits);
|
|
1137
|
-
const halfLifeHours = sessionConfig.quota?.empirical_half_life_hours ?? 24;
|
|
1138
|
-
const quotaSource = buildQuotaSource({ halfLifeHours });
|
|
1139
|
-
const quotaSourceSnapshot = await quotaSource.queryCurrentUsage(providerModelKey).catch(() => null);
|
|
1140
|
-
const hostConcurrencyLimit = resolveHostActiveSubagentLimit({
|
|
1141
|
-
sessionConfig,
|
|
1142
|
-
});
|
|
1143
|
-
const waveSchedule = scheduleWave({
|
|
1144
|
-
providerName: resolveFreshSessionProviderName(getExplicitProvider(argv), sessionConfig),
|
|
1145
|
-
sessionConfig,
|
|
1146
|
-
hostModel,
|
|
1147
|
-
requestedConcurrency: parallelWorkers,
|
|
1148
|
-
estimatedSlotTokens: slotTokenEstimates,
|
|
1149
|
-
quotaStateEntry,
|
|
1150
|
-
hostConcurrencyLimit,
|
|
1151
|
-
quotaSourceSnapshot,
|
|
1152
|
-
discoveredLimits,
|
|
1153
|
-
});
|
|
1154
|
-
const waveSize = waveSchedule.wave_size;
|
|
1155
|
-
if (waveSchedule.cooldown_until) {
|
|
1156
|
-
const waitMs = new Date(waveSchedule.cooldown_until).getTime() - Date.now();
|
|
1157
|
-
if (waitMs > 0) {
|
|
1158
|
-
const cappedWait = Math.min(waitMs, 120_000);
|
|
1159
|
-
process.stderr.write(`[quota] Cooldown active — waiting ${Math.ceil(cappedWait / 1000)}s before next wave.\n`);
|
|
1160
|
-
await new Promise((r) => setTimeout(r, cappedWait));
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
const taskGroups = candidateGroups.slice(0, waveSize);
|
|
1164
|
-
const workerSlots = [];
|
|
1165
|
-
for (const rawGroup of taskGroups) {
|
|
1166
|
-
const group = await addFileLineCountHints(root, rawGroup);
|
|
1167
|
-
runCount += 1;
|
|
1168
|
-
const slotRunId = buildRunId(obligationId, runCount);
|
|
1169
|
-
const slotPaths = getRunPaths(artifactsDir, slotRunId);
|
|
1170
|
-
const slotAuditResultsPath = join(slotPaths.runDir, "audit-results.json");
|
|
1171
|
-
const slotPendingTasksPath = join(slotPaths.runDir, "pending-audit-tasks.json");
|
|
1172
|
-
const slotReadPaths = new Set();
|
|
1173
|
-
for (const t of group) {
|
|
1174
|
-
for (const fp of t.file_paths)
|
|
1175
|
-
slotReadPaths.add(fp);
|
|
1176
|
-
}
|
|
1177
|
-
const slotTask = {
|
|
1178
|
-
contract_version: "audit-code-worker/v1alpha1",
|
|
1179
|
-
run_id: slotRunId,
|
|
1180
|
-
repo_root: root,
|
|
1181
|
-
artifacts_dir: artifactsDir,
|
|
1182
|
-
obligation_id: obligationId,
|
|
1183
|
-
preferred_executor: "agent",
|
|
1184
|
-
result_path: slotPaths.resultPath,
|
|
1185
|
-
worker_command: [process.execPath, selfCliPath, "worker-run", "--task", slotPaths.taskPath],
|
|
1186
|
-
audit_results_path: slotAuditResultsPath,
|
|
1187
|
-
pending_audit_tasks_path: slotPendingTasksPath,
|
|
1188
|
-
worker_command_mode: "deferred",
|
|
1189
|
-
timeout_ms: timeoutMs,
|
|
1190
|
-
max_retries: 0,
|
|
1191
|
-
access: {
|
|
1192
|
-
read_paths: [...slotReadPaths],
|
|
1193
|
-
write_paths: [slotAuditResultsPath, slotPaths.resultPath],
|
|
1194
|
-
},
|
|
1195
|
-
};
|
|
1196
|
-
const slotPrompt = renderWorkerPrompt(slotTask);
|
|
1197
|
-
await writeWorkerTaskFiles(slotTask, slotPrompt, slotPaths, artifactsDir, group, { updateDispatch: false });
|
|
1198
|
-
await writeJsonFile(slotPendingTasksPath, group);
|
|
1199
|
-
workerSlots.push({ runId: slotRunId, paths: slotPaths, auditResultsPath: slotAuditResultsPath, pendingTasksPath: slotPendingTasksPath, group });
|
|
1200
|
-
}
|
|
1201
|
-
await writeDispatchBatchFiles(artifactsDir, workerSlots.map((slot) => ({
|
|
1202
|
-
run_id: slot.runId,
|
|
1203
|
-
task_path: slot.paths.taskPath,
|
|
1204
|
-
prompt_path: slot.paths.promptPath,
|
|
1205
|
-
result_path: slot.paths.resultPath,
|
|
1206
|
-
status_path: slot.paths.statusPath,
|
|
1207
|
-
audit_results_path: slot.auditResultsPath,
|
|
1208
|
-
pending_audit_tasks_path: slot.pendingTasksPath,
|
|
1209
|
-
})), workerSlots.flatMap((slot) => slot.group));
|
|
1210
|
-
const parallelStartedAt = new Date().toISOString();
|
|
1211
|
-
await writeWaveManifest(artifactsDir, {
|
|
1212
|
-
obligation_id: obligationId ?? "unknown",
|
|
1213
|
-
started_at: parallelStartedAt,
|
|
1214
|
-
pid: process.pid,
|
|
1215
|
-
slots: workerSlots.map(buildWaveSlotEntry),
|
|
1216
|
-
});
|
|
1217
|
-
const { results: launchResults } = await runSlidingWindow(workerSlots.map((slot) => () => provider.launch({
|
|
1218
|
-
repoRoot: root,
|
|
1219
|
-
runId: slot.runId,
|
|
1220
|
-
obligationId,
|
|
1221
|
-
promptPath: slot.paths.promptPath,
|
|
1222
|
-
taskPath: slot.paths.taskPath,
|
|
1223
|
-
resultPath: slot.paths.resultPath,
|
|
1224
|
-
stdoutPath: slot.paths.stdoutPath,
|
|
1225
|
-
stderrPath: slot.paths.stderrPath,
|
|
1226
|
-
uiMode,
|
|
1227
|
-
timeoutMs,
|
|
1228
|
-
})), waveSize);
|
|
1229
|
-
const launchErrorsByRunId = new Map();
|
|
1230
|
-
for (let index = 0; index < launchResults.length; index++) {
|
|
1231
|
-
const outcome = launchResults[index];
|
|
1232
|
-
if (outcome?.status === "rejected") {
|
|
1233
|
-
launchErrorsByRunId.set(workerSlots[index].runId, outcome.reason instanceof Error
|
|
1234
|
-
? outcome.reason.message
|
|
1235
|
-
: String(outcome.reason));
|
|
1236
|
-
}
|
|
1237
|
-
else if (outcome?.status === "fulfilled") {
|
|
1238
|
-
const launchExitSummary = summarizeLaunchExit(outcome.value);
|
|
1239
|
-
if (launchExitSummary) {
|
|
1240
|
-
launchErrorsByRunId.set(workerSlots[index].runId, launchExitSummary);
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
// Result ingestion is intentionally sequential even though agent launch
|
|
1245
|
-
// was parallel. Writing to coverage_matrix.json is not atomic, so
|
|
1246
|
-
// concurrent ingest calls would race and corrupt coverage state.
|
|
1247
|
-
let batchProgress = false;
|
|
1248
|
-
const batchErrors = [];
|
|
1249
|
-
for (const slot of workerSlots) {
|
|
1250
|
-
const parallelEndedAt = new Date().toISOString();
|
|
1251
|
-
let workerResult = buildWorkerResult({
|
|
1252
|
-
runId: slot.runId,
|
|
1253
|
-
obligationId,
|
|
1254
|
-
status: "no_progress",
|
|
1255
|
-
progressMade: false,
|
|
1256
|
-
selectedExecutor: "agent",
|
|
1257
|
-
artifactsWritten: [],
|
|
1258
|
-
summary: "Parallel worker batch made no progress.",
|
|
1259
|
-
nextLikelyStep: obligationId,
|
|
1260
|
-
errors: [],
|
|
1261
|
-
});
|
|
1262
|
-
try {
|
|
1263
|
-
const launchError = launchErrorsByRunId.get(slot.runId);
|
|
1264
|
-
if (launchError) {
|
|
1265
|
-
throw new Error(`Worker launch failed: ${launchError}`);
|
|
1266
|
-
}
|
|
1267
|
-
const auditResults = await readJsonFile(slot.auditResultsPath);
|
|
1268
|
-
const pendingTaskIds = new Set(slot.group.map((t) => t.task_id));
|
|
1269
|
-
const matchedCount = auditResults.filter((r) => pendingTaskIds.has(r.task_id)).length;
|
|
1270
|
-
if (slot.group.length > 0 && matchedCount === 0) {
|
|
1271
|
-
throw new Error("Worker did not emit any audit results for the assigned tasks.");
|
|
1272
|
-
}
|
|
1273
|
-
const issues = validateAuditResults(auditResults, slot.group, {
|
|
1274
|
-
lineIndex: await buildLineIndexForPaths(root, slot.group.flatMap((task) => task.file_paths)),
|
|
1275
|
-
});
|
|
1276
|
-
const errors = issues.filter((issue) => issue.severity === "error");
|
|
1277
|
-
const warnings = issues.filter((issue) => issue.severity === "warning");
|
|
1278
|
-
if (warnings.length > 0) {
|
|
1279
|
-
process.stderr.write(`audit-results validation: ${warnings.length} warning(s) for ${slot.runId}:\n` +
|
|
1280
|
-
formatAuditResultIssues(warnings) + "\n");
|
|
1281
|
-
}
|
|
1282
|
-
if (errors.length > 0) {
|
|
1283
|
-
throw new Error(`audit-results validation failed with ${errors.length} error(s):\n` +
|
|
1284
|
-
formatAuditResultIssues(errors));
|
|
1285
|
-
}
|
|
1286
|
-
const stepResult = await runAuditStep({
|
|
1287
|
-
root,
|
|
1288
|
-
artifactsDir,
|
|
1289
|
-
preferredExecutor: "result_ingestion_executor",
|
|
1290
|
-
auditResultsPath: slot.auditResultsPath,
|
|
1291
|
-
});
|
|
1292
|
-
workerResult = buildWorkerResult({
|
|
1293
|
-
runId: slot.runId,
|
|
1294
|
-
obligationId,
|
|
1295
|
-
status: stepResult.progress_made ? "completed" : "no_progress",
|
|
1296
|
-
progressMade: stepResult.progress_made,
|
|
1297
|
-
selectedExecutor: stepResult.selected_executor,
|
|
1298
|
-
artifactsWritten: stepResult.artifacts_written,
|
|
1299
|
-
summary: stepResult.progress_summary,
|
|
1300
|
-
nextLikelyStep: stepResult.next_likely_step,
|
|
1301
|
-
errors: [],
|
|
1302
|
-
});
|
|
1303
|
-
batchProgress ||= stepResult.progress_made;
|
|
1304
|
-
if (stepResult.progress_made)
|
|
1305
|
-
anyProgress = true;
|
|
1306
|
-
for (const a of stepResult.artifacts_written)
|
|
1307
|
-
artifactsWritten.add(a);
|
|
1308
|
-
}
|
|
1309
|
-
catch (error) {
|
|
1310
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1311
|
-
batchErrors.push(`${slot.runId}: ${message}`);
|
|
1312
|
-
workerResult = buildWorkerResult({
|
|
1313
|
-
runId: slot.runId,
|
|
1314
|
-
obligationId,
|
|
1315
|
-
status: "failed",
|
|
1316
|
-
progressMade: false,
|
|
1317
|
-
selectedExecutor: "agent",
|
|
1318
|
-
artifactsWritten: [],
|
|
1319
|
-
summary: `Worker failed for executor agent: ${message}`,
|
|
1320
|
-
nextLikelyStep: obligationId,
|
|
1321
|
-
errors: [message],
|
|
1322
|
-
});
|
|
1323
|
-
process.stderr.write(`[agent-batch] ${slot.runId} failed: ${message}\n`);
|
|
1324
|
-
}
|
|
1325
|
-
await persistWorkerRunArtifacts(slot.paths, workerResult, "parallel-deferred-agent");
|
|
1326
|
-
await appendRunLedgerEntry(artifactsDir, {
|
|
1327
|
-
run_id: slot.runId,
|
|
1328
|
-
provider: provider.name,
|
|
1329
|
-
obligation_id: obligationId,
|
|
1330
|
-
selected_executor: workerResult.selected_executor,
|
|
1331
|
-
status: workerResult.status,
|
|
1332
|
-
started_at: parallelStartedAt,
|
|
1333
|
-
ended_at: parallelEndedAt,
|
|
1334
|
-
result_path: slot.paths.resultPath,
|
|
1335
|
-
});
|
|
1336
|
-
artifactsWritten.add("run-ledger.json");
|
|
1337
|
-
}
|
|
1338
|
-
// Record outcome for adaptive learning (best-effort — never blocks dispatch)
|
|
1339
|
-
{
|
|
1340
|
-
const rateLimitResults = batchErrors.map((e) => detectRateLimitError(e));
|
|
1341
|
-
const rateLimitHit = rateLimitResults.find((r) => r.isRateLimited);
|
|
1342
|
-
const retryAfterMs = rateLimitHit?.retryAfterMs ?? null;
|
|
1343
|
-
await recordWaveOutcome(providerModelKey, {
|
|
1344
|
-
concurrency: workerSlots.length,
|
|
1345
|
-
estimated_tokens: slotTokenEstimates.slice(0, workerSlots.length).reduce((a, b) => a + b, 0),
|
|
1346
|
-
outcome: rateLimitHit ? "rate_limited" : batchErrors.length > 0 ? "timeout" : "success",
|
|
1347
|
-
cooldown_until: rateLimitHit ? computeCooldownUntil(retryAfterMs) : null,
|
|
1348
|
-
}, sessionConfig.quota?.empirical_half_life_hours ?? 24).catch(() => undefined);
|
|
1349
|
-
}
|
|
1350
|
-
// Extract rate-limit headers from worker stderr (best-effort)
|
|
1351
|
-
{
|
|
1352
|
-
const extractor = getHeaderExtractorForProvider(provider.name);
|
|
1353
|
-
for (const slot of workerSlots) {
|
|
1354
|
-
try {
|
|
1355
|
-
const stderr = await readFile(slot.paths.stderrPath, "utf8");
|
|
1356
|
-
const extracted = extractor.extract(stderr);
|
|
1357
|
-
if (extracted && (extracted.requests_per_minute != null || extracted.input_tokens_per_minute != null)) {
|
|
1358
|
-
await updateDiscoveredLimits(providerModelKey, {
|
|
1359
|
-
requests_per_minute: extracted.requests_per_minute,
|
|
1360
|
-
input_tokens_per_minute: extracted.input_tokens_per_minute,
|
|
1361
|
-
source: "header_extraction",
|
|
1362
|
-
});
|
|
1363
|
-
break; // one successful extraction is enough
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
catch {
|
|
1367
|
-
// stderr file missing or unreadable — skip
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
await removeWaveManifest(artifactsDir);
|
|
1372
|
-
if (batchErrors.length > 0) {
|
|
1373
|
-
const bundleAfter = await loadArtifactBundle(artifactsDir);
|
|
1374
|
-
const blockedState = buildBlockedAuditState({
|
|
1375
|
-
state: bundleAfter.audit_state ?? deriveAuditState(bundleAfter),
|
|
1376
|
-
obligationId,
|
|
1377
|
-
executor: "agent",
|
|
1378
|
-
blocker: `Parallel worker batch failed for ${batchErrors.length} run(s). ` +
|
|
1379
|
-
batchErrors.slice(0, 3).join(" | "),
|
|
1380
|
-
});
|
|
1381
|
-
await writeCoreArtifacts(artifactsDir, {
|
|
1382
|
-
...bundleAfter,
|
|
1383
|
-
audit_state: blockedState,
|
|
1384
|
-
});
|
|
1385
|
-
await emitEnvelope({
|
|
1386
|
-
root,
|
|
1387
|
-
artifactsDir,
|
|
1388
|
-
bundle: { ...bundleAfter, audit_state: blockedState },
|
|
1389
|
-
audit_state: blockedState,
|
|
1390
|
-
selected_obligation: obligationId,
|
|
1391
|
-
selected_executor: "agent",
|
|
1392
|
-
progress_made: anyProgress,
|
|
1393
|
-
artifacts_written: Array.from(new Set([...artifactsWritten, "audit_state.json"])),
|
|
1394
|
-
progress_summary: `Parallel worker batch failed for ${batchErrors.length} run(s).\n` +
|
|
1395
|
-
batchErrors.join("\n"),
|
|
1396
|
-
next_likely_step: null,
|
|
1397
|
-
providerName: provider.name,
|
|
1398
|
-
});
|
|
1399
|
-
return;
|
|
1400
|
-
}
|
|
1401
|
-
if (!batchProgress) {
|
|
1402
|
-
const bundleAfter = await loadArtifactBundle(artifactsDir);
|
|
1403
|
-
const state = bundleAfter.audit_state ?? deriveAuditState(bundleAfter);
|
|
1404
|
-
await emitEnvelope({
|
|
1405
|
-
root,
|
|
1406
|
-
artifactsDir,
|
|
1407
|
-
bundle: bundleAfter,
|
|
1408
|
-
audit_state: state,
|
|
1409
|
-
selected_obligation: obligationId,
|
|
1410
|
-
selected_executor: "agent",
|
|
1411
|
-
progress_made: anyProgress,
|
|
1412
|
-
artifacts_written: Array.from(artifactsWritten),
|
|
1413
|
-
progress_summary: "Parallel worker batch made no progress.",
|
|
1414
|
-
next_likely_step: obligationId,
|
|
1415
|
-
providerName: provider.name,
|
|
1416
|
-
});
|
|
1417
|
-
return;
|
|
1418
|
-
}
|
|
1419
|
-
continue;
|
|
1420
|
-
}
|
|
1421
|
-
runCount += 1;
|
|
1422
|
-
const runId = buildRunId(obligationId, runCount);
|
|
1423
|
-
const paths = getRunPaths(artifactsDir, runId);
|
|
1424
|
-
if (shouldRunInlineExecutor(preferredExecutor)) {
|
|
1425
|
-
await clearDispatchFiles(artifactsDir);
|
|
1426
|
-
const startedAt = new Date().toISOString();
|
|
1427
|
-
let workerResult;
|
|
1428
|
-
try {
|
|
1429
|
-
const result = await runAuditStep({
|
|
1430
|
-
root,
|
|
1431
|
-
artifactsDir,
|
|
1432
|
-
preferredExecutor,
|
|
1433
|
-
auditResultsPath,
|
|
1434
|
-
runtimeUpdatesPath,
|
|
1435
|
-
externalAnalyzerPath,
|
|
1436
|
-
analyzers: sessionConfig.analyzers,
|
|
1437
|
-
since: getFlag(argv, "--since"),
|
|
1438
|
-
});
|
|
1439
|
-
workerResult = {
|
|
1440
|
-
contract_version: WORKER_RESULT_CONTRACT_VERSION,
|
|
1441
|
-
run_id: runId,
|
|
1442
|
-
obligation_id: obligationId,
|
|
1443
|
-
status: result.progress_made ? "completed" : "no_progress",
|
|
1444
|
-
progress_made: result.progress_made,
|
|
1445
|
-
selected_executor: result.selected_executor,
|
|
1446
|
-
artifacts_written: result.artifacts_written,
|
|
1447
|
-
summary: result.progress_summary,
|
|
1448
|
-
next_likely_step: result.next_likely_step,
|
|
1449
|
-
errors: [],
|
|
1450
|
-
};
|
|
1451
|
-
}
|
|
1452
|
-
catch (error) {
|
|
1453
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1454
|
-
workerResult = {
|
|
1455
|
-
contract_version: WORKER_RESULT_CONTRACT_VERSION,
|
|
1456
|
-
run_id: runId,
|
|
1457
|
-
obligation_id: obligationId,
|
|
1458
|
-
status: "failed",
|
|
1459
|
-
progress_made: false,
|
|
1460
|
-
selected_executor: preferredExecutor,
|
|
1461
|
-
artifacts_written: [],
|
|
1462
|
-
summary: `Inline executor failed for ${preferredExecutor}: ${message}`,
|
|
1463
|
-
next_likely_step: decision.selected_obligation,
|
|
1464
|
-
errors: [message],
|
|
1465
|
-
};
|
|
1466
|
-
}
|
|
1467
|
-
await persistWorkerRunArtifacts(paths, workerResult, "inline");
|
|
1468
|
-
await appendRunLedgerEntry(artifactsDir, {
|
|
1469
|
-
run_id: runId,
|
|
1470
|
-
provider: provider.name,
|
|
1471
|
-
obligation_id: obligationId,
|
|
1472
|
-
selected_executor: workerResult.selected_executor,
|
|
1473
|
-
status: workerResult.status,
|
|
1474
|
-
started_at: startedAt,
|
|
1475
|
-
ended_at: new Date().toISOString(),
|
|
1476
|
-
result_path: paths.resultPath,
|
|
1477
|
-
});
|
|
1478
|
-
lastResult = workerResult;
|
|
1479
|
-
if (workerResult.progress_made) {
|
|
1480
|
-
anyProgress = true;
|
|
1481
|
-
}
|
|
1482
|
-
for (const artifact of workerResult.artifacts_written) {
|
|
1483
|
-
artifactsWritten.add(artifact);
|
|
1484
|
-
}
|
|
1485
|
-
artifactsWritten.add("run-ledger.json");
|
|
1486
|
-
if (externalAnalyzerPath)
|
|
1487
|
-
pendingExternalAnalyzerPath = undefined;
|
|
1488
|
-
if (auditResultsPath &&
|
|
1489
|
-
pendingBatchAuditResults[0] === auditResultsPath &&
|
|
1490
|
-
preferredExecutor === "result_ingestion_executor" &&
|
|
1491
|
-
workerResult.status !== "failed" &&
|
|
1492
|
-
workerResult.status !== "blocked") {
|
|
1493
|
-
pendingBatchAuditResults.shift();
|
|
1494
|
-
}
|
|
1495
|
-
if (auditResultsPath)
|
|
1496
|
-
pendingAuditResultsPath = undefined;
|
|
1497
|
-
if (runtimeUpdatesPath)
|
|
1498
|
-
pendingRuntimeUpdatesPath = undefined;
|
|
1499
|
-
if (workerResult.status === "failed" ||
|
|
1500
|
-
workerResult.status === "blocked" ||
|
|
1501
|
-
workerResult.status === "no_progress") {
|
|
1502
|
-
const bundleAfter = await loadArtifactBundle(artifactsDir);
|
|
1503
|
-
const shouldBlock = workerResult.status === "failed" || workerResult.status === "blocked";
|
|
1504
|
-
const state = shouldBlock
|
|
1505
|
-
? buildBlockedAuditState({
|
|
1506
|
-
state: bundleAfter.audit_state ?? deriveAuditState(bundleAfter),
|
|
1507
|
-
obligationId: workerResult.obligation_id,
|
|
1508
|
-
executor: workerResult.selected_executor,
|
|
1509
|
-
blocker: buildWorkerFailureBlocker(workerResult),
|
|
1510
|
-
})
|
|
1511
|
-
: bundleAfter.audit_state ?? deriveAuditState(bundleAfter);
|
|
1512
|
-
if (shouldBlock) {
|
|
1513
|
-
await writeCoreArtifacts(artifactsDir, {
|
|
1514
|
-
...bundleAfter,
|
|
1515
|
-
audit_state: state,
|
|
1516
|
-
});
|
|
1517
|
-
}
|
|
1518
|
-
await emitEnvelope({
|
|
1519
|
-
root,
|
|
1520
|
-
artifactsDir,
|
|
1521
|
-
bundle: shouldBlock
|
|
1522
|
-
? { ...bundleAfter, audit_state: state }
|
|
1523
|
-
: bundleAfter,
|
|
1524
|
-
audit_state: state,
|
|
1525
|
-
selected_obligation: workerResult.obligation_id,
|
|
1526
|
-
selected_executor: workerResult.selected_executor,
|
|
1527
|
-
progress_made: anyProgress,
|
|
1528
|
-
artifacts_written: Array.from(shouldBlock
|
|
1529
|
-
? new Set([...artifactsWritten, "audit_state.json"])
|
|
1530
|
-
: artifactsWritten),
|
|
1531
|
-
progress_summary: buildWorkerFailureBlocker(workerResult),
|
|
1532
|
-
next_likely_step: shouldBlock ? null : workerResult.next_likely_step,
|
|
1533
|
-
providerName: provider.name,
|
|
1534
|
-
});
|
|
1535
|
-
return;
|
|
1536
|
-
}
|
|
1537
|
-
continue;
|
|
1538
|
-
}
|
|
1539
|
-
const pendingAuditTasks = preferredExecutor === "agent"
|
|
1540
|
-
? await addFileLineCountHints(root, buildPendingAuditTasks(bundle))
|
|
1541
|
-
: undefined;
|
|
1542
|
-
const pendingAuditTasksPath = preferredExecutor === "agent"
|
|
1543
|
-
? join(paths.runDir, "pending-audit-tasks.json")
|
|
1544
|
-
: undefined;
|
|
1545
|
-
const providerAuditResultsPath = preferredExecutor === "agent"
|
|
1546
|
-
? join(paths.runDir, "audit-results.json")
|
|
1547
|
-
: auditResultsPath;
|
|
1548
|
-
const providerReadPaths = new Set();
|
|
1549
|
-
if (pendingAuditTasks) {
|
|
1550
|
-
for (const pt of pendingAuditTasks) {
|
|
1551
|
-
for (const fp of pt.file_paths)
|
|
1552
|
-
providerReadPaths.add(fp);
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
const task = {
|
|
1556
|
-
contract_version: "audit-code-worker/v1alpha1",
|
|
1557
|
-
run_id: runId,
|
|
1558
|
-
repo_root: root,
|
|
1559
|
-
artifacts_dir: artifactsDir,
|
|
1560
|
-
obligation_id: obligationId,
|
|
1561
|
-
preferred_executor: preferredExecutor,
|
|
1562
|
-
result_path: paths.resultPath,
|
|
1563
|
-
worker_command: [
|
|
1564
|
-
process.execPath,
|
|
1565
|
-
selfCliPath,
|
|
1566
|
-
"worker-run",
|
|
1567
|
-
"--task",
|
|
1568
|
-
paths.taskPath,
|
|
1569
|
-
],
|
|
1570
|
-
audit_results_path: providerAuditResultsPath,
|
|
1571
|
-
pending_audit_tasks_path: pendingAuditTasksPath,
|
|
1572
|
-
runtime_updates_path: runtimeUpdatesPath,
|
|
1573
|
-
external_analyzer_results_path: externalAnalyzerPath,
|
|
1574
|
-
timeout_ms: timeoutMs,
|
|
1575
|
-
max_retries: 0,
|
|
1576
|
-
access: providerReadPaths.size > 0 ? {
|
|
1577
|
-
read_paths: [...providerReadPaths],
|
|
1578
|
-
write_paths: [providerAuditResultsPath ?? paths.resultPath, paths.resultPath],
|
|
1579
|
-
} : undefined,
|
|
1580
|
-
};
|
|
1581
|
-
const prompt = renderWorkerPrompt(task);
|
|
1582
|
-
await writeWorkerTaskFiles(task, prompt, paths, artifactsDir, pendingAuditTasks);
|
|
1583
|
-
if (pendingAuditTasksPath && pendingAuditTasks) {
|
|
1584
|
-
await writeJsonFile(pendingAuditTasksPath, pendingAuditTasks);
|
|
1585
|
-
}
|
|
1586
|
-
const startedAt = new Date().toISOString();
|
|
1587
|
-
let workerResult;
|
|
1588
|
-
let launchResult = null;
|
|
1589
|
-
try {
|
|
1590
|
-
launchResult = await provider.launch({
|
|
1591
|
-
repoRoot: root,
|
|
1592
|
-
runId,
|
|
1593
|
-
obligationId,
|
|
1594
|
-
promptPath: paths.promptPath,
|
|
1595
|
-
taskPath: paths.taskPath,
|
|
1596
|
-
resultPath: paths.resultPath,
|
|
1597
|
-
stdoutPath: paths.stdoutPath,
|
|
1598
|
-
stderrPath: paths.stderrPath,
|
|
1599
|
-
uiMode,
|
|
1600
|
-
timeoutMs,
|
|
1601
|
-
});
|
|
1602
|
-
const candidate = await readJsonFile(paths.resultPath);
|
|
1603
|
-
if (isWorkerResult(candidate)) {
|
|
1604
|
-
workerResult = candidate;
|
|
1605
|
-
}
|
|
1606
|
-
else {
|
|
1607
|
-
const launchExitSummary = summarizeLaunchExit(launchResult);
|
|
1608
|
-
workerResult = {
|
|
1609
|
-
contract_version: WORKER_RESULT_CONTRACT_VERSION,
|
|
1610
|
-
run_id: runId,
|
|
1611
|
-
obligation_id: obligationId,
|
|
1612
|
-
status: "failed",
|
|
1613
|
-
progress_made: false,
|
|
1614
|
-
selected_executor: preferredExecutor,
|
|
1615
|
-
artifacts_written: [],
|
|
1616
|
-
summary: launchExitSummary
|
|
1617
|
-
? `Worker did not emit a valid worker result after provider exit: ${launchExitSummary}`
|
|
1618
|
-
: "Worker did not emit a valid worker result.",
|
|
1619
|
-
next_likely_step: decision.selected_obligation,
|
|
1620
|
-
errors: ["Invalid worker result contract."],
|
|
1621
|
-
};
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
catch (error) {
|
|
1625
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1626
|
-
const launchExitSummary = launchResult && summarizeLaunchExit(launchResult);
|
|
1627
|
-
workerResult = {
|
|
1628
|
-
contract_version: WORKER_RESULT_CONTRACT_VERSION,
|
|
1629
|
-
run_id: runId,
|
|
1630
|
-
obligation_id: obligationId,
|
|
1631
|
-
status: "failed",
|
|
1632
|
-
progress_made: false,
|
|
1633
|
-
selected_executor: preferredExecutor,
|
|
1634
|
-
artifacts_written: [],
|
|
1635
|
-
summary: `Worker launch failed for ${preferredExecutor}: ${launchExitSummary ?? message}`,
|
|
1636
|
-
next_likely_step: decision.selected_obligation,
|
|
1637
|
-
errors: launchExitSummary ? [message, launchExitSummary] : [message],
|
|
1638
|
-
};
|
|
1639
|
-
await persistWorkerRunArtifacts(paths, workerResult, "provider-launch");
|
|
1640
|
-
}
|
|
1641
|
-
await appendRunLedgerEntry(artifactsDir, {
|
|
1642
|
-
run_id: runId,
|
|
1643
|
-
provider: provider.name,
|
|
1644
|
-
obligation_id: obligationId,
|
|
1645
|
-
selected_executor: workerResult.selected_executor,
|
|
1646
|
-
status: workerResult.status,
|
|
1647
|
-
started_at: startedAt,
|
|
1648
|
-
ended_at: new Date().toISOString(),
|
|
1649
|
-
result_path: paths.resultPath,
|
|
1650
|
-
});
|
|
1651
|
-
lastResult = workerResult;
|
|
1652
|
-
if (workerResult.progress_made) {
|
|
1653
|
-
anyProgress = true;
|
|
1654
|
-
}
|
|
1655
|
-
for (const artifact of workerResult.artifacts_written) {
|
|
1656
|
-
artifactsWritten.add(artifact);
|
|
1657
|
-
}
|
|
1658
|
-
artifactsWritten.add("run-ledger.json");
|
|
1659
|
-
if (externalAnalyzerPath)
|
|
1660
|
-
pendingExternalAnalyzerPath = undefined;
|
|
1661
|
-
if (auditResultsPath &&
|
|
1662
|
-
pendingBatchAuditResults[0] === auditResultsPath &&
|
|
1663
|
-
preferredExecutor === "result_ingestion_executor" &&
|
|
1664
|
-
workerResult.status !== "failed" &&
|
|
1665
|
-
workerResult.status !== "blocked") {
|
|
1666
|
-
pendingBatchAuditResults.shift();
|
|
1667
|
-
}
|
|
1668
|
-
if (providerAuditResultsPath)
|
|
1669
|
-
pendingAuditResultsPath = undefined;
|
|
1670
|
-
if (runtimeUpdatesPath)
|
|
1671
|
-
pendingRuntimeUpdatesPath = undefined;
|
|
1672
|
-
if (workerResult.status === "failed" ||
|
|
1673
|
-
workerResult.status === "blocked" ||
|
|
1674
|
-
workerResult.status === "no_progress") {
|
|
1675
|
-
const bundleAfter = await loadArtifactBundle(artifactsDir);
|
|
1676
|
-
const shouldBlock = workerResult.status === "failed" || workerResult.status === "blocked";
|
|
1677
|
-
const state = shouldBlock
|
|
1678
|
-
? buildBlockedAuditState({
|
|
1679
|
-
state: deriveAuditState(bundleAfter),
|
|
1680
|
-
obligationId: workerResult.obligation_id,
|
|
1681
|
-
executor: workerResult.selected_executor,
|
|
1682
|
-
blocker: buildWorkerFailureBlocker(workerResult),
|
|
1683
|
-
})
|
|
1684
|
-
: deriveAuditState(bundleAfter);
|
|
1685
|
-
if (shouldBlock) {
|
|
1686
|
-
await writeCoreArtifacts(artifactsDir, {
|
|
1687
|
-
...bundleAfter,
|
|
1688
|
-
audit_state: state,
|
|
1689
|
-
});
|
|
1690
|
-
}
|
|
1691
|
-
await emitEnvelope({
|
|
1692
|
-
root,
|
|
1693
|
-
artifactsDir,
|
|
1694
|
-
bundle: shouldBlock
|
|
1695
|
-
? { ...bundleAfter, audit_state: state }
|
|
1696
|
-
: bundleAfter,
|
|
1697
|
-
audit_state: state,
|
|
1698
|
-
selected_obligation: workerResult.obligation_id,
|
|
1699
|
-
selected_executor: workerResult.selected_executor,
|
|
1700
|
-
progress_made: anyProgress,
|
|
1701
|
-
artifacts_written: Array.from(shouldBlock
|
|
1702
|
-
? new Set([...artifactsWritten, "audit_state.json"])
|
|
1703
|
-
: artifactsWritten),
|
|
1704
|
-
progress_summary: buildWorkerFailureBlocker(workerResult),
|
|
1705
|
-
next_likely_step: shouldBlock ? null : workerResult.next_likely_step,
|
|
1706
|
-
providerName: provider.name,
|
|
1707
|
-
});
|
|
1708
|
-
return;
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
const bundle = await loadArtifactBundle(artifactsDir);
|
|
1712
|
-
const decision = decideNextStep(bundle);
|
|
1713
|
-
const state = decision.state;
|
|
1714
|
-
if (state.status === "complete") {
|
|
1715
|
-
await clearDispatchFiles(artifactsDir);
|
|
1716
|
-
}
|
|
1717
|
-
await emitEnvelope({
|
|
1718
|
-
root,
|
|
1719
|
-
artifactsDir,
|
|
1720
|
-
bundle,
|
|
1721
|
-
audit_state: state,
|
|
1722
|
-
selected_obligation: lastResult?.obligation_id ?? decision.selected_obligation,
|
|
1723
|
-
selected_executor: lastResult?.selected_executor ?? decision.selected_executor,
|
|
1724
|
-
progress_made: anyProgress,
|
|
1725
|
-
artifacts_written: Array.from(artifactsWritten),
|
|
1726
|
-
progress_summary: `Reached max run limit (${maxRuns}) before terminal state.`,
|
|
1727
|
-
next_likely_step: state.status === "complete" ? null : decision.selected_obligation,
|
|
1728
|
-
providerName: provider.name,
|
|
1729
|
-
});
|
|
1730
|
-
if (state.status === "complete") {
|
|
1731
|
-
await promoteFinalAuditReport({ artifactsDir, repoRoot: root });
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
223
|
async function cmdWorkerRun(argv) {
|
|
1735
224
|
const taskPath = getFlag(argv, "--task");
|
|
1736
225
|
if (!taskPath) {
|
|
@@ -2443,22 +932,6 @@ async function cmdSynthesize(argv) {
|
|
|
2443
932
|
progress_summary: result.progress_summary,
|
|
2444
933
|
}, null, 2));
|
|
2445
934
|
}
|
|
2446
|
-
async function cleanupStaleArtifactsDir(artifactsDir) {
|
|
2447
|
-
let status;
|
|
2448
|
-
try {
|
|
2449
|
-
const state = await readJsonFile(join(artifactsDir, "audit_state.json"));
|
|
2450
|
-
status = state.status;
|
|
2451
|
-
}
|
|
2452
|
-
catch (error) {
|
|
2453
|
-
if (!isFileMissingError(error)) {
|
|
2454
|
-
throw error;
|
|
2455
|
-
}
|
|
2456
|
-
return;
|
|
2457
|
-
}
|
|
2458
|
-
if (status === "complete" || status === "not_started") {
|
|
2459
|
-
await rm(artifactsDir, { recursive: true, force: true });
|
|
2460
|
-
}
|
|
2461
|
-
}
|
|
2462
935
|
async function cmdCleanup(argv) {
|
|
2463
936
|
const artifactsDir = getArtifactsDir(argv);
|
|
2464
937
|
const dryRun = hasFlag(argv, "--dry-run");
|