auditor-lambda 0.3.40 → 0.5.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.
Files changed (193) hide show
  1. package/audit-code-wrapper-lib.mjs +20 -2
  2. package/dist/cli/args.d.ts +59 -0
  3. package/dist/cli/args.js +244 -0
  4. package/dist/cli/dispatch.d.ts +80 -0
  5. package/dist/cli/dispatch.js +532 -0
  6. package/dist/cli/prompts.d.ts +37 -0
  7. package/dist/cli/prompts.js +225 -0
  8. package/dist/cli/steps.d.ts +29 -0
  9. package/dist/cli/steps.js +30 -0
  10. package/dist/cli/waveManifest.d.ts +40 -0
  11. package/dist/cli/waveManifest.js +41 -0
  12. package/dist/cli/workerResult.d.ts +18 -0
  13. package/dist/cli/workerResult.js +42 -0
  14. package/dist/cli.d.ts +2 -22
  15. package/dist/cli.js +442 -975
  16. package/dist/extractors/analyzers/css.d.ts +2 -0
  17. package/dist/extractors/analyzers/css.js +101 -0
  18. package/dist/extractors/analyzers/html.d.ts +2 -0
  19. package/dist/extractors/analyzers/html.js +92 -0
  20. package/dist/extractors/analyzers/merge.d.ts +14 -0
  21. package/dist/extractors/analyzers/merge.js +85 -0
  22. package/dist/extractors/analyzers/python.d.ts +2 -0
  23. package/dist/extractors/analyzers/python.js +104 -0
  24. package/dist/extractors/analyzers/registry.d.ts +33 -0
  25. package/dist/extractors/analyzers/registry.js +100 -0
  26. package/dist/extractors/analyzers/resourceUrl.d.ts +7 -0
  27. package/dist/extractors/analyzers/resourceUrl.js +25 -0
  28. package/dist/extractors/analyzers/sql.d.ts +2 -0
  29. package/dist/extractors/analyzers/sql.js +19 -0
  30. package/dist/extractors/analyzers/treeSitter.d.ts +34 -0
  31. package/dist/extractors/analyzers/treeSitter.js +111 -0
  32. package/dist/extractors/analyzers/types.d.ts +53 -0
  33. package/dist/extractors/analyzers/typescript.d.ts +2 -0
  34. package/dist/extractors/analyzers/typescript.js +257 -0
  35. package/dist/extractors/browserExtension.d.ts +1 -3
  36. package/dist/extractors/browserExtension.js +2 -2
  37. package/dist/extractors/designAssessment.d.ts +1 -3
  38. package/dist/extractors/disposition.d.ts +2 -1
  39. package/dist/extractors/disposition.js +11 -1
  40. package/dist/extractors/flows.d.ts +1 -3
  41. package/dist/extractors/flows.js +2 -2
  42. package/dist/extractors/graph.d.ts +2 -2
  43. package/dist/extractors/graph.js +171 -327
  44. package/dist/extractors/graphManifestEdges.d.ts +1 -1
  45. package/dist/extractors/graphPathUtils.d.ts +1 -1
  46. package/dist/extractors/graphPythonImports.d.ts +18 -0
  47. package/dist/extractors/graphPythonImports.js +362 -0
  48. package/dist/extractors/pathPatterns.d.ts +6 -0
  49. package/dist/extractors/pathPatterns.js +8 -0
  50. package/dist/extractors/risk.d.ts +1 -2
  51. package/dist/extractors/surfaces.d.ts +1 -3
  52. package/dist/extractors/surfaces.js +2 -2
  53. package/dist/io/artifacts.d.ts +12 -5
  54. package/dist/io/artifacts.js +13 -1
  55. package/dist/io/runArtifacts.js +1 -1
  56. package/dist/mcp/server.js +1 -1
  57. package/dist/orchestrator/advance.d.ts +21 -0
  58. package/dist/orchestrator/advance.js +69 -7
  59. package/dist/orchestrator/auditTaskUtils.d.ts +4 -0
  60. package/dist/orchestrator/auditTaskUtils.js +27 -0
  61. package/dist/orchestrator/dependencyMap.js +27 -0
  62. package/dist/orchestrator/edgeReasoning.d.ts +39 -0
  63. package/dist/orchestrator/edgeReasoning.js +125 -0
  64. package/dist/orchestrator/executors.js +11 -1
  65. package/dist/orchestrator/fileAnchors.d.ts +1 -1
  66. package/dist/orchestrator/fileIntegrity.d.ts +7 -0
  67. package/dist/orchestrator/fileIntegrity.js +41 -0
  68. package/dist/orchestrator/flowCoverage.d.ts +1 -1
  69. package/dist/orchestrator/flowPlanning.d.ts +1 -1
  70. package/dist/orchestrator/flowRequeue.d.ts +1 -1
  71. package/dist/orchestrator/graphEnrichmentExecutor.d.ts +29 -0
  72. package/dist/orchestrator/graphEnrichmentExecutor.js +196 -0
  73. package/dist/orchestrator/internalExecutors.d.ts +13 -2
  74. package/dist/orchestrator/internalExecutors.js +112 -16
  75. package/dist/orchestrator/localCommands.js +6 -25
  76. package/dist/orchestrator/nextStep.d.ts +2 -1
  77. package/dist/orchestrator/nextStep.js +3 -1
  78. package/dist/orchestrator/planning.d.ts +1 -1
  79. package/dist/orchestrator/requeueCommand.d.ts +1 -1
  80. package/dist/orchestrator/reviewPackets.d.ts +37 -4
  81. package/dist/orchestrator/reviewPackets.js +113 -158
  82. package/dist/orchestrator/runtimeValidation.d.ts +1 -1
  83. package/dist/orchestrator/runtimeValidation.js +4 -31
  84. package/dist/orchestrator/scope.d.ts +62 -0
  85. package/dist/orchestrator/scope.js +227 -0
  86. package/dist/orchestrator/state.js +2 -0
  87. package/dist/orchestrator/taskBuilder.d.ts +1 -1
  88. package/dist/orchestrator/taskBuilder.js +1 -12
  89. package/dist/orchestrator/unionFind.d.ts +7 -0
  90. package/dist/orchestrator/unionFind.js +32 -0
  91. package/dist/orchestrator/unitBuilder.d.ts +2 -2
  92. package/dist/orchestrator/unitBuilder.js +4 -18
  93. package/dist/prompts/renderWorkerPrompt.js +18 -1
  94. package/dist/providers/claudeCodeProvider.d.ts +4 -4
  95. package/dist/providers/claudeCodeProvider.js +9 -3
  96. package/dist/providers/constants.d.ts +1 -1
  97. package/dist/providers/constants.js +1 -1
  98. package/dist/providers/index.d.ts +1 -2
  99. package/dist/providers/index.js +5 -4
  100. package/dist/providers/localSubprocessProvider.d.ts +2 -2
  101. package/dist/providers/localSubprocessProvider.js +1 -1
  102. package/dist/providers/opencodeProvider.d.ts +4 -4
  103. package/dist/providers/opencodeProvider.js +7 -2
  104. package/dist/providers/spawnLoggedCommand.d.ts +3 -1
  105. package/dist/providers/spawnLoggedCommand.js +21 -0
  106. package/dist/providers/subprocessTemplateProvider.d.ts +4 -4
  107. package/dist/providers/subprocessTemplateProvider.js +8 -3
  108. package/dist/providers/vscodeTaskProvider.d.ts +3 -4
  109. package/dist/providers/vscodeTaskProvider.js +2 -2
  110. package/dist/quota/discoveredLimits.js +1 -1
  111. package/dist/quota/hostLimits.d.ts +1 -2
  112. package/dist/quota/hostLimits.js +4 -46
  113. package/dist/quota/index.d.ts +18 -15
  114. package/dist/quota/index.js +4 -9
  115. package/dist/quota/scheduler.d.ts +1 -3
  116. package/dist/quota/scheduler.js +1 -2
  117. package/dist/reporting/synthesis.d.ts +37 -3
  118. package/dist/reporting/synthesis.js +97 -16
  119. package/dist/reporting/synthesisNarrativePrompt.d.ts +7 -0
  120. package/dist/reporting/synthesisNarrativePrompt.js +60 -0
  121. package/dist/reporting/workBlocks.d.ts +2 -11
  122. package/dist/supervisor/operatorHandoff.js +1 -1
  123. package/dist/supervisor/runLedger.d.ts +1 -1
  124. package/dist/supervisor/runLedger.js +2 -2
  125. package/dist/supervisor/sessionConfig.d.ts +8 -1
  126. package/dist/supervisor/sessionConfig.js +22 -3
  127. package/dist/types/analyzerCapability.d.ts +16 -0
  128. package/dist/types/auditScope.d.ts +43 -0
  129. package/dist/types/auditScope.js +14 -0
  130. package/dist/types/reviewPlanning.d.ts +1 -1
  131. package/dist/types/synthesisNarrative.d.ts +7 -0
  132. package/dist/types/synthesisNarrative.js +5 -0
  133. package/dist/types/workerSession.d.ts +6 -0
  134. package/dist/types.d.ts +2 -19
  135. package/dist/validation/artifacts.d.ts +1 -1
  136. package/dist/validation/artifacts.js +10 -1
  137. package/dist/validation/auditResults.d.ts +1 -1
  138. package/dist/validation/auditResults.js +1 -1
  139. package/dist/validation/sessionConfig.d.ts +2 -3
  140. package/dist/validation/sessionConfig.js +25 -3
  141. package/package.json +7 -3
  142. package/schemas/analyzer_capability.schema.json +47 -0
  143. package/schemas/audit_findings.schema.json +141 -0
  144. package/schemas/finding.schema.json +2 -1
  145. package/schemas/graph_bundle.schema.json +5 -0
  146. package/schemas/scope.schema.json +46 -0
  147. package/scripts/postinstall.mjs +0 -1
  148. package/dist/io/json.d.ts +0 -10
  149. package/dist/io/json.js +0 -142
  150. package/dist/providers/types.d.ts +0 -33
  151. package/dist/quota/compositeQuotaSource.d.ts +0 -7
  152. package/dist/quota/compositeQuotaSource.js +0 -20
  153. package/dist/quota/errorParsers/claudeCodeErrorParser.d.ts +0 -6
  154. package/dist/quota/errorParsers/claudeCodeErrorParser.js +0 -39
  155. package/dist/quota/errorParsers/genericErrorParser.d.ts +0 -9
  156. package/dist/quota/errorParsers/genericErrorParser.js +0 -7
  157. package/dist/quota/errorParsers/index.d.ts +0 -5
  158. package/dist/quota/errorParsers/index.js +0 -12
  159. package/dist/quota/errorParsing.d.ts +0 -7
  160. package/dist/quota/errorParsing.js +0 -69
  161. package/dist/quota/fileLock.d.ts +0 -6
  162. package/dist/quota/fileLock.js +0 -64
  163. package/dist/quota/learnedQuotaSource.d.ts +0 -7
  164. package/dist/quota/learnedQuotaSource.js +0 -25
  165. package/dist/quota/limits.d.ts +0 -16
  166. package/dist/quota/limits.js +0 -77
  167. package/dist/quota/quotaSource.d.ts +0 -12
  168. package/dist/quota/slidingWindow.d.ts +0 -4
  169. package/dist/quota/slidingWindow.js +0 -28
  170. package/dist/quota/state.d.ts +0 -15
  171. package/dist/quota/state.js +0 -148
  172. package/dist/quota/types.d.ts +0 -67
  173. package/dist/quota/types.js +0 -1
  174. package/dist/reporting/rootCause.d.ts +0 -10
  175. package/dist/reporting/rootCause.js +0 -146
  176. package/dist/types/disposition.d.ts +0 -9
  177. package/dist/types/disposition.js +0 -1
  178. package/dist/types/flows.d.ts +0 -17
  179. package/dist/types/flows.js +0 -1
  180. package/dist/types/graph.d.ts +0 -22
  181. package/dist/types/graph.js +0 -1
  182. package/dist/types/risk.d.ts +0 -9
  183. package/dist/types/risk.js +0 -1
  184. package/dist/types/runLedger.d.ts +0 -17
  185. package/dist/types/runLedger.js +0 -6
  186. package/dist/types/sessionConfig.d.ts +0 -79
  187. package/dist/types/sessionConfig.js +0 -15
  188. package/dist/types/surfaces.d.ts +0 -15
  189. package/dist/types/surfaces.js +0 -1
  190. package/dist/validation/basic.d.ts +0 -13
  191. package/dist/validation/basic.js +0 -46
  192. /package/dist/{providers → extractors/analyzers}/types.js +0 -0
  193. /package/dist/{quota/quotaSource.js → types/analyzerCapability.js} +0 -0
@@ -0,0 +1,227 @@
1
+ import { changedFiles, gitRefExists, isGitRepo } from "@audit-tools/shared";
2
+ import { buildDispositionMap } from "../extractors/disposition.js";
3
+ import { buildPathLookup } from "../extractors/graph.js";
4
+ import { HIGH_FAN_DEGREE_THRESHOLD, buildGraphDegreeIndex, collectGraphEdges, graphEdgeConfidence, normalizeGraphPath, } from "./reviewPackets.js";
5
+ /** Default cap on in-scope files (seeds + expanded) before expansion stops. */
6
+ export const DEFAULT_SCOPE_MAX_FILES = 200;
7
+ /** Graph edges below this confidence are never traversed during expansion. */
8
+ export const SCOPE_EDGE_CONFIDENCE_FLOOR = 0.5;
9
+ /**
10
+ * Expansion stops along a path once the accumulated path-confidence (the product
11
+ * of the traversed edge confidences) drops below this floor. With no fixed hop
12
+ * count, this — together with hub-skipping and the file budget — bounds the
13
+ * frontier deterministically.
14
+ */
15
+ export const SCOPE_MIN_FRONTIER_CONFIDENCE = 0.5;
16
+ /**
17
+ * Deterministic priority-frontier expansion (Phase 3). Starting from the changed
18
+ * files (seeds), walk the dependency graph outward, always visiting the neighbour
19
+ * with the highest accumulated path-confidence first (tie-broken by path). High
20
+ * fan-in/out hubs are skipped so a single change near a hub does not drag the
21
+ * whole repo into scope, low-confidence edges are dropped, and expansion halts at
22
+ * the file budget or when the best remaining frontier confidence falls below the
23
+ * floor. Same inputs → identical scope.
24
+ */
25
+ export function computeAuditScope(input) {
26
+ const maxFiles = input.budget?.max_files ?? DEFAULT_SCOPE_MAX_FILES;
27
+ // normalized graph key -> canonical (repo-manifest) path for auditable files.
28
+ const canonicalByNorm = new Map();
29
+ for (const file of input.includedFiles) {
30
+ const key = normalizeGraphPath(file);
31
+ if (!canonicalByNorm.has(key)) {
32
+ canonicalByNorm.set(key, file);
33
+ }
34
+ }
35
+ // Seeds = changed files that are auditable (present in the manifest). Changed
36
+ // files that are excluded, deleted, or otherwise absent simply drop out.
37
+ const seedKeys = [];
38
+ const seedSeen = new Set();
39
+ for (const path of input.changed) {
40
+ const key = normalizeGraphPath(path);
41
+ if (canonicalByNorm.has(key) && !seedSeen.has(key)) {
42
+ seedSeen.add(key);
43
+ seedKeys.push(key);
44
+ }
45
+ }
46
+ const edges = collectGraphEdges(input.graphBundle);
47
+ const degree = buildGraphDegreeIndex(edges);
48
+ const isHub = (key) => (degree.fanIn.get(key) ?? 0) > HIGH_FAN_DEGREE_THRESHOLD ||
49
+ (degree.fanOut.get(key) ?? 0) > HIGH_FAN_DEGREE_THRESHOLD;
50
+ // Bidirectional adjacency: a change to a file is relevant to what it depends
51
+ // on AND to what depends on it. Edges below the confidence floor are dropped.
52
+ const adjacency = new Map();
53
+ const addEdge = (from, to, confidence) => {
54
+ const list = adjacency.get(from) ?? [];
55
+ list.push({ to, confidence });
56
+ adjacency.set(from, list);
57
+ };
58
+ for (const edge of edges) {
59
+ const confidence = graphEdgeConfidence(edge);
60
+ if (confidence < SCOPE_EDGE_CONFIDENCE_FLOOR) {
61
+ continue;
62
+ }
63
+ const from = normalizeGraphPath(edge.from);
64
+ const to = normalizeGraphPath(edge.to);
65
+ addEdge(from, to, confidence);
66
+ addEdge(to, from, confidence);
67
+ }
68
+ // Max-product shortest-path frontier. `best` holds the highest accumulated
69
+ // confidence discovered for each node; seeds start at 1.
70
+ const best = new Map();
71
+ for (const key of seedKeys) {
72
+ best.set(key, 1);
73
+ }
74
+ const visited = new Set();
75
+ const inScope = new Set(seedKeys);
76
+ const expandedKeys = [];
77
+ let budgetHit = false;
78
+ for (;;) {
79
+ let pick;
80
+ let pickConfidence = -1;
81
+ for (const [key, confidence] of best) {
82
+ if (visited.has(key))
83
+ continue;
84
+ if (confidence > pickConfidence ||
85
+ (confidence === pickConfidence && (pick === undefined || key < pick))) {
86
+ pick = key;
87
+ pickConfidence = confidence;
88
+ }
89
+ }
90
+ if (pick === undefined || pickConfidence < SCOPE_MIN_FRONTIER_CONFIDENCE) {
91
+ break;
92
+ }
93
+ visited.add(pick);
94
+ // Record newly-reached auditable files (seeds are already in scope).
95
+ if (canonicalByNorm.has(pick) && !inScope.has(pick)) {
96
+ if (inScope.size >= maxFiles) {
97
+ budgetHit = true;
98
+ break;
99
+ }
100
+ inScope.add(pick);
101
+ expandedKeys.push(pick);
102
+ }
103
+ // Relax neighbours, skipping hubs (never traverse through or into a hub) and
104
+ // non-auditable nodes.
105
+ for (const neighbour of adjacency.get(pick) ?? []) {
106
+ if (isHub(neighbour.to) || !canonicalByNorm.has(neighbour.to)) {
107
+ continue;
108
+ }
109
+ const candidate = pickConfidence * neighbour.confidence;
110
+ if (candidate < SCOPE_MIN_FRONTIER_CONFIDENCE) {
111
+ continue;
112
+ }
113
+ if (candidate > (best.get(neighbour.to) ?? 0)) {
114
+ best.set(neighbour.to, candidate);
115
+ }
116
+ }
117
+ }
118
+ const seedFiles = seedKeys
119
+ .map((key) => canonicalByNorm.get(key))
120
+ .sort((a, b) => a.localeCompare(b));
121
+ const expandedFiles = expandedKeys
122
+ .map((key) => canonicalByNorm.get(key))
123
+ .sort((a, b) => a.localeCompare(b));
124
+ const notes = [];
125
+ if (seedFiles.length === 0) {
126
+ notes.push(`No auditable files changed since ${input.since}.`);
127
+ }
128
+ if (budgetHit) {
129
+ notes.push(`Expansion stopped at the ${maxFiles}-file budget; some graph neighbours were left out of scope.`);
130
+ }
131
+ return {
132
+ mode: "delta",
133
+ since: input.since,
134
+ seed_files: seedFiles,
135
+ expanded_files: expandedFiles,
136
+ budget: { max_files: maxFiles },
137
+ ...(notes.length > 0 ? { dropped_note: notes.join(" ") } : {}),
138
+ };
139
+ }
140
+ /** A full-audit scope (the default, and every fallback). */
141
+ export function fullAuditScope(budget, droppedNote) {
142
+ return {
143
+ mode: "full",
144
+ since: null,
145
+ seed_files: [],
146
+ expanded_files: [],
147
+ budget: { max_files: budget?.max_files ?? DEFAULT_SCOPE_MAX_FILES },
148
+ ...(droppedNote ? { dropped_note: droppedNote } : {}),
149
+ };
150
+ }
151
+ /**
152
+ * Resolve the scope for a planning run. Returns a full-audit scope unless a
153
+ * `--since` ref was supplied against a real git repository; an unusable ref or
154
+ * missing root degrades to a full audit with an honest note. Reads the auditable
155
+ * file set from the repo manifest + disposition (the same lookup the graph
156
+ * extractor uses) and the dependency graph from the bundle.
157
+ */
158
+ export function resolveAuditScope(input) {
159
+ const since = input.since?.trim();
160
+ if (!since) {
161
+ return fullAuditScope(input.budget);
162
+ }
163
+ if (!input.root) {
164
+ return fullAuditScope(input.budget, `--since '${since}' was ignored: no repository root was available, so a full audit ran.`);
165
+ }
166
+ if (!isGitRepo(input.root)) {
167
+ return fullAuditScope(input.budget, `--since '${since}' was ignored: '${input.root}' is not a git repository, so a full audit ran.`);
168
+ }
169
+ if (!gitRefExists(input.root, since)) {
170
+ return fullAuditScope(input.budget, `--since '${since}' could not be resolved to a commit, so a full audit ran.`);
171
+ }
172
+ const dispositionMap = buildDispositionMap(input.bundle.file_disposition);
173
+ const includedFiles = input.bundle.repo_manifest
174
+ ? [
175
+ ...new Set(buildPathLookup(input.bundle.repo_manifest, dispositionMap).values()),
176
+ ].sort((a, b) => a.localeCompare(b))
177
+ : [];
178
+ return computeAuditScope({
179
+ since,
180
+ changed: changedFiles(input.root, since),
181
+ includedFiles,
182
+ graphBundle: input.bundle.graph_bundle,
183
+ budget: input.budget,
184
+ });
185
+ }
186
+ /**
187
+ * Apply a delta scope to a freshly-built coverage matrix. In-scope files (seeds
188
+ * + expanded neighbours) keep their fresh `pending` status to be re-audited.
189
+ * Out-of-scope files inherit a prior `complete` record verbatim when present (so
190
+ * previously-finished work is preserved, not re-run), and are otherwise excluded
191
+ * from this run with `classification_status: "out_of_scope_delta"`. Deterministic
192
+ * exclusions (non-auditable/trivial) are left untouched. A full scope is a no-op.
193
+ */
194
+ export function applyScopeToCoverage(coverage, scope, priorCoverage) {
195
+ if (scope.mode !== "delta") {
196
+ return coverage;
197
+ }
198
+ const inScope = new Set([
199
+ ...scope.seed_files,
200
+ ...scope.expanded_files,
201
+ ]);
202
+ const priorByPath = new Map((priorCoverage?.files ?? []).map((file) => [file.path, file]));
203
+ for (const file of coverage.files) {
204
+ if (file.audit_status === "excluded") {
205
+ continue;
206
+ }
207
+ if (inScope.has(file.path)) {
208
+ continue;
209
+ }
210
+ const prior = priorByPath.get(file.path);
211
+ if (prior && prior.audit_status === "complete") {
212
+ file.required_lenses = [...prior.required_lenses];
213
+ file.completed_lenses = [...prior.completed_lenses];
214
+ file.unit_ids = [...prior.unit_ids];
215
+ file.audit_status = "complete";
216
+ file.classification_status = prior.classification_status;
217
+ }
218
+ else {
219
+ file.required_lenses = [];
220
+ file.completed_lenses = [];
221
+ file.unit_ids = [];
222
+ file.audit_status = "excluded";
223
+ file.classification_status = "out_of_scope_delta";
224
+ }
225
+ }
226
+ return coverage;
227
+ }
@@ -29,6 +29,7 @@ export function deriveAuditState(bundle) {
29
29
  "critical_flows.json",
30
30
  "risk_register.json",
31
31
  ], structureReady)));
32
+ obligations.push(obligation("graph_enrichment_current", staleOrSatisfied(staleArtifacts, ["analyzer_capability.json"], has(bundle.analyzer_capability))));
32
33
  obligations.push(obligation("design_assessment_current", staleOrSatisfied(staleArtifacts, ["design_assessment.json"], has(bundle.design_assessment))));
33
34
  obligations.push(obligation("design_review_completed", bundle.design_assessment?.reviewed ? "satisfied" : "missing"));
34
35
  const planningReady = has(bundle.coverage_matrix) &&
@@ -69,6 +70,7 @@ export function deriveAuditState(bundle) {
69
70
  ? "No deterministic runtime validation tasks were planned."
70
71
  : undefined));
71
72
  obligations.push(obligation("synthesis_current", staleOrSatisfied(staleArtifacts, ["audit-report.md"], has(bundle.audit_report))));
73
+ obligations.push(obligation("synthesis_narrative_current", staleOrSatisfied(staleArtifacts, ["synthesis-narrative.json"], has(bundle.synthesis_narrative))));
72
74
  let status = "not_started";
73
75
  if (!has(bundle.repo_manifest)) {
74
76
  status = "not_started";
@@ -1,6 +1,6 @@
1
1
  import type { ExternalAnalyzerResults } from "../types/externalAnalyzer.js";
2
2
  import type { AuditTask, CoverageMatrix, Lens } from "../types.js";
3
- import type { CriticalFlowManifest } from "../types/flows.js";
3
+ import type { CriticalFlowManifest } from "@audit-tools/shared";
4
4
  export interface UnitLineIndex {
5
5
  [path: string]: number;
6
6
  }
@@ -1,6 +1,6 @@
1
1
  import { claimFlowReviewBlocks } from "./flowPlanning.js";
2
2
  import { isTrivialAuditPath } from "./trivialAudit.js";
3
- import { LENS_ORDER } from "./unitBuilder.js";
3
+ import { LENS_ORDER, priorityRank } from "./auditTaskUtils.js";
4
4
  import { isTestPath, normalizeExtractorPath, } from "../extractors/pathPatterns.js";
5
5
  function taskPriority(hasExternalSignal, lens, isCriticalFlow = false) {
6
6
  if (isCriticalFlow) {
@@ -17,17 +17,6 @@ function taskPriority(hasExternalSignal, lens, isCriticalFlow = false) {
17
17
  }
18
18
  return lens === "security" || lens === "data_integrity" ? "medium" : "low";
19
19
  }
20
- function priorityRank(priority) {
21
- switch (priority) {
22
- case "high":
23
- return 3;
24
- case "medium":
25
- return 2;
26
- case "low":
27
- default:
28
- return 1;
29
- }
30
- }
31
20
  function pickAnalyzerLens(category) {
32
21
  const normalized = category.toLowerCase();
33
22
  if (normalized.includes("security") ||
@@ -0,0 +1,7 @@
1
+ export declare class UnionFind {
2
+ private readonly parent;
3
+ constructor(keys: Iterable<string>);
4
+ find(key: string): string;
5
+ union(a: string, b: string): void;
6
+ groups(): Map<string, string[]>;
7
+ }
@@ -0,0 +1,32 @@
1
+ export class UnionFind {
2
+ parent;
3
+ constructor(keys) {
4
+ this.parent = new Map([...keys].map((key) => [key, key]));
5
+ }
6
+ find(key) {
7
+ const current = this.parent.get(key) ?? key;
8
+ if (current === key)
9
+ return key;
10
+ const root = this.find(current);
11
+ this.parent.set(key, root);
12
+ return root;
13
+ }
14
+ union(a, b) {
15
+ const rootA = this.find(a);
16
+ const rootB = this.find(b);
17
+ if (rootA === rootB)
18
+ return;
19
+ const [keep, move] = rootA.localeCompare(rootB) <= 0 ? [rootA, rootB] : [rootB, rootA];
20
+ this.parent.set(move, keep);
21
+ }
22
+ groups() {
23
+ const result = new Map();
24
+ for (const key of this.parent.keys()) {
25
+ const root = this.find(key);
26
+ const group = result.get(root) ?? [];
27
+ group.push(key);
28
+ result.set(root, group);
29
+ }
30
+ return result;
31
+ }
32
+ }
@@ -1,6 +1,6 @@
1
1
  import type { Lens, RepoManifest, UnitManifest } from "../types.js";
2
- import type { FileDisposition } from "../types/disposition.js";
3
- export declare const LENS_ORDER: Lens[];
2
+ import type { FileDisposition } from "@audit-tools/shared";
3
+ export { LENS_ORDER, sortLenses } from "./auditTaskUtils.js";
4
4
  export declare function deriveRequiredLensesForPath(path: string, options?: {
5
5
  isBrowserExtensionProject?: boolean;
6
6
  }): Lens[];
@@ -1,7 +1,8 @@
1
1
  import { deriveBrowserExtensionLensesForPath, hasBrowserExtensionManifestFile, inferBrowserExtensionUnitKind, } from "../extractors/browserExtension.js";
2
2
  import { bucketFile } from "../extractors/bucketing.js";
3
- import { isAuditExcludedStatus } from "../extractors/disposition.js";
3
+ import { buildDispositionMap, isAuditExcludedStatus } from "../extractors/disposition.js";
4
4
  import { pathTokens, normalizeExtractorPath } from "../extractors/pathPatterns.js";
5
+ import { sortLenses } from "./auditTaskUtils.js";
5
6
  const LENS_MAP = {
6
7
  runtime: ["correctness", "maintainability", "tests", "observability"],
7
8
  interface: ["correctness", "security", "reliability", "tests", "observability"],
@@ -76,22 +77,7 @@ function inferUnitId(path, kind) {
76
77
  }
77
78
  return `${kind}-${path.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
78
79
  }
79
- export const LENS_ORDER = [
80
- "security",
81
- "correctness",
82
- "reliability",
83
- "data_integrity",
84
- "performance",
85
- "operability",
86
- "config_deployment",
87
- "observability",
88
- "maintainability",
89
- "tests",
90
- ];
91
- function sortLenses(lenses) {
92
- const set = new Set(lenses);
93
- return LENS_ORDER.filter((lens) => set.has(lens));
94
- }
80
+ export { LENS_ORDER, sortLenses } from "./auditTaskUtils.js";
95
81
  function applyExtensionLensGuards(path, lenses) {
96
82
  const n = path.toLowerCase();
97
83
  if (n.endsWith(".schema.json") || n.endsWith(".schema.ts")) {
@@ -142,7 +128,7 @@ function inferCriticalFlows(files, requiredLenses) {
142
128
  export function buildUnitManifest(repoManifest, disposition) {
143
129
  const units = new Map();
144
130
  const isBrowserExtensionProject = hasBrowserExtensionManifestFile(repoManifest);
145
- const dispositionMap = new Map(disposition?.files.map((item) => [item.path, item.status]) ?? []);
131
+ const dispositionMap = buildDispositionMap(disposition);
146
132
  for (const file of repoManifest.files) {
147
133
  const status = dispositionMap.get(file.path);
148
134
  if (file.excluded || (status && isAuditExcludedStatus(status))) {
@@ -2,6 +2,20 @@ import { usesDeferredWorkerCommand, } from "../types/workerSession.js";
2
2
  function renderArgv(task) {
3
3
  return JSON.stringify(task.worker_command);
4
4
  }
5
+ function renderAccessSection(access) {
6
+ const lines = ["", "## File access"];
7
+ if (access.read_paths.length > 0) {
8
+ lines.push(`Read: ${access.read_paths.join(", ")}`);
9
+ }
10
+ if (access.write_paths.length > 0) {
11
+ lines.push(`Write: ${access.write_paths.join(", ")}`);
12
+ }
13
+ if (access.forbidden_patterns && access.forbidden_patterns.length > 0) {
14
+ lines.push(`Forbidden: ${access.forbidden_patterns.join(", ")}`);
15
+ }
16
+ lines.push("Do not read or write files outside these paths.");
17
+ return lines;
18
+ }
5
19
  export function renderWorkerPrompt(task) {
6
20
  const commandArgv = renderArgv(task);
7
21
  if (task.preferred_executor === "agent" && task.audit_results_path) {
@@ -12,7 +26,7 @@ export function renderWorkerPrompt(task) {
12
26
  `Read: ${tasksPath}`,
13
27
  "Scope: review only the tasks listed in the Read file. Do not add tasks,",
14
28
  "edit source files, remediate findings, run unrelated audits, or write result_path.",
15
- "Prefer host Read and Grep tools for source inspection. On native Windows, do not use Unix pipelines like `grep ... | head`; if shell search is unavoidable, use `Select-String` as a fallback.",
29
+ "Use host Read and Grep tools for source inspection. Do not use shell search commands.",
16
30
  "For each listed task: read the assigned file_paths under the specified lens,",
17
31
  "using targeted reads/searches where they give complete enough evidence without loading unrelated context,",
18
32
  "and emit exactly one AuditResult object with:",
@@ -34,6 +48,9 @@ export function renderWorkerPrompt(task) {
34
48
  else {
35
49
  lines.push("After writing audit_results_path, execute worker_command from task.json exactly.", "The worker command ingests audit_results_path and writes result_path.", `Command: ${commandArgv}`);
36
50
  }
51
+ if (task.access) {
52
+ lines.push(...renderAccessSection(task.access));
53
+ }
37
54
  return lines.join("\n");
38
55
  }
39
56
  return [
@@ -1,11 +1,11 @@
1
- import type { FreshSessionProvider, LaunchFreshSessionInput } from "./types.js";
2
- import type { ClaudeCodeConfig } from "../types/sessionConfig.js";
1
+ import type { FreshSessionProvider, LaunchFreshSessionInput, ClaudeCodeConfig, OpenTokenConfig } from "@audit-tools/shared";
3
2
  import { spawnLoggedCommand } from "./spawnLoggedCommand.js";
4
3
  export declare const ACTIVE_CLAUDE_CODE_SESSION_MESSAGE: string;
5
4
  export declare class ClaudeCodeProvider implements FreshSessionProvider {
6
5
  name: string;
7
6
  private readonly config;
7
+ private readonly opentoken;
8
8
  private readonly launchCommand;
9
- constructor(config?: ClaudeCodeConfig, launchCommand?: typeof spawnLoggedCommand);
10
- launch(input: LaunchFreshSessionInput): Promise<import("./types.js").LaunchFreshSessionResult>;
9
+ constructor(config?: ClaudeCodeConfig, launchCommand?: typeof spawnLoggedCommand, opentoken?: OpenTokenConfig);
10
+ launch(input: LaunchFreshSessionInput): Promise<import("@audit-tools/shared").LaunchFreshSessionResult>;
11
11
  }
@@ -6,10 +6,12 @@ export const ACTIVE_CLAUDE_CODE_SESSION_MESSAGE = "claude-code provider cannot b
6
6
  export class ClaudeCodeProvider {
7
7
  name = "claude-code";
8
8
  config;
9
+ opentoken;
9
10
  launchCommand;
10
- constructor(config = {}, launchCommand = spawnLoggedCommand) {
11
+ constructor(config = {}, launchCommand = spawnLoggedCommand, opentoken = {}) {
11
12
  this.config = config;
12
13
  this.launchCommand = launchCommand;
14
+ this.opentoken = opentoken;
13
15
  }
14
16
  async launch(input) {
15
17
  if (process.env.CLAUDECODE) {
@@ -17,14 +19,18 @@ export class ClaudeCodeProvider {
17
19
  }
18
20
  const prompt = await readFile(input.promptPath, "utf8");
19
21
  const command = this.config.command ?? "claude";
22
+ const promptFlag = this.config.prompt_flag ?? "-p";
20
23
  const args = [
21
- "-p",
24
+ promptFlag,
22
25
  prompt,
23
26
  ...(this.config.extra_args ?? []),
24
27
  ...(this.config.dangerously_skip_permissions
25
28
  ? ["--dangerously-skip-permissions"]
26
29
  : []),
27
30
  ];
28
- return await this.launchCommand(command, args, input);
31
+ return await this.launchCommand(command, args, input, undefined, {
32
+ opentoken: this.opentoken.enabled,
33
+ opentokenCommand: this.opentoken.command,
34
+ });
29
35
  }
30
36
  }
@@ -1 +1 @@
1
- export declare const LOCAL_SUBPROCESS_PROVIDER_NAME: "local-subprocess";
1
+ export { LOCAL_SUBPROCESS_PROVIDER_NAME } from "@audit-tools/shared";
@@ -1 +1 @@
1
- export const LOCAL_SUBPROCESS_PROVIDER_NAME = "local-subprocess";
1
+ export { LOCAL_SUBPROCESS_PROVIDER_NAME } from "@audit-tools/shared";
@@ -1,5 +1,4 @@
1
- import type { FreshSessionProvider } from "./types.js";
2
- import type { ResolvedProviderName, SessionConfig } from "../types/sessionConfig.js";
1
+ import type { FreshSessionProvider, ResolvedProviderName, SessionConfig } from "@audit-tools/shared";
3
2
  export declare function resolveFreshSessionProviderName(name: string | undefined, sessionConfig?: SessionConfig, options?: {
4
3
  env?: NodeJS.ProcessEnv;
5
4
  commandExists?: (command: string) => boolean;
@@ -64,6 +64,7 @@ export function resolveFreshSessionProviderName(name, sessionConfig = {}, option
64
64
  }
65
65
  export function createFreshSessionProvider(name, sessionConfig = {}) {
66
66
  const providerName = resolveFreshSessionProviderName(name, sessionConfig);
67
+ const opentoken = sessionConfig.opentoken ?? {};
67
68
  if (providerName === "local-subprocess" &&
68
69
  (name ?? sessionConfig.provider) === "auto") {
69
70
  process.stderr.write("audit-code: auto provider resolved to local-subprocess — no capable agent provider detected. " +
@@ -77,16 +78,16 @@ export function createFreshSessionProvider(name, sessionConfig = {}) {
77
78
  if (!sessionConfig.subprocess_template?.command_template?.length) {
78
79
  throw new Error("subprocess-template provider requires session-config.json with subprocess_template.command_template.");
79
80
  }
80
- return new SubprocessTemplateProvider(sessionConfig.subprocess_template);
81
+ return new SubprocessTemplateProvider(sessionConfig.subprocess_template, undefined, opentoken);
81
82
  case "claude-code":
82
- return new ClaudeCodeProvider(sessionConfig.claude_code);
83
+ return new ClaudeCodeProvider(sessionConfig.claude_code, undefined, opentoken);
83
84
  case "opencode":
84
- return new OpenCodeProvider(sessionConfig.opencode);
85
+ return new OpenCodeProvider(sessionConfig.opencode, opentoken);
85
86
  case "vscode-task":
86
87
  if (!sessionConfig.vscode_task?.command_template?.length) {
87
88
  throw new Error("vscode-task provider requires session-config.json with vscode_task.command_template.");
88
89
  }
89
- return new VSCodeTaskProvider(sessionConfig.vscode_task);
90
+ return new VSCodeTaskProvider(sessionConfig.vscode_task, opentoken);
90
91
  default:
91
92
  throw new Error(`Unknown provider: ${providerName}`);
92
93
  }
@@ -1,9 +1,9 @@
1
- import type { FreshSessionProvider, LaunchFreshSessionInput } from "./types.js";
1
+ import type { FreshSessionProvider, LaunchFreshSessionInput } from "@audit-tools/shared";
2
2
  import { spawnLoggedCommand } from "./spawnLoggedCommand.js";
3
3
  export declare const MISSING_WORKER_COMMAND_MESSAGE = "local-subprocess provider requires task.worker_command.";
4
4
  export declare class LocalSubprocessProvider implements FreshSessionProvider {
5
5
  name: string;
6
6
  private readonly launchCommand;
7
7
  constructor(launchCommand?: typeof spawnLoggedCommand);
8
- launch(input: LaunchFreshSessionInput): Promise<import("./types.js").LaunchFreshSessionResult>;
8
+ launch(input: LaunchFreshSessionInput): Promise<import("@audit-tools/shared").LaunchFreshSessionResult>;
9
9
  }
@@ -1,4 +1,4 @@
1
- import { readJsonFile } from "../io/json.js";
1
+ import { readJsonFile } from "@audit-tools/shared";
2
2
  import { spawnLoggedCommand } from "./spawnLoggedCommand.js";
3
3
  export const MISSING_WORKER_COMMAND_MESSAGE = "local-subprocess provider requires task.worker_command.";
4
4
  export class LocalSubprocessProvider {
@@ -1,8 +1,8 @@
1
- import type { FreshSessionProvider, LaunchFreshSessionInput } from "./types.js";
2
- import type { OpenCodeConfig } from "../types/sessionConfig.js";
1
+ import type { FreshSessionProvider, LaunchFreshSessionInput, OpenCodeConfig, OpenTokenConfig } from "@audit-tools/shared";
3
2
  export declare class OpenCodeProvider implements FreshSessionProvider {
4
3
  name: string;
5
4
  private readonly config;
6
- constructor(config?: OpenCodeConfig);
7
- launch(input: LaunchFreshSessionInput): Promise<import("./types.js").LaunchFreshSessionResult>;
5
+ private readonly opentoken;
6
+ constructor(config?: OpenCodeConfig, opentoken?: OpenTokenConfig);
7
+ launch(input: LaunchFreshSessionInput): Promise<import("@audit-tools/shared").LaunchFreshSessionResult>;
8
8
  }
@@ -22,14 +22,19 @@ function quoteCmdArg(value) {
22
22
  export class OpenCodeProvider {
23
23
  name = "opencode";
24
24
  config;
25
- constructor(config = {}) {
25
+ opentoken;
26
+ constructor(config = {}, opentoken = {}) {
26
27
  this.config = config;
28
+ this.opentoken = opentoken;
27
29
  }
28
30
  async launch(input) {
29
31
  const prompt = await readFile(input.promptPath, "utf8");
30
32
  const baseCommand = this.config.command ?? "opencode";
31
33
  const baseArgs = ["run", prompt, ...(this.config.extra_args ?? [])];
32
34
  const resolved = resolveOpenCodeSpawnCommand(baseCommand, baseArgs);
33
- return await spawnLoggedCommand(resolved.command, resolved.args, input);
35
+ return await spawnLoggedCommand(resolved.command, resolved.args, input, undefined, {
36
+ opentoken: this.opentoken.enabled,
37
+ opentokenCommand: this.opentoken.command,
38
+ });
34
39
  }
35
40
  }
@@ -1,10 +1,12 @@
1
1
  import { createWriteStream } from "node:fs";
2
2
  import { spawn } from "node:child_process";
3
- import type { LaunchFreshSessionInput, LaunchFreshSessionResult } from "./types.js";
3
+ import type { LaunchFreshSessionInput, LaunchFreshSessionResult } from "@audit-tools/shared";
4
4
  interface SpawnLoggedCommandOptions {
5
5
  createWriteStream?: typeof createWriteStream;
6
6
  spawn?: typeof spawn;
7
7
  killGraceMs?: number;
8
+ opentoken?: boolean;
9
+ opentokenCommand?: string;
8
10
  }
9
11
  export declare function spawnLoggedCommand(command: string, args: string[], input: LaunchFreshSessionInput, env?: Record<string, string>, options?: SpawnLoggedCommandOptions): Promise<LaunchFreshSessionResult>;
10
12
  export {};
@@ -6,6 +6,22 @@ const FORCE_KILL_GRACE_MS = 1_000;
6
6
  function formatCommand(command, args) {
7
7
  return [command, ...args].join(" ");
8
8
  }
9
+ function quoteCmdArg(value) {
10
+ if (/^[A-Za-z0-9_./:=@+-]+$/.test(value))
11
+ return value;
12
+ return `"${value.replace(/(["^&|<>%])/g, "^$1")}"`;
13
+ }
14
+ function applyOpenTokenWrap(command, args, opentokenCommand, platform = process.platform) {
15
+ if (platform === "win32") {
16
+ const shell = process.env.ComSpec ?? "cmd.exe";
17
+ const inner = [command, ...args].map(quoteCmdArg).join(" ");
18
+ return {
19
+ command: shell,
20
+ args: ["/d", "/s", "/c", `${opentokenCommand} wrap ${inner}`],
21
+ };
22
+ }
23
+ return { command: opentokenCommand, args: ["wrap", command, ...args] };
24
+ }
9
25
  // On Windows `command` must be the resolved .cmd / .exe path because `spawn`
10
26
  // does not consult PATH for executables without a shell. Callers should use
11
27
  // `platformCommand()` (scripts/smoke-packaged-audit-code.mjs) or similar to
@@ -14,6 +30,11 @@ export async function spawnLoggedCommand(command, args, input, env, options = {}
14
30
  const openWriteStream = options.createWriteStream ?? createWriteStream;
15
31
  const spawnProcess = options.spawn ?? spawn;
16
32
  const killGraceMs = options.killGraceMs ?? FORCE_KILL_GRACE_MS;
33
+ if (options.opentoken) {
34
+ const wrapped = applyOpenTokenWrap(command, args, options.opentokenCommand ?? "opentoken");
35
+ command = wrapped.command;
36
+ args = wrapped.args;
37
+ }
17
38
  return await new Promise((resolve, reject) => {
18
39
  const stdoutLog = openWriteStream(input.stdoutPath, { flags: "a" });
19
40
  const stderrLog = openWriteStream(input.stderrPath, { flags: "a" });
@@ -1,8 +1,8 @@
1
- import type { FreshSessionProvider, LaunchFreshSessionInput } from "./types.js";
2
- import type { SubprocessTemplateConfig } from "../types/sessionConfig.js";
1
+ import type { FreshSessionProvider, LaunchFreshSessionInput, SubprocessTemplateConfig, OpenTokenConfig } from "@audit-tools/shared";
3
2
  export declare class SubprocessTemplateProvider implements FreshSessionProvider {
4
3
  name: string;
5
4
  private readonly config;
6
- constructor(config: SubprocessTemplateConfig, name?: string);
7
- launch(input: LaunchFreshSessionInput): Promise<import("./types.js").LaunchFreshSessionResult>;
5
+ private readonly opentoken;
6
+ constructor(config: SubprocessTemplateConfig, name?: string, opentoken?: OpenTokenConfig);
7
+ launch(input: LaunchFreshSessionInput): Promise<import("@audit-tools/shared").LaunchFreshSessionResult>;
8
8
  }