auditor-lambda 0.2.1 → 0.2.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/audit-code-wrapper-lib.mjs +229 -289
- package/dist/adapters/eslint.js +4 -2
- package/dist/adapters/npmAudit.js +1 -1
- package/dist/cli.js +82 -45
- package/dist/extractors/bucketing.js +14 -35
- package/dist/extractors/disposition.js +8 -9
- package/dist/extractors/fileInventory.js +0 -2
- package/dist/extractors/flows.js +14 -23
- package/dist/extractors/fsIntake.js +4 -1
- package/dist/extractors/pathPatterns.d.ts +19 -0
- package/dist/extractors/pathPatterns.js +91 -0
- package/dist/extractors/surfaces.js +2 -7
- package/dist/io/artifacts.d.ts +23 -1
- package/dist/io/artifacts.js +4 -3
- package/dist/io/runArtifacts.js +1 -1
- package/dist/orchestrator/advance.js +53 -71
- package/dist/orchestrator/flowCoverage.js +1 -2
- package/dist/orchestrator/internalExecutors.js +4 -6
- package/dist/orchestrator/planning.js +12 -20
- package/dist/orchestrator/resultIngestion.js +3 -2
- package/dist/orchestrator/runtimeValidation.js +5 -0
- package/dist/orchestrator/syntaxResolutionExecutor.js +10 -2
- package/dist/orchestrator/taskBuilder.js +15 -28
- package/dist/prompts/renderWorkerPrompt.js +2 -1
- package/dist/providers/claudeCodeProvider.js +1 -2
- package/dist/providers/constants.d.ts +1 -0
- package/dist/providers/constants.js +1 -0
- package/dist/providers/index.js +7 -3
- package/dist/providers/opencodeProvider.js +1 -6
- package/dist/providers/spawnLoggedCommand.js +4 -0
- package/dist/providers/types.d.ts +0 -1
- package/dist/supervisor/operatorHandoff.d.ts +2 -0
- package/dist/supervisor/operatorHandoff.js +21 -9
- package/dist/supervisor/runLedger.js +7 -8
- package/dist/supervisor/sessionConfig.js +1 -0
- package/dist/types/flowCoverage.d.ts +1 -1
- package/dist/types/runLedger.d.ts +1 -1
- package/dist/types/runtimeValidation.d.ts +2 -1
- package/dist/types/sessionConfig.d.ts +0 -6
- package/dist/types/surfaces.d.ts +2 -1
- package/dist/types/workerSession.d.ts +2 -0
- package/dist/types.d.ts +0 -1
- package/dist/validation/sessionConfig.js +1 -15
- package/package.json +1 -1
- package/schemas/audit-code-v1alpha1.schema.json +4 -0
- package/schemas/audit_result.schema.json +9 -3
- package/schemas/audit_state.schema.json +2 -2
- package/schemas/audit_task.schema.json +14 -3
- package/schemas/blind_spot_register.schema.json +13 -3
- package/schemas/coverage_matrix.schema.json +16 -4
- package/schemas/critical_flows.schema.json +6 -3
- package/schemas/external_analyzer_results.schema.json +10 -4
- package/schemas/finding.schema.json +31 -3
- package/schemas/flow_coverage.schema.json +12 -3
- package/schemas/graph_bundle.schema.json +12 -6
- package/schemas/merged_findings.schema.json +7 -2
- package/schemas/risk_register.schema.json +5 -1
- package/schemas/root_cause_clusters.schema.json +2 -1
- package/schemas/runtime_validation_tasks.schema.json +4 -1
- package/schemas/surface_manifest.schema.json +4 -1
- package/schemas/unit_manifest.schema.json +10 -3
- package/skills/audit-code/audit-code.prompt.md +0 -2
package/dist/io/artifacts.d.ts
CHANGED
|
@@ -32,7 +32,29 @@ export interface ArtifactBundle {
|
|
|
32
32
|
audit_state?: AuditState;
|
|
33
33
|
artifact_metadata?: ArtifactMetadataManifest;
|
|
34
34
|
}
|
|
35
|
-
export declare const ARTIFACT_FILE_TO_BUNDLE_KEY:
|
|
35
|
+
export declare const ARTIFACT_FILE_TO_BUNDLE_KEY: {
|
|
36
|
+
readonly "repo_manifest.json": "repo_manifest";
|
|
37
|
+
readonly "file_disposition.json": "file_disposition";
|
|
38
|
+
readonly "auto_fixes_applied.json": "auto_fixes_applied";
|
|
39
|
+
readonly "unit_manifest.json": "unit_manifest";
|
|
40
|
+
readonly "graph_bundle.json": "graph_bundle";
|
|
41
|
+
readonly "surface_manifest.json": "surface_manifest";
|
|
42
|
+
readonly "critical_flows.json": "critical_flows";
|
|
43
|
+
readonly "flow_coverage.json": "flow_coverage";
|
|
44
|
+
readonly "risk_register.json": "risk_register";
|
|
45
|
+
readonly "coverage_matrix.json": "coverage_matrix";
|
|
46
|
+
readonly "runtime_validation_tasks.json": "runtime_validation_tasks";
|
|
47
|
+
readonly "runtime_validation_report.json": "runtime_validation_report";
|
|
48
|
+
readonly "external_analyzer_results.json": "external_analyzer_results";
|
|
49
|
+
readonly "audit_results.jsonl": "audit_results";
|
|
50
|
+
readonly "audit_tasks.json": "audit_tasks";
|
|
51
|
+
readonly "requeue_tasks.json": "requeue_tasks";
|
|
52
|
+
readonly "merged_findings.json": "merged_findings";
|
|
53
|
+
readonly "root_cause_clusters.json": "root_cause_clusters";
|
|
54
|
+
readonly "synthesis_report.json": "synthesis_report";
|
|
55
|
+
readonly "audit_state.json": "audit_state";
|
|
56
|
+
readonly "artifact_metadata.json": "artifact_metadata";
|
|
57
|
+
};
|
|
36
58
|
export declare function getArtifactValue(bundle: ArtifactBundle, artifactName: string): unknown;
|
|
37
59
|
export declare function loadArtifactBundle(root: string): Promise<ArtifactBundle>;
|
|
38
60
|
export declare function writeCoreArtifacts(root: string, bundle: ArtifactBundle): Promise<void>;
|
package/dist/io/artifacts.js
CHANGED
|
@@ -22,8 +22,10 @@ export const ARTIFACT_FILE_TO_BUNDLE_KEY = {
|
|
|
22
22
|
"audit_state.json": "audit_state",
|
|
23
23
|
"artifact_metadata.json": "artifact_metadata",
|
|
24
24
|
};
|
|
25
|
+
const _bundleKeyCoverage = true;
|
|
25
26
|
export function getArtifactValue(bundle, artifactName) {
|
|
26
|
-
const
|
|
27
|
+
const map = ARTIFACT_FILE_TO_BUNDLE_KEY;
|
|
28
|
+
const key = map[artifactName];
|
|
27
29
|
return key ? bundle[key] : undefined;
|
|
28
30
|
}
|
|
29
31
|
export async function loadArtifactBundle(root) {
|
|
@@ -51,8 +53,7 @@ export async function loadArtifactBundle(root) {
|
|
|
51
53
|
bundle.root_cause_clusters = await readOptionalJsonFile(`${root}/root_cause_clusters.json`);
|
|
52
54
|
bundle.synthesis_report = await readOptionalJsonFile(`${root}/synthesis_report.json`);
|
|
53
55
|
bundle.audit_state = await readOptionalJsonFile(`${root}/audit_state.json`);
|
|
54
|
-
bundle.artifact_metadata =
|
|
55
|
-
await readOptionalJsonFile(`${root}/artifact_metadata.json`);
|
|
56
|
+
bundle.artifact_metadata = await readOptionalJsonFile(`${root}/artifact_metadata.json`);
|
|
56
57
|
return bundle;
|
|
57
58
|
}
|
|
58
59
|
export async function writeCoreArtifacts(root, bundle) {
|
package/dist/io/runArtifacts.js
CHANGED
|
@@ -3,7 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { writeJsonFile } from "./json.js";
|
|
4
4
|
export function buildRunId(obligationId, index) {
|
|
5
5
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
6
|
-
const obligation = obligationId ?? "terminal";
|
|
6
|
+
const obligation = (obligationId ?? "terminal").replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
7
7
|
return `${timestamp}_${obligation}_${String(index).padStart(3, "0")}`;
|
|
8
8
|
}
|
|
9
9
|
export function getRunPaths(artifactsDir, runId) {
|
|
@@ -13,7 +13,7 @@ export async function advanceAudit(bundle, options = {}) {
|
|
|
13
13
|
: decision.selected_obligation;
|
|
14
14
|
if (!selectedExecutor) {
|
|
15
15
|
const state = deriveAuditState(bundle);
|
|
16
|
-
|
|
16
|
+
// Do not overwrite last_executor when no executor was selected — preserve prior value
|
|
17
17
|
state.last_obligation = selectedObligation ?? undefined;
|
|
18
18
|
return {
|
|
19
19
|
audit_state: state,
|
|
@@ -27,76 +27,58 @@ export async function advanceAudit(bundle, options = {}) {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
let run;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
catch (err) {
|
|
87
|
-
const state = deriveAuditState(bundle);
|
|
88
|
-
state.last_executor = selectedExecutor;
|
|
89
|
-
state.last_obligation = selectedObligation ?? undefined;
|
|
90
|
-
return {
|
|
91
|
-
audit_state: state,
|
|
92
|
-
selected_obligation: selectedObligation,
|
|
93
|
-
selected_executor: selectedExecutor,
|
|
94
|
-
progress_made: false,
|
|
95
|
-
artifacts_written: [],
|
|
96
|
-
progress_summary: `Executor ${selectedExecutor} failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
97
|
-
next_likely_step: selectedObligation,
|
|
98
|
-
updated_bundle: { ...bundle, audit_state: state },
|
|
99
|
-
};
|
|
30
|
+
switch (selectedExecutor) {
|
|
31
|
+
case "intake_executor":
|
|
32
|
+
if (!options.root)
|
|
33
|
+
throw new Error("advanceAudit intake_executor requires root");
|
|
34
|
+
run = await runIntakeExecutor(bundle, options.root);
|
|
35
|
+
break;
|
|
36
|
+
case "structure_executor":
|
|
37
|
+
run = runStructureExecutor(bundle);
|
|
38
|
+
break;
|
|
39
|
+
case "planning_executor":
|
|
40
|
+
run = runPlanningExecutor(bundle, options.lineIndex ?? {});
|
|
41
|
+
break;
|
|
42
|
+
case "result_ingestion_executor":
|
|
43
|
+
run = runResultIngestionExecutor(bundle, options.auditResults ?? bundle.audit_results ?? []);
|
|
44
|
+
break;
|
|
45
|
+
case "synthesis_executor":
|
|
46
|
+
run = runSynthesisExecutor(bundle, options.auditResults);
|
|
47
|
+
break;
|
|
48
|
+
case "runtime_validation_update_executor":
|
|
49
|
+
if (!options.runtimeValidationUpdates)
|
|
50
|
+
throw new Error("advanceAudit runtime_validation_update_executor requires runtimeValidationUpdates");
|
|
51
|
+
run = runRuntimeValidationUpdateExecutor(bundle, options.runtimeValidationUpdates);
|
|
52
|
+
break;
|
|
53
|
+
case "external_analyzer_import_executor":
|
|
54
|
+
if (!options.externalAnalyzerResults)
|
|
55
|
+
throw new Error("advanceAudit external_analyzer_import_executor requires externalAnalyzerResults");
|
|
56
|
+
run = runExternalAnalyzerImportExecutor(bundle, options.externalAnalyzerResults);
|
|
57
|
+
break;
|
|
58
|
+
case "auto_fix_executor":
|
|
59
|
+
if (!options.root)
|
|
60
|
+
throw new Error("advanceAudit auto_fix_executor requires root");
|
|
61
|
+
run = runAutoFixExecutor(bundle, options.root);
|
|
62
|
+
break;
|
|
63
|
+
case "syntax_resolution_executor":
|
|
64
|
+
if (!options.root)
|
|
65
|
+
throw new Error("advanceAudit syntax_resolution_executor requires root");
|
|
66
|
+
run = runSyntaxResolutionExecutor(bundle, options.root);
|
|
67
|
+
break;
|
|
68
|
+
default:
|
|
69
|
+
const state = deriveAuditState(bundle);
|
|
70
|
+
state.last_executor = selectedExecutor;
|
|
71
|
+
state.last_obligation = selectedObligation ?? undefined;
|
|
72
|
+
return {
|
|
73
|
+
audit_state: state,
|
|
74
|
+
selected_obligation: selectedObligation,
|
|
75
|
+
selected_executor: selectedExecutor,
|
|
76
|
+
progress_made: false,
|
|
77
|
+
artifacts_written: ["audit_state.json"],
|
|
78
|
+
progress_summary: `Executor ${selectedExecutor} is selected but not yet dispatched through advance-audit.`,
|
|
79
|
+
next_likely_step: selectedObligation,
|
|
80
|
+
updated_bundle: { ...bundle, audit_state: state },
|
|
81
|
+
};
|
|
100
82
|
}
|
|
101
83
|
const metadata = computeArtifactMetadata(run.updated, bundle.artifact_metadata);
|
|
102
84
|
const metadataBundle = { ...run.updated, artifact_metadata: metadata };
|
|
@@ -10,12 +10,11 @@ function lensSetForFlow(concerns) {
|
|
|
10
10
|
return concerns.filter((concern) => allowed.includes(concern));
|
|
11
11
|
}
|
|
12
12
|
export function buildFlowCoverage(criticalFlows, coverageMatrix) {
|
|
13
|
-
const fileIndex = new Map(coverageMatrix.files.map((file) => [file.path, file]));
|
|
14
13
|
const flows = criticalFlows.flows.map((flow) => {
|
|
15
14
|
const required = lensSetForFlow(flow.concerns);
|
|
16
15
|
const completed = new Set();
|
|
17
16
|
for (const path of flow.paths) {
|
|
18
|
-
const record =
|
|
17
|
+
const record = coverageMatrix.files.find((file) => file.path === path);
|
|
19
18
|
if (!record || record.audit_status === "excluded") {
|
|
20
19
|
continue;
|
|
21
20
|
}
|
|
@@ -118,9 +118,9 @@ export function runResultIngestionExecutor(bundle, results) {
|
|
|
118
118
|
if (!bundle.coverage_matrix) {
|
|
119
119
|
throw new Error("Cannot ingest results without coverage_matrix");
|
|
120
120
|
}
|
|
121
|
-
ingestAuditResults(bundle.coverage_matrix, results);
|
|
121
|
+
const updatedCoverageMatrix = ingestAuditResults(bundle.coverage_matrix, results);
|
|
122
122
|
const flowCoverage = bundle.critical_flows
|
|
123
|
-
? buildFlowCoverage(bundle.critical_flows,
|
|
123
|
+
? buildFlowCoverage(bundle.critical_flows, updatedCoverageMatrix)
|
|
124
124
|
: bundle.flow_coverage;
|
|
125
125
|
const runtimeValidationTasks = bundle.unit_manifest
|
|
126
126
|
? buildRuntimeValidationTasks(bundle.unit_manifest, bundle.critical_flows, flowCoverage)
|
|
@@ -128,15 +128,13 @@ export function runResultIngestionExecutor(bundle, results) {
|
|
|
128
128
|
const runtimeValidationReport = runtimeValidationTasks
|
|
129
129
|
? preserveOrPlaceholder(runtimeValidationTasks, bundle.runtime_validation_report)
|
|
130
130
|
: bundle.runtime_validation_report;
|
|
131
|
-
const requeuePayload = bundle.
|
|
132
|
-
? buildRequeuePayload(bundle.coverage_matrix, bundle.critical_flows, flowCoverage, bundle.external_analyzer_results)
|
|
133
|
-
: { tasks: [], task_count: 0 };
|
|
131
|
+
const requeuePayload = buildRequeuePayload(updatedCoverageMatrix, bundle.critical_flows, flowCoverage, bundle.external_analyzer_results);
|
|
134
132
|
const mergedResults = [...(bundle.audit_results ?? []), ...results];
|
|
135
133
|
const synthesisReport = buildSynthesisReport(mergedResults, runtimeValidationReport, bundle.external_analyzer_results);
|
|
136
134
|
return {
|
|
137
135
|
updated: {
|
|
138
136
|
...bundle,
|
|
139
|
-
coverage_matrix:
|
|
137
|
+
coverage_matrix: updatedCoverageMatrix,
|
|
140
138
|
flow_coverage: flowCoverage,
|
|
141
139
|
runtime_validation_tasks: runtimeValidationTasks,
|
|
142
140
|
runtime_validation_report: runtimeValidationReport,
|
|
@@ -1,27 +1,19 @@
|
|
|
1
1
|
import { applyUnitCoverage, createCoverageMatrix, markExcludedPath, } from "../coverage.js";
|
|
2
2
|
import { isAuditExcludedStatus } from "../extractors/disposition.js";
|
|
3
|
+
const CATEGORY_LENS_TABLE = [
|
|
4
|
+
[["security", "secret"], ["security", "correctness"]],
|
|
5
|
+
[["dependency", "vuln"], ["security", "config_deployment"]],
|
|
6
|
+
[["tests", "coverage"], ["tests"]],
|
|
7
|
+
[["data"], ["data_integrity", "correctness"]],
|
|
8
|
+
[["reliability", "concurrency"], ["reliability", "correctness"]],
|
|
9
|
+
[["maintainability", "lint", "style"], ["maintainability"]],
|
|
10
|
+
];
|
|
3
11
|
function analyzerCategoryToLenses(category) {
|
|
4
12
|
const normalized = category.toLowerCase();
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return ["security", "config_deployment"];
|
|
10
|
-
}
|
|
11
|
-
if (normalized.includes("tests") || normalized.includes("coverage")) {
|
|
12
|
-
return ["tests"];
|
|
13
|
-
}
|
|
14
|
-
if (normalized.includes("data")) {
|
|
15
|
-
return ["data_integrity", "correctness"];
|
|
16
|
-
}
|
|
17
|
-
if (normalized.includes("reliability") ||
|
|
18
|
-
normalized.includes("concurrency")) {
|
|
19
|
-
return ["reliability", "correctness"];
|
|
20
|
-
}
|
|
21
|
-
if (normalized.includes("maintainability") ||
|
|
22
|
-
normalized.includes("lint") ||
|
|
23
|
-
normalized.includes("style")) {
|
|
24
|
-
return ["maintainability"];
|
|
13
|
+
for (const [keywords, lenses] of CATEGORY_LENS_TABLE) {
|
|
14
|
+
if (keywords.some((kw) => normalized.includes(kw))) {
|
|
15
|
+
return lenses;
|
|
16
|
+
}
|
|
25
17
|
}
|
|
26
18
|
return ["correctness"];
|
|
27
19
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { applyReviewedRanges } from "../coverage.js";
|
|
2
2
|
export function ingestAuditResults(coverageMatrix, results) {
|
|
3
|
+
const matrix = JSON.parse(JSON.stringify(coverageMatrix));
|
|
3
4
|
const reviewedRanges = results.flatMap((result) => result.reviewed_ranges.map((range) => ({
|
|
4
5
|
path: range.path,
|
|
5
6
|
start: range.start,
|
|
@@ -8,6 +9,6 @@ export function ingestAuditResults(coverageMatrix, results) {
|
|
|
8
9
|
lens: result.lens,
|
|
9
10
|
agent_role: result.agent_role,
|
|
10
11
|
})));
|
|
11
|
-
applyReviewedRanges(
|
|
12
|
-
return
|
|
12
|
+
applyReviewedRanges(matrix, reviewedRanges);
|
|
13
|
+
return matrix;
|
|
13
14
|
}
|
|
@@ -63,6 +63,11 @@ export function buildRuntimeValidationTasks(unitManifest, criticalFlows, flowCov
|
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
+
const RUNTIME_TASK_CAP = 100;
|
|
67
|
+
if (tasks.length > RUNTIME_TASK_CAP) {
|
|
68
|
+
process.stderr.write(`[runtime-validation] generated ${tasks.length} tasks which exceeds the cap of ${RUNTIME_TASK_CAP}; truncating to avoid runaway expansion\n`);
|
|
69
|
+
tasks.splice(RUNTIME_TASK_CAP);
|
|
70
|
+
}
|
|
66
71
|
return { tasks };
|
|
67
72
|
}
|
|
68
73
|
export function buildPlaceholderRuntimeValidationReport(tasks) {
|
|
@@ -20,12 +20,16 @@ function runTsc(root) {
|
|
|
20
20
|
category: "correctness",
|
|
21
21
|
severity: "error",
|
|
22
22
|
path: match[1].replace(/\\/g, "/"),
|
|
23
|
+
line_start: parseInt(match[2], 10),
|
|
23
24
|
summary: match[3],
|
|
24
25
|
rule: "tsc",
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
}
|
|
30
|
+
else {
|
|
31
|
+
process.stderr.write(`[syntax-resolution] tsc exited with no stdout; stderr: ${(error.stderr?.toString() ?? "").slice(0, 200)}\n`);
|
|
32
|
+
}
|
|
29
33
|
}
|
|
30
34
|
return results;
|
|
31
35
|
}
|
|
@@ -51,6 +55,7 @@ function runEslint(root) {
|
|
|
51
55
|
path: fileResult.filePath
|
|
52
56
|
.replace(/\\/g, "/")
|
|
53
57
|
.replace(root.replace(/\\/g, "/") + "/", ""),
|
|
58
|
+
line_start: msg.line,
|
|
54
59
|
summary: msg.message,
|
|
55
60
|
rule: msg.ruleId || "eslint-error",
|
|
56
61
|
});
|
|
@@ -58,9 +63,12 @@ function runEslint(root) {
|
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
65
|
catch (e) {
|
|
61
|
-
|
|
66
|
+
process.stderr.write(`[syntax-resolution] eslint output could not be parsed: ${(error.stdout?.toString() ?? "").slice(0, 200)}\n`);
|
|
62
67
|
}
|
|
63
68
|
}
|
|
69
|
+
else {
|
|
70
|
+
process.stderr.write(`[syntax-resolution] eslint exited with no stdout; stderr: ${(error.stderr?.toString() ?? "").slice(0, 200)}\n`);
|
|
71
|
+
}
|
|
64
72
|
}
|
|
65
73
|
return results;
|
|
66
74
|
}
|
|
@@ -78,7 +86,7 @@ export function runSyntaxResolutionExecutor(bundle, root) {
|
|
|
78
86
|
const seen = new Set();
|
|
79
87
|
const deduped = [];
|
|
80
88
|
for (const r of merged) {
|
|
81
|
-
const key = `${r.path}:${r.rule}:${r.summary}`;
|
|
89
|
+
const key = `${r.path}:${r.line_start ?? ""}:${r.rule}:${r.summary}`;
|
|
82
90
|
if (!seen.has(key)) {
|
|
83
91
|
seen.add(key);
|
|
84
92
|
deduped.push(r);
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
function modelHintForLens(lens) {
|
|
2
|
-
switch (lens) {
|
|
3
|
-
case "security":
|
|
4
|
-
case "correctness":
|
|
5
|
-
case "reliability":
|
|
6
|
-
case "data_integrity":
|
|
7
|
-
return "capable";
|
|
8
|
-
case "architecture":
|
|
9
|
-
case "maintainability":
|
|
10
|
-
case "tests":
|
|
11
|
-
return "balanced";
|
|
12
|
-
default:
|
|
13
|
-
return "fast";
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
1
|
function taskPriority(hasExternalSignal, lens) {
|
|
17
2
|
if (hasExternalSignal &&
|
|
18
3
|
(lens === "security" || lens === "data_integrity" || lens === "reliability")) {
|
|
@@ -52,8 +37,9 @@ function pickAnalyzerLens(category) {
|
|
|
52
37
|
return "maintainability";
|
|
53
38
|
return "correctness";
|
|
54
39
|
}
|
|
40
|
+
const DEFAULT_FILE_SPLIT_THRESHOLD = 3000;
|
|
55
41
|
export function buildChunkedAuditTasks(unitManifest, unitLineIndex, options = {}) {
|
|
56
|
-
const fileSplitThreshold = options.file_split_threshold ??
|
|
42
|
+
const fileSplitThreshold = options.file_split_threshold ?? DEFAULT_FILE_SPLIT_THRESHOLD;
|
|
57
43
|
const allowed = new Set(options.limit_lenses ?? []);
|
|
58
44
|
const enforceLensFilter = allowed.size > 0;
|
|
59
45
|
const tasks = [];
|
|
@@ -91,7 +77,6 @@ export function buildChunkedAuditTasks(unitManifest, unitLineIndex, options = {}
|
|
|
91
77
|
rationale: `Audit ${unit.unit_id} (${normalFiles.length} file${normalFiles.length === 1 ? "" : "s"}) under the ${lens} lens.${hasExternalSignal ? " External analyzer signals raise priority." : ""}`,
|
|
92
78
|
priority,
|
|
93
79
|
tags,
|
|
94
|
-
model_hint: modelHintForLens(lens),
|
|
95
80
|
});
|
|
96
81
|
}
|
|
97
82
|
}
|
|
@@ -110,10 +95,7 @@ export function buildChunkedAuditTasks(unitManifest, unitLineIndex, options = {}
|
|
|
110
95
|
file_paths: [filePath],
|
|
111
96
|
rationale: `Audit ${filePath} (large file, split from unit) under the ${lens} lens.${fileHasSignal ? " External analyzer signals raise priority." : ""}`,
|
|
112
97
|
priority: taskPriority(fileHasSignal, lens),
|
|
113
|
-
tags: fileHasSignal
|
|
114
|
-
? ["external_analyzer_signal", "large_file"]
|
|
115
|
-
: ["large_file"],
|
|
116
|
-
model_hint: modelHintForLens(lens),
|
|
98
|
+
tags: fileHasSignal ? ["external_analyzer_signal", "large_file"] : ["large_file"],
|
|
117
99
|
});
|
|
118
100
|
}
|
|
119
101
|
}
|
|
@@ -125,37 +107,42 @@ export function buildChunkedAuditTasks(unitManifest, unitLineIndex, options = {}
|
|
|
125
107
|
return a.task_id.localeCompare(b.task_id);
|
|
126
108
|
});
|
|
127
109
|
}
|
|
110
|
+
/** Strip control characters and newlines, then truncate to maxLen. */
|
|
111
|
+
function sanitizeField(value, maxLen) {
|
|
112
|
+
return value.replace(/[\x00-\x1f\x7f]/g, " ").slice(0, maxLen).trimEnd();
|
|
113
|
+
}
|
|
128
114
|
export function buildExternalSignalTasks(coverageMatrix, _unitLineIndex, externalAnalyzerResults) {
|
|
129
115
|
if (!externalAnalyzerResults) {
|
|
130
116
|
return [];
|
|
131
117
|
}
|
|
132
118
|
const tasks = [];
|
|
133
119
|
const seen = new Set();
|
|
134
|
-
const coverageIndex = new Map(coverageMatrix.files.map((file) => [file.path, file]));
|
|
135
120
|
for (const result of externalAnalyzerResults.results) {
|
|
136
|
-
const
|
|
137
|
-
const
|
|
121
|
+
const safeCategory = sanitizeField(result.category, 80);
|
|
122
|
+
const safePath = sanitizeField(result.path ?? "", 260);
|
|
123
|
+
const safeSummary = sanitizeField(result.summary ?? "", 200);
|
|
124
|
+
const lens = pickAnalyzerLens(safeCategory);
|
|
125
|
+
const coverage = coverageMatrix.files.find((file) => file.path === result.path);
|
|
138
126
|
if (!coverage || coverage.audit_status === "excluded") {
|
|
139
127
|
continue;
|
|
140
128
|
}
|
|
141
|
-
const id = `analyzer:${externalAnalyzerResults.tool}:${lens}:${
|
|
129
|
+
const id = `analyzer:${externalAnalyzerResults.tool}:${lens}:${safePath}:${result.id}`;
|
|
142
130
|
if (seen.has(id)) {
|
|
143
131
|
continue;
|
|
144
132
|
}
|
|
145
133
|
seen.add(id);
|
|
146
134
|
tasks.push({
|
|
147
135
|
task_id: id,
|
|
148
|
-
unit_id: coverage.unit_ids[0] ?? `analyzer:${
|
|
136
|
+
unit_id: coverage.unit_ids[0] ?? `analyzer:${safePath}`,
|
|
149
137
|
pass_id: `analyzer:${externalAnalyzerResults.tool}:${lens}`,
|
|
150
138
|
lens,
|
|
151
139
|
file_paths: [result.path],
|
|
152
|
-
rationale: `Analyzer follow-up for ${
|
|
140
|
+
rationale: `Analyzer follow-up for ${safePath} under the ${lens} lens because ${externalAnalyzerResults.tool} reported: ${safeSummary}`,
|
|
153
141
|
priority: "high",
|
|
154
142
|
tags: [
|
|
155
143
|
"external_analyzer_signal",
|
|
156
144
|
`external_tool:${externalAnalyzerResults.tool}`,
|
|
157
145
|
],
|
|
158
|
-
model_hint: modelHintForLens(lens),
|
|
159
146
|
});
|
|
160
147
|
}
|
|
161
148
|
return tasks.sort((a, b) => a.task_id.localeCompare(b.task_id));
|
|
@@ -4,7 +4,8 @@ function shellQuote(arg) {
|
|
|
4
4
|
export function renderWorkerPrompt(task) {
|
|
5
5
|
const command = task.worker_command.map(shellQuote).join(" ");
|
|
6
6
|
if (task.preferred_executor === "agent" && task.audit_results_path) {
|
|
7
|
-
const tasksPath = task.pending_audit_tasks_path ??
|
|
7
|
+
const tasksPath = task.pending_audit_tasks_path ??
|
|
8
|
+
`${task.artifacts_dir}/audit_tasks.json`;
|
|
8
9
|
const lines = [
|
|
9
10
|
"You are executing one bounded audit task for audit-code.",
|
|
10
11
|
`Run ID: ${task.run_id}`,
|
|
@@ -9,7 +9,7 @@ export class ClaudeCodeProvider {
|
|
|
9
9
|
async launch(input) {
|
|
10
10
|
if (process.env.CLAUDECODE) {
|
|
11
11
|
throw new Error("claude-code provider cannot be used inside an active Claude Code session. " +
|
|
12
|
-
|
|
12
|
+
"Set provider to \"local-subprocess\" in .audit-artifacts/session-config.json, " +
|
|
13
13
|
"then run /audit-code conversationally and follow the dispatch prompts manually.");
|
|
14
14
|
}
|
|
15
15
|
const prompt = await readFile(input.promptPath, "utf8");
|
|
@@ -17,7 +17,6 @@ export class ClaudeCodeProvider {
|
|
|
17
17
|
const args = [
|
|
18
18
|
"-p",
|
|
19
19
|
prompt,
|
|
20
|
-
...(input.model ? ["--model", input.model] : []),
|
|
21
20
|
...(this.config.extra_args ?? []),
|
|
22
21
|
"--dangerously-skip-permissions",
|
|
23
22
|
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const LOCAL_SUBPROCESS_PROVIDER_NAME: "local-subprocess";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const LOCAL_SUBPROCESS_PROVIDER_NAME = "local-subprocess";
|
package/dist/providers/index.js
CHANGED
|
@@ -39,9 +39,7 @@ export function resolveFreshSessionProviderName(name, sessionConfig = {}, option
|
|
|
39
39
|
const opencodeCommand = sessionConfig.opencode?.command ?? "opencode";
|
|
40
40
|
const claudeAvailable = !insideClaudeCode && lookupCommand(claudeCommand);
|
|
41
41
|
const opencodeAvailable = lookupCommand(opencodeCommand);
|
|
42
|
-
if (!insideClaudeCode &&
|
|
43
|
-
hasConfiguredClaudeCode(sessionConfig) &&
|
|
44
|
-
claudeAvailable) {
|
|
42
|
+
if (!insideClaudeCode && hasConfiguredClaudeCode(sessionConfig) && claudeAvailable) {
|
|
45
43
|
return "claude-code";
|
|
46
44
|
}
|
|
47
45
|
if (hasConfiguredOpenCode(sessionConfig) && opencodeAvailable) {
|
|
@@ -57,6 +55,12 @@ export function resolveFreshSessionProviderName(name, sessionConfig = {}, option
|
|
|
57
55
|
}
|
|
58
56
|
export function createFreshSessionProvider(name, sessionConfig = {}) {
|
|
59
57
|
const providerName = resolveFreshSessionProviderName(name, sessionConfig);
|
|
58
|
+
if (providerName === "local-subprocess" &&
|
|
59
|
+
(name ?? sessionConfig.provider) === "auto") {
|
|
60
|
+
process.stderr.write("audit-code: auto provider resolved to local-subprocess — no capable agent provider detected. " +
|
|
61
|
+
"Agent tasks will require manual dispatch. Configure claude-code, opencode, or subprocess-template " +
|
|
62
|
+
"in session-config.json to automate them.\n");
|
|
63
|
+
}
|
|
60
64
|
switch (providerName) {
|
|
61
65
|
case "local-subprocess":
|
|
62
66
|
return new LocalSubprocessProvider();
|
|
@@ -9,12 +9,7 @@ export class OpenCodeProvider {
|
|
|
9
9
|
async launch(input) {
|
|
10
10
|
const prompt = await readFile(input.promptPath, "utf8");
|
|
11
11
|
const command = this.config.command ?? "opencode";
|
|
12
|
-
const args = [
|
|
13
|
-
"run",
|
|
14
|
-
prompt,
|
|
15
|
-
...(input.model ? ["--model", input.model] : []),
|
|
16
|
-
...(this.config.extra_args ?? []),
|
|
17
|
-
];
|
|
12
|
+
const args = ["run", prompt, ...(this.config.extra_args ?? [])];
|
|
18
13
|
return await spawnLoggedCommand(command, args, input);
|
|
19
14
|
}
|
|
20
15
|
}
|
|
@@ -3,6 +3,10 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
function tee(write, chunk) {
|
|
4
4
|
write.write(chunk);
|
|
5
5
|
}
|
|
6
|
+
// On Windows `command` must be the resolved .cmd / .exe path because `spawn`
|
|
7
|
+
// does not consult PATH for executables without a shell. Callers should use
|
|
8
|
+
// `platformCommand()` (scripts/smoke-packaged-audit-code.mjs) or similar to
|
|
9
|
+
// supply the correct command form for the host OS.
|
|
6
10
|
export async function spawnLoggedCommand(command, args, input, env) {
|
|
7
11
|
return await new Promise((resolve, reject) => {
|
|
8
12
|
const stdoutLog = createWriteStream(input.stdoutPath, { flags: "a" });
|
|
@@ -14,6 +14,7 @@ export interface AuditCodeHandoffArtifactPaths {
|
|
|
14
14
|
audit_tasks: string | null;
|
|
15
15
|
runtime_validation_tasks: string | null;
|
|
16
16
|
}
|
|
17
|
+
export declare const CONFIG_ERROR_BLOCKER_PREFIX = "config-error:";
|
|
17
18
|
export interface AuditCodeHandoff {
|
|
18
19
|
status: AuditTopLevelStatus;
|
|
19
20
|
repo_root: string;
|
|
@@ -33,5 +34,6 @@ export declare function buildAuditCodeHandoff(params: {
|
|
|
33
34
|
bundle: ArtifactBundle;
|
|
34
35
|
providerName?: string | null;
|
|
35
36
|
progressSummary: string;
|
|
37
|
+
isConfigError?: boolean;
|
|
36
38
|
}): AuditCodeHandoff;
|
|
37
39
|
export declare function writeAuditCodeHandoffArtifacts(handoff: AuditCodeHandoff): Promise<void>;
|