auditor-lambda 0.6.11 → 0.7.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 +44 -1
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.js +8 -0
- package/dist/cli/dispatch.js +14 -3
- package/dist/cli/nextStepCommand.js +37 -0
- package/dist/cli/prompts.js +2 -0
- package/dist/cli.js +22 -13
- package/dist/extractors/fileInventory.js +15 -2
- package/dist/extractors/graphManifestEdges.d.ts +2 -4
- package/dist/extractors/graphManifestEdges.js +5 -28
- package/dist/extractors/graphPathUtils.d.ts +16 -0
- package/dist/extractors/graphPathUtils.js +40 -0
- package/dist/orchestrator/artifactMetadata.d.ts +1 -0
- package/dist/orchestrator/artifactMetadata.js +15 -0
- package/dist/orchestrator/flowRequeue.js +1 -14
- package/dist/orchestrator/reviewPacketSizing.d.ts +25 -0
- package/dist/orchestrator/reviewPacketSizing.js +60 -0
- package/dist/orchestrator/reviewPackets.d.ts +4 -15
- package/dist/orchestrator/reviewPackets.js +9 -78
- package/dist/orchestrator.js +1 -4
- package/dist/quota/index.d.ts +1 -1
- package/dist/quota/index.js +1 -1
- package/dist/types/workerSession.d.ts +1 -3
- package/dist/types.d.ts +6 -0
- package/dist/types.js +20 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import { access, cp, mkdir, open, readFile, readdir, stat, unlink, writeFile } f
|
|
|
2
2
|
import { constants } from 'node:fs';
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
|
-
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
|
+
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
|
|
8
8
|
const repoRoot = dirname(fileURLToPath(import.meta.url));
|
|
@@ -247,11 +247,54 @@ async function acquireBuildLock() {
|
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
// Pure, testable core of the build preflight. `sharedManifestPath` is the
|
|
251
|
+
// resolved path of @audit-tools/shared's package.json (or null if it could not
|
|
252
|
+
// be resolved at all); `checkoutRoot` is the root this wrapper belongs to.
|
|
253
|
+
export function assertWorkspaceInstalled({ checkoutRoot, sharedManifestPath }) {
|
|
254
|
+
if (!sharedManifestPath) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
'Dependencies are not installed for this checkout. Run `npm install` from ' +
|
|
257
|
+
'the repository root, then retry — building from source needs node_modules ' +
|
|
258
|
+
'(including the @audit-tools/shared workspace link).',
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const relToCheckout = relative(checkoutRoot, sharedManifestPath);
|
|
263
|
+
if (relToCheckout.startsWith('..') || isAbsolute(relToCheckout)) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`@audit-tools/shared resolved to ${sharedManifestPath}, outside this ` +
|
|
266
|
+
`checkout (${checkoutRoot}). node_modules was never installed here — ` +
|
|
267
|
+
'common in a fresh git worktree — so building would typecheck against ' +
|
|
268
|
+
"another checkout's stale dist and report phantom \"missing export\" " +
|
|
269
|
+
"errors. Run `npm install` from this checkout's root.",
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Catches the common fresh-checkout trap before `npm run build` runs: with no
|
|
275
|
+
// local node_modules, Node/tsc resolve @audit-tools/shared against a different
|
|
276
|
+
// checkout (e.g. the main repo when running inside a git worktree).
|
|
277
|
+
async function preflightWorkspace() {
|
|
278
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
279
|
+
let sharedManifestPath = null;
|
|
280
|
+
try {
|
|
281
|
+
sharedManifestPath = requireFromHere.resolve('@audit-tools/shared/package.json');
|
|
282
|
+
} catch {
|
|
283
|
+
sharedManifestPath = null;
|
|
284
|
+
}
|
|
285
|
+
assertWorkspaceInstalled({
|
|
286
|
+
checkoutRoot: resolve(repoRoot, '..', '..'),
|
|
287
|
+
sharedManifestPath,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
250
291
|
async function ensureBuilt() {
|
|
251
292
|
if (!(await shouldBuildDist())) {
|
|
252
293
|
return;
|
|
253
294
|
}
|
|
254
295
|
|
|
296
|
+
await preflightWorkspace();
|
|
297
|
+
|
|
255
298
|
const lockHandle = await acquireBuildLock();
|
|
256
299
|
if (!lockHandle) {
|
|
257
300
|
return;
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare function summarizeLaunchExit(result: {
|
|
|
35
35
|
error?: string;
|
|
36
36
|
}): string | null;
|
|
37
37
|
export declare function taskResultPath(taskResultsDir: string, taskId: string): string;
|
|
38
|
+
export declare function isCanonicalResultFilename(filename: string): boolean;
|
|
38
39
|
export declare function packetPromptPath(taskResultsDir: string, packetId: string): string;
|
|
39
40
|
export declare function readStdinText(): Promise<string>;
|
|
40
41
|
export declare function normalizePositiveInteger(value: unknown): number | undefined;
|
package/dist/cli/args.js
CHANGED
|
@@ -101,6 +101,14 @@ export function summarizeLaunchExit(result) {
|
|
|
101
101
|
export function taskResultPath(taskResultsDir, taskId) {
|
|
102
102
|
return join(taskResultsDir, artifactNameForId(taskId, "json"));
|
|
103
103
|
}
|
|
104
|
+
const CANONICAL_RESULT_FILENAME = /_[0-9a-f]{12}\.json$/i;
|
|
105
|
+
// True when `filename` matches the canonical per-task result naming produced by
|
|
106
|
+
// artifactNameForId (stem + "_" + 12-hex sha256 digest + ".json"). Lets
|
|
107
|
+
// merge-and-ingest tell legitimate prior-round results apart from genuinely
|
|
108
|
+
// stray files (e.g. packet-23-results.json) left in task-results/.
|
|
109
|
+
export function isCanonicalResultFilename(filename) {
|
|
110
|
+
return CANONICAL_RESULT_FILENAME.test(filename);
|
|
111
|
+
}
|
|
104
112
|
export function packetPromptPath(taskResultsDir, packetId) {
|
|
105
113
|
return join(taskResultsDir, artifactNameForId(packetId, "prompt.md"));
|
|
106
114
|
}
|
package/dist/cli/dispatch.js
CHANGED
|
@@ -8,7 +8,7 @@ import { orderTasksForPacketReview, buildReviewPackets, sizeIndexFromManifest, }
|
|
|
8
8
|
import { buildFileAnchorSummary } from "../orchestrator/fileAnchors.js";
|
|
9
9
|
import { resolveFreshSessionProviderName } from "../providers/index.js";
|
|
10
10
|
import { loadSessionConfig } from "../supervisor/sessionConfig.js";
|
|
11
|
-
import { scheduleWave, buildProviderModelKey, readQuotaState, resolveHostActiveSubagentLimit, lookupDiscoveredLimits, mergeDiscoveredLimits, } from "../quota/index.js";
|
|
11
|
+
import { scheduleWave, buildProviderModelKey, resolveHostModel, readQuotaState, resolveHostActiveSubagentLimit, lookupDiscoveredLimits, mergeDiscoveredLimits, } from "../quota/index.js";
|
|
12
12
|
import { taskResultPath, packetPromptPath, artifactNameForId, toBase64Url, fromBase64Url, getFlag, } from "./args.js";
|
|
13
13
|
export const LARGE_FILE_PACKET_TARGET_LINES = 2500;
|
|
14
14
|
export const SMALL_MODEL_HINT_MAX_LINES = 500;
|
|
@@ -391,7 +391,9 @@ export async function prepareDispatchArtifacts(params) {
|
|
|
391
391
|
...taskSections,
|
|
392
392
|
"## Output",
|
|
393
393
|
"Do not write files directly. Do not use a Write tool, create temp files, edit source files,",
|
|
394
|
-
"remediate findings,
|
|
394
|
+
"remediate findings, run unrelated audits, or write any result file yourself (e.g.",
|
|
395
|
+
"packet-*-result.json / audit_result_*.json) — the submit-packet command below is the only",
|
|
396
|
+
"way to record results, and it writes them inside the artifacts directory for you.",
|
|
395
397
|
"Produce one JSON array containing exactly one AuditResult object for each listed task.",
|
|
396
398
|
"",
|
|
397
399
|
"Required AuditResult fields:",
|
|
@@ -453,9 +455,18 @@ export async function prepareDispatchArtifacts(params) {
|
|
|
453
455
|
run_id: runId,
|
|
454
456
|
entries: resultMapEntries,
|
|
455
457
|
});
|
|
456
|
-
const hostModel = params.hostModel ?? null;
|
|
457
458
|
const perPacketTokens = plan.map((p) => p.complexity.estimated_tokens);
|
|
458
459
|
const quotaProviderName = resolveFreshSessionProviderName(undefined, sessionConfig);
|
|
460
|
+
// Resolve the host model (explicit/CLI override → block_quota.host_model → env
|
|
461
|
+
// → per-provider default) so per-model quota detection engages with realistic
|
|
462
|
+
// limits instead of the conservative unknown-model floor. params.hostModel
|
|
463
|
+
// carries any caller/CLI override.
|
|
464
|
+
const hostModel = resolveHostModel({
|
|
465
|
+
providerName: quotaProviderName,
|
|
466
|
+
sessionConfig,
|
|
467
|
+
explicitModel: params.hostModel,
|
|
468
|
+
envVar: "AUDIT_CODE_HOST_MODEL",
|
|
469
|
+
});
|
|
459
470
|
const quotaProviderKey = buildProviderModelKey(quotaProviderName, hostModel);
|
|
460
471
|
const quotaState = await readQuotaState().catch(() => ({ version: 2, entries: {} }));
|
|
461
472
|
const quotaStateEntry = quotaState.entries[quotaProviderKey] ?? null;
|
|
@@ -3,6 +3,7 @@ import { join, resolve } from "node:path";
|
|
|
3
3
|
import { isFileMissingError, readJsonFile, writeJsonFile, } from "@audit-tools/shared";
|
|
4
4
|
import { loadArtifactBundle, promoteFinalAuditReport, writeCoreArtifacts, AUDIT_REPORT_FILENAME, } from "../io/artifacts.js";
|
|
5
5
|
import { advanceAudit } from "../orchestrator/advance.js";
|
|
6
|
+
import { computeArtifactStateSignature } from "../orchestrator/artifactMetadata.js";
|
|
6
7
|
import { decideNextStep } from "../orchestrator/nextStep.js";
|
|
7
8
|
import { deriveAuditState } from "../orchestrator/state.js";
|
|
8
9
|
import { checkFileIntegrity } from "../orchestrator/fileIntegrity.js";
|
|
@@ -25,6 +26,15 @@ import { getArtifactsDir, getFlag, getHostMaxActiveSubagents, getMaxRuns, getOpt
|
|
|
25
26
|
async function runDeterministicForNextStep(params) {
|
|
26
27
|
let lastSummary = "";
|
|
27
28
|
let analyzers = params.analyzers;
|
|
29
|
+
// Finalization thrashing guard. A converging run produces a (mostly) new
|
|
30
|
+
// artifact state each iteration, so the iteration count tracks the number of
|
|
31
|
+
// distinct states closely (a few idempotent passes are normal). When
|
|
32
|
+
// iterations outrun distinct states by this tolerance, deterministic executors
|
|
33
|
+
// are revisiting states (a staleness ping-pong, e.g. runtime_validation <->
|
|
34
|
+
// synthesis) rather than progressing — stop instead of spinning to maxRuns.
|
|
35
|
+
const FINALIZATION_CYCLE_TOLERANCE = 16;
|
|
36
|
+
const seenStateSignatures = new Set();
|
|
37
|
+
const obligationTrail = [];
|
|
28
38
|
for (let index = 0; index < params.maxRuns; index++) {
|
|
29
39
|
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
30
40
|
const decision = decideNextStep(bundle);
|
|
@@ -286,6 +296,33 @@ async function runDeterministicForNextStep(params) {
|
|
|
286
296
|
reason: result.progress_summary,
|
|
287
297
|
};
|
|
288
298
|
}
|
|
299
|
+
// Finalization cycle guard. If this iteration returned the audit to an
|
|
300
|
+
// artifact state already produced this run, the deterministic loop is
|
|
301
|
+
// thrashing (no net progress) rather than converging. The canonical outputs
|
|
302
|
+
// are already rendered, so stop and surface the cycling obligations instead
|
|
303
|
+
// of spinning to maxRuns and crashing.
|
|
304
|
+
obligationTrail.push(decision.selected_obligation ?? "unknown");
|
|
305
|
+
seenStateSignatures.add(computeArtifactStateSignature(result.updated_bundle));
|
|
306
|
+
if (index + 1 - seenStateSignatures.size >= FINALIZATION_CYCLE_TOLERANCE) {
|
|
307
|
+
const cycle = Array.from(new Set(obligationTrail.slice(-FINALIZATION_CYCLE_TOLERANCE)));
|
|
308
|
+
await writeJsonFile(join(params.artifactsDir, "steps", "deterministic-progress.json"), {
|
|
309
|
+
iteration: index + 1,
|
|
310
|
+
max_runs: params.maxRuns,
|
|
311
|
+
cycle_detected: true,
|
|
312
|
+
cycling_obligations: cycle,
|
|
313
|
+
summary: "Finalization kept revisiting prior artifact states without net " +
|
|
314
|
+
`progress; stopping. Cycling obligations: ${cycle.join(" -> ")}.`,
|
|
315
|
+
timestamp: new Date().toISOString(),
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
kind: "blocked",
|
|
319
|
+
state: result.audit_state,
|
|
320
|
+
bundle: result.updated_bundle,
|
|
321
|
+
reason: "Finalization is not converging: deterministic executors kept revisiting " +
|
|
322
|
+
`prior artifact states (${cycle.join(" -> ")}). The report has been ` +
|
|
323
|
+
"rendered; review whether these obligations are erroneously invalidating each other.",
|
|
324
|
+
};
|
|
325
|
+
}
|
|
289
326
|
}
|
|
290
327
|
const bundle = await loadArtifactBundle(params.artifactsDir);
|
|
291
328
|
const state = deriveAuditState(bundle);
|
package/dist/cli/prompts.js
CHANGED
|
@@ -68,6 +68,8 @@ export function renderDispatchReviewPrompt(params) {
|
|
|
68
68
|
"`host_concurrency_limit` records any detected hard host cap that contributed to `wave_size`.",
|
|
69
69
|
"",
|
|
70
70
|
"For each wave: use the `task` tool (or equivalent subagent dispatch) to launch up to `wave_size` subagents in parallel (one per entry), wait for all to finish, then start the next wave.",
|
|
71
|
+
"",
|
|
72
|
+
'If a subagent reports a host session/usage limit (e.g. "hit your session limit · resets <time>") instead of submitting its result, do not immediately re-dispatch it: run merge-and-ingest with the results you did get, then wait until the stated reset time before running next-step to re-dispatch the remaining packets. Re-dispatching into an active limit just loses the wave.',
|
|
71
73
|
]
|
|
72
74
|
: [
|
|
73
75
|
"Read this generated dispatch plan:",
|
package/dist/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ import { runAuditCodeMcpServer } from "./mcp/server.js";
|
|
|
26
26
|
import { scheduleWave, buildProviderModelKey, readQuotaState, resolveLimits, resolveHostActiveSubagentLimit, probeProvider, computeMaxSafeConcurrency, getQuotaStatePath, lookupDiscoveredLimits, setQuotaStateDir, } from "./quota/index.js";
|
|
27
27
|
// Re-exports from extracted modules
|
|
28
28
|
export { resolveHostDispatchCapability, DIRECT_CLI_DEFAULTS, getFlag, hasFlag, getOptionalBooleanFlag, getArtifactsDir, getRootDir, getBatchResultsDir, getMaxRuns, getAgentBatchSize, getParallelWorkers, getTimeoutMs, chunkArray, getUiMode, looksLikeCliFlag, countLines, warnIfNotGitRepo, } from "./cli/args.js";
|
|
29
|
-
import { DIRECT_CLI_DEFAULTS, getFlag, hasFlag, fromBase64Url, taskResultPath, readStdinText, getArtifactsDir, getRootDir, warnIfNotGitRepo, getBatchResultsDir, getMaxRuns, getAgentBatchSize, getParallelWorkers, getTimeoutMs, getExplicitProvider, getHostModel, getHostMaxActiveSubagents, getQuotaProbeMode, resolveRunProviderName, chunkArray, getUiMode, looksLikeCliFlag, countLines, } from "./cli/args.js";
|
|
29
|
+
import { DIRECT_CLI_DEFAULTS, getFlag, hasFlag, fromBase64Url, taskResultPath, isCanonicalResultFilename, readStdinText, getArtifactsDir, getRootDir, warnIfNotGitRepo, getBatchResultsDir, getMaxRuns, getAgentBatchSize, getParallelWorkers, getTimeoutMs, getExplicitProvider, getHostModel, getHostMaxActiveSubagents, getQuotaProbeMode, resolveRunProviderName, chunkArray, getUiMode, looksLikeCliFlag, countLines, } from "./cli/args.js";
|
|
30
30
|
import { WORKER_RESULT_CONTRACT_VERSION, buildWorkerResult, formatAuditResultValidationError, } from "./cli/workerResult.js";
|
|
31
31
|
import { DISPATCH_RESULT_MAP_FILENAME, ACTIVE_DISPATCH_FILENAME, resolveRunScopedArg, loadDispatchResultMap, entriesByTaskId, buildPendingAuditTasks, prepareDispatchArtifacts, } from "./cli/dispatch.js";
|
|
32
32
|
import { buildLineIndex, buildLineIndexForPaths, addFileLineCountHints, } from "./cli/lineIndex.js";
|
|
@@ -510,20 +510,29 @@ async function cmdMergeAndIngest(argv) {
|
|
|
510
510
|
const fallbackByTaskId = new Map();
|
|
511
511
|
for (const filename of files) {
|
|
512
512
|
const filePath = resolve(join(taskResultsDir, filename));
|
|
513
|
-
if (
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
513
|
+
if (expectedPaths.has(filePath))
|
|
514
|
+
continue;
|
|
515
|
+
// Not part of this round's plan. Still read it so a current task can be
|
|
516
|
+
// recovered by task_id (e.g. a subagent wrote a valid result under a
|
|
517
|
+
// non-assigned name).
|
|
518
|
+
try {
|
|
519
|
+
const raw = await readFile(filePath, "utf8");
|
|
520
|
+
const parsed = JSON.parse(raw);
|
|
521
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
522
|
+
const tid = typeof parsed.task_id === "string"
|
|
523
|
+
? String(parsed.task_id) : undefined;
|
|
524
|
+
if (tid && !fallbackByTaskId.has(tid)) {
|
|
525
|
+
fallbackByTaskId.set(tid, parsed);
|
|
524
526
|
}
|
|
525
527
|
}
|
|
526
|
-
|
|
528
|
+
}
|
|
529
|
+
catch { /* not parseable — skip */ }
|
|
530
|
+
// Only genuinely stray files are "spurious". Canonical per-task result files
|
|
531
|
+
// (<stem>_<digest>.json) left by prior deepening rounds in the same
|
|
532
|
+
// task-results/ dir are legitimate and must not inflate the count or bury
|
|
533
|
+
// the real stray-file signal (3 -> 191 over a run before this fix).
|
|
534
|
+
if (!isCanonicalResultFilename(filename)) {
|
|
535
|
+
spuriousFileCount++;
|
|
527
536
|
process.stderr.write(`[merge-and-ingest] Warning: unexpected file in task-results/: ${filename}\n`);
|
|
528
537
|
}
|
|
529
538
|
}
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import { normalizeExtractorPath } from "./pathPatterns.js";
|
|
2
2
|
import { LANGUAGE_BY_EXTENSION } from "./languageMap.generated.js";
|
|
3
|
+
// The generated linguist map resolves a few common extensions to obscure
|
|
4
|
+
// languages that outrank the everyday one (".md" -> GCC machine description,
|
|
5
|
+
// ".yml"/".yaml" -> MiniYAML). These overrides win over the generated map so the
|
|
6
|
+
// file inventory does not mislabel ordinary docs/config. Keep this list small
|
|
7
|
+
// and limited to extensions whose generated mapping is demonstrably wrong.
|
|
8
|
+
const EXTENSION_LANGUAGE_OVERRIDES = {
|
|
9
|
+
md: "markdown",
|
|
10
|
+
markdown: "markdown",
|
|
11
|
+
yaml: "yaml",
|
|
12
|
+
yml: "yaml",
|
|
13
|
+
};
|
|
3
14
|
function inferLanguage(path) {
|
|
4
15
|
const normalized = normalizeExtractorPath(path);
|
|
5
16
|
const base = normalized.split("/").pop() ?? normalized;
|
|
6
|
-
const extension = base.includes(".") ? base.split(".").pop() ?? "" : "";
|
|
7
|
-
return
|
|
17
|
+
const extension = (base.includes(".") ? base.split(".").pop() ?? "" : "").toLowerCase();
|
|
18
|
+
return (EXTENSION_LANGUAGE_OVERRIDES[extension] ??
|
|
19
|
+
LANGUAGE_BY_EXTENSION[extension] ??
|
|
20
|
+
"unknown");
|
|
8
21
|
}
|
|
9
22
|
export function buildRepoManifest(repositoryName, files) {
|
|
10
23
|
return {
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import type { GraphEdge } from "@audit-tools/shared";
|
|
2
|
+
import { isCargoManifestPath, isGoWorkspaceManifestPath, isMavenPomPath, isPyprojectPath } from "./graphPathUtils.js";
|
|
3
|
+
export { isCargoManifestPath, isGoWorkspaceManifestPath, isMavenPomPath, isPyprojectPath, };
|
|
2
4
|
export declare function extractPackageEntrypointEdges(fromPath: string, content: string, pathLookup: Map<string, string>): GraphEdge[];
|
|
3
5
|
export declare function extractPackageScriptEdges(fromPath: string, content: string, pathLookup: Map<string, string>): GraphEdge[];
|
|
4
|
-
export declare function isGoWorkspaceManifestPath(path: string): boolean;
|
|
5
|
-
export declare function isCargoManifestPath(path: string): boolean;
|
|
6
|
-
export declare function isMavenPomPath(path: string): boolean;
|
|
7
6
|
export declare function extractWorkspacePackageEdges(fromPath: string, content: string, pathLookup: Map<string, string>): GraphEdge[];
|
|
8
7
|
export declare function extractCargoWorkspaceMemberEdges(fromPath: string, content: string, pathLookup: Map<string, string>): GraphEdge[];
|
|
9
8
|
export declare function extractTypescriptProjectReferenceEdges(fromPath: string, content: string, pathLookup: Map<string, string>): GraphEdge[];
|
|
10
9
|
export declare function extractGoWorkspaceModuleEdges(fromPath: string, content: string, pathLookup: Map<string, string>): GraphEdge[];
|
|
11
10
|
export declare function extractMavenModuleEdges(fromPath: string, content: string, pathLookup: Map<string, string>): GraphEdge[];
|
|
12
|
-
export declare function isPyprojectPath(path: string): boolean;
|
|
13
11
|
export declare function extractPyprojectTestpathLinks(fromPath: string, content: string, pathLookup: Map<string, string>): GraphEdge[];
|
|
14
12
|
export declare function extractYamlPathReferenceEdges(fromPath: string, content: string, pathLookup: Map<string, string>): GraphEdge[];
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { posix } from "node:path";
|
|
2
|
-
import { graphEdge, normalizeGraphPath, resolveCandidate } from "./graphPathUtils.js";
|
|
2
|
+
import { graphEdge, normalizeGraphPath, resolveCandidate, isCargoManifestPath, isGoModuleManifestPath, isGoWorkspaceManifestPath, isMavenPomPath, isPackageManifestPath, isPnpmWorkspaceManifestPath, isPyprojectPath, isTypescriptProjectConfigPath, } from "./graphPathUtils.js";
|
|
3
|
+
// Re-exported for the graph builder, which imports these manifest predicates
|
|
4
|
+
// from here for historical reasons; the canonical definitions live in
|
|
5
|
+
// graphPathUtils.
|
|
6
|
+
export { isCargoManifestPath, isGoWorkspaceManifestPath, isMavenPomPath, isPyprojectPath, };
|
|
3
7
|
const PACKAGE_ENTRYPOINT_EDGE_CONFIDENCE = 0.9;
|
|
4
8
|
const PACKAGE_SCRIPT_EDGE_CONFIDENCE = 0.88;
|
|
5
9
|
const WORKSPACE_PACKAGE_EDGE_CONFIDENCE = 0.86;
|
|
@@ -8,9 +12,6 @@ const GO_WORKSPACE_MODULE_EDGE_CONFIDENCE = 0.87;
|
|
|
8
12
|
const CARGO_WORKSPACE_MEMBER_EDGE_CONFIDENCE = 0.87;
|
|
9
13
|
const MAVEN_MODULE_EDGE_CONFIDENCE = 0.87;
|
|
10
14
|
const PACKAGE_SCRIPT_REFERENCE_PATTERN = /(?:^|[\s"'`])((?:\.{1,2}\/)?(?:[\w.-]+\/)*[\w.-]+\.(?:cjs|cts|js|jsx|mjs|mts|ts|tsx))(?:$|[\s"'`])/gi;
|
|
11
|
-
function isPackageManifestPath(path) {
|
|
12
|
-
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "package.json";
|
|
13
|
-
}
|
|
14
15
|
function collectPackageEntrypointValues(value, fieldPath, entries) {
|
|
15
16
|
if (typeof value === "string") {
|
|
16
17
|
if (value.trim().length > 0) {
|
|
@@ -177,27 +178,6 @@ function packageWorkspacePatterns(content) {
|
|
|
177
178
|
}
|
|
178
179
|
return patterns;
|
|
179
180
|
}
|
|
180
|
-
function isPnpmWorkspaceManifestPath(path) {
|
|
181
|
-
return (posix.basename(normalizeGraphPath(path)).toLowerCase() ===
|
|
182
|
-
"pnpm-workspace.yaml");
|
|
183
|
-
}
|
|
184
|
-
export function isGoWorkspaceManifestPath(path) {
|
|
185
|
-
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "go.work";
|
|
186
|
-
}
|
|
187
|
-
function isGoModuleManifestPath(path) {
|
|
188
|
-
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "go.mod";
|
|
189
|
-
}
|
|
190
|
-
export function isCargoManifestPath(path) {
|
|
191
|
-
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "cargo.toml";
|
|
192
|
-
}
|
|
193
|
-
export function isMavenPomPath(path) {
|
|
194
|
-
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "pom.xml";
|
|
195
|
-
}
|
|
196
|
-
function isTypescriptProjectConfigPath(path) {
|
|
197
|
-
const basename = posix.basename(normalizeGraphPath(path)).toLowerCase();
|
|
198
|
-
return (basename === "tsconfig.json" ||
|
|
199
|
-
(basename.startsWith("tsconfig.") && basename.endsWith(".json")));
|
|
200
|
-
}
|
|
201
181
|
function stripJsonComments(content) {
|
|
202
182
|
let result = "";
|
|
203
183
|
let inString = false;
|
|
@@ -987,9 +967,6 @@ export function extractMavenModuleEdges(fromPath, content, pathLookup) {
|
|
|
987
967
|
}
|
|
988
968
|
// ─── Pyproject / pytest ───────────────────────────────────────────────────────
|
|
989
969
|
const PYPROJECT_TESTPATHS_LINK_CONFIDENCE = 0.85;
|
|
990
|
-
export function isPyprojectPath(path) {
|
|
991
|
-
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "pyproject.toml";
|
|
992
|
-
}
|
|
993
970
|
function pyprojectTestpaths(content) {
|
|
994
971
|
const values = [];
|
|
995
972
|
let currentSection = "";
|
|
@@ -15,3 +15,19 @@ export declare function resolveReferenceLiteral(fromPath: string, literal: strin
|
|
|
15
15
|
export declare function isJsonSchemaPath(path: string): boolean;
|
|
16
16
|
/** True for pytest `conftest.py` files. */
|
|
17
17
|
export declare function isPytestConftestPath(path: string): boolean;
|
|
18
|
+
/** True for `package.json` files. */
|
|
19
|
+
export declare function isPackageManifestPath(path: string): boolean;
|
|
20
|
+
/** True for `tsconfig.json` / `tsconfig.<name>.json` project config files. */
|
|
21
|
+
export declare function isTypescriptProjectConfigPath(path: string): boolean;
|
|
22
|
+
/** True for Go `go.mod` module manifests. */
|
|
23
|
+
export declare function isGoModuleManifestPath(path: string): boolean;
|
|
24
|
+
/** True for Go `go.work` workspace manifests. */
|
|
25
|
+
export declare function isGoWorkspaceManifestPath(path: string): boolean;
|
|
26
|
+
/** True for Rust `Cargo.toml` manifests. */
|
|
27
|
+
export declare function isCargoManifestPath(path: string): boolean;
|
|
28
|
+
/** True for Maven `pom.xml` manifests. */
|
|
29
|
+
export declare function isMavenPomPath(path: string): boolean;
|
|
30
|
+
/** True for Python `pyproject.toml` manifests. */
|
|
31
|
+
export declare function isPyprojectPath(path: string): boolean;
|
|
32
|
+
/** True for `pnpm-workspace.yaml` workspace manifests. */
|
|
33
|
+
export declare function isPnpmWorkspaceManifestPath(path: string): boolean;
|
|
@@ -131,3 +131,43 @@ export function isJsonSchemaPath(path) {
|
|
|
131
131
|
export function isPytestConftestPath(path) {
|
|
132
132
|
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "conftest.py";
|
|
133
133
|
}
|
|
134
|
+
// ---- Build-manifest path predicates ----
|
|
135
|
+
// One canonical set, shared by the graph manifest-edge extractor and the
|
|
136
|
+
// packetizer. Each preserves path case (so distinct files differing only by
|
|
137
|
+
// case are never collapsed) and matches the manifest filename
|
|
138
|
+
// case-insensitively, since manifest names are conventionally lowercase.
|
|
139
|
+
/** True for `package.json` files. */
|
|
140
|
+
export function isPackageManifestPath(path) {
|
|
141
|
+
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "package.json";
|
|
142
|
+
}
|
|
143
|
+
/** True for `tsconfig.json` / `tsconfig.<name>.json` project config files. */
|
|
144
|
+
export function isTypescriptProjectConfigPath(path) {
|
|
145
|
+
const basename = posix.basename(normalizeGraphPath(path)).toLowerCase();
|
|
146
|
+
return (basename === "tsconfig.json" ||
|
|
147
|
+
(basename.startsWith("tsconfig.") && basename.endsWith(".json")));
|
|
148
|
+
}
|
|
149
|
+
/** True for Go `go.mod` module manifests. */
|
|
150
|
+
export function isGoModuleManifestPath(path) {
|
|
151
|
+
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "go.mod";
|
|
152
|
+
}
|
|
153
|
+
/** True for Go `go.work` workspace manifests. */
|
|
154
|
+
export function isGoWorkspaceManifestPath(path) {
|
|
155
|
+
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "go.work";
|
|
156
|
+
}
|
|
157
|
+
/** True for Rust `Cargo.toml` manifests. */
|
|
158
|
+
export function isCargoManifestPath(path) {
|
|
159
|
+
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "cargo.toml";
|
|
160
|
+
}
|
|
161
|
+
/** True for Maven `pom.xml` manifests. */
|
|
162
|
+
export function isMavenPomPath(path) {
|
|
163
|
+
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "pom.xml";
|
|
164
|
+
}
|
|
165
|
+
/** True for Python `pyproject.toml` manifests. */
|
|
166
|
+
export function isPyprojectPath(path) {
|
|
167
|
+
return posix.basename(normalizeGraphPath(path)).toLowerCase() === "pyproject.toml";
|
|
168
|
+
}
|
|
169
|
+
/** True for `pnpm-workspace.yaml` workspace manifests. */
|
|
170
|
+
export function isPnpmWorkspaceManifestPath(path) {
|
|
171
|
+
return (posix.basename(normalizeGraphPath(path)).toLowerCase() ===
|
|
172
|
+
"pnpm-workspace.yaml");
|
|
173
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ArtifactMetadataManifest } from "../types/artifactMetadata.js";
|
|
2
2
|
import type { ArtifactBundle } from "../io/artifacts.js";
|
|
3
3
|
export declare function present(bundle: ArtifactBundle, artifactName: string): boolean;
|
|
4
|
+
export declare function computeArtifactStateSignature(bundle: ArtifactBundle): string;
|
|
4
5
|
export declare function computeArtifactMetadata(bundle: ArtifactBundle, previous?: ArtifactMetadataManifest, updatedArtifacts?: Iterable<string>): ArtifactMetadataManifest;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { getArtifactValue } from "../io/artifacts.js";
|
|
2
3
|
import { buildReverseDependencyMap, hashArtifactValue, stableStringify, } from "./artifactFreshness.js";
|
|
3
4
|
const REVERSE_DEPENDENCY_MAP = buildReverseDependencyMap();
|
|
@@ -31,6 +32,20 @@ export function present(bundle, artifactName) {
|
|
|
31
32
|
const value = getArtifactValue(bundle, artifactName);
|
|
32
33
|
return value !== undefined && value !== null;
|
|
33
34
|
}
|
|
35
|
+
// Stable signature of the overall artifact state, keyed on per-artifact CONTENT
|
|
36
|
+
// hashes — deliberately NOT revisions, which only ever increment. A
|
|
37
|
+
// deterministic advance loop that revisits a signature it already produced this
|
|
38
|
+
// run is cycling (e.g. a runtime_validation <-> synthesis staleness ping-pong);
|
|
39
|
+
// the content-hash basis catches that even while revisions churn underneath.
|
|
40
|
+
export function computeArtifactStateSignature(bundle) {
|
|
41
|
+
const metadata = bundle.artifact_metadata;
|
|
42
|
+
if (!metadata)
|
|
43
|
+
return "no-metadata";
|
|
44
|
+
const entries = Object.entries(metadata.artifacts)
|
|
45
|
+
.map(([name, entry]) => `${name}:${entry.content_hash}`)
|
|
46
|
+
.sort();
|
|
47
|
+
return createHash("sha256").update(entries.join("\n")).digest("hex");
|
|
48
|
+
}
|
|
34
49
|
export function computeArtifactMetadata(bundle, previous, updatedArtifacts = []) {
|
|
35
50
|
const artifacts = {};
|
|
36
51
|
const updated = new Set(updatedArtifacts);
|
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
return [
|
|
3
|
-
"correctness",
|
|
4
|
-
"architecture",
|
|
5
|
-
"maintainability",
|
|
6
|
-
"security",
|
|
7
|
-
"reliability",
|
|
8
|
-
"performance",
|
|
9
|
-
"data_integrity",
|
|
10
|
-
"tests",
|
|
11
|
-
"operability",
|
|
12
|
-
"config_deployment",
|
|
13
|
-
].includes(String(value));
|
|
14
|
-
}
|
|
1
|
+
import { isLens } from "../types.js";
|
|
15
2
|
function getExternalSignalPaths(externalAnalyzerResults) {
|
|
16
3
|
const results = Array.isArray(externalAnalyzerResults?.results)
|
|
17
4
|
? externalAnalyzerResults.results
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AuditTask } from "../types.js";
|
|
2
|
+
export declare const DEFAULT_MAX_TASKS_PER_PACKET = 0;
|
|
3
|
+
export declare const ESTIMATED_TOKENS_PER_LINE = 4;
|
|
4
|
+
export declare const ESTIMATED_PACKET_PROMPT_TOKENS = 900;
|
|
5
|
+
export declare const DEFAULT_TARGET_PACKET_TOKENS: number;
|
|
6
|
+
/**
|
|
7
|
+
* Build a path → size_bytes index from a repo manifest. Byte counts are
|
|
8
|
+
* recorded during intake, so this never reads files. Review packet token
|
|
9
|
+
* estimates are derived from these bytes (Phase 2) instead of counted lines.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sizeIndexFromManifest(repoManifest?: {
|
|
12
|
+
files: ReadonlyArray<{
|
|
13
|
+
path: string;
|
|
14
|
+
size_bytes: number;
|
|
15
|
+
}>;
|
|
16
|
+
}): Record<string, number>;
|
|
17
|
+
/** Estimated content tokens for one task across all of its files. */
|
|
18
|
+
export declare function taskContentTokens(task: AuditTask, sizeIndex?: Record<string, number>, lineIndex?: Record<string, number>): number;
|
|
19
|
+
/**
|
|
20
|
+
* Estimated content tokens across a set of file paths, resolving an owning task
|
|
21
|
+
* per path so the line fallback can read its `file_line_counts`. Shared files
|
|
22
|
+
* are counted once.
|
|
23
|
+
*/
|
|
24
|
+
export declare function fileGroupContentTokens(filePaths: Iterable<string>, tasks: AuditTask[], sizeIndex?: Record<string, number>, lineIndex?: Record<string, number>): number;
|
|
25
|
+
export declare function estimateTaskGroupTokens(tasks: AuditTask[], sizeIndex?: Record<string, number>, lineIndex?: Record<string, number>): number;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { estimateTokensFromBytes } from "@audit-tools/shared";
|
|
2
|
+
// Per-packet sizing / token-budget arithmetic for review packetization,
|
|
3
|
+
// extracted from reviewPackets.ts. Estimates derive from manifest byte counts
|
|
4
|
+
// (recorded at intake) with a line-count fallback for manually built tasks.
|
|
5
|
+
export const DEFAULT_MAX_TASKS_PER_PACKET = 0;
|
|
6
|
+
const DEFAULT_TARGET_PACKET_LINES = 8000;
|
|
7
|
+
export const ESTIMATED_TOKENS_PER_LINE = 4;
|
|
8
|
+
export const ESTIMATED_PACKET_PROMPT_TOKENS = 900;
|
|
9
|
+
// Default per-packet content-token budget. Kept equal to the legacy
|
|
10
|
+
// line-target × per-line estimate so byte-derived sizing lands on the same
|
|
11
|
+
// thresholds as the old line-based sizing when the line fallback is in effect.
|
|
12
|
+
export const DEFAULT_TARGET_PACKET_TOKENS = DEFAULT_TARGET_PACKET_LINES * ESTIMATED_TOKENS_PER_LINE;
|
|
13
|
+
/**
|
|
14
|
+
* Build a path → size_bytes index from a repo manifest. Byte counts are
|
|
15
|
+
* recorded during intake, so this never reads files. Review packet token
|
|
16
|
+
* estimates are derived from these bytes (Phase 2) instead of counted lines.
|
|
17
|
+
*/
|
|
18
|
+
export function sizeIndexFromManifest(repoManifest) {
|
|
19
|
+
if (!repoManifest)
|
|
20
|
+
return {};
|
|
21
|
+
return Object.fromEntries(repoManifest.files.map((file) => [file.path, file.size_bytes]));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Estimated content tokens for a single file. Prefers a byte-based estimate
|
|
25
|
+
* from `sizeIndex` (sourced from the repo manifest); falls back to the legacy
|
|
26
|
+
* line-based estimate when no positive byte count is available (e.g. manually
|
|
27
|
+
* built tasks in tests, or paths absent from the manifest).
|
|
28
|
+
*/
|
|
29
|
+
function pathContentTokens(owner, path, sizeIndex, lineIndex) {
|
|
30
|
+
const bytes = sizeIndex?.[path];
|
|
31
|
+
if (typeof bytes === "number" && bytes > 0) {
|
|
32
|
+
return estimateTokensFromBytes(bytes);
|
|
33
|
+
}
|
|
34
|
+
const lines = owner?.file_line_counts?.[path] ?? lineIndex?.[path] ?? 0;
|
|
35
|
+
return lines * ESTIMATED_TOKENS_PER_LINE;
|
|
36
|
+
}
|
|
37
|
+
/** Estimated content tokens for one task across all of its files. */
|
|
38
|
+
export function taskContentTokens(task, sizeIndex, lineIndex) {
|
|
39
|
+
return task.file_paths.reduce((sum, path) => sum + pathContentTokens(task, path, sizeIndex, lineIndex), 0);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Estimated content tokens across a set of file paths, resolving an owning task
|
|
43
|
+
* per path so the line fallback can read its `file_line_counts`. Shared files
|
|
44
|
+
* are counted once.
|
|
45
|
+
*/
|
|
46
|
+
export function fileGroupContentTokens(filePaths, tasks, sizeIndex, lineIndex) {
|
|
47
|
+
let total = 0;
|
|
48
|
+
for (const path of filePaths) {
|
|
49
|
+
const owner = tasks.find((task) => task.file_paths.includes(path));
|
|
50
|
+
total += pathContentTokens(owner, path, sizeIndex, lineIndex);
|
|
51
|
+
}
|
|
52
|
+
return total;
|
|
53
|
+
}
|
|
54
|
+
export function estimateTaskGroupTokens(tasks, sizeIndex, lineIndex) {
|
|
55
|
+
let contentTokens = 0;
|
|
56
|
+
for (const task of tasks) {
|
|
57
|
+
contentTokens += taskContentTokens(task, sizeIndex, lineIndex);
|
|
58
|
+
}
|
|
59
|
+
return ESTIMATED_PACKET_PROMPT_TOKENS + contentTokens;
|
|
60
|
+
}
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
import type { AuditTask } from "../types.js";
|
|
2
2
|
import type { AuditPlanMetrics, ReviewPacket } from "../types/reviewPlanning.js";
|
|
3
3
|
import type { GraphBundle, GraphEdge } from "@audit-tools/shared";
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* recorded during intake, so this never reads files. Review packet token
|
|
9
|
-
* estimates are derived from these bytes (Phase 2) instead of counted lines.
|
|
10
|
-
*/
|
|
11
|
-
export declare function sizeIndexFromManifest(repoManifest?: {
|
|
12
|
-
files: ReadonlyArray<{
|
|
13
|
-
path: string;
|
|
14
|
-
size_bytes: number;
|
|
15
|
-
}>;
|
|
16
|
-
}): Record<string, number>;
|
|
17
|
-
export declare function estimateTaskGroupTokens(tasks: AuditTask[], sizeIndex?: Record<string, number>, lineIndex?: Record<string, number>): number;
|
|
4
|
+
import { normalizeGraphPath } from "../extractors/graphPathUtils.js";
|
|
5
|
+
export { normalizeGraphPath };
|
|
6
|
+
import { ESTIMATED_TOKENS_PER_LINE, ESTIMATED_PACKET_PROMPT_TOKENS, sizeIndexFromManifest, estimateTaskGroupTokens } from "./reviewPacketSizing.js";
|
|
7
|
+
export { ESTIMATED_TOKENS_PER_LINE, ESTIMATED_PACKET_PROMPT_TOKENS, sizeIndexFromManifest, estimateTaskGroupTokens, };
|
|
18
8
|
/**
|
|
19
9
|
* Fan-in / fan-out degree above which a node is treated as a hub. Exported so
|
|
20
10
|
* the Phase 3 delta-scope expansion skips the same hubs that packet planning
|
|
@@ -40,7 +30,6 @@ export interface BuildReviewPacketOptions {
|
|
|
40
30
|
*/
|
|
41
31
|
maxContextTokens?: number;
|
|
42
32
|
}
|
|
43
|
-
export declare function normalizeGraphPath(path: string): string;
|
|
44
33
|
export declare function collectGraphEdges(graphBundle?: GraphBundle): GraphEdge[];
|
|
45
34
|
export declare function graphEdgeConfidence(edge: GraphEdge): number;
|
|
46
35
|
export interface GraphDegreeIndex {
|
|
@@ -1,63 +1,14 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { isRecord } from "@audit-tools/shared";
|
|
3
3
|
import { LENS_ORDER, priorityRank, sortLenses } from "./auditTaskUtils.js";
|
|
4
4
|
import { UnionFind } from "./unionFind.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
const DEFAULT_TARGET_PACKET_TOKENS = DEFAULT_TARGET_PACKET_LINES * ESTIMATED_TOKENS_PER_LINE;
|
|
13
|
-
/**
|
|
14
|
-
* Build a path → size_bytes index from a repo manifest. Byte counts are
|
|
15
|
-
* recorded during intake, so this never reads files. Review packet token
|
|
16
|
-
* estimates are derived from these bytes (Phase 2) instead of counted lines.
|
|
17
|
-
*/
|
|
18
|
-
export function sizeIndexFromManifest(repoManifest) {
|
|
19
|
-
if (!repoManifest)
|
|
20
|
-
return {};
|
|
21
|
-
return Object.fromEntries(repoManifest.files.map((file) => [file.path, file.size_bytes]));
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Estimated content tokens for a single file. Prefers a byte-based estimate
|
|
25
|
-
* from `sizeIndex` (sourced from the repo manifest); falls back to the legacy
|
|
26
|
-
* line-based estimate when no positive byte count is available (e.g. manually
|
|
27
|
-
* built tasks in tests, or paths absent from the manifest).
|
|
28
|
-
*/
|
|
29
|
-
function pathContentTokens(owner, path, sizeIndex, lineIndex) {
|
|
30
|
-
const bytes = sizeIndex?.[path];
|
|
31
|
-
if (typeof bytes === "number" && bytes > 0) {
|
|
32
|
-
return estimateTokensFromBytes(bytes);
|
|
33
|
-
}
|
|
34
|
-
const lines = owner?.file_line_counts?.[path] ?? lineIndex?.[path] ?? 0;
|
|
35
|
-
return lines * ESTIMATED_TOKENS_PER_LINE;
|
|
36
|
-
}
|
|
37
|
-
/** Estimated content tokens for one task across all of its files. */
|
|
38
|
-
function taskContentTokens(task, sizeIndex, lineIndex) {
|
|
39
|
-
return task.file_paths.reduce((sum, path) => sum + pathContentTokens(task, path, sizeIndex, lineIndex), 0);
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Estimated content tokens across a set of file paths, resolving an owning task
|
|
43
|
-
* per path so the line fallback can read its `file_line_counts`. Shared files
|
|
44
|
-
* are counted once.
|
|
45
|
-
*/
|
|
46
|
-
function fileGroupContentTokens(filePaths, tasks, sizeIndex, lineIndex) {
|
|
47
|
-
let total = 0;
|
|
48
|
-
for (const path of filePaths) {
|
|
49
|
-
const owner = tasks.find((task) => task.file_paths.includes(path));
|
|
50
|
-
total += pathContentTokens(owner, path, sizeIndex, lineIndex);
|
|
51
|
-
}
|
|
52
|
-
return total;
|
|
53
|
-
}
|
|
54
|
-
export function estimateTaskGroupTokens(tasks, sizeIndex, lineIndex) {
|
|
55
|
-
let contentTokens = 0;
|
|
56
|
-
for (const task of tasks) {
|
|
57
|
-
contentTokens += taskContentTokens(task, sizeIndex, lineIndex);
|
|
58
|
-
}
|
|
59
|
-
return ESTIMATED_PACKET_PROMPT_TOKENS + contentTokens;
|
|
60
|
-
}
|
|
5
|
+
import { normalizeGraphPath, isPackageManifestPath, isTypescriptProjectConfigPath, isGoModuleManifestPath, isCargoManifestPath, isMavenPomPath, } from "../extractors/graphPathUtils.js";
|
|
6
|
+
// Re-exported for scope.ts, which imports the canonical path normalizer here.
|
|
7
|
+
export { normalizeGraphPath };
|
|
8
|
+
import { DEFAULT_MAX_TASKS_PER_PACKET, DEFAULT_TARGET_PACKET_TOKENS, ESTIMATED_TOKENS_PER_LINE, ESTIMATED_PACKET_PROMPT_TOKENS, sizeIndexFromManifest, fileGroupContentTokens, taskContentTokens, estimateTaskGroupTokens, } from "./reviewPacketSizing.js";
|
|
9
|
+
// Sizing / token-budget arithmetic moved to reviewPacketSizing.ts; re-exported
|
|
10
|
+
// here for the modules that import it from reviewPackets.
|
|
11
|
+
export { ESTIMATED_TOKENS_PER_LINE, ESTIMATED_PACKET_PROMPT_TOKENS, sizeIndexFromManifest, estimateTaskGroupTokens, };
|
|
61
12
|
const PACKET_EXPANSION_MIN_CONFIDENCE = 0.65;
|
|
62
13
|
/**
|
|
63
14
|
* Fan-in / fan-out degree above which a node is treated as a hub. Exported so
|
|
@@ -132,9 +83,6 @@ function buildTaskGroups(tasks) {
|
|
|
132
83
|
}
|
|
133
84
|
return groups;
|
|
134
85
|
}
|
|
135
|
-
export function normalizeGraphPath(path) {
|
|
136
|
-
return path.replace(/\\/g, "/").replace(/^\.\//, "").toLowerCase();
|
|
137
|
-
}
|
|
138
86
|
export function collectGraphEdges(graphBundle) {
|
|
139
87
|
if (!graphBundle?.graphs) {
|
|
140
88
|
return [];
|
|
@@ -429,28 +377,11 @@ function buildSubsystemClusterEdges(groups, graphEdges, lineIndex, sizeIndex, ta
|
|
|
429
377
|
}
|
|
430
378
|
function packageManifestRoot(path) {
|
|
431
379
|
const segments = normalizeGraphPath(path).split("/").filter(Boolean);
|
|
432
|
-
if (
|
|
380
|
+
if (!isPackageManifestPath(path) || segments.length < 2) {
|
|
433
381
|
return undefined;
|
|
434
382
|
}
|
|
435
383
|
return segments.slice(0, -1).join("/");
|
|
436
384
|
}
|
|
437
|
-
function isTypescriptProjectConfigPath(path) {
|
|
438
|
-
const basename = normalizeGraphPath(path).split("/").at(-1);
|
|
439
|
-
if (!basename) {
|
|
440
|
-
return false;
|
|
441
|
-
}
|
|
442
|
-
return (basename === "tsconfig.json" ||
|
|
443
|
-
(basename.startsWith("tsconfig.") && basename.endsWith(".json")));
|
|
444
|
-
}
|
|
445
|
-
function isGoModuleManifestPath(path) {
|
|
446
|
-
return normalizeGraphPath(path).split("/").at(-1) === "go.mod";
|
|
447
|
-
}
|
|
448
|
-
function isCargoManifestPath(path) {
|
|
449
|
-
return normalizeGraphPath(path).split("/").at(-1) === "cargo.toml";
|
|
450
|
-
}
|
|
451
|
-
function isMavenPomPath(path) {
|
|
452
|
-
return normalizeGraphPath(path).split("/").at(-1) === "pom.xml";
|
|
453
|
-
}
|
|
454
385
|
function configFileRoot(path, predicate) {
|
|
455
386
|
const segments = normalizeGraphPath(path).split("/").filter(Boolean);
|
|
456
387
|
if (!predicate(path) || segments.length < 2) {
|
package/dist/orchestrator.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isLens } from "./types.js";
|
|
1
2
|
const DEFAULT_LENS_ORDER = [
|
|
2
3
|
"correctness",
|
|
3
4
|
"architecture",
|
|
@@ -10,13 +11,9 @@ const DEFAULT_LENS_ORDER = [
|
|
|
10
11
|
"operability",
|
|
11
12
|
"config_deployment",
|
|
12
13
|
];
|
|
13
|
-
const VALID_LENSES = new Set(DEFAULT_LENS_ORDER);
|
|
14
14
|
function isRecord(value) {
|
|
15
15
|
return value !== null && typeof value === "object";
|
|
16
16
|
}
|
|
17
|
-
function isLens(value) {
|
|
18
|
-
return typeof value === "string" && VALID_LENSES.has(value);
|
|
19
|
-
}
|
|
20
17
|
function assertStringArray(value, label) {
|
|
21
18
|
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
22
19
|
throw new TypeError(`${label} must be an array of strings.`);
|
package/dist/quota/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ResolvedLimits as _ResolvedLimits, LimitConfidence as _LimitConfidence, LimitSource as _LimitSource, HostConcurrencyLimit as _HostConcurrencyLimit, QuotaUsageSnapshot as _QuotaUsageSnapshot, BackoffState as _BackoffState } from "@audit-tools/shared";
|
|
2
2
|
export { resolveLimits, lookupKnownModel, classifyProvider, readQuotaState, writeQuotaState, computeMaxSafeConcurrency, recordWaveOutcome, getQuotaStatePath, decayWeight, applyDecayToEntry, computeBackoffCooldownMs, computeBackoffFailureWeight, computeRampUpConcurrency, setQuotaStateDir, detectRateLimitError, computeCooldownUntil, acquireLock, releaseLock, withFileLock, FileLockTimeoutError, runSlidingWindow, LearnedQuotaSource, CompositeQuotaSource, GenericErrorParser, ClaudeCodeErrorParser, getErrorParserForProvider, } from "@audit-tools/shared";
|
|
3
3
|
export type { LimitResolutionResult, ResolveLimitsOptions, ProviderType, ResolvedLimits, LimitSource, LimitConfidence, HostConcurrencyLimit, HostConcurrencyLimitSource, QuotaState, QuotaStateEntry, ConcurrencyBucket, WaveSchedule, BackoffState, ObservedWaveOutcome, RateLimitDetectionResult, SlidingWindowResult, QuotaSource, QuotaUsageSnapshot, ErrorParser, } from "@audit-tools/shared";
|
|
4
|
-
export { scheduleWave, buildProviderModelKey } from "@audit-tools/shared";
|
|
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
7
|
export { probeProvider } from "./probe.js";
|
package/dist/quota/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export { resolveLimits, lookupKnownModel, classifyProvider, readQuotaState, writ
|
|
|
3
3
|
// Wave scheduler now lives in @audit-tools/shared (single source of truth for
|
|
4
4
|
// both orchestrators). Auditor passes its discovered-limits via the structural
|
|
5
5
|
// DiscoveredRateLimitsInput the shared scheduler accepts.
|
|
6
|
-
export { scheduleWave, buildProviderModelKey } from "@audit-tools/shared";
|
|
6
|
+
export { scheduleWave, buildProviderModelKey, resolveHostModel } from "@audit-tools/shared";
|
|
7
7
|
// Auditor-specific: probe, discovered limits, header extraction
|
|
8
8
|
export { detectHostActiveSubagentLimit, resolveHostActiveSubagentLimit, } from "./hostLimits.js";
|
|
9
9
|
export { probeProvider } from "./probe.js";
|
|
@@ -23,10 +23,8 @@ export interface WorkerTask {
|
|
|
23
23
|
runtime_updates_path?: string;
|
|
24
24
|
external_analyzer_results_path?: string;
|
|
25
25
|
worker_command_mode?: WorkerCommandMode;
|
|
26
|
-
/** @deprecated Prefer worker_command_mode: "deferred" for new task files. */
|
|
27
|
-
skip_worker_command?: boolean;
|
|
28
26
|
timeout_ms?: number;
|
|
29
27
|
max_retries?: number;
|
|
30
28
|
access?: AccessDeclaration;
|
|
31
29
|
}
|
|
32
|
-
export declare function usesDeferredWorkerCommand(task: Pick<WorkerTask, "worker_command_mode"
|
|
30
|
+
export declare function usesDeferredWorkerCommand(task: Pick<WorkerTask, "worker_command_mode">): boolean;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { Finding as SharedFinding } from "@audit-tools/shared";
|
|
2
2
|
export type Lens = "correctness" | "architecture" | "maintainability" | "security" | "reliability" | "performance" | "data_integrity" | "tests" | "operability" | "config_deployment" | "observability";
|
|
3
|
+
/** Canonical list of every valid {@link Lens}. Single source of truth — import
|
|
4
|
+
* {@link isLens} / `ALL_LENSES` instead of hand-copying lens lists into local
|
|
5
|
+
* guards, which drift (a copy omitting "observability" caused it to be wrongly
|
|
6
|
+
* rejected in flow requeue). */
|
|
7
|
+
export declare const ALL_LENSES: readonly Lens[];
|
|
8
|
+
export declare function isLens(value: unknown): value is Lens;
|
|
3
9
|
export interface FileRecord {
|
|
4
10
|
path: string;
|
|
5
11
|
language: string;
|
package/dist/types.js
CHANGED
|
@@ -1 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
/** Canonical list of every valid {@link Lens}. Single source of truth — import
|
|
2
|
+
* {@link isLens} / `ALL_LENSES` instead of hand-copying lens lists into local
|
|
3
|
+
* guards, which drift (a copy omitting "observability" caused it to be wrongly
|
|
4
|
+
* rejected in flow requeue). */
|
|
5
|
+
export const ALL_LENSES = [
|
|
6
|
+
"correctness",
|
|
7
|
+
"architecture",
|
|
8
|
+
"maintainability",
|
|
9
|
+
"security",
|
|
10
|
+
"reliability",
|
|
11
|
+
"performance",
|
|
12
|
+
"data_integrity",
|
|
13
|
+
"tests",
|
|
14
|
+
"operability",
|
|
15
|
+
"config_deployment",
|
|
16
|
+
"observability",
|
|
17
|
+
];
|
|
18
|
+
export function isLens(value) {
|
|
19
|
+
return (typeof value === "string" && ALL_LENSES.includes(value));
|
|
20
|
+
}
|