auditor-lambda 0.3.41 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/dispatch.js +5 -1
- package/dist/cli/prompts.d.ts +19 -0
- package/dist/cli/prompts.js +95 -0
- package/dist/cli/steps.d.ts +1 -1
- package/dist/cli.js +398 -78
- package/dist/extractors/analyzers/css.d.ts +2 -0
- package/dist/extractors/analyzers/css.js +101 -0
- package/dist/extractors/analyzers/html.d.ts +2 -0
- package/dist/extractors/analyzers/html.js +92 -0
- package/dist/extractors/analyzers/merge.d.ts +14 -0
- package/dist/extractors/analyzers/merge.js +85 -0
- package/dist/extractors/analyzers/python.d.ts +2 -0
- package/dist/extractors/analyzers/python.js +104 -0
- package/dist/extractors/analyzers/registry.d.ts +33 -0
- package/dist/extractors/analyzers/registry.js +100 -0
- package/dist/extractors/analyzers/resourceUrl.d.ts +7 -0
- package/dist/extractors/analyzers/resourceUrl.js +25 -0
- package/dist/extractors/analyzers/sql.d.ts +2 -0
- package/dist/extractors/analyzers/sql.js +19 -0
- package/dist/extractors/analyzers/treeSitter.d.ts +34 -0
- package/dist/extractors/analyzers/treeSitter.js +111 -0
- package/dist/extractors/analyzers/types.d.ts +53 -0
- package/dist/extractors/analyzers/types.js +1 -0
- package/dist/extractors/analyzers/typescript.d.ts +2 -0
- package/dist/extractors/analyzers/typescript.js +257 -0
- package/dist/extractors/disposition.js +8 -1
- package/dist/extractors/graph.d.ts +1 -0
- package/dist/extractors/graph.js +167 -1
- package/dist/extractors/graphPythonImports.d.ts +15 -0
- package/dist/extractors/graphPythonImports.js +36 -0
- package/dist/extractors/pathPatterns.d.ts +6 -0
- package/dist/extractors/pathPatterns.js +8 -0
- package/dist/io/artifacts.d.ts +13 -1
- package/dist/io/artifacts.js +19 -3
- package/dist/mcp/server.js +3 -3
- package/dist/orchestrator/advance.d.ts +20 -0
- package/dist/orchestrator/advance.js +61 -2
- package/dist/orchestrator/dependencyMap.js +27 -0
- package/dist/orchestrator/edgeReasoning.d.ts +39 -0
- package/dist/orchestrator/edgeReasoning.js +125 -0
- package/dist/orchestrator/executors.js +11 -1
- package/dist/orchestrator/graphEnrichmentExecutor.d.ts +29 -0
- package/dist/orchestrator/graphEnrichmentExecutor.js +196 -0
- package/dist/orchestrator/internalExecutors.d.ts +10 -1
- package/dist/orchestrator/internalExecutors.js +89 -11
- package/dist/orchestrator/localCommands.js +6 -25
- package/dist/orchestrator/nextStep.js +2 -0
- package/dist/orchestrator/reviewPackets.d.ts +37 -4
- package/dist/orchestrator/reviewPackets.js +93 -46
- package/dist/orchestrator/runtimeValidation.js +4 -31
- package/dist/orchestrator/scope.d.ts +62 -0
- package/dist/orchestrator/scope.js +227 -0
- package/dist/orchestrator/state.js +2 -0
- package/dist/reporting/synthesis.d.ts +37 -2
- package/dist/reporting/synthesis.js +95 -16
- package/dist/reporting/synthesisNarrativePrompt.d.ts +7 -0
- package/dist/reporting/synthesisNarrativePrompt.js +60 -0
- package/dist/reporting/workBlocks.d.ts +2 -10
- package/dist/supervisor/operatorHandoff.d.ts +1 -1
- package/dist/supervisor/operatorHandoff.js +26 -16
- package/dist/supervisor/sessionConfig.d.ts +8 -1
- package/dist/supervisor/sessionConfig.js +22 -1
- package/dist/types/analyzerCapability.d.ts +16 -0
- package/dist/types/analyzerCapability.js +1 -0
- package/dist/types/auditScope.d.ts +43 -0
- package/dist/types/auditScope.js +14 -0
- package/dist/types/synthesisNarrative.d.ts +7 -0
- package/dist/types/synthesisNarrative.js +5 -0
- package/dist/types.d.ts +2 -19
- package/dist/validation/artifacts.js +9 -0
- package/dist/validation/sessionConfig.js +24 -1
- package/docs/contracts.md +10 -3
- package/package.json +4 -2
- package/schemas/analyzer_capability.schema.json +47 -0
- package/schemas/audit_findings.schema.json +141 -0
- package/schemas/finding.schema.json +2 -1
- package/schemas/graph_bundle.schema.json +5 -0
- package/schemas/scope.schema.json +46 -0
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, readFile, readdir, rename, rm, unlink } from "node:fs/promises";
|
|
1
|
+
import { mkdir, readFile, readdir, rename, rm, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -10,8 +10,8 @@ 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, } from "./io/artifacts.js";
|
|
14
|
-
import { isFileMissingError, readJsonFile, writeJsonFile, prefixValidationIssues } from "@audit-tools/shared";
|
|
13
|
+
import { loadArtifactBundle, writeCoreArtifacts, promoteFinalAuditReport, AUDIT_REPORT_FILENAME, } from "./io/artifacts.js";
|
|
14
|
+
import { isFileMissingError, readJsonFile, writeJsonFile, prefixValidationIssues, RunLogger } from "@audit-tools/shared";
|
|
15
15
|
import { validateArtifactBundle } from "./validation/artifacts.js";
|
|
16
16
|
import { validateAuditResults, formatAuditResultIssues, } from "./validation/auditResults.js";
|
|
17
17
|
import { validateConfiguredProviderEnvironment, validateSessionConfig, } from "./validation/sessionConfig.js";
|
|
@@ -20,21 +20,26 @@ import { deriveAuditState } from "./orchestrator/state.js";
|
|
|
20
20
|
import { advanceAudit } from "./orchestrator/advance.js";
|
|
21
21
|
import { checkFileIntegrity } from "./orchestrator/fileIntegrity.js";
|
|
22
22
|
import { decideNextStep } from "./orchestrator/nextStep.js";
|
|
23
|
+
import { collectLowConfidenceEdges, buildEdgeReasoningPrompt, edgeReasoningContentHash, } from "./orchestrator/edgeReasoning.js";
|
|
23
24
|
import { renderDesignReviewPrompt } from "./orchestrator/designReviewPrompt.js";
|
|
25
|
+
import { renderSynthesisNarrativePrompt } from "./reporting/synthesisNarrativePrompt.js";
|
|
24
26
|
import { createFreshSessionProvider, resolveFreshSessionProviderName, } from "./providers/index.js";
|
|
25
27
|
import { appendRunLedgerEntry, loadRunLedger } from "./supervisor/runLedger.js";
|
|
26
28
|
import { buildAuditCodeHandoff, writeAuditCodeHandoffArtifacts, } from "./supervisor/operatorHandoff.js";
|
|
27
|
-
import { getSessionConfigPath, loadSessionConfig, readSessionConfigFile, } from "./supervisor/sessionConfig.js";
|
|
29
|
+
import { getSessionConfigPath, loadSessionConfig, persistAnalyzerSettings, readSessionConfigFile, } from "./supervisor/sessionConfig.js";
|
|
30
|
+
import { resolveAnalyzerPlan, needsInstallDecision, } from "./extractors/analyzers/registry.js";
|
|
31
|
+
import { buildPathLookup } from "./extractors/graph.js";
|
|
32
|
+
import { buildDispositionMap } from "./extractors/disposition.js";
|
|
28
33
|
import { clearDispatchFiles, buildRunId, ensureSupervisorDirs, getRunPaths, writeDispatchBatchFiles, writeWorkerTaskFiles, } from "./io/runArtifacts.js";
|
|
29
34
|
import { renderWorkerPrompt } from "./prompts/renderWorkerPrompt.js";
|
|
30
|
-
import { estimateTaskGroupTokens, } from "./orchestrator/reviewPackets.js";
|
|
35
|
+
import { estimateTaskGroupTokens, sizeIndexFromManifest, } from "./orchestrator/reviewPackets.js";
|
|
31
36
|
import { LOCAL_SUBPROCESS_PROVIDER_NAME } from "./providers/constants.js";
|
|
32
37
|
import { runAuditCodeMcpServer } from "./mcp/server.js";
|
|
33
38
|
import { scheduleWave, buildProviderModelKey, readQuotaState, recordWaveOutcome, resolveLimits, resolveHostActiveSubagentLimit, probeProvider, computeMaxSafeConcurrency, getQuotaStatePath, detectRateLimitError, computeCooldownUntil, runSlidingWindow, LearnedQuotaSource, CompositeQuotaSource, lookupDiscoveredLimits, updateDiscoveredLimits, mergeDiscoveredLimits, getHeaderExtractorForProvider, setQuotaStateDir, } from "./quota/index.js";
|
|
34
39
|
// Re-exports from extracted modules
|
|
35
40
|
export { resolveHostDispatchCapability, DIRECT_CLI_DEFAULTS, getFlag, hasFlag, getOptionalBooleanFlag, getArtifactsDir, getRootDir, getBatchResultsDir, getMaxRuns, getAgentBatchSize, getParallelWorkers, getTimeoutMs, chunkArray, getUiMode, looksLikeCliFlag, countLines, warnIfNotGitRepo, } from "./cli/args.js";
|
|
36
41
|
import { DIRECT_CLI_DEFAULTS, getFlag, hasFlag, getOptionalBooleanFlag, fromBase64Url, renderCommand, summarizeLaunchExit, taskResultPath, readStdinText, getArtifactsDir, getRootDir, warnIfNotGitRepo, getBatchResultsDir, getMaxRuns, getAgentBatchSize, getParallelWorkers, getTimeoutMs, getExplicitProvider, getHostModel, getHostMaxActiveSubagents, getQuotaProbeMode, resolveRunProviderName, chunkArray, getUiMode, looksLikeCliFlag, resolveHostDispatchCapability, countLines, listBatchResultFiles, } from "./cli/args.js";
|
|
37
|
-
import { nextStepCommand, mergeAndIngestCommand, renderDispatchReviewPrompt, renderSingleTaskFallbackStepPrompt, renderPresentReportPrompt, renderBlockedStepPrompt, } from "./cli/prompts.js";
|
|
42
|
+
import { nextStepCommand, mergeAndIngestCommand, renderDispatchReviewPrompt, renderSingleTaskFallbackStepPrompt, renderPresentReportPrompt, renderAnalyzerInstallPrompt, renderEdgeReasoningStepPrompt, renderEdgeReasoningDispatchPrompt, renderBlockedStepPrompt, } from "./cli/prompts.js";
|
|
38
43
|
import { writeCurrentStep, } from "./cli/steps.js";
|
|
39
44
|
import { WORKER_RESULT_CONTRACT_VERSION, buildWorkerResult, persistWorkerRunArtifacts, isWorkerResult, buildWorkerFailureBlocker, formatAuditResultValidationError, } from "./cli/workerResult.js";
|
|
40
45
|
import { DISPATCH_RESULT_MAP_FILENAME, ACTIVE_DISPATCH_FILENAME, resolveRunScopedArg, loadDispatchResultMap, entriesByTaskId, buildPendingAuditTasks, prepareDispatchArtifacts, } from "./cli/dispatch.js";
|
|
@@ -313,9 +318,15 @@ async function maybeArchiveLegacyPendingResults(auditResultsPath) {
|
|
|
313
318
|
}
|
|
314
319
|
async function runAuditStep(options) {
|
|
315
320
|
const bundle = await loadArtifactBundle(options.artifactsDir);
|
|
321
|
+
const runLogger = new RunLogger(join(options.artifactsDir, "run.log.jsonl"), {
|
|
322
|
+
enabled: options.runLog ?? true,
|
|
323
|
+
});
|
|
316
324
|
const lineIndex = bundle.repo_manifest
|
|
317
325
|
? await buildLineIndex(options.root, bundle.repo_manifest)
|
|
318
326
|
: undefined;
|
|
327
|
+
const sizeIndex = bundle.repo_manifest
|
|
328
|
+
? sizeIndexFromManifest(bundle.repo_manifest)
|
|
329
|
+
: undefined;
|
|
319
330
|
if (looksLikeCliFlag(options.auditResultsPath)) {
|
|
320
331
|
throw new Error(`Invalid audit results path '${options.auditResultsPath}'. This looks like a CLI flag rather than a file path.`);
|
|
321
332
|
}
|
|
@@ -343,14 +354,27 @@ async function runAuditStep(options) {
|
|
|
343
354
|
const externalAnalyzerResults = options.externalAnalyzerPath
|
|
344
355
|
? await readJsonFile(options.externalAnalyzerPath)
|
|
345
356
|
: undefined;
|
|
357
|
+
const narrativeResults = options.narrativeResultsPath
|
|
358
|
+
? await readJsonFile(options.narrativeResultsPath)
|
|
359
|
+
: undefined;
|
|
360
|
+
const edgeReasoningResults = options.edgeReasoningResultsPath
|
|
361
|
+
? await readJsonFile(options.edgeReasoningResultsPath)
|
|
362
|
+
: undefined;
|
|
346
363
|
const result = await advanceAudit(bundle, {
|
|
347
364
|
root: options.root,
|
|
348
365
|
lineIndex,
|
|
366
|
+
sizeIndex,
|
|
349
367
|
auditResults: auditResults,
|
|
350
368
|
runtimeValidationUpdates,
|
|
351
369
|
externalAnalyzerResults,
|
|
370
|
+
narrativeResults,
|
|
371
|
+
edgeReasoningResults,
|
|
372
|
+
analyzers: options.analyzers,
|
|
373
|
+
graphLlmEdgeReasoning: options.graphLlmEdgeReasoning,
|
|
374
|
+
since: options.since,
|
|
352
375
|
preferredExecutor: options.preferredExecutor,
|
|
353
376
|
opentoken: options.opentoken,
|
|
377
|
+
runLogger,
|
|
354
378
|
});
|
|
355
379
|
await writeCoreArtifacts(options.artifactsDir, result.updated_bundle);
|
|
356
380
|
const archivedPendingResults = await maybeArchiveLegacyPendingResults(options.auditResultsPath);
|
|
@@ -555,7 +579,11 @@ async function cmdAdvanceAudit(argv) {
|
|
|
555
579
|
auditResultsPath: getFlag(argv, "--results"),
|
|
556
580
|
runtimeUpdatesPath: getFlag(argv, "--updates"),
|
|
557
581
|
externalAnalyzerPath,
|
|
582
|
+
analyzers: sessionConfig.analyzers,
|
|
583
|
+
graphLlmEdgeReasoning: sessionConfig.graph?.llm_edge_reasoning,
|
|
584
|
+
since: getFlag(argv, "--since"),
|
|
558
585
|
opentoken: sessionConfig.opentoken?.enabled,
|
|
586
|
+
runLog: sessionConfig.observability?.run_log,
|
|
559
587
|
});
|
|
560
588
|
if (result.selected_executor !== "agent") {
|
|
561
589
|
await clearDispatchFiles(artifactsDir);
|
|
@@ -579,6 +607,7 @@ async function cmdAdvanceAudit(argv) {
|
|
|
579
607
|
}
|
|
580
608
|
async function runDeterministicForNextStep(params) {
|
|
581
609
|
let lastSummary = "";
|
|
610
|
+
let analyzers = params.analyzers;
|
|
582
611
|
for (let index = 0; index < params.maxRuns; index++) {
|
|
583
612
|
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
584
613
|
const decision = decideNextStep(bundle);
|
|
@@ -601,8 +630,8 @@ async function runDeterministicForNextStep(params) {
|
|
|
601
630
|
state,
|
|
602
631
|
bundle,
|
|
603
632
|
finalReportPath: promoted.promoted
|
|
604
|
-
? join(params.root,
|
|
605
|
-
: join(params.artifactsDir,
|
|
633
|
+
? join(params.root, AUDIT_REPORT_FILENAME)
|
|
634
|
+
: join(params.artifactsDir, AUDIT_REPORT_FILENAME),
|
|
606
635
|
};
|
|
607
636
|
}
|
|
608
637
|
if (index === 0 && bundle.repo_manifest) {
|
|
@@ -621,6 +650,89 @@ async function runDeterministicForNextStep(params) {
|
|
|
621
650
|
}
|
|
622
651
|
}
|
|
623
652
|
}
|
|
653
|
+
if (decision.selected_executor === "graph_enrichment_executor") {
|
|
654
|
+
const includedFiles = bundle.repo_manifest
|
|
655
|
+
? [
|
|
656
|
+
...new Set(buildPathLookup(bundle.repo_manifest, buildDispositionMap(bundle.file_disposition)).values()),
|
|
657
|
+
]
|
|
658
|
+
: [];
|
|
659
|
+
const plan = resolveAnalyzerPlan(params.root, analyzers, includedFiles);
|
|
660
|
+
const unresolved = plan.filter(needsInstallDecision);
|
|
661
|
+
if (unresolved.length > 0) {
|
|
662
|
+
const decisionsPath = join(params.artifactsDir, "incoming", "analyzer-decisions.json");
|
|
663
|
+
let decisions;
|
|
664
|
+
try {
|
|
665
|
+
decisions = await readJsonFile(decisionsPath);
|
|
666
|
+
}
|
|
667
|
+
catch (error) {
|
|
668
|
+
if (!isFileMissingError(error))
|
|
669
|
+
throw error;
|
|
670
|
+
}
|
|
671
|
+
if (decisions && typeof decisions === "object") {
|
|
672
|
+
const settings = {};
|
|
673
|
+
for (const [id, value] of Object.entries(decisions)) {
|
|
674
|
+
if (value === "ephemeral" ||
|
|
675
|
+
value === "permanent" ||
|
|
676
|
+
value === "skip" ||
|
|
677
|
+
value === "repo" ||
|
|
678
|
+
value === "auto") {
|
|
679
|
+
settings[id] = value;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
if (Object.keys(settings).length > 0) {
|
|
683
|
+
const merged = await persistAnalyzerSettings(params.artifactsDir, settings);
|
|
684
|
+
analyzers = merged.analyzers;
|
|
685
|
+
}
|
|
686
|
+
await unlink(decisionsPath).catch(() => { });
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
return {
|
|
690
|
+
kind: "analyzer_install",
|
|
691
|
+
state,
|
|
692
|
+
bundle,
|
|
693
|
+
unresolved,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
// Phase 4B — optional edge-reasoning producing turn. Once analyzer installs
|
|
697
|
+
// are resolved, if the flag is on and the floor carries low-confidence
|
|
698
|
+
// (< 0.65) edges, emit one bounded host turn (subagent dispatch or a single
|
|
699
|
+
// host step) to produce reason rewrites, then re-run. The enrichment
|
|
700
|
+
// executor applies the host-supplied rewrites in the SAME advanceAudit call
|
|
701
|
+
// that merges analyzer edges and writes analyzer_capability, so graph_bundle
|
|
702
|
+
// and its marker stay revision-consistent (no staleness loop). Flag off or
|
|
703
|
+
// no candidates → fall through and run the executor with no rewrites.
|
|
704
|
+
if (params.graphLlmEdgeReasoning === true && bundle.graph_bundle) {
|
|
705
|
+
const candidates = collectLowConfidenceEdges(bundle.graph_bundle);
|
|
706
|
+
if (candidates.length > 0) {
|
|
707
|
+
const edgeReasoningResultsPath = join(params.artifactsDir, "incoming", "edge-reasoning.json");
|
|
708
|
+
let edgeReasoningResults;
|
|
709
|
+
try {
|
|
710
|
+
edgeReasoningResults = await readJsonFile(edgeReasoningResultsPath);
|
|
711
|
+
}
|
|
712
|
+
catch (error) {
|
|
713
|
+
if (!isFileMissingError(error))
|
|
714
|
+
throw error;
|
|
715
|
+
}
|
|
716
|
+
if (edgeReasoningResults) {
|
|
717
|
+
await runAuditStep({
|
|
718
|
+
root: params.root,
|
|
719
|
+
artifactsDir: params.artifactsDir,
|
|
720
|
+
analyzers,
|
|
721
|
+
graphLlmEdgeReasoning: true,
|
|
722
|
+
edgeReasoningResultsPath,
|
|
723
|
+
since: params.since,
|
|
724
|
+
opentoken: params.opentoken,
|
|
725
|
+
});
|
|
726
|
+
await unlink(edgeReasoningResultsPath).catch(() => { });
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
return { kind: "edge_reasoning", state, bundle, candidates };
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
// No undecided installs (and no pending edge reasoning): fall through to run
|
|
733
|
+
// the executor below (it installs for ephemeral/permanent, uses repo/cache,
|
|
734
|
+
// skips the rest).
|
|
735
|
+
}
|
|
624
736
|
if (decision.selected_executor === "design_review") {
|
|
625
737
|
const findingsPath = join(params.artifactsDir, "incoming", "design-review-findings.json");
|
|
626
738
|
let reviewFindings;
|
|
@@ -647,6 +759,36 @@ async function runDeterministicForNextStep(params) {
|
|
|
647
759
|
bundle,
|
|
648
760
|
};
|
|
649
761
|
}
|
|
762
|
+
if (decision.selected_executor === "synthesis_narrative_executor") {
|
|
763
|
+
const narrativePath = join(params.artifactsDir, "incoming", "synthesis-narrative.json");
|
|
764
|
+
let narrativeResults;
|
|
765
|
+
try {
|
|
766
|
+
narrativeResults = await readJsonFile(narrativePath);
|
|
767
|
+
}
|
|
768
|
+
catch (error) {
|
|
769
|
+
if (!isFileMissingError(error))
|
|
770
|
+
throw error;
|
|
771
|
+
}
|
|
772
|
+
if (narrativeResults) {
|
|
773
|
+
await runAuditStep({
|
|
774
|
+
root: params.root,
|
|
775
|
+
artifactsDir: params.artifactsDir,
|
|
776
|
+
preferredExecutor: "synthesis_narrative_executor",
|
|
777
|
+
narrativeResultsPath: narrativePath,
|
|
778
|
+
opentoken: params.opentoken,
|
|
779
|
+
});
|
|
780
|
+
await unlink(narrativePath).catch(() => { });
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
if (params.narrativeEnabled) {
|
|
784
|
+
return {
|
|
785
|
+
kind: "synthesis_narrative",
|
|
786
|
+
state,
|
|
787
|
+
bundle,
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
// Narrative disabled: fall through so the deterministic omit runs below.
|
|
791
|
+
}
|
|
650
792
|
if (decision.selected_executor === "agent") {
|
|
651
793
|
return {
|
|
652
794
|
kind: "semantic_review",
|
|
@@ -682,6 +824,9 @@ async function runDeterministicForNextStep(params) {
|
|
|
682
824
|
result = await runAuditStep({
|
|
683
825
|
root: params.root,
|
|
684
826
|
artifactsDir: params.artifactsDir,
|
|
827
|
+
analyzers,
|
|
828
|
+
graphLlmEdgeReasoning: params.graphLlmEdgeReasoning,
|
|
829
|
+
since: params.since,
|
|
685
830
|
opentoken: params.opentoken,
|
|
686
831
|
});
|
|
687
832
|
}
|
|
@@ -734,6 +879,89 @@ async function runDeterministicForNextStep(params) {
|
|
|
734
879
|
reason: `Reached max run limit (${params.maxRuns}) before a review, report, or blocker step was ready.`,
|
|
735
880
|
};
|
|
736
881
|
}
|
|
882
|
+
// Renders the actionable semantic-review step (packet dispatch or single-task
|
|
883
|
+
// fallback) and writes steps/current-step.json. Shared by next-step and
|
|
884
|
+
// run-to-completion so the backend produces the actionable step itself rather
|
|
885
|
+
// than handing the host a second command. Host dispatch capability is resolved
|
|
886
|
+
// by the caller (flag -> session config -> env -> default true) and is never
|
|
887
|
+
// required from the host to make progress.
|
|
888
|
+
async function renderSemanticReviewStep(params) {
|
|
889
|
+
const { root, artifactsDir, activeReviewRun } = params;
|
|
890
|
+
if (!params.hostCanDispatch) {
|
|
891
|
+
const singleTaskPromptPath = join(artifactsDir, "dispatch", "current-single-task-prompt.md");
|
|
892
|
+
const workerCommand = renderCommand(activeReviewRun.worker_command);
|
|
893
|
+
return writeCurrentStep({
|
|
894
|
+
artifactsDir,
|
|
895
|
+
stepKind: "single_task_fallback",
|
|
896
|
+
status: "ready",
|
|
897
|
+
runId: activeReviewRun.run_id,
|
|
898
|
+
allowedCommands: [workerCommand],
|
|
899
|
+
stopCondition: "Run the exact worker_command after one result, then stop without looping.",
|
|
900
|
+
repoRoot: root,
|
|
901
|
+
artifactPaths: {
|
|
902
|
+
active_review_task: activeReviewRun.task_path,
|
|
903
|
+
active_review_prompt: activeReviewRun.prompt_path,
|
|
904
|
+
pending_audit_tasks: activeReviewRun.pending_audit_tasks_path ?? null,
|
|
905
|
+
audit_results: activeReviewRun.audit_results_path,
|
|
906
|
+
single_task_prompt: singleTaskPromptPath,
|
|
907
|
+
},
|
|
908
|
+
prompt: renderSingleTaskFallbackStepPrompt({
|
|
909
|
+
singleTaskPromptPath,
|
|
910
|
+
activeReviewRun,
|
|
911
|
+
}),
|
|
912
|
+
access: {
|
|
913
|
+
read_paths: [singleTaskPromptPath],
|
|
914
|
+
write_paths: [activeReviewRun.audit_results_path],
|
|
915
|
+
},
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
const dispatch = await prepareDispatchArtifacts({
|
|
919
|
+
packageRoot,
|
|
920
|
+
runId: activeReviewRun.run_id,
|
|
921
|
+
artifactsDir,
|
|
922
|
+
root,
|
|
923
|
+
hostActiveSubagentLimit: params.hostMaxActiveSubagents,
|
|
924
|
+
});
|
|
925
|
+
const mergeCommand = mergeAndIngestCommand(artifactsDir, activeReviewRun.run_id);
|
|
926
|
+
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
927
|
+
return writeCurrentStep({
|
|
928
|
+
artifactsDir,
|
|
929
|
+
stepKind: "dispatch_review",
|
|
930
|
+
status: "ready",
|
|
931
|
+
runId: activeReviewRun.run_id,
|
|
932
|
+
allowedCommands: [
|
|
933
|
+
"auditor_merge_and_ingest",
|
|
934
|
+
"auditor_continue_audit",
|
|
935
|
+
mergeCommand,
|
|
936
|
+
continueCommand,
|
|
937
|
+
],
|
|
938
|
+
stopCondition: "Dispatch every packet, run merge-and-ingest once, then run next-step.",
|
|
939
|
+
repoRoot: root,
|
|
940
|
+
artifactPaths: {
|
|
941
|
+
dispatch_plan: dispatch.dispatch_plan_path,
|
|
942
|
+
dispatch_quota: dispatch.dispatch_quota_path,
|
|
943
|
+
dispatch_warnings: dispatch.dispatch_warnings_path,
|
|
944
|
+
active_review_task: activeReviewRun.task_path,
|
|
945
|
+
pending_audit_tasks: activeReviewRun.pending_audit_tasks_path ?? null,
|
|
946
|
+
},
|
|
947
|
+
prompt: renderDispatchReviewPrompt({
|
|
948
|
+
root,
|
|
949
|
+
artifactsDir,
|
|
950
|
+
activeReviewRun,
|
|
951
|
+
dispatchPlanPath: dispatch.dispatch_plan_path,
|
|
952
|
+
dispatchQuotaPath: dispatch.dispatch_quota_path,
|
|
953
|
+
hostCanRestrictSubagentTools: params.hostCanRestrictSubagentTools,
|
|
954
|
+
hostCanSelectSubagentModel: params.hostCanSelectSubagentModel,
|
|
955
|
+
}),
|
|
956
|
+
access: {
|
|
957
|
+
read_paths: [
|
|
958
|
+
dispatch.dispatch_plan_path,
|
|
959
|
+
...(dispatch.dispatch_quota_path ? [dispatch.dispatch_quota_path] : []),
|
|
960
|
+
],
|
|
961
|
+
write_paths: [],
|
|
962
|
+
},
|
|
963
|
+
});
|
|
964
|
+
}
|
|
737
965
|
async function cmdNextStep(argv) {
|
|
738
966
|
const root = getRootDir(argv);
|
|
739
967
|
warnIfNotGitRepo(root);
|
|
@@ -783,6 +1011,10 @@ async function cmdNextStep(argv) {
|
|
|
783
1011
|
timeoutMs: getTimeoutMs(argv, sessionConfig),
|
|
784
1012
|
maxRuns: getMaxRuns(argv),
|
|
785
1013
|
opentoken: sessionConfig.opentoken?.enabled,
|
|
1014
|
+
narrativeEnabled: sessionConfig.synthesis?.narrative !== false,
|
|
1015
|
+
analyzers: sessionConfig.analyzers,
|
|
1016
|
+
graphLlmEdgeReasoning: sessionConfig.graph?.llm_edge_reasoning,
|
|
1017
|
+
since: getFlag(argv, "--since"),
|
|
786
1018
|
});
|
|
787
1019
|
if (result.kind === "complete") {
|
|
788
1020
|
const step = await writeCurrentStep({
|
|
@@ -850,81 +1082,138 @@ async function cmdNextStep(argv) {
|
|
|
850
1082
|
console.log(JSON.stringify(step, null, 2));
|
|
851
1083
|
return;
|
|
852
1084
|
}
|
|
853
|
-
if (
|
|
854
|
-
const
|
|
855
|
-
|
|
1085
|
+
if (result.kind === "analyzer_install") {
|
|
1086
|
+
const decisionsPath = join(artifactsDir, "incoming", "analyzer-decisions.json");
|
|
1087
|
+
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
1088
|
+
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
856
1089
|
const step = await writeCurrentStep({
|
|
857
1090
|
artifactsDir,
|
|
858
|
-
stepKind: "
|
|
1091
|
+
stepKind: "analyzer_install",
|
|
859
1092
|
status: "ready",
|
|
860
|
-
runId:
|
|
861
|
-
allowedCommands: [
|
|
862
|
-
stopCondition: "
|
|
1093
|
+
runId: null,
|
|
1094
|
+
allowedCommands: [continueCommand],
|
|
1095
|
+
stopCondition: "Write analyzer install decisions to the results path, then run next-step.",
|
|
863
1096
|
repoRoot: root,
|
|
864
1097
|
artifactPaths: {
|
|
865
|
-
|
|
866
|
-
active_review_prompt: result.activeReviewRun.prompt_path,
|
|
867
|
-
pending_audit_tasks: result.activeReviewRun.pending_audit_tasks_path ?? null,
|
|
868
|
-
audit_results: result.activeReviewRun.audit_results_path,
|
|
869
|
-
single_task_prompt: singleTaskPromptPath,
|
|
1098
|
+
analyzer_decisions: decisionsPath,
|
|
870
1099
|
},
|
|
871
|
-
prompt:
|
|
872
|
-
|
|
873
|
-
|
|
1100
|
+
prompt: renderAnalyzerInstallPrompt({
|
|
1101
|
+
unresolved: result.unresolved,
|
|
1102
|
+
decisionsPath,
|
|
1103
|
+
continueCommand,
|
|
1104
|
+
}),
|
|
1105
|
+
});
|
|
1106
|
+
console.log(JSON.stringify(step, null, 2));
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
if (result.kind === "edge_reasoning") {
|
|
1110
|
+
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
1111
|
+
const edgeReasoningResultsPath = join(artifactsDir, "incoming", "edge-reasoning.json");
|
|
1112
|
+
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
1113
|
+
const basePrompt = buildEdgeReasoningPrompt(result.candidates);
|
|
1114
|
+
const contentHash = edgeReasoningContentHash(result.candidates);
|
|
1115
|
+
if (hostCanDispatch) {
|
|
1116
|
+
// Dispatch path: isolate the (potentially large) edge-list prompt in a file
|
|
1117
|
+
// and have the host fan it out to one subagent, mirroring the packet review
|
|
1118
|
+
// dispatch contract. The subagent writes the rewrites file; next-step applies.
|
|
1119
|
+
const edgeReasoningPromptPath = join(artifactsDir, "incoming", "edge-reasoning-prompt.md");
|
|
1120
|
+
await writeFile(edgeReasoningPromptPath, basePrompt, "utf8");
|
|
1121
|
+
const step = await writeCurrentStep({
|
|
1122
|
+
artifactsDir,
|
|
1123
|
+
stepKind: "edge_reasoning_dispatch",
|
|
1124
|
+
status: "ready",
|
|
1125
|
+
runId: null,
|
|
1126
|
+
allowedCommands: [continueCommand],
|
|
1127
|
+
stopCondition: "Dispatch one subagent to write the edge-reasoning rewrites, then run next-step.",
|
|
1128
|
+
repoRoot: root,
|
|
1129
|
+
artifactPaths: {
|
|
1130
|
+
edge_reasoning_prompt: edgeReasoningPromptPath,
|
|
1131
|
+
edge_reasoning_results: edgeReasoningResultsPath,
|
|
1132
|
+
},
|
|
1133
|
+
prompt: renderEdgeReasoningDispatchPrompt({
|
|
1134
|
+
promptPath: edgeReasoningPromptPath,
|
|
1135
|
+
resultsPath: edgeReasoningResultsPath,
|
|
1136
|
+
continueCommand,
|
|
1137
|
+
contentHash,
|
|
1138
|
+
candidateCount: result.candidates.length,
|
|
1139
|
+
}),
|
|
1140
|
+
access: {
|
|
1141
|
+
read_paths: [edgeReasoningPromptPath],
|
|
1142
|
+
write_paths: [edgeReasoningResultsPath],
|
|
1143
|
+
},
|
|
1144
|
+
});
|
|
1145
|
+
console.log(JSON.stringify(step, null, 2));
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
// One-step fallback (no callable subagent facility): the host produces the
|
|
1149
|
+
// rewrites itself in a single bounded turn, mirroring the narrative step.
|
|
1150
|
+
const step = await writeCurrentStep({
|
|
1151
|
+
artifactsDir,
|
|
1152
|
+
stepKind: "edge_reasoning",
|
|
1153
|
+
status: "ready",
|
|
1154
|
+
runId: null,
|
|
1155
|
+
allowedCommands: [continueCommand],
|
|
1156
|
+
stopCondition: "Write the edge-reasoning rewrites to the results path, then run next-step.",
|
|
1157
|
+
repoRoot: root,
|
|
1158
|
+
artifactPaths: {
|
|
1159
|
+
edge_reasoning_results: edgeReasoningResultsPath,
|
|
1160
|
+
},
|
|
1161
|
+
prompt: renderEdgeReasoningStepPrompt({
|
|
1162
|
+
basePrompt,
|
|
1163
|
+
resultsPath: edgeReasoningResultsPath,
|
|
1164
|
+
continueCommand,
|
|
1165
|
+
contentHash,
|
|
874
1166
|
}),
|
|
875
1167
|
access: {
|
|
876
|
-
read_paths: [
|
|
877
|
-
write_paths: [
|
|
1168
|
+
read_paths: [],
|
|
1169
|
+
write_paths: [edgeReasoningResultsPath],
|
|
878
1170
|
},
|
|
879
1171
|
});
|
|
880
1172
|
console.log(JSON.stringify(step, null, 2));
|
|
881
1173
|
return;
|
|
882
1174
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
artifactsDir
|
|
1175
|
+
if (result.kind === "synthesis_narrative") {
|
|
1176
|
+
const narrativeResultsPath = join(artifactsDir, "incoming", "synthesis-narrative.json");
|
|
1177
|
+
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
1178
|
+
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
1179
|
+
const basePrompt = result.bundle.audit_findings
|
|
1180
|
+
? renderSynthesisNarrativePrompt(result.bundle.audit_findings)
|
|
1181
|
+
: "# Synthesis narrative\n\nNo findings report is available; write an empty themes array.";
|
|
1182
|
+
const fullPrompt = [
|
|
1183
|
+
basePrompt,
|
|
1184
|
+
"## Results path",
|
|
1185
|
+
"",
|
|
1186
|
+
"Write the SynthesisNarrative JSON object to:",
|
|
1187
|
+
"",
|
|
1188
|
+
` ${narrativeResultsPath}`,
|
|
1189
|
+
"",
|
|
1190
|
+
`Then run: ${continueCommand}`,
|
|
1191
|
+
"",
|
|
1192
|
+
].join("\n");
|
|
1193
|
+
const step = await writeCurrentStep({
|
|
1194
|
+
artifactsDir,
|
|
1195
|
+
stepKind: "synthesis_narrative",
|
|
1196
|
+
status: "ready",
|
|
1197
|
+
runId: null,
|
|
1198
|
+
allowedCommands: [continueCommand],
|
|
1199
|
+
stopCondition: "Write the synthesis narrative to the results path, then run next-step.",
|
|
1200
|
+
repoRoot: root,
|
|
1201
|
+
artifactPaths: {
|
|
1202
|
+
synthesis_narrative_results: narrativeResultsPath,
|
|
1203
|
+
},
|
|
1204
|
+
prompt: fullPrompt,
|
|
1205
|
+
});
|
|
1206
|
+
console.log(JSON.stringify(step, null, 2));
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
const step = await renderSemanticReviewStep({
|
|
887
1210
|
root,
|
|
888
|
-
hostActiveSubagentLimit: hostMaxActiveSubagents,
|
|
889
|
-
});
|
|
890
|
-
const mergeCommand = mergeAndIngestCommand(artifactsDir, result.activeReviewRun.run_id);
|
|
891
|
-
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
892
|
-
const step = await writeCurrentStep({
|
|
893
1211
|
artifactsDir,
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
"auditor_continue_audit",
|
|
900
|
-
mergeCommand,
|
|
901
|
-
continueCommand,
|
|
902
|
-
],
|
|
903
|
-
stopCondition: "Dispatch every packet, run merge-and-ingest once, then run next-step.",
|
|
904
|
-
repoRoot: root,
|
|
905
|
-
artifactPaths: {
|
|
906
|
-
dispatch_plan: dispatch.dispatch_plan_path,
|
|
907
|
-
dispatch_quota: dispatch.dispatch_quota_path,
|
|
908
|
-
dispatch_warnings: dispatch.dispatch_warnings_path,
|
|
909
|
-
active_review_task: result.activeReviewRun.task_path,
|
|
910
|
-
pending_audit_tasks: result.activeReviewRun.pending_audit_tasks_path ?? null,
|
|
911
|
-
},
|
|
912
|
-
prompt: renderDispatchReviewPrompt({
|
|
913
|
-
root,
|
|
914
|
-
artifactsDir,
|
|
915
|
-
activeReviewRun: result.activeReviewRun,
|
|
916
|
-
dispatchPlanPath: dispatch.dispatch_plan_path,
|
|
917
|
-
dispatchQuotaPath: dispatch.dispatch_quota_path,
|
|
918
|
-
hostCanRestrictSubagentTools,
|
|
919
|
-
hostCanSelectSubagentModel,
|
|
920
|
-
}),
|
|
921
|
-
access: {
|
|
922
|
-
read_paths: [
|
|
923
|
-
dispatch.dispatch_plan_path,
|
|
924
|
-
...(dispatch.dispatch_quota_path ? [dispatch.dispatch_quota_path] : []),
|
|
925
|
-
],
|
|
926
|
-
write_paths: [],
|
|
927
|
-
},
|
|
1212
|
+
activeReviewRun: result.activeReviewRun,
|
|
1213
|
+
hostCanDispatch,
|
|
1214
|
+
hostMaxActiveSubagents,
|
|
1215
|
+
hostCanRestrictSubagentTools,
|
|
1216
|
+
hostCanSelectSubagentModel,
|
|
928
1217
|
});
|
|
929
1218
|
console.log(JSON.stringify(step, null, 2));
|
|
930
1219
|
}
|
|
@@ -1092,6 +1381,37 @@ async function cmdRunToCompletion(argv) {
|
|
|
1092
1381
|
const blockPrompt = renderWorkerPrompt(blockTask);
|
|
1093
1382
|
await writeWorkerTaskFiles(blockTask, blockPrompt, blockPaths, artifactsDir, blockPendingTasks);
|
|
1094
1383
|
await writeJsonFile(blockPendingTasksPath, blockPendingTasks);
|
|
1384
|
+
const reviewRun = {
|
|
1385
|
+
run_id: blockRunId,
|
|
1386
|
+
task_path: blockPaths.taskPath,
|
|
1387
|
+
prompt_path: blockPaths.promptPath,
|
|
1388
|
+
pending_audit_tasks_path: blockPendingTasksPath,
|
|
1389
|
+
audit_results_path: blockAuditResultsPath,
|
|
1390
|
+
worker_command: blockTask.worker_command,
|
|
1391
|
+
};
|
|
1392
|
+
// Render the actionable dispatch / single-task step here instead of
|
|
1393
|
+
// leaving the host to issue next-step as a second command. Capability is
|
|
1394
|
+
// resolved from flags/config/env with a sane default, so nothing is
|
|
1395
|
+
// required from the host to make progress. If rendering fails we still
|
|
1396
|
+
// emit the hand-off below — run-to-completion is never worse than before,
|
|
1397
|
+
// and next-step will re-render and surface the error loudly.
|
|
1398
|
+
try {
|
|
1399
|
+
await renderSemanticReviewStep({
|
|
1400
|
+
root,
|
|
1401
|
+
artifactsDir,
|
|
1402
|
+
activeReviewRun: reviewRun,
|
|
1403
|
+
hostCanDispatch: resolveHostDispatchCapability({
|
|
1404
|
+
explicit: getOptionalBooleanFlag(argv, "--host-can-dispatch-subagents"),
|
|
1405
|
+
sessionConfig,
|
|
1406
|
+
}),
|
|
1407
|
+
hostMaxActiveSubagents: getHostMaxActiveSubagents(argv),
|
|
1408
|
+
hostCanRestrictSubagentTools: getOptionalBooleanFlag(argv, "--host-can-restrict-subagent-tools") ?? false,
|
|
1409
|
+
hostCanSelectSubagentModel: getOptionalBooleanFlag(argv, "--host-can-select-subagent-model") ?? false,
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
catch (stepError) {
|
|
1413
|
+
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`);
|
|
1414
|
+
}
|
|
1095
1415
|
await emitEnvelope({
|
|
1096
1416
|
root,
|
|
1097
1417
|
artifactsDir,
|
|
@@ -1107,14 +1427,7 @@ async function cmdRunToCompletion(argv) {
|
|
|
1107
1427
|
progress_summary: blocker,
|
|
1108
1428
|
next_likely_step: null,
|
|
1109
1429
|
providerName: provider.name,
|
|
1110
|
-
activeReviewRun:
|
|
1111
|
-
run_id: blockRunId,
|
|
1112
|
-
task_path: blockPaths.taskPath,
|
|
1113
|
-
prompt_path: blockPaths.promptPath,
|
|
1114
|
-
pending_audit_tasks_path: blockPendingTasksPath,
|
|
1115
|
-
audit_results_path: blockAuditResultsPath,
|
|
1116
|
-
worker_command: blockTask.worker_command,
|
|
1117
|
-
},
|
|
1430
|
+
activeReviewRun: reviewRun,
|
|
1118
1431
|
});
|
|
1119
1432
|
return;
|
|
1120
1433
|
}
|
|
@@ -1151,7 +1464,8 @@ async function cmdRunToCompletion(argv) {
|
|
|
1151
1464
|
const quotaStateEntry = quotaState.entries[providerModelKey] ?? null;
|
|
1152
1465
|
const allCandidateTasks = buildPendingAuditTasks(bundle);
|
|
1153
1466
|
const candidateGroups = chunkArray(allCandidateTasks.slice(0, parallelWorkers * agentBatchSize), agentBatchSize);
|
|
1154
|
-
const
|
|
1467
|
+
const candidateSizeIndex = sizeIndexFromManifest(bundle.repo_manifest);
|
|
1468
|
+
const slotTokenEstimates = candidateGroups.map((g) => estimateTaskGroupTokens(g, candidateSizeIndex));
|
|
1155
1469
|
const providerLimits = await provider.queryLimits?.(hostModel)
|
|
1156
1470
|
.then((r) => r ? { ...r, source: "provider_query" } : null)
|
|
1157
1471
|
.catch(() => null)
|
|
@@ -1457,6 +1771,8 @@ async function cmdRunToCompletion(argv) {
|
|
|
1457
1771
|
auditResultsPath,
|
|
1458
1772
|
runtimeUpdatesPath,
|
|
1459
1773
|
externalAnalyzerPath,
|
|
1774
|
+
analyzers: sessionConfig.analyzers,
|
|
1775
|
+
since: getFlag(argv, "--since"),
|
|
1460
1776
|
});
|
|
1461
1777
|
workerResult = {
|
|
1462
1778
|
contract_version: WORKER_RESULT_CONTRACT_VERSION,
|
|
@@ -2290,7 +2606,11 @@ async function cmdIntake(argv) {
|
|
|
2290
2606
|
}
|
|
2291
2607
|
async function cmdPlan(argv) {
|
|
2292
2608
|
const artifactsDir = getArtifactsDir(argv);
|
|
2293
|
-
const result = await runAuditStep({
|
|
2609
|
+
const result = await runAuditStep({
|
|
2610
|
+
root: getRootDir(argv),
|
|
2611
|
+
artifactsDir,
|
|
2612
|
+
since: getFlag(argv, "--since"),
|
|
2613
|
+
});
|
|
2294
2614
|
console.log(JSON.stringify({
|
|
2295
2615
|
artifacts_dir: artifactsDir,
|
|
2296
2616
|
selected_executor: result.selected_executor,
|