auditor-lambda 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/audit-code-wrapper-lib.mjs +149 -129
- package/dist/adapters/normalizeExternal.js +6 -3
- package/dist/cli/args.d.ts +0 -1
- package/dist/cli/args.js +0 -6
- package/dist/cli/dispatch.js +3 -2
- package/dist/cli/lineIndex.js +4 -1
- package/dist/cli/mergeAndIngestCommand.d.ts +1 -0
- package/dist/cli/mergeAndIngestCommand.js +219 -0
- package/dist/cli/nextStepCommand.js +5 -1
- package/dist/cli/runToCompletion.d.ts +9 -0
- package/dist/cli/runToCompletion.js +655 -480
- package/dist/cli/statusCommand.d.ts +1 -0
- package/dist/cli/statusCommand.js +113 -0
- package/dist/cli/submitPacketCommand.d.ts +1 -0
- package/dist/cli/submitPacketCommand.js +155 -0
- package/dist/cli/workerResult.d.ts +1 -1
- package/dist/cli/workerRunCommand.d.ts +1 -0
- package/dist/cli/workerRunCommand.js +88 -0
- package/dist/cli.js +14 -563
- package/dist/extractors/analyzers/sql.js +4 -1
- package/dist/extractors/analyzers/treeSitter.js +29 -15
- package/dist/extractors/analyzers/typescript.js +10 -8
- package/dist/extractors/designAssessment.js +43 -24
- package/dist/extractors/graph.js +139 -73
- package/dist/extractors/pathPatterns.js +17 -5
- package/dist/io/runArtifactTypes.d.ts +18 -0
- package/dist/io/runArtifactTypes.js +1 -0
- package/dist/io/runArtifacts.d.ts +2 -18
- package/dist/io/runArtifacts.js +14 -3
- package/dist/mcp/server.js +9 -0
- package/dist/orchestrator/advance.js +37 -22
- package/dist/orchestrator/artifactFreshness.js +2 -2
- package/dist/orchestrator/autoFixExecutor.d.ts +1 -1
- package/dist/orchestrator/autoFixExecutor.js +16 -8
- package/dist/orchestrator/dependencyMap.d.ts +1 -1
- package/dist/orchestrator/dependencyMap.js +7 -1
- package/dist/orchestrator/fileAnchors.js +14 -3
- package/dist/orchestrator/flowCoverage.js +1 -0
- package/dist/orchestrator/flowRequeue.js +4 -1
- package/dist/orchestrator/{internalExecutors.d.ts → ingestionExecutors.d.ts} +0 -6
- package/dist/orchestrator/ingestionExecutors.js +237 -0
- package/dist/orchestrator/intakeExecutors.d.ts +3 -0
- package/dist/orchestrator/intakeExecutors.js +25 -0
- package/dist/orchestrator/planningExecutors.d.ts +4 -0
- package/dist/orchestrator/planningExecutors.js +95 -0
- package/dist/orchestrator/runtimeCommand.js +7 -15
- package/dist/orchestrator/selectiveDeepening/conflict.d.ts +8 -0
- package/dist/orchestrator/selectiveDeepening/conflict.js +71 -0
- package/dist/orchestrator/selectiveDeepening/findingFollowup.d.ts +10 -0
- package/dist/orchestrator/selectiveDeepening/findingFollowup.js +52 -0
- package/dist/orchestrator/selectiveDeepening/highRiskClean.d.ts +7 -0
- package/dist/orchestrator/selectiveDeepening/highRiskClean.js +44 -0
- package/dist/orchestrator/selectiveDeepening/index.d.ts +18 -0
- package/dist/orchestrator/selectiveDeepening/index.js +128 -0
- package/dist/orchestrator/selectiveDeepening/lensVerification.d.ts +12 -0
- package/dist/orchestrator/selectiveDeepening/lensVerification.js +242 -0
- package/dist/orchestrator/selectiveDeepening/runtimeValidation.d.ts +13 -0
- package/dist/orchestrator/selectiveDeepening/runtimeValidation.js +57 -0
- package/dist/orchestrator/selectiveDeepening/shared.d.ts +45 -0
- package/dist/orchestrator/selectiveDeepening/shared.js +128 -0
- package/dist/orchestrator/selectiveDeepening/stewardFollowup.d.ts +6 -0
- package/dist/orchestrator/selectiveDeepening/stewardFollowup.js +72 -0
- package/dist/orchestrator/selectiveDeepening.d.ts +2 -20
- package/dist/orchestrator/selectiveDeepening.js +6 -760
- package/dist/orchestrator/staleness.js +3 -3
- package/dist/orchestrator/structureExecutors.d.ts +5 -0
- package/dist/orchestrator/structureExecutors.js +94 -0
- package/dist/orchestrator/taskBuilder.d.ts +2 -2
- package/dist/orchestrator/taskBuilder.js +101 -82
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.js +14 -95
- package/dist/quota/discoveredLimits.d.ts +1 -0
- package/dist/quota/discoveredLimits.js +7 -1
- package/dist/quota/index.d.ts +0 -2
- package/dist/quota/index.js +1 -2
- package/dist/reporting/workBlocks.js +7 -4
- package/dist/types/reviewPlanning.d.ts +23 -16
- package/dist/validation/auditResults.js +97 -95
- package/dist/validation/sessionConfig.d.ts +2 -2
- package/dist/validation/sessionConfig.js +14 -7
- package/package.json +3 -2
- package/schemas/audit_findings.schema.json +3 -3
- package/schemas/critical_flows.schema.json +3 -2
- package/schemas/dispatch_quota.schema.json +1 -1
- package/schemas/graph_bundle.schema.json +1 -1
- package/schemas/review_packets.schema.json +1 -1
- package/schemas/step_contract.schema.json +80 -0
- package/scripts/postinstall.mjs +19 -2
- package/skills/audit-code/opencode-command-template.txt +3 -3
- package/dist/orchestrator/internalExecutors.js +0 -424
- package/dist/providers/localSubprocessProvider.d.ts +0 -9
- package/dist/providers/localSubprocessProvider.js +0 -18
- package/dist/providers/subprocessTemplateProvider.d.ts +0 -8
- package/dist/providers/subprocessTemplateProvider.js +0 -59
- package/dist/providers/vscodeTaskProvider.d.ts +0 -7
- package/dist/providers/vscodeTaskProvider.js +0 -14
- package/dist/quota/probe.d.ts +0 -10
- package/dist/quota/probe.js +0 -18
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getArtifactValue } from "../io/artifacts.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ARTIFACT_DEPENDENTS_MAP } from "./dependencyMap.js";
|
|
3
3
|
import { present } from "./artifactMetadata.js";
|
|
4
4
|
import { buildReverseDependencyMap, hashArtifactValue, stableStringify, } from "./artifactFreshness.js";
|
|
5
5
|
function computeContentHash(artifactName, bundle) {
|
|
@@ -54,7 +54,7 @@ export function computeStaleArtifacts(bundle) {
|
|
|
54
54
|
stale.add(artifactName);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
for (const [upstream, downstreamList] of Object.entries(
|
|
57
|
+
for (const [upstream, downstreamList] of Object.entries(ARTIFACT_DEPENDENTS_MAP)) {
|
|
58
58
|
if (upstream === "tooling_manifest.json" && !present(bundle, upstream)) {
|
|
59
59
|
continue;
|
|
60
60
|
}
|
|
@@ -70,7 +70,7 @@ export function computeStaleArtifacts(bundle) {
|
|
|
70
70
|
let changed = true;
|
|
71
71
|
while (changed) {
|
|
72
72
|
changed = false;
|
|
73
|
-
for (const [upstream, downstreamList] of Object.entries(
|
|
73
|
+
for (const [upstream, downstreamList] of Object.entries(ARTIFACT_DEPENDENTS_MAP)) {
|
|
74
74
|
if (!stale.has(upstream)) {
|
|
75
75
|
continue;
|
|
76
76
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ArtifactBundle } from "../io/artifacts.js";
|
|
2
|
+
import type { ExecutorRunResult } from "./executorResult.js";
|
|
3
|
+
export declare function runStructureExecutor(bundle: ArtifactBundle, root?: string): Promise<ExecutorRunResult>;
|
|
4
|
+
export declare function runDesignAssessmentExecutor(bundle: ArtifactBundle): ExecutorRunResult;
|
|
5
|
+
export declare function runDesignReviewAutoComplete(bundle: ArtifactBundle): ExecutorRunResult;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { buildFileDisposition } from "../extractors/disposition.js";
|
|
2
|
+
import { buildGraphBundle, buildGraphBundleFromFs, } from "../extractors/graph.js";
|
|
3
|
+
import { buildCriticalFlowManifest } from "../extractors/flows.js";
|
|
4
|
+
import { buildRiskRegister } from "../extractors/risk.js";
|
|
5
|
+
import { buildSurfaceManifest } from "../extractors/surfaces.js";
|
|
6
|
+
import { buildUnitManifest } from "./unitBuilder.js";
|
|
7
|
+
import { buildDesignAssessment } from "../extractors/designAssessment.js";
|
|
8
|
+
export async function runStructureExecutor(bundle, root) {
|
|
9
|
+
if (!bundle.repo_manifest) {
|
|
10
|
+
throw new Error("Cannot run structure executor without repo_manifest");
|
|
11
|
+
}
|
|
12
|
+
const externalAnalyzerResults = bundle.external_analyzer_results;
|
|
13
|
+
const disposition = bundle.file_disposition ?? buildFileDisposition(bundle.repo_manifest);
|
|
14
|
+
const unitManifest = buildUnitManifest(bundle.repo_manifest, disposition);
|
|
15
|
+
const graphBundle = root
|
|
16
|
+
? await buildGraphBundleFromFs(bundle.repo_manifest, root, disposition, {
|
|
17
|
+
externalAnalyzerResults,
|
|
18
|
+
})
|
|
19
|
+
: buildGraphBundle(bundle.repo_manifest, disposition, {
|
|
20
|
+
externalAnalyzerResults,
|
|
21
|
+
});
|
|
22
|
+
const surfaceManifest = buildSurfaceManifest(bundle.repo_manifest, disposition, { graphBundle });
|
|
23
|
+
const criticalFlows = buildCriticalFlowManifest(bundle.repo_manifest, surfaceManifest, disposition);
|
|
24
|
+
const riskRegister = buildRiskRegister(unitManifest, criticalFlows, externalAnalyzerResults);
|
|
25
|
+
return {
|
|
26
|
+
updated: {
|
|
27
|
+
...bundle,
|
|
28
|
+
file_disposition: disposition,
|
|
29
|
+
unit_manifest: unitManifest,
|
|
30
|
+
surface_manifest: surfaceManifest,
|
|
31
|
+
graph_bundle: graphBundle,
|
|
32
|
+
critical_flows: criticalFlows,
|
|
33
|
+
risk_register: riskRegister,
|
|
34
|
+
},
|
|
35
|
+
artifacts_written: [
|
|
36
|
+
"file_disposition.json",
|
|
37
|
+
"unit_manifest.json",
|
|
38
|
+
"surface_manifest.json",
|
|
39
|
+
"graph_bundle.json",
|
|
40
|
+
"critical_flows.json",
|
|
41
|
+
"risk_register.json",
|
|
42
|
+
],
|
|
43
|
+
progress_summary: `Built structure artifacts for ${unitManifest.units.length} units and ${criticalFlows.flows.length} critical flows.` +
|
|
44
|
+
(criticalFlows.fallback_required
|
|
45
|
+
? " Deterministic flow inference did not fully meet the confidence bar."
|
|
46
|
+
: ""),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function runDesignAssessmentExecutor(bundle) {
|
|
50
|
+
if (!bundle.unit_manifest ||
|
|
51
|
+
!bundle.graph_bundle ||
|
|
52
|
+
!bundle.critical_flows ||
|
|
53
|
+
!bundle.risk_register) {
|
|
54
|
+
throw new Error("Cannot run design assessment executor without structure artifacts");
|
|
55
|
+
}
|
|
56
|
+
const designAssessment = buildDesignAssessment({
|
|
57
|
+
unitManifest: bundle.unit_manifest,
|
|
58
|
+
graphBundle: bundle.graph_bundle,
|
|
59
|
+
criticalFlows: bundle.critical_flows,
|
|
60
|
+
riskRegister: bundle.risk_register,
|
|
61
|
+
});
|
|
62
|
+
const previous = bundle.design_assessment;
|
|
63
|
+
if (previous?.reviewed) {
|
|
64
|
+
designAssessment.reviewed = true;
|
|
65
|
+
designAssessment.review_findings = previous.review_findings ?? [];
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
updated: {
|
|
69
|
+
...bundle,
|
|
70
|
+
design_assessment: designAssessment,
|
|
71
|
+
},
|
|
72
|
+
artifacts_written: ["design_assessment.json"],
|
|
73
|
+
progress_summary: `Design assessment complete: ${designAssessment.findings.length} structural finding(s).`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function runDesignReviewAutoComplete(bundle) {
|
|
77
|
+
const existing = bundle.design_assessment;
|
|
78
|
+
if (!existing) {
|
|
79
|
+
throw new Error("Cannot auto-complete design review without design_assessment artifact");
|
|
80
|
+
}
|
|
81
|
+
const updated = {
|
|
82
|
+
...existing,
|
|
83
|
+
reviewed: true,
|
|
84
|
+
review_findings: existing.review_findings ?? [],
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
updated: {
|
|
88
|
+
...bundle,
|
|
89
|
+
design_assessment: updated,
|
|
90
|
+
},
|
|
91
|
+
artifacts_written: ["design_assessment.json"],
|
|
92
|
+
progress_summary: "Design review auto-completed (host-agent review available via next-step).",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -7,8 +7,8 @@ export interface UnitLineIndex {
|
|
|
7
7
|
export interface BuildChunkedTaskOptions {
|
|
8
8
|
/**
|
|
9
9
|
* Line count above which a single file gets its own task rather than being
|
|
10
|
-
* grouped with the rest of its unit. Default:
|
|
11
|
-
* splitting entirely.
|
|
10
|
+
* grouped with the rest of its unit. Default: `DEFAULT_FILE_SPLIT_THRESHOLD`
|
|
11
|
+
* (5000). Set to 0 to disable splitting entirely.
|
|
12
12
|
*/
|
|
13
13
|
file_split_threshold?: number;
|
|
14
14
|
/**
|
|
@@ -40,6 +40,97 @@ const DEFAULT_MAX_TASK_LINES = 3000;
|
|
|
40
40
|
const DEFAULT_MAX_TASK_FILES = 15;
|
|
41
41
|
const DEFAULT_TINY_TEST_FILE_LINES = 250;
|
|
42
42
|
const TINY_TEST_UNIT_ID = "tests-tiny-files";
|
|
43
|
+
// Split a flat list of file paths into review-task-sized chunks, bounded by both
|
|
44
|
+
// an aggregate line budget and a max file count. Hoisted out of
|
|
45
|
+
// `buildChunkedAuditTasks` so it no longer closes over the loop's locals; the
|
|
46
|
+
// line index and limits are passed in explicitly.
|
|
47
|
+
function chunkByTaskBudget(filePaths, unitLineIndex, limits) {
|
|
48
|
+
const { maxTaskLines, maxTaskFiles } = limits;
|
|
49
|
+
if (filePaths.length === 0) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
if (maxTaskLines <= 0 && maxTaskFiles <= 0) {
|
|
53
|
+
return [filePaths];
|
|
54
|
+
}
|
|
55
|
+
const chunks = [];
|
|
56
|
+
let current = [];
|
|
57
|
+
let currentLines = 0;
|
|
58
|
+
for (const path of filePaths) {
|
|
59
|
+
const lineCount = unitLineIndex[path] ?? 0;
|
|
60
|
+
const wouldExceedFiles = maxTaskFiles > 0 && current.length >= maxTaskFiles;
|
|
61
|
+
const wouldExceedLines = maxTaskLines > 0 &&
|
|
62
|
+
current.length > 0 &&
|
|
63
|
+
currentLines + lineCount > maxTaskLines;
|
|
64
|
+
if (wouldExceedFiles || wouldExceedLines) {
|
|
65
|
+
chunks.push(current);
|
|
66
|
+
current = [];
|
|
67
|
+
currentLines = 0;
|
|
68
|
+
}
|
|
69
|
+
current.push(path);
|
|
70
|
+
currentLines += lineCount;
|
|
71
|
+
}
|
|
72
|
+
if (current.length > 0) {
|
|
73
|
+
chunks.push(current);
|
|
74
|
+
}
|
|
75
|
+
return chunks;
|
|
76
|
+
}
|
|
77
|
+
// Emit one or more audit tasks for a scope/lens. Normal-sized files are grouped
|
|
78
|
+
// into budget-bounded chunks; files over `fileSplitThreshold` get their own
|
|
79
|
+
// isolated task. Hoisted to module scope: the per-call mutable accumulators
|
|
80
|
+
// (`tasks`, `seen`) and budget config are now explicit parameters instead of
|
|
81
|
+
// captured closure state.
|
|
82
|
+
function addTaskBlock(params, context) {
|
|
83
|
+
const { tasks, seen, unitLineIndex, fileSplitThreshold, budgetLimits } = context;
|
|
84
|
+
const oversizedFiles = fileSplitThreshold > 0
|
|
85
|
+
? params.filePaths.filter((path) => (unitLineIndex[path] ?? 0) > fileSplitThreshold)
|
|
86
|
+
: [];
|
|
87
|
+
const oversizedSet = new Set(oversizedFiles);
|
|
88
|
+
const normalFiles = params.filePaths.filter((path) => !oversizedSet.has(path));
|
|
89
|
+
const normalChunks = chunkByTaskBudget(normalFiles, unitLineIndex, budgetLimits);
|
|
90
|
+
for (let index = 0; index < normalChunks.length; index++) {
|
|
91
|
+
const chunk = normalChunks[index];
|
|
92
|
+
const splitKind = normalChunks.length > 1 ? "budget" : "none";
|
|
93
|
+
const taskId = splitKind === "budget"
|
|
94
|
+
? `${params.scopeId}:${params.lens}:part-${index + 1}`
|
|
95
|
+
: `${params.scopeId}:${params.lens}`;
|
|
96
|
+
if (!seen.has(taskId)) {
|
|
97
|
+
seen.add(taskId);
|
|
98
|
+
tasks.push({
|
|
99
|
+
task_id: taskId,
|
|
100
|
+
unit_id: params.unitId,
|
|
101
|
+
pass_id: params.passId,
|
|
102
|
+
lens: params.lens,
|
|
103
|
+
file_paths: chunk,
|
|
104
|
+
rationale: params.rationale(chunk, splitKind),
|
|
105
|
+
priority: params.priority,
|
|
106
|
+
tags: splitKind === "budget"
|
|
107
|
+
? [...new Set([...params.tags, "line_budget_split"])]
|
|
108
|
+
: params.tags.length > 0
|
|
109
|
+
? params.tags
|
|
110
|
+
: undefined,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const filePath of oversizedFiles) {
|
|
115
|
+
const taskId = `${params.scopeId}:${params.lens}:${filePath}`;
|
|
116
|
+
if (seen.has(taskId)) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
seen.add(taskId);
|
|
120
|
+
tasks.push({
|
|
121
|
+
task_id: taskId,
|
|
122
|
+
unit_id: params.unitId,
|
|
123
|
+
pass_id: params.passId,
|
|
124
|
+
lens: params.lens,
|
|
125
|
+
file_paths: [filePath],
|
|
126
|
+
rationale: params.rationale([filePath], "large_file"),
|
|
127
|
+
priority: params.priority,
|
|
128
|
+
tags: params.tags.length > 0
|
|
129
|
+
? [...new Set([...params.tags, "large_file"])]
|
|
130
|
+
: ["large_file"],
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
43
134
|
function buildCoverageIndex(coverageMatrix) {
|
|
44
135
|
return new Map(coverageMatrix.files.map((file) => [file.path, file]));
|
|
45
136
|
}
|
|
@@ -94,86 +185,14 @@ export function buildChunkedAuditTasks(coverageMatrix, unitLineIndex, options =
|
|
|
94
185
|
pendingByLens.set(lens, pending);
|
|
95
186
|
}
|
|
96
187
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
let current = [];
|
|
106
|
-
let currentLines = 0;
|
|
107
|
-
for (const path of filePaths) {
|
|
108
|
-
const lineCount = unitLineIndex[path] ?? 0;
|
|
109
|
-
const wouldExceedFiles = maxTaskFiles > 0 && current.length >= maxTaskFiles;
|
|
110
|
-
const wouldExceedLines = maxTaskLines > 0 &&
|
|
111
|
-
current.length > 0 &&
|
|
112
|
-
currentLines + lineCount > maxTaskLines;
|
|
113
|
-
if (wouldExceedFiles || wouldExceedLines) {
|
|
114
|
-
chunks.push(current);
|
|
115
|
-
current = [];
|
|
116
|
-
currentLines = 0;
|
|
117
|
-
}
|
|
118
|
-
current.push(path);
|
|
119
|
-
currentLines += lineCount;
|
|
120
|
-
}
|
|
121
|
-
if (current.length > 0) {
|
|
122
|
-
chunks.push(current);
|
|
123
|
-
}
|
|
124
|
-
return chunks;
|
|
125
|
-
}
|
|
126
|
-
function addTaskBlock(params) {
|
|
127
|
-
const oversizedFiles = fileSplitThreshold > 0
|
|
128
|
-
? params.filePaths.filter((path) => (unitLineIndex[path] ?? 0) > fileSplitThreshold)
|
|
129
|
-
: [];
|
|
130
|
-
const oversizedSet = new Set(oversizedFiles);
|
|
131
|
-
const normalFiles = params.filePaths.filter((path) => !oversizedSet.has(path));
|
|
132
|
-
const normalChunks = chunkByTaskBudget(normalFiles);
|
|
133
|
-
for (let index = 0; index < normalChunks.length; index++) {
|
|
134
|
-
const chunk = normalChunks[index];
|
|
135
|
-
const splitKind = normalChunks.length > 1 ? "budget" : "none";
|
|
136
|
-
const taskId = splitKind === "budget"
|
|
137
|
-
? `${params.scopeId}:${params.lens}:part-${index + 1}`
|
|
138
|
-
: `${params.scopeId}:${params.lens}`;
|
|
139
|
-
if (!seen.has(taskId)) {
|
|
140
|
-
seen.add(taskId);
|
|
141
|
-
tasks.push({
|
|
142
|
-
task_id: taskId,
|
|
143
|
-
unit_id: params.unitId,
|
|
144
|
-
pass_id: params.passId,
|
|
145
|
-
lens: params.lens,
|
|
146
|
-
file_paths: chunk,
|
|
147
|
-
rationale: params.rationale(chunk, splitKind),
|
|
148
|
-
priority: params.priority,
|
|
149
|
-
tags: splitKind === "budget"
|
|
150
|
-
? [...new Set([...params.tags, "line_budget_split"])]
|
|
151
|
-
: params.tags.length > 0
|
|
152
|
-
? params.tags
|
|
153
|
-
: undefined,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
for (const filePath of oversizedFiles) {
|
|
158
|
-
const taskId = `${params.scopeId}:${params.lens}:${filePath}`;
|
|
159
|
-
if (seen.has(taskId)) {
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
seen.add(taskId);
|
|
163
|
-
tasks.push({
|
|
164
|
-
task_id: taskId,
|
|
165
|
-
unit_id: params.unitId,
|
|
166
|
-
pass_id: params.passId,
|
|
167
|
-
lens: params.lens,
|
|
168
|
-
file_paths: [filePath],
|
|
169
|
-
rationale: params.rationale([filePath], "large_file"),
|
|
170
|
-
priority: params.priority,
|
|
171
|
-
tags: params.tags.length > 0
|
|
172
|
-
? [...new Set([...params.tags, "large_file"])]
|
|
173
|
-
: ["large_file"],
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|
|
188
|
+
const budgetLimits = { maxTaskLines, maxTaskFiles };
|
|
189
|
+
const taskBlockContext = {
|
|
190
|
+
tasks,
|
|
191
|
+
seen,
|
|
192
|
+
unitLineIndex,
|
|
193
|
+
fileSplitThreshold,
|
|
194
|
+
budgetLimits,
|
|
195
|
+
};
|
|
177
196
|
const assigned = new Set();
|
|
178
197
|
const flowBlocks = options.critical_flows
|
|
179
198
|
? claimFlowReviewBlocks(options.critical_flows, pendingByLens, assigned)
|
|
@@ -195,7 +214,7 @@ export function buildChunkedAuditTasks(coverageMatrix, unitLineIndex, options =
|
|
|
195
214
|
: splitKind === "budget"
|
|
196
215
|
? `Audit part of critical flow ${block.flow_id} (${filePaths.length} file${filePaths.length === 1 ? "" : "s"}) under the ${block.lens} lens.${hasExternalSignal ? " External analyzer signals raise priority." : ""}`
|
|
197
216
|
: `Audit critical flow ${block.flow_id} (${filePaths.length} file${filePaths.length === 1 ? "" : "s"}) under the ${block.lens} lens.${hasExternalSignal ? " External analyzer signals raise priority." : ""}`,
|
|
198
|
-
});
|
|
217
|
+
}, taskBlockContext);
|
|
199
218
|
}
|
|
200
219
|
const groupedRemainders = new Map();
|
|
201
220
|
for (const lens of LENS_ORDER) {
|
|
@@ -246,7 +265,7 @@ export function buildChunkedAuditTasks(coverageMatrix, unitLineIndex, options =
|
|
|
246
265
|
: splitKind === "budget"
|
|
247
266
|
? `Audit part of ${block.unitId} (${filePaths.length} file${filePaths.length === 1 ? "" : "s"}) under the ${block.lens} lens.${hasExternalSignal ? " External analyzer signals raise priority." : ""}`
|
|
248
267
|
: `Audit ${block.unitId} (${filePaths.length} file${filePaths.length === 1 ? "" : "s"}) under the ${block.lens} lens.${hasExternalSignal ? " External analyzer signals raise priority." : ""}`,
|
|
249
|
-
});
|
|
268
|
+
}, taskBlockContext);
|
|
250
269
|
}
|
|
251
270
|
return tasks.sort((a, b) => {
|
|
252
271
|
const priorityDelta = priorityRank(b.priority) - priorityRank(a.priority);
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { FreshSessionProvider, ResolvedProviderName, SessionConfig } from "@audit-tools/shared";
|
|
2
|
+
/**
|
|
3
|
+
* Auto-resolution and provider wiring are single-sourced in `@audit-tools/shared`.
|
|
4
|
+
* This module is a thin audit-code-specific adapter: it injects audit-code's own
|
|
5
|
+
* `ClaudeCodeProvider`/`OpenCodeProvider` (whose prompt delivery and
|
|
6
|
+
* skip-permissions semantics differ from the remediator's) and attributes the
|
|
7
|
+
* auto-fallback warning to `audit-code`.
|
|
8
|
+
*/
|
|
2
9
|
export declare function resolveFreshSessionProviderName(name: string | undefined, sessionConfig?: SessionConfig, options?: {
|
|
3
10
|
env?: NodeJS.ProcessEnv;
|
|
4
11
|
commandExists?: (command: string) => boolean;
|
package/dist/providers/index.js
CHANGED
|
@@ -1,101 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { LocalSubprocessProvider } from "./localSubprocessProvider.js";
|
|
3
|
-
import { SubprocessTemplateProvider } from "./subprocessTemplateProvider.js";
|
|
1
|
+
import { createFreshSessionProvider as createSharedFreshSessionProvider, resolveFreshSessionProviderName as resolveSharedFreshSessionProviderName, } from "@audit-tools/shared";
|
|
4
2
|
import { ClaudeCodeProvider } from "./claudeCodeProvider.js";
|
|
5
3
|
import { OpenCodeProvider } from "./opencodeProvider.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
sessionConfig.claude_code?.dangerously_skip_permissions === true);
|
|
14
|
-
}
|
|
15
|
-
function hasConfiguredOpenCode(sessionConfig) {
|
|
16
|
-
return (Boolean(sessionConfig.opencode?.command?.trim()) ||
|
|
17
|
-
hasEntries(sessionConfig.opencode?.extra_args));
|
|
18
|
-
}
|
|
19
|
-
function commandExists(command) {
|
|
20
|
-
const lookupCommand = process.platform === "win32" ? "where" : "which";
|
|
21
|
-
const result = spawnSync(lookupCommand, [command], { stdio: "ignore" });
|
|
22
|
-
return result.status === 0;
|
|
23
|
-
}
|
|
4
|
+
/**
|
|
5
|
+
* Auto-resolution and provider wiring are single-sourced in `@audit-tools/shared`.
|
|
6
|
+
* This module is a thin audit-code-specific adapter: it injects audit-code's own
|
|
7
|
+
* `ClaudeCodeProvider`/`OpenCodeProvider` (whose prompt delivery and
|
|
8
|
+
* skip-permissions semantics differ from the remediator's) and attributes the
|
|
9
|
+
* auto-fallback warning to `audit-code`.
|
|
10
|
+
*/
|
|
24
11
|
export function resolveFreshSessionProviderName(name, sessionConfig = {}, options = {}) {
|
|
25
|
-
|
|
26
|
-
const shouldAutoDetect = requestedProvider === undefined ||
|
|
27
|
-
requestedProvider === "auto" ||
|
|
28
|
-
(name === undefined && requestedProvider === "local-subprocess");
|
|
29
|
-
if (!shouldAutoDetect) {
|
|
30
|
-
return requestedProvider;
|
|
31
|
-
}
|
|
32
|
-
const env = options.env ?? process.env;
|
|
33
|
-
const lookupCommand = options.commandExists ?? commandExists;
|
|
34
|
-
const inVSCode = (env.TERM_PROGRAM ?? "").toLowerCase() === "vscode";
|
|
35
|
-
const insideClaudeCode = Boolean(env.CLAUDECODE);
|
|
36
|
-
const insideOpenCode = Boolean(env.OPENCODE);
|
|
37
|
-
// If we're inside a specific IDE/conversation, use that as the provider
|
|
38
|
-
if (insideOpenCode) {
|
|
39
|
-
return "opencode";
|
|
40
|
-
}
|
|
41
|
-
if (insideClaudeCode) {
|
|
42
|
-
return "claude-code";
|
|
43
|
-
}
|
|
44
|
-
if (inVSCode && hasEntries(sessionConfig.vscode_task?.command_template)) {
|
|
45
|
-
return "vscode-task";
|
|
46
|
-
}
|
|
47
|
-
if (hasEntries(sessionConfig.subprocess_template?.command_template)) {
|
|
48
|
-
return "subprocess-template";
|
|
49
|
-
}
|
|
50
|
-
const claudeCommand = sessionConfig.claude_code?.command ?? "claude";
|
|
51
|
-
const opencodeCommand = sessionConfig.opencode?.command ?? "opencode";
|
|
52
|
-
const claudeAvailable = !insideClaudeCode && lookupCommand(claudeCommand);
|
|
53
|
-
const opencodeAvailable = lookupCommand(opencodeCommand);
|
|
54
|
-
if (!insideClaudeCode && hasConfiguredClaudeCode(sessionConfig) && claudeAvailable) {
|
|
55
|
-
return "claude-code";
|
|
56
|
-
}
|
|
57
|
-
if (hasConfiguredOpenCode(sessionConfig) && opencodeAvailable) {
|
|
58
|
-
return "opencode";
|
|
59
|
-
}
|
|
60
|
-
if (claudeAvailable && !opencodeAvailable) {
|
|
61
|
-
return "claude-code";
|
|
62
|
-
}
|
|
63
|
-
if (opencodeAvailable && !claudeAvailable) {
|
|
64
|
-
return "opencode";
|
|
65
|
-
}
|
|
66
|
-
return "local-subprocess";
|
|
12
|
+
return resolveSharedFreshSessionProviderName(name, sessionConfig, options);
|
|
67
13
|
}
|
|
68
14
|
export function createFreshSessionProvider(name, sessionConfig = {}) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
(name === undefined && requestedProvider === "local-subprocess");
|
|
75
|
-
if (providerName === "local-subprocess" &&
|
|
76
|
-
autoDetectionRequested) {
|
|
77
|
-
process.stderr.write("audit-code: auto provider resolved to local-subprocess — no capable agent provider detected. " +
|
|
78
|
-
"Agent tasks will require manual dispatch. Configure claude-code, opencode, or subprocess-template " +
|
|
79
|
-
"in session-config.json to automate them.\n");
|
|
80
|
-
}
|
|
81
|
-
switch (providerName) {
|
|
82
|
-
case "local-subprocess":
|
|
83
|
-
return new LocalSubprocessProvider();
|
|
84
|
-
case "subprocess-template":
|
|
85
|
-
if (!sessionConfig.subprocess_template?.command_template?.length) {
|
|
86
|
-
throw new Error("subprocess-template provider requires session-config.json with subprocess_template.command_template.");
|
|
87
|
-
}
|
|
88
|
-
return new SubprocessTemplateProvider(sessionConfig.subprocess_template, undefined, opentoken);
|
|
89
|
-
case "claude-code":
|
|
90
|
-
return new ClaudeCodeProvider(sessionConfig.claude_code, undefined, opentoken);
|
|
91
|
-
case "opencode":
|
|
92
|
-
return new OpenCodeProvider(sessionConfig.opencode, opentoken);
|
|
93
|
-
case "vscode-task":
|
|
94
|
-
if (!sessionConfig.vscode_task?.command_template?.length) {
|
|
95
|
-
throw new Error("vscode-task provider requires session-config.json with vscode_task.command_template.");
|
|
96
|
-
}
|
|
97
|
-
return new VSCodeTaskProvider(sessionConfig.vscode_task, opentoken);
|
|
98
|
-
default:
|
|
99
|
-
throw new Error(`Unknown provider: ${providerName}`);
|
|
100
|
-
}
|
|
15
|
+
return createSharedFreshSessionProvider(name, sessionConfig, {
|
|
16
|
+
orchestratorName: "audit-code",
|
|
17
|
+
createClaudeCodeProvider: (config, opentoken) => new ClaudeCodeProvider(config, undefined, opentoken),
|
|
18
|
+
createOpenCodeProvider: (config, opentoken) => new OpenCodeProvider(config, opentoken),
|
|
19
|
+
});
|
|
101
20
|
}
|
|
@@ -41,6 +41,9 @@ export async function updateDiscoveredLimits(providerModelKey, limits) {
|
|
|
41
41
|
if (limits.input_tokens_per_minute != null) {
|
|
42
42
|
entry.input_tokens_per_minute = limits.input_tokens_per_minute;
|
|
43
43
|
}
|
|
44
|
+
if (limits.output_tokens_per_minute != null) {
|
|
45
|
+
entry.output_tokens_per_minute = limits.output_tokens_per_minute;
|
|
46
|
+
}
|
|
44
47
|
cache.entries[providerModelKey] = entry;
|
|
45
48
|
await writeDiscoveredLimitsCache(cache);
|
|
46
49
|
}
|
|
@@ -49,11 +52,14 @@ export async function lookupDiscoveredLimits(providerModelKey) {
|
|
|
49
52
|
const entry = cache.entries[providerModelKey];
|
|
50
53
|
if (!entry)
|
|
51
54
|
return null;
|
|
52
|
-
if (entry.requests_per_minute == null &&
|
|
55
|
+
if (entry.requests_per_minute == null &&
|
|
56
|
+
entry.input_tokens_per_minute == null &&
|
|
57
|
+
entry.output_tokens_per_minute == null)
|
|
53
58
|
return null;
|
|
54
59
|
return {
|
|
55
60
|
requests_per_minute: entry.requests_per_minute ?? null,
|
|
56
61
|
input_tokens_per_minute: entry.input_tokens_per_minute ?? null,
|
|
62
|
+
output_tokens_per_minute: entry.output_tokens_per_minute ?? null,
|
|
57
63
|
source: entry.source,
|
|
58
64
|
};
|
|
59
65
|
}
|
package/dist/quota/index.d.ts
CHANGED
|
@@ -4,8 +4,6 @@ export type { LimitResolutionResult, ResolveLimitsOptions, ProviderType, Resolve
|
|
|
4
4
|
export { scheduleWave, buildProviderModelKey, resolveHostModel } from "@audit-tools/shared";
|
|
5
5
|
export type { ScheduleWaveOptions } from "@audit-tools/shared";
|
|
6
6
|
export { detectHostActiveSubagentLimit, resolveHostActiveSubagentLimit, } from "./hostLimits.js";
|
|
7
|
-
export { probeProvider } from "./probe.js";
|
|
8
|
-
export type { ProbeResult } from "./probe.js";
|
|
9
7
|
export { lookupDiscoveredLimits, updateDiscoveredLimits, mergeDiscoveredLimits, readDiscoveredLimitsCache, writeDiscoveredLimitsCache, } from "./discoveredLimits.js";
|
|
10
8
|
export type { DiscoveredRateLimits, DiscoveredLimitsCache, DiscoveredLimitsCacheEntry } from "./discoveredLimits.js";
|
|
11
9
|
export { extractRateLimitHeaders } from "./headerExtraction.js";
|
package/dist/quota/index.js
CHANGED
|
@@ -4,9 +4,8 @@ export { resolveLimits, lookupKnownModel, classifyProvider, readQuotaState, writ
|
|
|
4
4
|
// both orchestrators). Auditor passes its discovered-limits via the structural
|
|
5
5
|
// DiscoveredRateLimitsInput the shared scheduler accepts.
|
|
6
6
|
export { scheduleWave, buildProviderModelKey, resolveHostModel } from "@audit-tools/shared";
|
|
7
|
-
// Auditor-specific:
|
|
7
|
+
// Auditor-specific: discovered limits, header extraction
|
|
8
8
|
export { detectHostActiveSubagentLimit, resolveHostActiveSubagentLimit, } from "./hostLimits.js";
|
|
9
|
-
export { probeProvider } from "./probe.js";
|
|
10
9
|
export { lookupDiscoveredLimits, updateDiscoveredLimits, mergeDiscoveredLimits, readDiscoveredLimitsCache, writeDiscoveredLimitsCache, } from "./discoveredLimits.js";
|
|
11
10
|
export { extractRateLimitHeaders } from "./headerExtraction.js";
|
|
12
11
|
export { GenericHeaderExtractor, ClaudeCodeHeaderExtractor, getHeaderExtractorForProvider } from "./headerExtractors/index.js";
|
|
@@ -54,14 +54,17 @@ function computeDependencies(params) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
for (const flow of params.criticalFlows?.flows ?? []) {
|
|
57
|
-
|
|
57
|
+
// Order blocks by first appearance along the flow path so dependency
|
|
58
|
+
// direction follows the flow's traversal order, not block-id lexical order.
|
|
59
|
+
const ordered = [];
|
|
60
|
+
const seen = new Set();
|
|
58
61
|
for (const path of flow.paths) {
|
|
59
62
|
const blockId = blockByFile.get(path);
|
|
60
|
-
if (blockId) {
|
|
61
|
-
|
|
63
|
+
if (blockId && !seen.has(blockId)) {
|
|
64
|
+
seen.add(blockId);
|
|
65
|
+
ordered.push(blockId);
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
|
-
const ordered = [...flowBlocks].sort();
|
|
65
68
|
for (let i = 1; i < ordered.length; i++) {
|
|
66
69
|
dependsOn.get(ordered[i - 1])?.add(ordered[i]);
|
|
67
70
|
}
|
|
@@ -36,6 +36,28 @@ export interface ReviewPacket {
|
|
|
36
36
|
rationale: string;
|
|
37
37
|
estimated_tokens: number;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Aggregate quality signals for a planned set of review packets — cohesion,
|
|
41
|
+
* boundary-crossing counts, and the weakly-explained-packet diagnostics. Promoted
|
|
42
|
+
* from an inline anonymous object on {@link AuditPlanMetrics} so it can be named
|
|
43
|
+
* and referenced.
|
|
44
|
+
*/
|
|
45
|
+
export interface PacketQuality {
|
|
46
|
+
average_cohesion_score: number;
|
|
47
|
+
boundary_crossing_count: number;
|
|
48
|
+
merge_edge_kind_counts: Record<string, number>;
|
|
49
|
+
boundary_edge_kind_counts: Record<string, number>;
|
|
50
|
+
orphan_task_count: number;
|
|
51
|
+
high_fan_in_file_count: number;
|
|
52
|
+
high_fan_out_file_count: number;
|
|
53
|
+
weakly_explained_gap_counts: Record<WeaklyExplainedPacketSample["primary_gap"], number>;
|
|
54
|
+
weakly_explained_file_extension_counts: Record<string, number>;
|
|
55
|
+
weakly_explained_packet_count: number;
|
|
56
|
+
weakly_explained_packet_ids: string[];
|
|
57
|
+
weakly_explained_packet_samples: WeaklyExplainedPacketSample[];
|
|
58
|
+
largest_unexplained_packet_id?: string;
|
|
59
|
+
largest_unexplained_packet_files: number;
|
|
60
|
+
}
|
|
39
61
|
export interface AuditPlanMetrics {
|
|
40
62
|
generated_at: string;
|
|
41
63
|
task_count: number;
|
|
@@ -55,22 +77,7 @@ export interface AuditPlanMetrics {
|
|
|
55
77
|
largest_packet_id?: string;
|
|
56
78
|
lens_task_counts: Partial<Record<Lens, number>>;
|
|
57
79
|
priority_task_counts: Record<NonNullable<AuditTask["priority"]>, number>;
|
|
58
|
-
packet_quality:
|
|
59
|
-
average_cohesion_score: number;
|
|
60
|
-
boundary_crossing_count: number;
|
|
61
|
-
merge_edge_kind_counts: Record<string, number>;
|
|
62
|
-
boundary_edge_kind_counts: Record<string, number>;
|
|
63
|
-
orphan_task_count: number;
|
|
64
|
-
high_fan_in_file_count: number;
|
|
65
|
-
high_fan_out_file_count: number;
|
|
66
|
-
weakly_explained_gap_counts: Record<WeaklyExplainedPacketSample["primary_gap"], number>;
|
|
67
|
-
weakly_explained_file_extension_counts: Record<string, number>;
|
|
68
|
-
weakly_explained_packet_count: number;
|
|
69
|
-
weakly_explained_packet_ids: string[];
|
|
70
|
-
weakly_explained_packet_samples: WeaklyExplainedPacketSample[];
|
|
71
|
-
largest_unexplained_packet_id?: string;
|
|
72
|
-
largest_unexplained_packet_files: number;
|
|
73
|
-
};
|
|
80
|
+
packet_quality: PacketQuality;
|
|
74
81
|
packet_size: {
|
|
75
82
|
single_task_packets: number;
|
|
76
83
|
multi_task_packets: number;
|