auditor-lambda 0.10.2 → 0.10.7
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/audit-code-wrapper-build.mjs +198 -0
- package/audit-code-wrapper-install-hosts.mjs +1140 -0
- package/audit-code-wrapper-io.mjs +155 -0
- package/audit-code-wrapper-legacy.mjs +125 -0
- package/audit-code-wrapper-lib.mjs +17 -1801
- package/audit-code-wrapper-opencode.mjs +256 -0
- package/dispatch/merge-results.mjs +5 -3
- package/dispatch/validate-result.mjs +2 -2
- package/dist/adapters/coverageSummary.js +6 -2
- package/dist/adapters/normalizeExternal.js +16 -1
- package/dist/adapters/npmAudit.js +20 -9
- package/dist/adapters/semgrep.js +26 -1
- package/dist/cli/advanceAuditCommand.d.ts +1 -0
- package/dist/cli/advanceAuditCommand.js +95 -0
- package/dist/cli/args.js +1 -2
- package/dist/cli/auditStep.js +2 -2
- package/dist/cli/cleanup.d.ts +11 -1
- package/dist/cli/cleanup.js +25 -5
- package/dist/cli/cleanupCommand.d.ts +1 -0
- package/dist/cli/cleanupCommand.js +24 -0
- package/dist/cli/dispatch.d.ts +55 -31
- package/dist/cli/dispatch.js +298 -241
- package/dist/cli/dispatchStatusCommand.d.ts +1 -0
- package/dist/cli/dispatchStatusCommand.js +68 -0
- package/dist/cli/explainTaskCommand.d.ts +1 -0
- package/dist/cli/explainTaskCommand.js +33 -0
- package/dist/cli/importExternalAnalyzerCommand.d.ts +1 -0
- package/dist/cli/importExternalAnalyzerCommand.js +20 -0
- package/dist/cli/ingestResultsCommand.d.ts +1 -0
- package/dist/cli/ingestResultsCommand.js +34 -0
- package/dist/cli/intakeCommand.d.ts +1 -0
- package/dist/cli/intakeCommand.js +17 -0
- package/dist/cli/lineIndex.js +19 -12
- package/dist/cli/mergeAndIngestCommand.js +68 -26
- package/dist/cli/nextStepCommand.d.ts +139 -0
- package/dist/cli/nextStepCommand.js +281 -232
- package/dist/cli/planCommand.d.ts +1 -0
- package/dist/cli/planCommand.js +16 -0
- package/dist/cli/prepareDispatchCommand.d.ts +1 -0
- package/dist/cli/prepareDispatchCommand.js +25 -0
- package/dist/cli/quotaCommand.d.ts +1 -0
- package/dist/cli/quotaCommand.js +56 -0
- package/dist/cli/requeueCommand.d.ts +1 -0
- package/dist/cli/requeueCommand.js +10 -0
- package/dist/cli/runToCompletion.js +451 -412
- package/dist/cli/sampleRunCommand.d.ts +1 -0
- package/dist/cli/sampleRunCommand.js +93 -0
- package/dist/cli/statusCommand.js +1 -1
- package/dist/cli/steps.js +4 -1
- package/dist/cli/submitPacketCommand.js +16 -15
- package/dist/cli/synthesizeCommand.d.ts +1 -0
- package/dist/cli/synthesizeCommand.js +15 -0
- package/dist/cli/updateRuntimeValidationCommand.d.ts +1 -0
- package/dist/cli/updateRuntimeValidationCommand.js +16 -0
- package/dist/cli/validateCommand.d.ts +1 -0
- package/dist/cli/validateCommand.js +41 -0
- package/dist/cli/validateResultCommand.d.ts +1 -0
- package/dist/cli/validateResultCommand.js +63 -0
- package/dist/cli/validateResultsCommand.d.ts +1 -0
- package/dist/cli/validateResultsCommand.js +31 -0
- package/dist/cli/workerRunCommand.d.ts +15 -1
- package/dist/cli/workerRunCommand.js +40 -4
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +21 -628
- package/dist/coverage.js +7 -3
- package/dist/extractors/analyzers/css.js +2 -2
- package/dist/extractors/analyzers/html.js +2 -2
- package/dist/extractors/analyzers/python.js +2 -2
- package/dist/extractors/analyzers/registry.js +17 -36
- package/dist/extractors/analyzers/treeSitter.d.ts +10 -1
- package/dist/extractors/analyzers/treeSitter.js +28 -6
- package/dist/extractors/analyzers/typescript.js +104 -85
- package/dist/extractors/browserExtension.js +4 -1
- package/dist/extractors/designAssessment.js +21 -21
- package/dist/extractors/fsIntake.js +34 -10
- package/dist/extractors/graph.js +17 -7
- package/dist/extractors/graphManifestEdges/cargo.d.ts +4 -0
- package/dist/extractors/graphManifestEdges/cargo.js +107 -0
- package/dist/extractors/graphManifestEdges/go.d.ts +5 -0
- package/dist/extractors/graphManifestEdges/go.js +151 -0
- package/dist/extractors/graphManifestEdges/index.d.ts +8 -0
- package/dist/extractors/graphManifestEdges/index.js +11 -0
- package/dist/extractors/graphManifestEdges/jsonc.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/jsonc.js +97 -0
- package/dist/extractors/graphManifestEdges/maven.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/maven.js +73 -0
- package/dist/extractors/graphManifestEdges/packageJson.d.ts +19 -0
- package/dist/extractors/graphManifestEdges/packageJson.js +204 -0
- package/dist/extractors/graphManifestEdges/pnpm.d.ts +2 -0
- package/dist/extractors/graphManifestEdges/pnpm.js +42 -0
- package/dist/extractors/graphManifestEdges/pyproject.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/pyproject.js +83 -0
- package/dist/extractors/graphManifestEdges/toml.d.ts +4 -0
- package/dist/extractors/graphManifestEdges/toml.js +68 -0
- package/dist/extractors/graphManifestEdges/typescript.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/typescript.js +56 -0
- package/dist/extractors/graphManifestEdges/workspace.d.ts +10 -0
- package/dist/extractors/graphManifestEdges/workspace.js +72 -0
- package/dist/extractors/graphManifestEdges/yaml.d.ts +3 -0
- package/dist/extractors/graphManifestEdges/yaml.js +59 -0
- package/dist/extractors/graphManifestEdges/yamlPaths.d.ts +4 -0
- package/dist/extractors/graphManifestEdges/yamlPaths.js +89 -0
- package/dist/extractors/graphPythonImports.js +4 -20
- package/dist/extractors/pathPatterns.js +3 -13
- package/dist/io/artifacts.d.ts +1 -1
- package/dist/io/artifacts.js +4 -1
- package/dist/io/runArtifacts.d.ts +8 -2
- package/dist/io/runArtifacts.js +103 -69
- package/dist/io/toolingManifest.js +2 -1
- package/dist/orchestrator/advance.js +36 -0
- package/dist/orchestrator/artifactFreshness.d.ts +1 -1
- package/dist/orchestrator/artifactFreshness.js +1 -1
- package/dist/orchestrator/artifactMetadata.js +5 -5
- package/dist/orchestrator/auditTaskUtils.d.ts +4 -0
- package/dist/orchestrator/auditTaskUtils.js +8 -12
- package/dist/orchestrator/autoFixExecutor.js +40 -26
- package/dist/orchestrator/dependencyMap.js +1 -1
- package/dist/orchestrator/executorResult.d.ts +33 -0
- package/dist/orchestrator/executors.d.ts +7 -0
- package/dist/orchestrator/executors.js +24 -0
- package/dist/orchestrator/fileAnchors.js +42 -29
- package/dist/orchestrator/fileIntegrity.js +6 -1
- package/dist/orchestrator/flowCoverage.js +1 -2
- package/dist/orchestrator/flowPlanning.js +8 -4
- package/dist/orchestrator/graphEnrichmentExecutor.js +67 -45
- package/dist/orchestrator/ingestionExecutors.js +9 -1
- package/dist/orchestrator/intakeExecutors.d.ts +0 -4
- package/dist/orchestrator/intakeExecutors.js +24 -14
- package/dist/orchestrator/localCommands.d.ts +1 -0
- package/dist/orchestrator/localCommands.js +10 -17
- package/dist/orchestrator/nextStep.js +3 -1
- package/dist/orchestrator/requeueCommand.js +4 -0
- package/dist/orchestrator/reviewPacketGraph.js +50 -18
- package/dist/orchestrator/reviewPackets.js +10 -8
- package/dist/orchestrator/runtimeCommand.js +35 -7
- package/dist/orchestrator/runtimeValidationUpdate.js +6 -0
- package/dist/orchestrator/selectiveDeepening/highRiskClean.js +3 -2
- package/dist/orchestrator/selectiveDeepening/lensVerification.js +44 -18
- package/dist/orchestrator/staleness.js +3 -3
- package/dist/orchestrator/state.js +1 -1
- package/dist/orchestrator/syntaxResolutionExecutor.js +17 -24
- package/dist/orchestrator/synthesisExecutors.js +1 -0
- package/dist/orchestrator/taskBuilder.js +5 -4
- package/dist/providers/claudeCodeProvider.js +4 -1
- package/dist/providers/opencodeProvider.js +4 -1
- package/dist/quota/discoveredLimits.js +3 -3
- package/dist/quota/headerExtraction.js +5 -2
- package/dist/quota/headerExtractors/claudeCodeHeaderExtractor.js +3 -0
- package/dist/quota/headerExtractors/index.js +3 -3
- package/dist/quota/index.d.ts +3 -1
- package/dist/quota/index.js +3 -0
- package/dist/reporting/findingIdentity.d.ts +21 -0
- package/dist/reporting/findingIdentity.js +72 -0
- package/dist/reporting/findingRanks.d.ts +3 -0
- package/dist/reporting/findingRanks.js +24 -0
- package/dist/reporting/mergeFindings.js +1 -24
- package/dist/reporting/synthesis.d.ts +3 -1
- package/dist/reporting/synthesis.js +36 -7
- package/dist/reporting/synthesisNarrativePrompt.js +3 -0
- package/dist/reporting/workBlocks.js +1 -14
- package/dist/supervisor/operatorHandoff.js +2 -6
- package/dist/supervisor/runLedger.js +30 -41
- package/dist/types/activeDispatch.d.ts +31 -0
- package/dist/types/activeDispatch.js +2 -0
- package/dist/types.d.ts +21 -4
- package/dist/types.js +24 -16
- package/dist/validation/artifacts.js +3 -0
- package/dist/validation/auditResults.js +8 -2
- package/package.json +2 -2
- package/schemas/audit_findings.schema.json +5 -1
- package/schemas/audit_plan_metrics.schema.json +1 -1
- package/schemas/audit_result.schema.json +5 -6
- package/schemas/audit_task.schema.json +1 -4
- package/schemas/blind_spot_register.schema.json +1 -1
- package/schemas/coverage_matrix.schema.json +2 -8
- package/schemas/finding.schema.json +3 -17
- package/schemas/flow_coverage.schema.json +2 -8
- package/schemas/graph_bundle.schema.json +31 -0
- package/schemas/lens.schema.json +7 -0
- package/schemas/review_packets.schema.json +6 -17
- package/schemas/step_contract.schema.json +8 -2
- package/schemas/unit_manifest.schema.json +1 -4
- package/scripts/postinstall.mjs +3 -1
- package/skills/audit-code/audit-code.prompt.md +2 -3
- package/dist/extractors/graphManifestEdges.d.ts +0 -12
- package/dist/extractors/graphManifestEdges.js +0 -1135
|
@@ -5,6 +5,7 @@ import { loadArtifactBundle, promoteFinalAuditReport, writeCoreArtifacts, AUDIT_
|
|
|
5
5
|
import { advanceAudit } from "../orchestrator/advance.js";
|
|
6
6
|
import { computeArtifactStateSignature } from "../orchestrator/artifactMetadata.js";
|
|
7
7
|
import { decideNextStep } from "../orchestrator/nextStep.js";
|
|
8
|
+
import { isHostDelegationExecutor } from "../orchestrator/executors.js";
|
|
8
9
|
import { deriveAuditState } from "../orchestrator/state.js";
|
|
9
10
|
import { checkFileIntegrity } from "../orchestrator/fileIntegrity.js";
|
|
10
11
|
import { buildEdgeReasoningPrompt, collectLowConfidenceEdges, edgeReasoningContentHash, } from "../orchestrator/edgeReasoning.js";
|
|
@@ -23,54 +24,260 @@ import { renderSemanticReviewStep } from "./semanticReviewStep.js";
|
|
|
23
24
|
import { writeCurrentStep } from "./steps.js";
|
|
24
25
|
import { nextStepCommand, renderAnalyzerInstallPrompt, renderBlockedStepPrompt, renderEdgeReasoningDispatchPrompt, renderEdgeReasoningStepPrompt, renderPresentReportPrompt, } from "./prompts.js";
|
|
25
26
|
import { getArtifactsDir, getFlag, getHostMaxActiveSubagents, getMaxRuns, getOptionalBooleanFlag, getRootDir, getTimeoutMs, resolveHostDispatchCapability, warnIfNotGitRepo, } from "./args.js";
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
27
|
+
// ── Incoming-artifact helper ──────────────────────────────────────────────────
|
|
28
|
+
/**
|
|
29
|
+
* Read a JSON file from the `incoming/` subdirectory of `artifactsDir`.
|
|
30
|
+
* Returns `{ value, path }` when the file exists and parses successfully.
|
|
31
|
+
* Returns `undefined` when the file is absent (ENOENT-family errors).
|
|
32
|
+
* Re-throws all other IO errors unchanged.
|
|
33
|
+
*/
|
|
34
|
+
export async function tryConsumeIncoming(artifactsDir, filename) {
|
|
35
|
+
const filePath = join(artifactsDir, "incoming", filename);
|
|
36
|
+
try {
|
|
37
|
+
const value = await readJsonFile(filePath);
|
|
38
|
+
return { value, path: filePath };
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (isFileMissingError(error))
|
|
42
|
+
return undefined;
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ── Extracted helpers ─────────────────────────────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* Build the terminal step for a deterministic loop that has stopped advancing
|
|
49
|
+
* (hit the run backstop or the finalization cycle guard). A rendered report is
|
|
50
|
+
* the deliverable: if synthesis already produced one — or the state is formally
|
|
51
|
+
* complete — present it instead of reporting the stopped loop as a bare
|
|
52
|
+
* "blocked" failure. A completed audit must never surface as blocked just
|
|
53
|
+
* because finalization kept churning (e.g. a runtime_validation <-> synthesis
|
|
54
|
+
* ping-pong, or revision churn from filesystem retries) after the report was
|
|
55
|
+
* written. With no report yet, the stop is a genuine block.
|
|
56
|
+
*/
|
|
57
|
+
export async function buildTerminalStep(params, bundle, state, blockedReason) {
|
|
58
|
+
const reportRendered = state.status === "complete" || Boolean(bundle.audit_report);
|
|
59
|
+
await writeHandoffOnly({
|
|
60
|
+
root: params.root,
|
|
61
|
+
artifactsDir: params.artifactsDir,
|
|
62
|
+
bundle,
|
|
63
|
+
audit_state: state,
|
|
64
|
+
progress_summary: reportRendered && state.status !== "complete"
|
|
65
|
+
? `Audit report already rendered; ending run. ${blockedReason}`
|
|
66
|
+
: blockedReason,
|
|
67
|
+
providerName: LOCAL_SUBPROCESS_PROVIDER_NAME,
|
|
68
|
+
});
|
|
69
|
+
if (!reportRendered) {
|
|
70
|
+
return { kind: "blocked", state, bundle, reason: blockedReason };
|
|
71
|
+
}
|
|
72
|
+
const promoted = await promoteFinalAuditReport({
|
|
73
|
+
artifactsDir: params.artifactsDir,
|
|
74
|
+
repoRoot: params.root,
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
kind: "complete",
|
|
78
|
+
state,
|
|
79
|
+
bundle,
|
|
80
|
+
finalReportPath: promoted.promoted
|
|
81
|
+
? join(params.root, AUDIT_REPORT_FILENAME)
|
|
82
|
+
: join(params.artifactsDir, AUDIT_REPORT_FILENAME),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Handle the `graph_enrichment_executor` incoming-artifact polling block.
|
|
87
|
+
* Checks for pending analyzer install decisions and edge-reasoning results.
|
|
88
|
+
* Returns an action object:
|
|
89
|
+
* - `continue` → caller should `continue` the for-loop (already consumed an artifact).
|
|
90
|
+
* - `return` → caller should return the embedded result to cmdNextStep.
|
|
91
|
+
* - `fallthrough` → no incoming artifacts; fall through to the deterministic executor.
|
|
92
|
+
*/
|
|
93
|
+
export async function handleGraphEnrichmentBranch(params, bundle, state, analyzersRef) {
|
|
94
|
+
const includedFiles = bundle.repo_manifest
|
|
95
|
+
? [
|
|
96
|
+
...new Set(buildPathLookup(bundle.repo_manifest, buildDispositionMap(bundle.file_disposition)).values()),
|
|
97
|
+
]
|
|
98
|
+
: [];
|
|
99
|
+
const plan = resolveAnalyzerPlan(params.root, analyzersRef.value, includedFiles);
|
|
100
|
+
const unresolved = plan.filter(needsInstallDecision);
|
|
101
|
+
if (unresolved.length > 0) {
|
|
102
|
+
const incoming = await tryConsumeIncoming(params.artifactsDir, "analyzer-decisions.json");
|
|
103
|
+
if (incoming && typeof incoming.value === "object") {
|
|
104
|
+
const settings = {};
|
|
105
|
+
for (const [id, value] of Object.entries(incoming.value)) {
|
|
106
|
+
if (value === "ephemeral" ||
|
|
107
|
+
value === "permanent" ||
|
|
108
|
+
value === "skip" ||
|
|
109
|
+
value === "repo" ||
|
|
110
|
+
value === "auto") {
|
|
111
|
+
settings[id] = value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (Object.keys(settings).length > 0) {
|
|
115
|
+
const merged = await persistAnalyzerSettings(params.artifactsDir, settings);
|
|
116
|
+
analyzersRef.value = merged.analyzers;
|
|
117
|
+
}
|
|
118
|
+
await unlink(incoming.path).catch(() => { });
|
|
119
|
+
return { action: "continue" };
|
|
120
|
+
}
|
|
121
|
+
return { action: "return", result: { kind: "analyzer_install", state, bundle, unresolved } };
|
|
122
|
+
}
|
|
123
|
+
// Phase 4B — optional edge-reasoning producing turn. Once analyzer installs
|
|
124
|
+
// are resolved, if the flag is on and the floor carries low-confidence
|
|
125
|
+
// (< 0.65) edges, emit one bounded host turn (subagent dispatch or a single
|
|
126
|
+
// host step) to produce reason rewrites, then re-run. The enrichment
|
|
127
|
+
// executor applies the host-supplied rewrites in the SAME advanceAudit call
|
|
128
|
+
// that merges analyzer edges and writes analyzer_capability, so graph_bundle
|
|
129
|
+
// and its marker stay revision-consistent (no staleness loop). Flag off or
|
|
130
|
+
// no candidates → fall through and run the executor with no rewrites.
|
|
131
|
+
if (params.graphLlmEdgeReasoning === true && bundle.graph_bundle) {
|
|
132
|
+
const candidates = collectLowConfidenceEdges(bundle.graph_bundle);
|
|
133
|
+
if (candidates.length > 0) {
|
|
134
|
+
const edgeReasoningIncoming = await tryConsumeIncoming(params.artifactsDir, "edge-reasoning.json");
|
|
135
|
+
if (edgeReasoningIncoming) {
|
|
136
|
+
await runAuditStep({
|
|
137
|
+
root: params.root,
|
|
138
|
+
artifactsDir: params.artifactsDir,
|
|
139
|
+
analyzers: analyzersRef.value,
|
|
140
|
+
graphLlmEdgeReasoning: true,
|
|
141
|
+
edgeReasoningResultsPath: edgeReasoningIncoming.path,
|
|
142
|
+
since: params.since,
|
|
143
|
+
opentoken: params.opentoken,
|
|
144
|
+
});
|
|
145
|
+
await unlink(edgeReasoningIncoming.path).catch(() => { });
|
|
146
|
+
return { action: "continue" };
|
|
147
|
+
}
|
|
148
|
+
return { action: "return", result: { kind: "edge_reasoning", state, bundle, candidates } };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// No undecided installs (and no pending edge reasoning): fall through to run
|
|
152
|
+
// the executor below (it installs for ephemeral/permanent, uses repo/cache,
|
|
153
|
+
// skips the rest).
|
|
154
|
+
return { action: "fallthrough" };
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Handle the `design_review` incoming-artifact polling block.
|
|
158
|
+
* Returns `continue` if an incoming findings file was consumed, or `return`
|
|
159
|
+
* with a design_review kind when the host turn is still needed.
|
|
160
|
+
*/
|
|
161
|
+
export async function handleDesignReviewBranch(params, bundle, state) {
|
|
162
|
+
const findingsIncoming = await tryConsumeIncoming(params.artifactsDir, "design-review-findings.json");
|
|
163
|
+
if (findingsIncoming && Array.isArray(findingsIncoming.value)) {
|
|
164
|
+
const existing = bundle.design_assessment;
|
|
165
|
+
if (existing) {
|
|
166
|
+
existing.review_findings = findingsIncoming.value;
|
|
167
|
+
existing.reviewed = true;
|
|
168
|
+
await writeJsonFile(join(params.artifactsDir, "design_assessment.json"), existing);
|
|
169
|
+
await unlink(findingsIncoming.path).catch(() => { });
|
|
170
|
+
return { action: "continue" };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return { action: "return", result: { kind: "design_review", state, bundle } };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Handle the `synthesis_narrative_executor` incoming-artifact polling block.
|
|
177
|
+
* Returns `continue` if an incoming narrative file was consumed, or `return`
|
|
178
|
+
* with a synthesis_narrative kind when the host turn is still needed (and
|
|
179
|
+
* narrative is enabled), or `continue` when narrative is disabled (so the
|
|
180
|
+
* deterministic omit runs below).
|
|
181
|
+
*/
|
|
182
|
+
export async function handleSynthesisNarrativeBranch(params, bundle, state) {
|
|
183
|
+
const narrativeIncoming = await tryConsumeIncoming(params.artifactsDir, "synthesis-narrative.json");
|
|
184
|
+
if (narrativeIncoming) {
|
|
185
|
+
await runAuditStep({
|
|
49
186
|
root: params.root,
|
|
50
187
|
artifactsDir: params.artifactsDir,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
? `Audit report already rendered; ending run. ${blockedReason}`
|
|
55
|
-
: blockedReason,
|
|
56
|
-
providerName: LOCAL_SUBPROCESS_PROVIDER_NAME,
|
|
188
|
+
preferredExecutor: "synthesis_narrative_executor",
|
|
189
|
+
narrativeResultsPath: narrativeIncoming.path,
|
|
190
|
+
opentoken: params.opentoken,
|
|
57
191
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
192
|
+
await unlink(narrativeIncoming.path).catch(() => { });
|
|
193
|
+
return { action: "continue" };
|
|
194
|
+
}
|
|
195
|
+
if (params.narrativeEnabled) {
|
|
196
|
+
return { action: "return", result: { kind: "synthesis_narrative", state, bundle } };
|
|
197
|
+
}
|
|
198
|
+
// Narrative disabled: fall through so the deterministic omit runs below.
|
|
199
|
+
return { action: "continue" };
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Execute one deterministic audit step and record its progress. Throws (with
|
|
203
|
+
* cause) if the executor fails, preserving the existing throw-with-cause pattern.
|
|
204
|
+
*/
|
|
205
|
+
export async function executeAndRecord(params, analyzers, decision, index, lastSummary) {
|
|
206
|
+
try {
|
|
207
|
+
const result = await runAuditStep({
|
|
208
|
+
root: params.root,
|
|
62
209
|
artifactsDir: params.artifactsDir,
|
|
63
|
-
|
|
210
|
+
analyzers,
|
|
211
|
+
graphLlmEdgeReasoning: params.graphLlmEdgeReasoning,
|
|
212
|
+
since: params.since,
|
|
213
|
+
opentoken: params.opentoken,
|
|
64
214
|
});
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
215
|
+
await writeJsonFile(join(params.artifactsDir, "steps", "deterministic-progress.json"), {
|
|
216
|
+
iteration: index + 1,
|
|
217
|
+
max_runs: params.maxRuns,
|
|
218
|
+
last_executor: result.selected_executor,
|
|
219
|
+
last_obligation: decision.selected_obligation,
|
|
220
|
+
progress_made: result.progress_made,
|
|
221
|
+
summary: result.progress_summary,
|
|
222
|
+
timestamp: new Date().toISOString(),
|
|
223
|
+
});
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
const current = await loadArtifactBundle(params.artifactsDir);
|
|
228
|
+
const currentState = deriveAuditState(current);
|
|
229
|
+
currentState.last_executor = decision.selected_executor ?? undefined;
|
|
230
|
+
currentState.last_obligation = decision.selected_obligation ?? undefined;
|
|
231
|
+
await writeCoreArtifacts(params.artifactsDir, { ...current, audit_state: currentState });
|
|
232
|
+
await writeJsonFile(join(params.artifactsDir, "steps", "deterministic-progress.json"), {
|
|
233
|
+
iteration: index + 1,
|
|
234
|
+
max_runs: params.maxRuns,
|
|
235
|
+
last_executor: decision.selected_executor,
|
|
236
|
+
last_obligation: decision.selected_obligation,
|
|
237
|
+
prior_summary: lastSummary || null,
|
|
238
|
+
error: error instanceof Error ? error.message : String(error),
|
|
239
|
+
timestamp: new Date().toISOString(),
|
|
240
|
+
});
|
|
241
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
242
|
+
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 });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Check for a finalization cycle: when iterations outrun distinct artifact
|
|
247
|
+
* states by FINALIZATION_CYCLE_TOLERANCE, the deterministic executors are
|
|
248
|
+
* revisiting states rather than progressing. Returns a terminal-step result
|
|
249
|
+
* when a cycle is detected, or undefined when the run is still progressing.
|
|
250
|
+
*/
|
|
251
|
+
export async function checkFinalizationCycle(ctx) {
|
|
252
|
+
ctx.obligationTrail.push(ctx.selectedObligation ?? "unknown");
|
|
253
|
+
ctx.seenStateSignatures.add(computeArtifactStateSignature(ctx.result.updated_bundle));
|
|
254
|
+
if (ctx.index + 1 - ctx.seenStateSignatures.size < ctx.tolerance) {
|
|
255
|
+
return undefined;
|
|
73
256
|
}
|
|
257
|
+
const cycle = Array.from(new Set(ctx.obligationTrail.slice(-ctx.tolerance)));
|
|
258
|
+
await writeJsonFile(join(ctx.params.artifactsDir, "steps", "deterministic-progress.json"), {
|
|
259
|
+
iteration: ctx.index + 1,
|
|
260
|
+
max_runs: ctx.params.maxRuns,
|
|
261
|
+
cycle_detected: true,
|
|
262
|
+
cycling_obligations: cycle,
|
|
263
|
+
summary: "Finalization kept revisiting prior artifact states without net " +
|
|
264
|
+
`progress; stopping. Cycling obligations: ${cycle.join(" -> ")}.`,
|
|
265
|
+
timestamp: new Date().toISOString(),
|
|
266
|
+
});
|
|
267
|
+
return buildTerminalStep(ctx.params, ctx.result.updated_bundle, ctx.result.audit_state, "Finalization is not converging: deterministic executors kept revisiting " +
|
|
268
|
+
`prior artifact states (${cycle.join(" -> ")}). Review whether these ` +
|
|
269
|
+
"obligations are erroneously invalidating each other.");
|
|
270
|
+
}
|
|
271
|
+
// ── Coordinator ───────────────────────────────────────────────────────────────
|
|
272
|
+
async function runDeterministicForNextStep(params) {
|
|
273
|
+
let lastSummary = "";
|
|
274
|
+
const analyzersRef = {
|
|
275
|
+
value: params.analyzers,
|
|
276
|
+
};
|
|
277
|
+
// Finalization thrashing guard — see checkFinalizationCycle for details.
|
|
278
|
+
const FINALIZATION_CYCLE_TOLERANCE = 16;
|
|
279
|
+
const seenStateSignatures = new Set();
|
|
280
|
+
const obligationTrail = [];
|
|
74
281
|
for (let index = 0; index < params.maxRuns; index++) {
|
|
75
282
|
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
76
283
|
const decision = decideNextStep(bundle);
|
|
@@ -118,145 +325,28 @@ async function runDeterministicForNextStep(params) {
|
|
|
118
325
|
}
|
|
119
326
|
}
|
|
120
327
|
if (decision.selected_executor === "graph_enrichment_executor") {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const unresolved = plan.filter(needsInstallDecision);
|
|
128
|
-
if (unresolved.length > 0) {
|
|
129
|
-
const decisionsPath = join(params.artifactsDir, "incoming", "analyzer-decisions.json");
|
|
130
|
-
let decisions;
|
|
131
|
-
try {
|
|
132
|
-
decisions = await readJsonFile(decisionsPath);
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
if (!isFileMissingError(error))
|
|
136
|
-
throw error;
|
|
137
|
-
}
|
|
138
|
-
if (decisions && typeof decisions === "object") {
|
|
139
|
-
const settings = {};
|
|
140
|
-
for (const [id, value] of Object.entries(decisions)) {
|
|
141
|
-
if (value === "ephemeral" ||
|
|
142
|
-
value === "permanent" ||
|
|
143
|
-
value === "skip" ||
|
|
144
|
-
value === "repo" ||
|
|
145
|
-
value === "auto") {
|
|
146
|
-
settings[id] = value;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if (Object.keys(settings).length > 0) {
|
|
150
|
-
const merged = await persistAnalyzerSettings(params.artifactsDir, settings);
|
|
151
|
-
analyzers = merged.analyzers;
|
|
152
|
-
}
|
|
153
|
-
await unlink(decisionsPath).catch(() => { });
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
return {
|
|
157
|
-
kind: "analyzer_install",
|
|
158
|
-
state,
|
|
159
|
-
bundle,
|
|
160
|
-
unresolved,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
// Phase 4B — optional edge-reasoning producing turn. Once analyzer installs
|
|
164
|
-
// are resolved, if the flag is on and the floor carries low-confidence
|
|
165
|
-
// (< 0.65) edges, emit one bounded host turn (subagent dispatch or a single
|
|
166
|
-
// host step) to produce reason rewrites, then re-run. The enrichment
|
|
167
|
-
// executor applies the host-supplied rewrites in the SAME advanceAudit call
|
|
168
|
-
// that merges analyzer edges and writes analyzer_capability, so graph_bundle
|
|
169
|
-
// and its marker stay revision-consistent (no staleness loop). Flag off or
|
|
170
|
-
// no candidates → fall through and run the executor with no rewrites.
|
|
171
|
-
if (params.graphLlmEdgeReasoning === true && bundle.graph_bundle) {
|
|
172
|
-
const candidates = collectLowConfidenceEdges(bundle.graph_bundle);
|
|
173
|
-
if (candidates.length > 0) {
|
|
174
|
-
const edgeReasoningResultsPath = join(params.artifactsDir, "incoming", "edge-reasoning.json");
|
|
175
|
-
let edgeReasoningResults;
|
|
176
|
-
try {
|
|
177
|
-
edgeReasoningResults = await readJsonFile(edgeReasoningResultsPath);
|
|
178
|
-
}
|
|
179
|
-
catch (error) {
|
|
180
|
-
if (!isFileMissingError(error))
|
|
181
|
-
throw error;
|
|
182
|
-
}
|
|
183
|
-
if (edgeReasoningResults) {
|
|
184
|
-
await runAuditStep({
|
|
185
|
-
root: params.root,
|
|
186
|
-
artifactsDir: params.artifactsDir,
|
|
187
|
-
analyzers,
|
|
188
|
-
graphLlmEdgeReasoning: true,
|
|
189
|
-
edgeReasoningResultsPath,
|
|
190
|
-
since: params.since,
|
|
191
|
-
opentoken: params.opentoken,
|
|
192
|
-
});
|
|
193
|
-
await unlink(edgeReasoningResultsPath).catch(() => { });
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
return { kind: "edge_reasoning", state, bundle, candidates };
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
// No undecided installs (and no pending edge reasoning): fall through to run
|
|
200
|
-
// the executor below (it installs for ephemeral/permanent, uses repo/cache,
|
|
201
|
-
// skips the rest).
|
|
328
|
+
const branch = await handleGraphEnrichmentBranch(params, bundle, state, analyzersRef);
|
|
329
|
+
if (branch.action === "continue")
|
|
330
|
+
continue;
|
|
331
|
+
if (branch.action === "return")
|
|
332
|
+
return branch.result;
|
|
333
|
+
// fallthrough: run the executor below
|
|
202
334
|
}
|
|
335
|
+
// Host-delegation executors (design_review, agent) exit the deterministic
|
|
336
|
+
// loop entirely — they pause the pipeline and hand control to the LLM agent.
|
|
203
337
|
if (decision.selected_executor === "design_review") {
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
catch (error) {
|
|
210
|
-
if (!isFileMissingError(error))
|
|
211
|
-
throw error;
|
|
212
|
-
}
|
|
213
|
-
if (reviewFindings && Array.isArray(reviewFindings)) {
|
|
214
|
-
const existing = bundle.design_assessment;
|
|
215
|
-
if (existing) {
|
|
216
|
-
existing.review_findings = reviewFindings;
|
|
217
|
-
existing.reviewed = true;
|
|
218
|
-
await writeJsonFile(join(params.artifactsDir, "design_assessment.json"), existing);
|
|
219
|
-
await unlink(findingsPath).catch(() => { });
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return {
|
|
224
|
-
kind: "design_review",
|
|
225
|
-
state,
|
|
226
|
-
bundle,
|
|
227
|
-
};
|
|
338
|
+
const branch = await handleDesignReviewBranch(params, bundle, state);
|
|
339
|
+
if (branch.action === "continue")
|
|
340
|
+
continue;
|
|
341
|
+
return branch.result;
|
|
228
342
|
}
|
|
229
343
|
if (decision.selected_executor === "synthesis_narrative_executor") {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
try {
|
|
233
|
-
narrativeResults = await readJsonFile(narrativePath);
|
|
234
|
-
}
|
|
235
|
-
catch (error) {
|
|
236
|
-
if (!isFileMissingError(error))
|
|
237
|
-
throw error;
|
|
238
|
-
}
|
|
239
|
-
if (narrativeResults) {
|
|
240
|
-
await runAuditStep({
|
|
241
|
-
root: params.root,
|
|
242
|
-
artifactsDir: params.artifactsDir,
|
|
243
|
-
preferredExecutor: "synthesis_narrative_executor",
|
|
244
|
-
narrativeResultsPath: narrativePath,
|
|
245
|
-
opentoken: params.opentoken,
|
|
246
|
-
});
|
|
247
|
-
await unlink(narrativePath).catch(() => { });
|
|
344
|
+
const branch = await handleSynthesisNarrativeBranch(params, bundle, state);
|
|
345
|
+
if (branch.action === "continue")
|
|
248
346
|
continue;
|
|
249
|
-
|
|
250
|
-
if (params.narrativeEnabled) {
|
|
251
|
-
return {
|
|
252
|
-
kind: "synthesis_narrative",
|
|
253
|
-
state,
|
|
254
|
-
bundle,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
// Narrative disabled: fall through so the deterministic omit runs below.
|
|
347
|
+
return branch.result;
|
|
258
348
|
}
|
|
259
|
-
if (decision.selected_executor
|
|
349
|
+
if (isHostDelegationExecutor(decision.selected_executor ?? "")) {
|
|
260
350
|
return {
|
|
261
351
|
kind: "semantic_review",
|
|
262
352
|
...(await ensureSemanticReviewRun({
|
|
@@ -286,46 +376,9 @@ async function runDeterministicForNextStep(params) {
|
|
|
286
376
|
reason: lastSummary || decision.reason,
|
|
287
377
|
};
|
|
288
378
|
}
|
|
289
|
-
|
|
290
|
-
try {
|
|
291
|
-
result = await runAuditStep({
|
|
292
|
-
root: params.root,
|
|
293
|
-
artifactsDir: params.artifactsDir,
|
|
294
|
-
analyzers,
|
|
295
|
-
graphLlmEdgeReasoning: params.graphLlmEdgeReasoning,
|
|
296
|
-
since: params.since,
|
|
297
|
-
opentoken: params.opentoken,
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
catch (error) {
|
|
301
|
-
const current = await loadArtifactBundle(params.artifactsDir);
|
|
302
|
-
const currentState = deriveAuditState(current);
|
|
303
|
-
currentState.last_executor = decision.selected_executor ?? undefined;
|
|
304
|
-
currentState.last_obligation = decision.selected_obligation ?? undefined;
|
|
305
|
-
await writeCoreArtifacts(params.artifactsDir, { ...current, audit_state: currentState });
|
|
306
|
-
await writeJsonFile(join(params.artifactsDir, "steps", "deterministic-progress.json"), {
|
|
307
|
-
iteration: index + 1,
|
|
308
|
-
max_runs: params.maxRuns,
|
|
309
|
-
last_executor: decision.selected_executor,
|
|
310
|
-
last_obligation: decision.selected_obligation,
|
|
311
|
-
prior_summary: lastSummary || null,
|
|
312
|
-
error: error instanceof Error ? error.message : String(error),
|
|
313
|
-
timestamp: new Date().toISOString(),
|
|
314
|
-
});
|
|
315
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
316
|
-
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 });
|
|
317
|
-
}
|
|
379
|
+
const result = await executeAndRecord(params, analyzersRef.value, decision, index, lastSummary);
|
|
318
380
|
lastSummary = result.progress_summary;
|
|
319
|
-
|
|
320
|
-
iteration: index + 1,
|
|
321
|
-
max_runs: params.maxRuns,
|
|
322
|
-
last_executor: result.selected_executor,
|
|
323
|
-
last_obligation: decision.selected_obligation,
|
|
324
|
-
progress_made: result.progress_made,
|
|
325
|
-
summary: result.progress_summary,
|
|
326
|
-
timestamp: new Date().toISOString(),
|
|
327
|
-
});
|
|
328
|
-
if (result.selected_executor !== "agent") {
|
|
381
|
+
if (!isHostDelegationExecutor(result.selected_executor ?? "")) {
|
|
329
382
|
await clearDispatchFiles(params.artifactsDir);
|
|
330
383
|
}
|
|
331
384
|
if (!result.progress_made) {
|
|
@@ -341,27 +394,23 @@ async function runDeterministicForNextStep(params) {
|
|
|
341
394
|
// thrashing (no net progress) rather than converging. The canonical outputs
|
|
342
395
|
// are already rendered, so stop and surface the cycling obligations instead
|
|
343
396
|
// of spinning to maxRuns and crashing.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return await terminalStep(result.updated_bundle, result.audit_state, "Finalization is not converging: deterministic executors kept revisiting " +
|
|
358
|
-
`prior artifact states (${cycle.join(" -> ")}). Review whether these ` +
|
|
359
|
-
"obligations are erroneously invalidating each other.");
|
|
360
|
-
}
|
|
397
|
+
const cycleResult = await checkFinalizationCycle({
|
|
398
|
+
index,
|
|
399
|
+
obligationTrail,
|
|
400
|
+
seenStateSignatures,
|
|
401
|
+
tolerance: FINALIZATION_CYCLE_TOLERANCE,
|
|
402
|
+
params,
|
|
403
|
+
bundle,
|
|
404
|
+
state,
|
|
405
|
+
result,
|
|
406
|
+
selectedObligation: decision.selected_obligation,
|
|
407
|
+
});
|
|
408
|
+
if (cycleResult !== undefined)
|
|
409
|
+
return cycleResult;
|
|
361
410
|
}
|
|
362
411
|
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
363
412
|
const state = deriveAuditState(bundle);
|
|
364
|
-
return
|
|
413
|
+
return buildTerminalStep(params, bundle, state, `Reached max run limit (${params.maxRuns}) before a review, report, or blocker step was ready.`);
|
|
365
414
|
}
|
|
366
415
|
export async function cmdNextStep(argv) {
|
|
367
416
|
const root = getRootDir(argv);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdPlan(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { runAuditStep } from "./auditStep.js";
|
|
2
|
+
import { getArtifactsDir, getFlag, getRootDir } from "./args.js";
|
|
3
|
+
export async function cmdPlan(argv) {
|
|
4
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
5
|
+
const result = await runAuditStep({
|
|
6
|
+
root: getRootDir(argv),
|
|
7
|
+
artifactsDir,
|
|
8
|
+
since: getFlag(argv, "--since"),
|
|
9
|
+
});
|
|
10
|
+
console.log(JSON.stringify({
|
|
11
|
+
artifacts_dir: artifactsDir,
|
|
12
|
+
selected_executor: result.selected_executor,
|
|
13
|
+
progress_summary: result.progress_summary,
|
|
14
|
+
next_likely_step: result.next_likely_step,
|
|
15
|
+
}, null, 2));
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdPrepareDispatch(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getArtifactsDir, getExplicitProvider, getFlag, getHostMaxActiveSubagents, getHostModel, getRootDir } from "./args.js";
|
|
2
|
+
import { createFreshSessionProvider } from "../providers/index.js";
|
|
3
|
+
import { loadSessionConfig } from "../supervisor/sessionConfig.js";
|
|
4
|
+
import { prepareDispatchArtifacts } from "./dispatch.js";
|
|
5
|
+
import { packageRoot } from "./paths.js";
|
|
6
|
+
export async function cmdPrepareDispatch(argv) {
|
|
7
|
+
const runId = getFlag(argv, "--run-id");
|
|
8
|
+
if (!runId)
|
|
9
|
+
throw new Error("prepare-dispatch requires --run-id <run_id>");
|
|
10
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
11
|
+
const sessionConfig = await loadSessionConfig(artifactsDir).catch(() => ({}));
|
|
12
|
+
const provider = createFreshSessionProvider(getExplicitProvider(argv), sessionConfig);
|
|
13
|
+
const hostModel = getHostModel(argv) ?? sessionConfig.block_quota?.host_model ?? null;
|
|
14
|
+
const result = await prepareDispatchArtifacts({
|
|
15
|
+
packageRoot,
|
|
16
|
+
runId,
|
|
17
|
+
artifactsDir,
|
|
18
|
+
root: getFlag(argv, "--root") ? getRootDir(argv) : undefined,
|
|
19
|
+
sessionConfig,
|
|
20
|
+
hostModel,
|
|
21
|
+
queryLimits: provider.queryLimits?.bind(provider),
|
|
22
|
+
hostActiveSubagentLimit: getHostMaxActiveSubagents(argv),
|
|
23
|
+
});
|
|
24
|
+
console.log(JSON.stringify(result, null, 2));
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdQuota(argv: string[]): Promise<void>;
|