auditor-lambda 0.5.0 → 0.6.3
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 +9 -5
- package/dist/cli.js +124 -84
- package/dist/io/artifacts.d.ts +1 -0
- package/dist/io/artifacts.js +7 -3
- package/dist/mcp/server.js +3 -3
- package/dist/supervisor/operatorHandoff.d.ts +1 -1
- package/dist/supervisor/operatorHandoff.js +26 -16
- package/docs/contracts.md +10 -3
- package/package.json +1 -1
package/dist/cli/dispatch.js
CHANGED
|
@@ -187,9 +187,12 @@ export async function prepareDispatchArtifacts(params) {
|
|
|
187
187
|
}
|
|
188
188
|
const bundle = await loadArtifactBundle(artifactsDir);
|
|
189
189
|
const tasksPath = join(runDir, "pending-audit-tasks.json");
|
|
190
|
-
const tasks = await readJsonFile(tasksPath).catch((error) => {
|
|
191
|
-
if (isFileMissingError(error))
|
|
192
|
-
|
|
190
|
+
const tasks = await readJsonFile(tasksPath).catch(async (error) => {
|
|
191
|
+
if (isFileMissingError(error)) {
|
|
192
|
+
const generated = buildPendingAuditTasks(bundle);
|
|
193
|
+
await writeJsonFile(tasksPath, generated);
|
|
194
|
+
return generated;
|
|
195
|
+
}
|
|
193
196
|
throw error;
|
|
194
197
|
});
|
|
195
198
|
const sessionConfig = params.sessionConfig ?? (await loadSessionConfig(artifactsDir).catch(() => ({})));
|
|
@@ -353,7 +356,7 @@ export async function prepareDispatchArtifacts(params) {
|
|
|
353
356
|
"",
|
|
354
357
|
];
|
|
355
358
|
});
|
|
356
|
-
const submitCommand = `
|
|
359
|
+
const submitCommand = `node packages/audit-code/audit-code.mjs submit-packet ` +
|
|
357
360
|
`--run-id-b64 ${toBase64Url(runId)} ` +
|
|
358
361
|
`--packet-id-b64 ${toBase64Url(packet.packet_id)} ` +
|
|
359
362
|
`--artifacts-dir-b64 ${toBase64Url(artifactsDir)}`;
|
|
@@ -395,7 +398,7 @@ export async function prepareDispatchArtifacts(params) {
|
|
|
395
398
|
" unit_id copy from the task metadata",
|
|
396
399
|
" pass_id copy from the task metadata",
|
|
397
400
|
" lens copy from the task metadata",
|
|
398
|
-
" file_coverage [{path, total_lines}] - copy the exact template from each task section above",
|
|
401
|
+
" file_coverage [{path, total_lines}] - copy the exact template from each task section above. You MUST include total_lines. Do not omit or zero it out, as this will cause fatal validation errors.",
|
|
399
402
|
" findings [] or array of finding objects",
|
|
400
403
|
"",
|
|
401
404
|
"Lens verification tasks:",
|
|
@@ -425,6 +428,7 @@ export async function prepareDispatchArtifacts(params) {
|
|
|
425
428
|
"## Submit",
|
|
426
429
|
"Pipe the JSON array on stdin to this command:",
|
|
427
430
|
` ${submitCommand}`,
|
|
431
|
+
" (If using Windows PowerShell, you MUST use `Get-Content <file> | & <command>` instead of the `<` operator.)",
|
|
428
432
|
"",
|
|
429
433
|
"The command validates and writes the packet-owned result files. Exit 0 means accepted.",
|
|
430
434
|
"Non-zero: read the errors, fix the JSON, and run the same submit command again. Retry up to 3 times.",
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ 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";
|
|
13
|
+
import { loadArtifactBundle, writeCoreArtifacts, promoteFinalAuditReport, AUDIT_REPORT_FILENAME, } from "./io/artifacts.js";
|
|
14
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";
|
|
@@ -630,8 +630,8 @@ async function runDeterministicForNextStep(params) {
|
|
|
630
630
|
state,
|
|
631
631
|
bundle,
|
|
632
632
|
finalReportPath: promoted.promoted
|
|
633
|
-
? join(params.root,
|
|
634
|
-
: join(params.artifactsDir,
|
|
633
|
+
? join(params.root, AUDIT_REPORT_FILENAME)
|
|
634
|
+
: join(params.artifactsDir, AUDIT_REPORT_FILENAME),
|
|
635
635
|
};
|
|
636
636
|
}
|
|
637
637
|
if (index === 0 && bundle.repo_manifest) {
|
|
@@ -879,6 +879,89 @@ async function runDeterministicForNextStep(params) {
|
|
|
879
879
|
reason: `Reached max run limit (${params.maxRuns}) before a review, report, or blocker step was ready.`,
|
|
880
880
|
};
|
|
881
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
|
+
}
|
|
882
965
|
async function cmdNextStep(argv) {
|
|
883
966
|
const root = getRootDir(argv);
|
|
884
967
|
warnIfNotGitRepo(root);
|
|
@@ -1123,81 +1206,14 @@ async function cmdNextStep(argv) {
|
|
|
1123
1206
|
console.log(JSON.stringify(step, null, 2));
|
|
1124
1207
|
return;
|
|
1125
1208
|
}
|
|
1126
|
-
|
|
1127
|
-
const singleTaskPromptPath = join(artifactsDir, "dispatch", "current-single-task-prompt.md");
|
|
1128
|
-
const workerCommand = renderCommand(result.activeReviewRun.worker_command);
|
|
1129
|
-
const step = await writeCurrentStep({
|
|
1130
|
-
artifactsDir,
|
|
1131
|
-
stepKind: "single_task_fallback",
|
|
1132
|
-
status: "ready",
|
|
1133
|
-
runId: result.activeReviewRun.run_id,
|
|
1134
|
-
allowedCommands: [workerCommand],
|
|
1135
|
-
stopCondition: "Run the exact worker_command after one result, then stop without looping.",
|
|
1136
|
-
repoRoot: root,
|
|
1137
|
-
artifactPaths: {
|
|
1138
|
-
active_review_task: result.activeReviewRun.task_path,
|
|
1139
|
-
active_review_prompt: result.activeReviewRun.prompt_path,
|
|
1140
|
-
pending_audit_tasks: result.activeReviewRun.pending_audit_tasks_path ?? null,
|
|
1141
|
-
audit_results: result.activeReviewRun.audit_results_path,
|
|
1142
|
-
single_task_prompt: singleTaskPromptPath,
|
|
1143
|
-
},
|
|
1144
|
-
prompt: renderSingleTaskFallbackStepPrompt({
|
|
1145
|
-
singleTaskPromptPath,
|
|
1146
|
-
activeReviewRun: result.activeReviewRun,
|
|
1147
|
-
}),
|
|
1148
|
-
access: {
|
|
1149
|
-
read_paths: [singleTaskPromptPath],
|
|
1150
|
-
write_paths: [result.activeReviewRun.audit_results_path],
|
|
1151
|
-
},
|
|
1152
|
-
});
|
|
1153
|
-
console.log(JSON.stringify(step, null, 2));
|
|
1154
|
-
return;
|
|
1155
|
-
}
|
|
1156
|
-
const dispatch = await prepareDispatchArtifacts({
|
|
1157
|
-
packageRoot,
|
|
1158
|
-
runId: result.activeReviewRun.run_id,
|
|
1159
|
-
artifactsDir,
|
|
1209
|
+
const step = await renderSemanticReviewStep({
|
|
1160
1210
|
root,
|
|
1161
|
-
hostActiveSubagentLimit: hostMaxActiveSubagents,
|
|
1162
|
-
});
|
|
1163
|
-
const mergeCommand = mergeAndIngestCommand(artifactsDir, result.activeReviewRun.run_id);
|
|
1164
|
-
const continueCommand = nextStepCommand(root, artifactsDir);
|
|
1165
|
-
const step = await writeCurrentStep({
|
|
1166
1211
|
artifactsDir,
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
"auditor_continue_audit",
|
|
1173
|
-
mergeCommand,
|
|
1174
|
-
continueCommand,
|
|
1175
|
-
],
|
|
1176
|
-
stopCondition: "Dispatch every packet, run merge-and-ingest once, then run next-step.",
|
|
1177
|
-
repoRoot: root,
|
|
1178
|
-
artifactPaths: {
|
|
1179
|
-
dispatch_plan: dispatch.dispatch_plan_path,
|
|
1180
|
-
dispatch_quota: dispatch.dispatch_quota_path,
|
|
1181
|
-
dispatch_warnings: dispatch.dispatch_warnings_path,
|
|
1182
|
-
active_review_task: result.activeReviewRun.task_path,
|
|
1183
|
-
pending_audit_tasks: result.activeReviewRun.pending_audit_tasks_path ?? null,
|
|
1184
|
-
},
|
|
1185
|
-
prompt: renderDispatchReviewPrompt({
|
|
1186
|
-
root,
|
|
1187
|
-
artifactsDir,
|
|
1188
|
-
activeReviewRun: result.activeReviewRun,
|
|
1189
|
-
dispatchPlanPath: dispatch.dispatch_plan_path,
|
|
1190
|
-
dispatchQuotaPath: dispatch.dispatch_quota_path,
|
|
1191
|
-
hostCanRestrictSubagentTools,
|
|
1192
|
-
hostCanSelectSubagentModel,
|
|
1193
|
-
}),
|
|
1194
|
-
access: {
|
|
1195
|
-
read_paths: [
|
|
1196
|
-
dispatch.dispatch_plan_path,
|
|
1197
|
-
...(dispatch.dispatch_quota_path ? [dispatch.dispatch_quota_path] : []),
|
|
1198
|
-
],
|
|
1199
|
-
write_paths: [],
|
|
1200
|
-
},
|
|
1212
|
+
activeReviewRun: result.activeReviewRun,
|
|
1213
|
+
hostCanDispatch,
|
|
1214
|
+
hostMaxActiveSubagents,
|
|
1215
|
+
hostCanRestrictSubagentTools,
|
|
1216
|
+
hostCanSelectSubagentModel,
|
|
1201
1217
|
});
|
|
1202
1218
|
console.log(JSON.stringify(step, null, 2));
|
|
1203
1219
|
}
|
|
@@ -1365,6 +1381,37 @@ async function cmdRunToCompletion(argv) {
|
|
|
1365
1381
|
const blockPrompt = renderWorkerPrompt(blockTask);
|
|
1366
1382
|
await writeWorkerTaskFiles(blockTask, blockPrompt, blockPaths, artifactsDir, blockPendingTasks);
|
|
1367
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
|
+
}
|
|
1368
1415
|
await emitEnvelope({
|
|
1369
1416
|
root,
|
|
1370
1417
|
artifactsDir,
|
|
@@ -1380,14 +1427,7 @@ async function cmdRunToCompletion(argv) {
|
|
|
1380
1427
|
progress_summary: blocker,
|
|
1381
1428
|
next_likely_step: null,
|
|
1382
1429
|
providerName: provider.name,
|
|
1383
|
-
activeReviewRun:
|
|
1384
|
-
run_id: blockRunId,
|
|
1385
|
-
task_path: blockPaths.taskPath,
|
|
1386
|
-
prompt_path: blockPaths.promptPath,
|
|
1387
|
-
pending_audit_tasks_path: blockPendingTasksPath,
|
|
1388
|
-
audit_results_path: blockAuditResultsPath,
|
|
1389
|
-
worker_command: blockTask.worker_command,
|
|
1390
|
-
},
|
|
1430
|
+
activeReviewRun: reviewRun,
|
|
1391
1431
|
});
|
|
1392
1432
|
return;
|
|
1393
1433
|
}
|
package/dist/io/artifacts.d.ts
CHANGED
|
@@ -55,6 +55,7 @@ interface ArtifactDefinition<K extends ArtifactBundleKey = ArtifactBundleKey> {
|
|
|
55
55
|
read: (path: string) => Promise<ArtifactPayloadMap[K] | undefined>;
|
|
56
56
|
write: (path: string, value: ArtifactPayloadMap[K]) => Promise<void>;
|
|
57
57
|
}
|
|
58
|
+
export declare const AUDIT_REPORT_FILENAME = "audit-report.md";
|
|
58
59
|
export declare const ARTIFACT_DEFINITIONS: {
|
|
59
60
|
readonly repo_manifest: ArtifactDefinition<"repo_manifest">;
|
|
60
61
|
readonly file_disposition: ArtifactDefinition<"file_disposition">;
|
package/dist/io/artifacts.js
CHANGED
|
@@ -2,6 +2,10 @@ import { cp, rm, unlink } from "node:fs/promises";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { isFileMissingError, readOptionalJsonFile, readOptionalNdjsonFile, readOptionalTextFile, writeJsonFile, writeNdjsonFile, writeTextFile, } from "@audit-tools/shared";
|
|
4
4
|
import { buildToolingManifest } from "./toolingManifest.js";
|
|
5
|
+
// Canonical filename for the rendered findings report. Single source of truth
|
|
6
|
+
// for path construction. The dependency table below still lists it as plain
|
|
7
|
+
// data alongside its sibling artifact-name literals.
|
|
8
|
+
export const AUDIT_REPORT_FILENAME = "audit-report.md";
|
|
5
9
|
function jsonArtifact(fileName, phase) {
|
|
6
10
|
return {
|
|
7
11
|
fileName,
|
|
@@ -49,7 +53,7 @@ export const ARTIFACT_DEFINITIONS = {
|
|
|
49
53
|
audit_plan_metrics: jsonArtifact("audit_plan_metrics.json", "execution"),
|
|
50
54
|
review_packets: jsonArtifact("review_packets.json", "execution"),
|
|
51
55
|
requeue_tasks: jsonArtifact("requeue_tasks.json", "execution"),
|
|
52
|
-
audit_report: textArtifact(
|
|
56
|
+
audit_report: textArtifact(AUDIT_REPORT_FILENAME, "reporting"),
|
|
53
57
|
audit_findings: jsonArtifact("audit-findings.json", "reporting"),
|
|
54
58
|
synthesis_narrative: jsonArtifact("synthesis-narrative.json", "reporting"),
|
|
55
59
|
audit_state: jsonArtifact("audit_state.json", "supervisor"),
|
|
@@ -103,8 +107,8 @@ export async function cleanupIntermediateArtifacts(root) {
|
|
|
103
107
|
return deleted;
|
|
104
108
|
}
|
|
105
109
|
export async function promoteFinalAuditReport(params, options = {}) {
|
|
106
|
-
const source = join(params.artifactsDir,
|
|
107
|
-
const destination = join(params.repoRoot,
|
|
110
|
+
const source = join(params.artifactsDir, AUDIT_REPORT_FILENAME);
|
|
111
|
+
const destination = join(params.repoRoot, AUDIT_REPORT_FILENAME);
|
|
108
112
|
const copy = options.copy ?? cp;
|
|
109
113
|
const remove = options.remove ?? rm;
|
|
110
114
|
const warn = options.warn ?? ((message) => process.stderr.write(`${message}\n`));
|
package/dist/mcp/server.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { loadArtifactBundle } from "../io/artifacts.js";
|
|
5
|
+
import { loadArtifactBundle, AUDIT_REPORT_FILENAME } from "../io/artifacts.js";
|
|
6
6
|
import { readOptionalTextFile } from "@audit-tools/shared";
|
|
7
7
|
import { deriveAuditState } from "../orchestrator/state.js";
|
|
8
8
|
import { decideNextStep } from "../orchestrator/nextStep.js";
|
|
@@ -225,8 +225,8 @@ export const resourceRegistry = [
|
|
|
225
225
|
description: "Current deterministic audit report if available.",
|
|
226
226
|
mimeType: "text/markdown",
|
|
227
227
|
async read(context) {
|
|
228
|
-
const report = (await readOptionalTextFile(join(context.artifactsDir,
|
|
229
|
-
(await readOptionalTextFile(join(context.root,
|
|
228
|
+
const report = (await readOptionalTextFile(join(context.artifactsDir, AUDIT_REPORT_FILENAME))) ??
|
|
229
|
+
(await readOptionalTextFile(join(context.root, AUDIT_REPORT_FILENAME))) ??
|
|
230
230
|
"The audit report has not been rendered yet.";
|
|
231
231
|
return { mimeType: this.mimeType, text: report };
|
|
232
232
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ArtifactBundle } from "../io/artifacts.js";
|
|
2
2
|
import type { AuditState, AuditTopLevelStatus } from "../types/auditState.js";
|
|
3
3
|
export interface AuditCodeHandoffInput {
|
|
4
4
|
flag: "--results" | "--batch-results" | "--updates" | "--external-analyzer-results";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { writeJsonFile } from "@audit-tools/shared";
|
|
4
|
+
import { AUDIT_REPORT_FILENAME } from "../io/artifacts.js";
|
|
4
5
|
import { LOCAL_SUBPROCESS_PROVIDER_NAME } from "../providers/constants.js";
|
|
5
6
|
export const CONFIG_ERROR_BLOCKER_PREFIX = "config-error:";
|
|
6
7
|
const INCOMING_DIRNAME = "incoming";
|
|
@@ -11,8 +12,6 @@ const RUN_LEDGER_FILENAME = "run-ledger.json";
|
|
|
11
12
|
const CURRENT_TASK_FILENAME = "current-task.json";
|
|
12
13
|
const CURRENT_PROMPT_FILENAME = "current-prompt.md";
|
|
13
14
|
const CURRENT_TASKS_FILENAME = "current-tasks.json";
|
|
14
|
-
const CURRENT_SINGLE_TASK_FILENAME = "current-single-task.json";
|
|
15
|
-
const CURRENT_SINGLE_TASK_PROMPT_FILENAME = "current-single-task-prompt.md";
|
|
16
15
|
const AUDIT_TASKS_FILENAME = "audit_tasks.json";
|
|
17
16
|
const RUNTIME_VALIDATION_TASKS_FILENAME = "runtime_validation_tasks.json";
|
|
18
17
|
const BLOCKED_STATUS = "blocked";
|
|
@@ -123,6 +122,26 @@ function buildInteractiveProviderHint(status, providerName, sessionConfigPath, i
|
|
|
123
122
|
const providerLabel = providerName ?? LOCAL_SUBPROCESS_PROVIDER_NAME;
|
|
124
123
|
return `Provider: ${providerLabel}. This is a deterministic semantic-review handoff, not a failed audit. Use host subagents when the active toolset provides them; otherwise use the single-task fallback and stop after the worker command. For automatic LLM review, configure an interactive provider in ${sessionConfigPath}; that is only needed for backend-launched review.`;
|
|
125
124
|
}
|
|
125
|
+
// Single source for which artifact paths render in the markdown handoff and how
|
|
126
|
+
// absent ones read. renderMarkdown and file_map both source paths from the
|
|
127
|
+
// artifact path model (AuditCodeHandoffArtifactPaths), so adding or renaming a
|
|
128
|
+
// handoff artifact is one edit here instead of coordinated edits across sites.
|
|
129
|
+
const ARTIFACT_PATH_RENDER_FIELDS = [
|
|
130
|
+
{ label: "operator handoff json", key: "operator_handoff_json" },
|
|
131
|
+
{ label: "operator handoff markdown", key: "operator_handoff_markdown" },
|
|
132
|
+
{ label: "incoming dir", key: "incoming_dir" },
|
|
133
|
+
{ label: "session config", key: "session_config" },
|
|
134
|
+
{ label: "run ledger", key: "run_ledger" },
|
|
135
|
+
{ label: "current task", key: "current_task", fallback: "not available" },
|
|
136
|
+
{ label: "current prompt", key: "current_prompt", fallback: "not available" },
|
|
137
|
+
{ label: "current tasks", key: "current_tasks", fallback: "not available" },
|
|
138
|
+
{ label: "audit tasks", key: "audit_tasks", fallback: "not available yet" },
|
|
139
|
+
{
|
|
140
|
+
label: "runtime validation tasks",
|
|
141
|
+
key: "runtime_validation_tasks",
|
|
142
|
+
fallback: "not available yet",
|
|
143
|
+
},
|
|
144
|
+
];
|
|
126
145
|
function renderMarkdown(handoff) {
|
|
127
146
|
const lines = [
|
|
128
147
|
"# audit-code operator handoff",
|
|
@@ -145,16 +164,10 @@ function renderMarkdown(handoff) {
|
|
|
145
164
|
}
|
|
146
165
|
}
|
|
147
166
|
lines.push("", "Useful artifact paths:");
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
lines.push(`- run ledger: ${handoff.artifact_paths.run_ledger}`);
|
|
153
|
-
lines.push(`- current task: ${handoff.artifact_paths.current_task ?? "not available"}`);
|
|
154
|
-
lines.push(`- current prompt: ${handoff.artifact_paths.current_prompt ?? "not available"}`);
|
|
155
|
-
lines.push(`- current tasks: ${handoff.artifact_paths.current_tasks ?? "not available"}`);
|
|
156
|
-
lines.push(`- audit tasks: ${handoff.artifact_paths.audit_tasks ?? "not available yet"}`);
|
|
157
|
-
lines.push(`- runtime validation tasks: ${handoff.artifact_paths.runtime_validation_tasks ?? "not available yet"}`);
|
|
167
|
+
for (const field of ARTIFACT_PATH_RENDER_FIELDS) {
|
|
168
|
+
const value = handoff.artifact_paths[field.key];
|
|
169
|
+
lines.push(`- ${field.label}: ${value ?? field.fallback ?? "not available"}`);
|
|
170
|
+
}
|
|
158
171
|
if (handoff.suggested_inputs.length > 0) {
|
|
159
172
|
lines.push("", "Suggested evidence inputs:");
|
|
160
173
|
for (const item of handoff.suggested_inputs) {
|
|
@@ -238,11 +251,8 @@ export function buildAuditCodeHandoff(params) {
|
|
|
238
251
|
handoff.file_map = {
|
|
239
252
|
current_task: artifactPaths.current_task,
|
|
240
253
|
current_prompt: artifactPaths.current_prompt,
|
|
241
|
-
single_task: join(params.artifactsDir, "dispatch", CURRENT_SINGLE_TASK_FILENAME),
|
|
242
|
-
single_task_prompt: join(params.artifactsDir, "dispatch", CURRENT_SINGLE_TASK_PROMPT_FILENAME),
|
|
243
|
-
dispatch_plan: join(params.artifactsDir, "runs", params.activeReviewRun.run_id, "dispatch-plan.json"),
|
|
244
254
|
audit_results: params.activeReviewRun.audit_results_path,
|
|
245
|
-
final_report: join(params.root,
|
|
255
|
+
final_report: join(params.root, AUDIT_REPORT_FILENAME),
|
|
246
256
|
};
|
|
247
257
|
}
|
|
248
258
|
return handoff;
|
package/docs/contracts.md
CHANGED
|
@@ -90,9 +90,16 @@ backend writes the current step contract to:
|
|
|
90
90
|
includes `step_kind`, `prompt_path`, `status`, `run_id`, `allowed_commands`,
|
|
91
91
|
`stop_condition`, `repo_root`, `artifacts_dir`, and relevant `artifact_paths`.
|
|
92
92
|
|
|
93
|
-
When semantic review is
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
When semantic review is reached, the backend resolves host dispatch capability
|
|
94
|
+
from `--host-can-dispatch-subagents true|false` (optional) → session config
|
|
95
|
+
(`host_can_dispatch_subagents`) → `AUDIT_CODE_HOST_CAN_DISPATCH` → default
|
|
96
|
+
`true`, then renders exactly one review path: packet dispatch (`dispatch_review`)
|
|
97
|
+
or the single-task fallback (`single_task_fallback`). No capability handshake is
|
|
98
|
+
required; pass the flag only to override the resolved default.
|
|
99
|
+
|
|
100
|
+
`run-to-completion` renders the same step when it reaches the semantic-review
|
|
101
|
+
boundary, so a host on the batch entrypoint can act on `steps/current-step.json`
|
|
102
|
+
directly instead of issuing a second `next-step`.
|
|
96
103
|
|
|
97
104
|
## Dispatch packets
|
|
98
105
|
|