auditor-lambda 0.6.10 → 0.6.12
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/dist/extractors/graphManifestEdges.d.ts +2 -4
- package/dist/extractors/graphManifestEdges.js +5 -28
- package/dist/extractors/graphPathUtils.d.ts +16 -0
- package/dist/extractors/graphPathUtils.js +40 -0
- package/dist/orchestrator/reviewPackets.d.ts +2 -1
- package/dist/orchestrator/reviewPackets.js +4 -21
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cleanupStaleArtifactsDir(artifactsDir: string): Promise<void>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { isFileMissingError, readJsonFile } from "@audit-tools/shared";
|
|
4
|
+
// Remove a stale artifacts directory before starting a fresh run: if the prior
|
|
5
|
+
// run completed or never started, its artifacts are safe to clear. A missing
|
|
6
|
+
// state file means there is nothing to clean. Shared by run-to-completion
|
|
7
|
+
// (clears before a fresh loop) and the cleanup command.
|
|
8
|
+
export async function cleanupStaleArtifactsDir(artifactsDir) {
|
|
9
|
+
let status;
|
|
10
|
+
try {
|
|
11
|
+
const state = await readJsonFile(join(artifactsDir, "audit_state.json"));
|
|
12
|
+
status = state.status;
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (!isFileMissingError(error)) {
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (status === "complete" || status === "not_started") {
|
|
21
|
+
await rm(artifactsDir, { recursive: true, force: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdNextStep(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
import { mkdir, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { isFileMissingError, readJsonFile, writeJsonFile, } from "@audit-tools/shared";
|
|
4
|
+
import { loadArtifactBundle, promoteFinalAuditReport, writeCoreArtifacts, AUDIT_REPORT_FILENAME, } from "../io/artifacts.js";
|
|
5
|
+
import { advanceAudit } from "../orchestrator/advance.js";
|
|
6
|
+
import { decideNextStep } from "../orchestrator/nextStep.js";
|
|
7
|
+
import { deriveAuditState } from "../orchestrator/state.js";
|
|
8
|
+
import { checkFileIntegrity } from "../orchestrator/fileIntegrity.js";
|
|
9
|
+
import { buildEdgeReasoningPrompt, collectLowConfidenceEdges, edgeReasoningContentHash, } from "../orchestrator/edgeReasoning.js";
|
|
10
|
+
import { renderDesignReviewPrompt } from "../orchestrator/designReviewPrompt.js";
|
|
11
|
+
import { renderSynthesisNarrativePrompt } from "../reporting/synthesisNarrativePrompt.js";
|
|
12
|
+
import { buildPathLookup } from "../extractors/graph.js";
|
|
13
|
+
import { buildDispositionMap } from "../extractors/disposition.js";
|
|
14
|
+
import { resolveAnalyzerPlan, needsInstallDecision, } from "../extractors/analyzers/registry.js";
|
|
15
|
+
import { loadSessionConfig, persistAnalyzerSettings, } from "../supervisor/sessionConfig.js";
|
|
16
|
+
import { LOCAL_SUBPROCESS_PROVIDER_NAME } from "../providers/constants.js";
|
|
17
|
+
import { clearDispatchFiles, ensureSupervisorDirs } from "../io/runArtifacts.js";
|
|
18
|
+
import { runAuditStep } from "./auditStep.js";
|
|
19
|
+
import { writeHandoffOnly, ensureSemanticReviewRun, persistConfigErrorHandoff, } from "./reviewRun.js";
|
|
20
|
+
import { buildPendingAuditTasks } from "./dispatch.js";
|
|
21
|
+
import { renderSemanticReviewStep } from "./semanticReviewStep.js";
|
|
22
|
+
import { writeCurrentStep } from "./steps.js";
|
|
23
|
+
import { nextStepCommand, renderAnalyzerInstallPrompt, renderBlockedStepPrompt, renderEdgeReasoningDispatchPrompt, renderEdgeReasoningStepPrompt, renderPresentReportPrompt, } from "./prompts.js";
|
|
24
|
+
import { getArtifactsDir, getFlag, getHostMaxActiveSubagents, getMaxRuns, getOptionalBooleanFlag, getRootDir, getTimeoutMs, resolveHostDispatchCapability, warnIfNotGitRepo, } from "./args.js";
|
|
25
|
+
async function runDeterministicForNextStep(params) {
|
|
26
|
+
let lastSummary = "";
|
|
27
|
+
let analyzers = params.analyzers;
|
|
28
|
+
for (let index = 0; index < params.maxRuns; index++) {
|
|
29
|
+
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
30
|
+
const decision = decideNextStep(bundle);
|
|
31
|
+
const state = decision.state;
|
|
32
|
+
if (state.status === "complete") {
|
|
33
|
+
await writeHandoffOnly({
|
|
34
|
+
root: params.root,
|
|
35
|
+
artifactsDir: params.artifactsDir,
|
|
36
|
+
bundle,
|
|
37
|
+
audit_state: state,
|
|
38
|
+
progress_summary: decision.reason,
|
|
39
|
+
providerName: LOCAL_SUBPROCESS_PROVIDER_NAME,
|
|
40
|
+
});
|
|
41
|
+
const promoted = await promoteFinalAuditReport({
|
|
42
|
+
artifactsDir: params.artifactsDir,
|
|
43
|
+
repoRoot: params.root,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
kind: "complete",
|
|
47
|
+
state,
|
|
48
|
+
bundle,
|
|
49
|
+
finalReportPath: promoted.promoted
|
|
50
|
+
? join(params.root, AUDIT_REPORT_FILENAME)
|
|
51
|
+
: join(params.artifactsDir, AUDIT_REPORT_FILENAME),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (index === 0 && bundle.repo_manifest) {
|
|
55
|
+
const pendingTasks = buildPendingAuditTasks(bundle);
|
|
56
|
+
const taskFiles = new Set();
|
|
57
|
+
for (const task of pendingTasks) {
|
|
58
|
+
for (const fp of Object.keys(task.file_line_counts ?? {}))
|
|
59
|
+
taskFiles.add(fp);
|
|
60
|
+
}
|
|
61
|
+
if (taskFiles.size > 0) {
|
|
62
|
+
const integrity = await checkFileIntegrity(params.root, bundle.repo_manifest, [...taskFiles]);
|
|
63
|
+
if (!integrity.is_clean) {
|
|
64
|
+
console.log(`File integrity check: ${integrity.changed_files.length} changed, ${integrity.missing_files.length} missing — re-running intake.`);
|
|
65
|
+
await advanceAudit(bundle, { root: params.root, preferredExecutor: "intake_executor", opentoken: params.opentoken });
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (decision.selected_executor === "graph_enrichment_executor") {
|
|
71
|
+
const includedFiles = bundle.repo_manifest
|
|
72
|
+
? [
|
|
73
|
+
...new Set(buildPathLookup(bundle.repo_manifest, buildDispositionMap(bundle.file_disposition)).values()),
|
|
74
|
+
]
|
|
75
|
+
: [];
|
|
76
|
+
const plan = resolveAnalyzerPlan(params.root, analyzers, includedFiles);
|
|
77
|
+
const unresolved = plan.filter(needsInstallDecision);
|
|
78
|
+
if (unresolved.length > 0) {
|
|
79
|
+
const decisionsPath = join(params.artifactsDir, "incoming", "analyzer-decisions.json");
|
|
80
|
+
let decisions;
|
|
81
|
+
try {
|
|
82
|
+
decisions = await readJsonFile(decisionsPath);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (!isFileMissingError(error))
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
if (decisions && typeof decisions === "object") {
|
|
89
|
+
const settings = {};
|
|
90
|
+
for (const [id, value] of Object.entries(decisions)) {
|
|
91
|
+
if (value === "ephemeral" ||
|
|
92
|
+
value === "permanent" ||
|
|
93
|
+
value === "skip" ||
|
|
94
|
+
value === "repo" ||
|
|
95
|
+
value === "auto") {
|
|
96
|
+
settings[id] = value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (Object.keys(settings).length > 0) {
|
|
100
|
+
const merged = await persistAnalyzerSettings(params.artifactsDir, settings);
|
|
101
|
+
analyzers = merged.analyzers;
|
|
102
|
+
}
|
|
103
|
+
await unlink(decisionsPath).catch(() => { });
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
kind: "analyzer_install",
|
|
108
|
+
state,
|
|
109
|
+
bundle,
|
|
110
|
+
unresolved,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// Phase 4B — optional edge-reasoning producing turn. Once analyzer installs
|
|
114
|
+
// are resolved, if the flag is on and the floor carries low-confidence
|
|
115
|
+
// (< 0.65) edges, emit one bounded host turn (subagent dispatch or a single
|
|
116
|
+
// host step) to produce reason rewrites, then re-run. The enrichment
|
|
117
|
+
// executor applies the host-supplied rewrites in the SAME advanceAudit call
|
|
118
|
+
// that merges analyzer edges and writes analyzer_capability, so graph_bundle
|
|
119
|
+
// and its marker stay revision-consistent (no staleness loop). Flag off or
|
|
120
|
+
// no candidates → fall through and run the executor with no rewrites.
|
|
121
|
+
if (params.graphLlmEdgeReasoning === true && bundle.graph_bundle) {
|
|
122
|
+
const candidates = collectLowConfidenceEdges(bundle.graph_bundle);
|
|
123
|
+
if (candidates.length > 0) {
|
|
124
|
+
const edgeReasoningResultsPath = join(params.artifactsDir, "incoming", "edge-reasoning.json");
|
|
125
|
+
let edgeReasoningResults;
|
|
126
|
+
try {
|
|
127
|
+
edgeReasoningResults = await readJsonFile(edgeReasoningResultsPath);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
if (!isFileMissingError(error))
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
if (edgeReasoningResults) {
|
|
134
|
+
await runAuditStep({
|
|
135
|
+
root: params.root,
|
|
136
|
+
artifactsDir: params.artifactsDir,
|
|
137
|
+
analyzers,
|
|
138
|
+
graphLlmEdgeReasoning: true,
|
|
139
|
+
edgeReasoningResultsPath,
|
|
140
|
+
since: params.since,
|
|
141
|
+
opentoken: params.opentoken,
|
|
142
|
+
});
|
|
143
|
+
await unlink(edgeReasoningResultsPath).catch(() => { });
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
return { kind: "edge_reasoning", state, bundle, candidates };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// No undecided installs (and no pending edge reasoning): fall through to run
|
|
150
|
+
// the executor below (it installs for ephemeral/permanent, uses repo/cache,
|
|
151
|
+
// skips the rest).
|
|
152
|
+
}
|
|
153
|
+
if (decision.selected_executor === "design_review") {
|
|
154
|
+
const findingsPath = join(params.artifactsDir, "incoming", "design-review-findings.json");
|
|
155
|
+
let reviewFindings;
|
|
156
|
+
try {
|
|
157
|
+
reviewFindings = await readJsonFile(findingsPath);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
if (!isFileMissingError(error))
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
if (reviewFindings && Array.isArray(reviewFindings)) {
|
|
164
|
+
const existing = bundle.design_assessment;
|
|
165
|
+
if (existing) {
|
|
166
|
+
existing.review_findings = reviewFindings;
|
|
167
|
+
existing.reviewed = true;
|
|
168
|
+
await writeJsonFile(join(params.artifactsDir, "design_assessment.json"), existing);
|
|
169
|
+
await unlink(findingsPath).catch(() => { });
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
kind: "design_review",
|
|
175
|
+
state,
|
|
176
|
+
bundle,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (decision.selected_executor === "synthesis_narrative_executor") {
|
|
180
|
+
const narrativePath = join(params.artifactsDir, "incoming", "synthesis-narrative.json");
|
|
181
|
+
let narrativeResults;
|
|
182
|
+
try {
|
|
183
|
+
narrativeResults = await readJsonFile(narrativePath);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
if (!isFileMissingError(error))
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
if (narrativeResults) {
|
|
190
|
+
await runAuditStep({
|
|
191
|
+
root: params.root,
|
|
192
|
+
artifactsDir: params.artifactsDir,
|
|
193
|
+
preferredExecutor: "synthesis_narrative_executor",
|
|
194
|
+
narrativeResultsPath: narrativePath,
|
|
195
|
+
opentoken: params.opentoken,
|
|
196
|
+
});
|
|
197
|
+
await unlink(narrativePath).catch(() => { });
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (params.narrativeEnabled) {
|
|
201
|
+
return {
|
|
202
|
+
kind: "synthesis_narrative",
|
|
203
|
+
state,
|
|
204
|
+
bundle,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
// Narrative disabled: fall through so the deterministic omit runs below.
|
|
208
|
+
}
|
|
209
|
+
if (decision.selected_executor === "agent") {
|
|
210
|
+
return {
|
|
211
|
+
kind: "semantic_review",
|
|
212
|
+
...(await ensureSemanticReviewRun({
|
|
213
|
+
root: params.root,
|
|
214
|
+
artifactsDir: params.artifactsDir,
|
|
215
|
+
bundle,
|
|
216
|
+
state,
|
|
217
|
+
obligationId: decision.selected_obligation,
|
|
218
|
+
selfCliPath: params.selfCliPath,
|
|
219
|
+
timeoutMs: params.timeoutMs,
|
|
220
|
+
})),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
if (!decision.selected_executor) {
|
|
224
|
+
await writeHandoffOnly({
|
|
225
|
+
root: params.root,
|
|
226
|
+
artifactsDir: params.artifactsDir,
|
|
227
|
+
bundle,
|
|
228
|
+
audit_state: state,
|
|
229
|
+
progress_summary: lastSummary || decision.reason,
|
|
230
|
+
providerName: LOCAL_SUBPROCESS_PROVIDER_NAME,
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
kind: "blocked",
|
|
234
|
+
state,
|
|
235
|
+
bundle,
|
|
236
|
+
reason: lastSummary || decision.reason,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
let result;
|
|
240
|
+
try {
|
|
241
|
+
result = await runAuditStep({
|
|
242
|
+
root: params.root,
|
|
243
|
+
artifactsDir: params.artifactsDir,
|
|
244
|
+
analyzers,
|
|
245
|
+
graphLlmEdgeReasoning: params.graphLlmEdgeReasoning,
|
|
246
|
+
since: params.since,
|
|
247
|
+
opentoken: params.opentoken,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
const current = await loadArtifactBundle(params.artifactsDir);
|
|
252
|
+
const currentState = deriveAuditState(current);
|
|
253
|
+
currentState.last_executor = decision.selected_executor ?? undefined;
|
|
254
|
+
currentState.last_obligation = decision.selected_obligation ?? undefined;
|
|
255
|
+
await writeCoreArtifacts(params.artifactsDir, { ...current, audit_state: currentState });
|
|
256
|
+
await writeJsonFile(join(params.artifactsDir, "steps", "deterministic-progress.json"), {
|
|
257
|
+
iteration: index + 1,
|
|
258
|
+
max_runs: params.maxRuns,
|
|
259
|
+
last_executor: decision.selected_executor,
|
|
260
|
+
last_obligation: decision.selected_obligation,
|
|
261
|
+
prior_summary: lastSummary || null,
|
|
262
|
+
error: error instanceof Error ? error.message : String(error),
|
|
263
|
+
timestamp: new Date().toISOString(),
|
|
264
|
+
});
|
|
265
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
266
|
+
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 });
|
|
267
|
+
}
|
|
268
|
+
lastSummary = result.progress_summary;
|
|
269
|
+
await writeJsonFile(join(params.artifactsDir, "steps", "deterministic-progress.json"), {
|
|
270
|
+
iteration: index + 1,
|
|
271
|
+
max_runs: params.maxRuns,
|
|
272
|
+
last_executor: result.selected_executor,
|
|
273
|
+
last_obligation: decision.selected_obligation,
|
|
274
|
+
progress_made: result.progress_made,
|
|
275
|
+
summary: result.progress_summary,
|
|
276
|
+
timestamp: new Date().toISOString(),
|
|
277
|
+
});
|
|
278
|
+
if (result.selected_executor !== "agent") {
|
|
279
|
+
await clearDispatchFiles(params.artifactsDir);
|
|
280
|
+
}
|
|
281
|
+
if (!result.progress_made) {
|
|
282
|
+
return {
|
|
283
|
+
kind: "blocked",
|
|
284
|
+
state: result.audit_state,
|
|
285
|
+
bundle: result.updated_bundle,
|
|
286
|
+
reason: result.progress_summary,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
291
|
+
const state = deriveAuditState(bundle);
|
|
292
|
+
return {
|
|
293
|
+
kind: "blocked",
|
|
294
|
+
state,
|
|
295
|
+
bundle,
|
|
296
|
+
reason: `Reached max run limit (${params.maxRuns}) before a review, report, or blocker step was ready.`,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
export async function cmdNextStep(argv) {
|
|
300
|
+
const root = getRootDir(argv);
|
|
301
|
+
warnIfNotGitRepo(root);
|
|
302
|
+
const artifactsDir = getArtifactsDir(argv);
|
|
303
|
+
await mkdir(artifactsDir, { recursive: true });
|
|
304
|
+
await ensureSupervisorDirs(artifactsDir);
|
|
305
|
+
const hostCanDispatchSubagents = getOptionalBooleanFlag(argv, "--host-can-dispatch-subagents");
|
|
306
|
+
const hostCanRestrictSubagentTools = getOptionalBooleanFlag(argv, "--host-can-restrict-subagent-tools") ??
|
|
307
|
+
false;
|
|
308
|
+
const hostCanSelectSubagentModel = getOptionalBooleanFlag(argv, "--host-can-select-subagent-model") ?? false;
|
|
309
|
+
const hostMaxActiveSubagents = getHostMaxActiveSubagents(argv);
|
|
310
|
+
let sessionConfig;
|
|
311
|
+
try {
|
|
312
|
+
sessionConfig = await loadSessionConfig(artifactsDir);
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
316
|
+
await persistConfigErrorHandoff({
|
|
317
|
+
root,
|
|
318
|
+
artifactsDir,
|
|
319
|
+
progressSummary: reason,
|
|
320
|
+
});
|
|
321
|
+
const step = await writeCurrentStep({
|
|
322
|
+
artifactsDir,
|
|
323
|
+
stepKind: "blocked",
|
|
324
|
+
status: "blocked",
|
|
325
|
+
runId: null,
|
|
326
|
+
allowedCommands: [],
|
|
327
|
+
stopCondition: "Report the configuration blocker and stop.",
|
|
328
|
+
repoRoot: root,
|
|
329
|
+
artifactPaths: {
|
|
330
|
+
operator_handoff: join(artifactsDir, "operator-handoff.json"),
|
|
331
|
+
},
|
|
332
|
+
prompt: renderBlockedStepPrompt(reason),
|
|
333
|
+
});
|
|
334
|
+
console.log(JSON.stringify(step, null, 2));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const hostCanDispatch = resolveHostDispatchCapability({
|
|
338
|
+
explicit: hostCanDispatchSubagents,
|
|
339
|
+
sessionConfig,
|
|
340
|
+
});
|
|
341
|
+
const result = await runDeterministicForNextStep({
|
|
342
|
+
root,
|
|
343
|
+
artifactsDir,
|
|
344
|
+
selfCliPath: resolve(argv[1] ?? process.argv[1] ?? ""),
|
|
345
|
+
timeoutMs: getTimeoutMs(argv, sessionConfig),
|
|
346
|
+
maxRuns: getMaxRuns(argv),
|
|
347
|
+
opentoken: sessionConfig.opentoken?.enabled,
|
|
348
|
+
narrativeEnabled: sessionConfig.synthesis?.narrative !== false,
|
|
349
|
+
analyzers: sessionConfig.analyzers,
|
|
350
|
+
graphLlmEdgeReasoning: sessionConfig.graph?.llm_edge_reasoning,
|
|
351
|
+
since: getFlag(argv, "--since"),
|
|
352
|
+
});
|
|
353
|
+
if (result.kind === "complete") {
|
|
354
|
+
const step = await writeCurrentStep({
|
|
355
|
+
artifactsDir,
|
|
356
|
+
stepKind: "present_report",
|
|
357
|
+
status: "complete",
|
|
358
|
+
runId: null,
|
|
359
|
+
allowedCommands: [],
|
|
360
|
+
stopCondition: "Present the final report and stop.",
|
|
361
|
+
repoRoot: root,
|
|
362
|
+
artifactPaths: {
|
|
363
|
+
final_report: result.finalReportPath,
|
|
364
|
+
},
|
|
365
|
+
prompt: renderPresentReportPrompt(result.finalReportPath),
|
|
366
|
+
});
|
|
367
|
+
console.log(JSON.stringify(step, null, 2));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
if (result.kind === "blocked") {
|
|
371
|
+
const step = await writeCurrentStep({
|
|
372
|
+
artifactsDir,
|
|
373
|
+
stepKind: "blocked",
|
|
374
|
+
status: "blocked",
|
|
375
|
+
runId: null,
|
|
376
|
+
allowedCommands: [],
|
|
377
|
+
stopCondition: "Report the blocker and stop.",
|
|
378
|
+
repoRoot: root,
|
|
379
|
+
artifactPaths: {
|
|
380
|
+
operator_handoff: join(artifactsDir, "operator-handoff.json"),
|
|
381
|
+
},
|
|
382
|
+
prompt: renderBlockedStepPrompt(result.reason),
|
|
383
|
+
});
|
|
384
|
+
console.log(JSON.stringify(step, null, 2));
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (result.kind === "design_review") {
|
|
388
|
+
const designReviewResultsPath = join(artifactsDir, "incoming", "design-review-findings.json");
|
|
389
|
+
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
390
|
+
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
391
|
+
const prompt = renderDesignReviewPrompt(result.bundle);
|
|
392
|
+
const fullPrompt = [
|
|
393
|
+
prompt,
|
|
394
|
+
"## Results path",
|
|
395
|
+
"",
|
|
396
|
+
`Write the JSON array of findings to:`,
|
|
397
|
+
"",
|
|
398
|
+
` ${designReviewResultsPath}`,
|
|
399
|
+
"",
|
|
400
|
+
`Then run: ${continueCommand}`,
|
|
401
|
+
"",
|
|
402
|
+
].join("\n");
|
|
403
|
+
const step = await writeCurrentStep({
|
|
404
|
+
artifactsDir,
|
|
405
|
+
stepKind: "design_review",
|
|
406
|
+
status: "ready",
|
|
407
|
+
runId: null,
|
|
408
|
+
allowedCommands: [continueCommand],
|
|
409
|
+
stopCondition: "Write design review findings to the results path, then run next-step.",
|
|
410
|
+
repoRoot: root,
|
|
411
|
+
artifactPaths: {
|
|
412
|
+
design_review_results: designReviewResultsPath,
|
|
413
|
+
},
|
|
414
|
+
prompt: fullPrompt,
|
|
415
|
+
});
|
|
416
|
+
console.log(JSON.stringify(step, null, 2));
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (result.kind === "analyzer_install") {
|
|
420
|
+
const decisionsPath = join(artifactsDir, "incoming", "analyzer-decisions.json");
|
|
421
|
+
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
422
|
+
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
423
|
+
const step = await writeCurrentStep({
|
|
424
|
+
artifactsDir,
|
|
425
|
+
stepKind: "analyzer_install",
|
|
426
|
+
status: "ready",
|
|
427
|
+
runId: null,
|
|
428
|
+
allowedCommands: [continueCommand],
|
|
429
|
+
stopCondition: "Write analyzer install decisions to the results path, then run next-step.",
|
|
430
|
+
repoRoot: root,
|
|
431
|
+
artifactPaths: {
|
|
432
|
+
analyzer_decisions: decisionsPath,
|
|
433
|
+
},
|
|
434
|
+
prompt: renderAnalyzerInstallPrompt({
|
|
435
|
+
unresolved: result.unresolved,
|
|
436
|
+
decisionsPath,
|
|
437
|
+
continueCommand,
|
|
438
|
+
}),
|
|
439
|
+
});
|
|
440
|
+
console.log(JSON.stringify(step, null, 2));
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (result.kind === "edge_reasoning") {
|
|
444
|
+
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
445
|
+
const edgeReasoningResultsPath = join(artifactsDir, "incoming", "edge-reasoning.json");
|
|
446
|
+
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
447
|
+
const basePrompt = buildEdgeReasoningPrompt(result.candidates);
|
|
448
|
+
const contentHash = edgeReasoningContentHash(result.candidates);
|
|
449
|
+
if (hostCanDispatch) {
|
|
450
|
+
// Dispatch path: isolate the (potentially large) edge-list prompt in a file
|
|
451
|
+
// and have the host fan it out to one subagent, mirroring the packet review
|
|
452
|
+
// dispatch contract. The subagent writes the rewrites file; next-step applies.
|
|
453
|
+
const edgeReasoningPromptPath = join(artifactsDir, "incoming", "edge-reasoning-prompt.md");
|
|
454
|
+
await writeFile(edgeReasoningPromptPath, basePrompt, "utf8");
|
|
455
|
+
const step = await writeCurrentStep({
|
|
456
|
+
artifactsDir,
|
|
457
|
+
stepKind: "edge_reasoning_dispatch",
|
|
458
|
+
status: "ready",
|
|
459
|
+
runId: null,
|
|
460
|
+
allowedCommands: [continueCommand],
|
|
461
|
+
stopCondition: "Dispatch one subagent to write the edge-reasoning rewrites, then run next-step.",
|
|
462
|
+
repoRoot: root,
|
|
463
|
+
artifactPaths: {
|
|
464
|
+
edge_reasoning_prompt: edgeReasoningPromptPath,
|
|
465
|
+
edge_reasoning_results: edgeReasoningResultsPath,
|
|
466
|
+
},
|
|
467
|
+
prompt: renderEdgeReasoningDispatchPrompt({
|
|
468
|
+
promptPath: edgeReasoningPromptPath,
|
|
469
|
+
resultsPath: edgeReasoningResultsPath,
|
|
470
|
+
continueCommand,
|
|
471
|
+
contentHash,
|
|
472
|
+
candidateCount: result.candidates.length,
|
|
473
|
+
}),
|
|
474
|
+
access: {
|
|
475
|
+
read_paths: [edgeReasoningPromptPath],
|
|
476
|
+
write_paths: [edgeReasoningResultsPath],
|
|
477
|
+
},
|
|
478
|
+
});
|
|
479
|
+
console.log(JSON.stringify(step, null, 2));
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
// One-step fallback (no callable subagent facility): the host produces the
|
|
483
|
+
// rewrites itself in a single bounded turn, mirroring the narrative step.
|
|
484
|
+
const step = await writeCurrentStep({
|
|
485
|
+
artifactsDir,
|
|
486
|
+
stepKind: "edge_reasoning",
|
|
487
|
+
status: "ready",
|
|
488
|
+
runId: null,
|
|
489
|
+
allowedCommands: [continueCommand],
|
|
490
|
+
stopCondition: "Write the edge-reasoning rewrites to the results path, then run next-step.",
|
|
491
|
+
repoRoot: root,
|
|
492
|
+
artifactPaths: {
|
|
493
|
+
edge_reasoning_results: edgeReasoningResultsPath,
|
|
494
|
+
},
|
|
495
|
+
prompt: renderEdgeReasoningStepPrompt({
|
|
496
|
+
basePrompt,
|
|
497
|
+
resultsPath: edgeReasoningResultsPath,
|
|
498
|
+
continueCommand,
|
|
499
|
+
contentHash,
|
|
500
|
+
}),
|
|
501
|
+
access: {
|
|
502
|
+
read_paths: [],
|
|
503
|
+
write_paths: [edgeReasoningResultsPath],
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
console.log(JSON.stringify(step, null, 2));
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (result.kind === "synthesis_narrative") {
|
|
510
|
+
const narrativeResultsPath = join(artifactsDir, "incoming", "synthesis-narrative.json");
|
|
511
|
+
await mkdir(join(artifactsDir, "incoming"), { recursive: true });
|
|
512
|
+
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
513
|
+
const basePrompt = result.bundle.audit_findings
|
|
514
|
+
? renderSynthesisNarrativePrompt(result.bundle.audit_findings)
|
|
515
|
+
: "# Synthesis narrative\n\nNo findings report is available; write an empty themes array.";
|
|
516
|
+
const fullPrompt = [
|
|
517
|
+
basePrompt,
|
|
518
|
+
"## Results path",
|
|
519
|
+
"",
|
|
520
|
+
"Write the SynthesisNarrative JSON object to:",
|
|
521
|
+
"",
|
|
522
|
+
` ${narrativeResultsPath}`,
|
|
523
|
+
"",
|
|
524
|
+
`Then run: ${continueCommand}`,
|
|
525
|
+
"",
|
|
526
|
+
].join("\n");
|
|
527
|
+
const step = await writeCurrentStep({
|
|
528
|
+
artifactsDir,
|
|
529
|
+
stepKind: "synthesis_narrative",
|
|
530
|
+
status: "ready",
|
|
531
|
+
runId: null,
|
|
532
|
+
allowedCommands: [continueCommand],
|
|
533
|
+
stopCondition: "Write the synthesis narrative to the results path, then run next-step.",
|
|
534
|
+
repoRoot: root,
|
|
535
|
+
artifactPaths: {
|
|
536
|
+
synthesis_narrative_results: narrativeResultsPath,
|
|
537
|
+
},
|
|
538
|
+
prompt: fullPrompt,
|
|
539
|
+
});
|
|
540
|
+
console.log(JSON.stringify(step, null, 2));
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const step = await renderSemanticReviewStep({
|
|
544
|
+
root,
|
|
545
|
+
artifactsDir,
|
|
546
|
+
activeReviewRun: result.activeReviewRun,
|
|
547
|
+
hostCanDispatch,
|
|
548
|
+
hostMaxActiveSubagents,
|
|
549
|
+
hostCanRestrictSubagentTools,
|
|
550
|
+
hostCanSelectSubagentModel,
|
|
551
|
+
});
|
|
552
|
+
console.log(JSON.stringify(step, null, 2));
|
|
553
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const packageRoot: string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
// Absolute path to the package root (packages/audit-code). Computed relative to
|
|
4
|
+
// this module's location (src/cli/), so it stays correct regardless of which
|
|
5
|
+
// command module imports it. Shared by renderSemanticReviewStep and
|
|
6
|
+
// cmdRunToCompletion to locate packaged assets when preparing dispatch.
|
|
7
|
+
export const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
package/dist/cli/reviewRun.d.ts
CHANGED
|
@@ -27,3 +27,8 @@ export declare function ensureSemanticReviewRun(params: {
|
|
|
27
27
|
bundle: ArtifactBundle;
|
|
28
28
|
activeReviewRun: ActiveReviewRun;
|
|
29
29
|
}>;
|
|
30
|
+
export declare function persistConfigErrorHandoff(params: {
|
|
31
|
+
root: string;
|
|
32
|
+
artifactsDir: string;
|
|
33
|
+
progressSummary: string;
|
|
34
|
+
}): Promise<void>;
|
package/dist/cli/reviewRun.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { isFileMissingError, readJsonFile, writeJsonFile } from "@audit-tools/shared";
|
|
3
|
-
import { writeCoreArtifacts } from "../io/artifacts.js";
|
|
3
|
+
import { loadArtifactBundle, writeCoreArtifacts, } from "../io/artifacts.js";
|
|
4
|
+
import { deriveAuditState } from "../orchestrator/state.js";
|
|
4
5
|
import { buildRunId, getRunPaths, writeWorkerTaskFiles, } from "../io/runArtifacts.js";
|
|
5
6
|
import { renderWorkerPrompt } from "../prompts/renderWorkerPrompt.js";
|
|
6
7
|
import { buildAuditCodeHandoff, writeAuditCodeHandoffArtifacts, } from "../supervisor/operatorHandoff.js";
|
|
@@ -141,3 +142,25 @@ export async function ensureSemanticReviewRun(params) {
|
|
|
141
142
|
});
|
|
142
143
|
return { state: blockedState, bundle: blockedBundle, activeReviewRun };
|
|
143
144
|
}
|
|
145
|
+
export async function persistConfigErrorHandoff(params) {
|
|
146
|
+
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
147
|
+
const blockedState = buildBlockedAuditState({
|
|
148
|
+
state: bundle.audit_state ?? deriveAuditState(bundle),
|
|
149
|
+
obligationId: null,
|
|
150
|
+
executor: null,
|
|
151
|
+
blocker: params.progressSummary,
|
|
152
|
+
});
|
|
153
|
+
await writeCoreArtifacts(params.artifactsDir, {
|
|
154
|
+
...bundle,
|
|
155
|
+
audit_state: blockedState,
|
|
156
|
+
});
|
|
157
|
+
const handoff = buildAuditCodeHandoff({
|
|
158
|
+
root: params.root,
|
|
159
|
+
artifactsDir: params.artifactsDir,
|
|
160
|
+
state: blockedState,
|
|
161
|
+
bundle: { ...bundle, audit_state: blockedState },
|
|
162
|
+
progressSummary: params.progressSummary,
|
|
163
|
+
isConfigError: true,
|
|
164
|
+
});
|
|
165
|
+
await writeAuditCodeHandoffArtifacts(handoff);
|
|
166
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdRunToCompletion(argv: string[]): Promise<void>;
|