auditor-lambda 0.6.6 → 0.6.8

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.
@@ -59,6 +59,25 @@ function nodeExecutable() {
59
59
  return process.execPath;
60
60
  }
61
61
 
62
+ // When the wrapper runs from a source checkout (its package dir is NOT inside a
63
+ // node_modules tree), generated continuation commands should re-invoke THIS
64
+ // wrapper via `node <path>` so a dogfooded monorepo run stays pinned to local
65
+ // code instead of silently falling back to a globally-installed `audit-code`
66
+ // bin. Installed copies leave the hint unset so the dist CLI keeps emitting the
67
+ // `audit-code` bin. Returned as an env fragment scoped to the spawned child so
68
+ // it never leaks into the parent process (e.g. the test runner).
69
+ function selfInvocationEnv() {
70
+ if (process.env.AUDIT_CODE_INVOCATION) {
71
+ return { AUDIT_CODE_INVOCATION: process.env.AUDIT_CODE_INVOCATION };
72
+ }
73
+ if (/[\\/]node_modules[\\/]/.test(repoRoot)) {
74
+ return {};
75
+ }
76
+ return {
77
+ AUDIT_CODE_INVOCATION: JSON.stringify(['node', join(repoRoot, 'audit-code.mjs')]),
78
+ };
79
+ }
80
+
62
81
  function quoteForCmd(arg) {
63
82
  if (arg.length === 0) return '""';
64
83
  if (!/[\s"]/u.test(arg)) return arg;
@@ -82,7 +101,7 @@ function run(command, args, options = {}) {
82
101
  const child = spawn(resolved.command, resolved.args, {
83
102
  cwd: repoRoot,
84
103
  stdio: options.capture ? ['ignore', 'pipe', 'pipe'] : 'inherit',
85
- env: process.env
104
+ env: options.env ?? process.env
86
105
  });
87
106
 
88
107
  let stdout = '';
@@ -1008,6 +1027,13 @@ function renderSharedMcpLauncher(sourcePackageRoot) {
1008
1027
  'const scriptDir = dirname(fileURLToPath(import.meta.url));',
1009
1028
  "const repoRoot = resolve(scriptDir, '..', '..');",
1010
1029
  "const artifactsDir = join(repoRoot, '.audit-artifacts');",
1030
+ '// Absolute path to the auditor-lambda package that generated this launcher.',
1031
+ '// Intentionally machine-specific: it is a load-bearing fallback candidate',
1032
+ '// (tried after a repo-local dependency) that lets the launcher find the',
1033
+ '// backend entrypoint when the target repo has no local auditor-lambda dep.',
1034
+ '// This file is regenerated per-install by `audit-code ensure`/`install` and',
1035
+ '// is gitignored, so the path is always re-derived for the current machine;',
1036
+ '// if it ever goes stale the launcher degrades gracefully to PATH/npx below.',
1011
1037
  `const sourcePackageRoot = ${JSON.stringify(sourcePackageRoot)};`,
1012
1038
  '',
1013
1039
  'async function exists(path) {',
@@ -2767,7 +2793,9 @@ async function runDistCommand(commandName, argv, { ensureArtifactsDir = false }
2767
2793
  }
2768
2794
 
2769
2795
  await ensureBuilt();
2770
- await run(nodeExecutable(), [distEntry, commandName, ...commandArgs]);
2796
+ await run(nodeExecutable(), [distEntry, commandName, ...commandArgs], {
2797
+ env: { ...process.env, ...selfInvocationEnv() },
2798
+ });
2771
2799
  }
2772
2800
 
2773
2801
  async function runDistCommandInline(commandName, argv) {
@@ -2781,6 +2809,12 @@ async function runDistCommandInline(commandName, argv) {
2781
2809
  await mkdir(artifactsDir, { recursive: true });
2782
2810
  await ensureBuilt();
2783
2811
 
2812
+ // Propagate the invocation hint into this (long-lived) server process so it
2813
+ // and the wrapper subprocesses it spawns emit continuation commands that
2814
+ // match how the backend was launched. Safe here: this path is only the `mcp`
2815
+ // server, not a shared/test process.
2816
+ Object.assign(process.env, selfInvocationEnv());
2817
+
2784
2818
  // Import the module that exports runCli (dist/cli.js). dist/index.js has no
2785
2819
  // exports — it is the bare entrypoint that runs `runCli(process.argv)` as an
2786
2820
  // import side effect — so importing it here both fails to provide runCli and
@@ -41,6 +41,8 @@ export interface PrepareDispatchResult {
41
41
  packet_count: number;
42
42
  task_count: number;
43
43
  skipped_task_count: number;
44
+ /** Subagent parallelism resolved for this dispatch run. */
45
+ wave_size: number;
44
46
  largest_packet: {
45
47
  packet_id: string;
46
48
  total_lines: number;
@@ -533,6 +533,7 @@ export async function prepareDispatchArtifacts(params) {
533
533
  packet_count: plan.length,
534
534
  task_count: orderedTasks.length,
535
535
  skipped_task_count: priorResultTaskIds.size,
536
+ wave_size: waveSchedule.wave_size,
536
537
  largest_packet: largestPacketId
537
538
  ? {
538
539
  packet_id: largestPacketId,
@@ -1,7 +1,32 @@
1
1
  import { renderCommand } from "./args.js";
2
+ /**
3
+ * Token prefix the host should use to re-invoke the backend in generated
4
+ * continuation commands. Defaults to the `audit-code` bin (correct for an
5
+ * installed global). The wrapper sets `AUDIT_CODE_INVOCATION` to e.g.
6
+ * `["node","<path>/audit-code.mjs"]` when it runs from a source checkout, so a
7
+ * dogfooded monorepo run keeps generated commands pinned to local code instead
8
+ * of silently falling back to a globally-installed `audit-code`.
9
+ */
10
+ function cliInvocationTokens() {
11
+ const raw = process.env.AUDIT_CODE_INVOCATION;
12
+ if (raw) {
13
+ try {
14
+ const parsed = JSON.parse(raw);
15
+ if (Array.isArray(parsed) &&
16
+ parsed.length > 0 &&
17
+ parsed.every((t) => typeof t === "string" && t.length > 0)) {
18
+ return parsed;
19
+ }
20
+ }
21
+ catch {
22
+ // malformed override — fall back to the default bin
23
+ }
24
+ }
25
+ return ["audit-code"];
26
+ }
2
27
  export function nextStepCommand(root, artifactsDir) {
3
28
  return renderCommand([
4
- "audit-code",
29
+ ...cliInvocationTokens(),
5
30
  "next-step",
6
31
  "--root",
7
32
  root,
@@ -11,7 +36,7 @@ export function nextStepCommand(root, artifactsDir) {
11
36
  }
12
37
  export function mergeAndIngestCommand(artifactsDir, runId) {
13
38
  return renderCommand([
14
- "audit-code",
39
+ ...cliInvocationTokens(),
15
40
  "merge-and-ingest",
16
41
  "--artifacts-dir",
17
42
  artifactsDir,
@@ -22,9 +47,12 @@ export function mergeAndIngestCommand(artifactsDir, runId) {
22
47
  export function renderDispatchReviewPrompt(params) {
23
48
  const mergeCommand = mergeAndIngestCommand(params.artifactsDir, params.activeReviewRun.run_id);
24
49
  const continueCommand = nextStepCommand(params.root, params.artifactsDir);
50
+ // Only mention model_hint when the host can actually act on it. When it
51
+ // cannot, the field is left as inert plan metadata rather than surfacing a
52
+ // contradictory "here is model_hint, now ignore it" instruction.
25
53
  const modelLine = params.hostCanSelectSubagentModel
26
54
  ? "When launching each subagent, map `entry.model_hint.tier` (`small`, `standard`, `deep`) to an available host model without asking the user for model names."
27
- : "Ignore `entry.model_hint`; this host did not report per-subagent model selection.";
55
+ : null;
28
56
  const toolsLine = params.hostCanRestrictSubagentTools
29
57
  ? "Restrict review subagents to read/search plus the packet submit command named in their prompt. Do not give them source edit/write tools."
30
58
  : "Do not ask the user about per-subagent tool restrictions; this host did not report a callable restriction facility.";
@@ -59,7 +87,7 @@ export function renderDispatchReviewPrompt(params) {
59
87
  "",
60
88
  ' Read and follow the audit instructions in: <entry.prompt_path>',
61
89
  "",
62
- modelLine,
90
+ ...(modelLine ? [modelLine] : []),
63
91
  toolsLine,
64
92
  "",
65
93
  "Each subagent must submit its packet through the submit command printed in its packet prompt and stop after successful submission.",
@@ -2,13 +2,39 @@ import type { StepStatus } from "@audit-tools/shared";
2
2
  import type { AccessDeclaration } from "../types/workerSession.js";
3
3
  export declare const STEP_CONTRACT_VERSION = "audit-code-step/v1alpha1";
4
4
  export type StepKind = "dispatch_review" | "single_task_fallback" | "design_review" | "analyzer_install" | "edge_reasoning" | "edge_reasoning_dispatch" | "synthesis_narrative" | "present_report" | "blocked";
5
+ /**
6
+ * Lightweight run-level orientation surfaced in the step contract so a host
7
+ * resuming an in-flight audit knows where it stands without reading artifacts.
8
+ */
9
+ export interface StepProgress {
10
+ /** One-line, human-readable summary safe to show a resuming host. */
11
+ summary: string;
12
+ /** Pending review packets in the active dispatch run, when applicable. */
13
+ pending_packets?: number;
14
+ /** Audit tasks covered by the pending packets. */
15
+ pending_tasks?: number;
16
+ /** Audit tasks already completed before this run (skipped as done). */
17
+ completed_tasks?: number;
18
+ /** Subagent parallelism resolved for this dispatch run. */
19
+ wave_size?: number;
20
+ }
5
21
  export interface StepArtifact {
6
22
  contract_version: typeof STEP_CONTRACT_VERSION;
7
23
  step_kind: StepKind;
8
24
  prompt_path: string;
9
25
  status: StepStatus;
10
26
  run_id: string | null;
27
+ /** Run-level orientation; omitted for steps that have no meaningful summary. */
28
+ progress?: StepProgress;
29
+ /** Shell commands the host may run for this step. */
11
30
  allowed_commands: string[];
31
+ /**
32
+ * MCP tool names equivalent to `allowed_commands`, for hosts driving the
33
+ * backend through the MCP adapter. Omitted when the step has no MCP
34
+ * equivalents, so a shell host never has to guess which list entries are
35
+ * tool names versus runnable commands.
36
+ */
37
+ allowed_mcp_tools?: string[];
12
38
  stop_condition: string;
13
39
  repo_root: string;
14
40
  artifacts_dir: string;
@@ -21,6 +47,8 @@ export declare function writeCurrentStep(params: {
21
47
  status: StepStatus;
22
48
  runId: string | null;
23
49
  allowedCommands: string[];
50
+ allowedMcpTools?: string[];
51
+ progress?: StepProgress;
24
52
  stopCondition: string;
25
53
  repoRoot: string;
26
54
  artifactPaths: Record<string, string | null>;
package/dist/cli/steps.js CHANGED
@@ -14,7 +14,11 @@ export async function writeCurrentStep(params) {
14
14
  prompt_path: promptPath,
15
15
  status: params.status,
16
16
  run_id: params.runId,
17
+ ...(params.progress ? { progress: params.progress } : {}),
17
18
  allowed_commands: params.allowedCommands,
19
+ ...(params.allowedMcpTools && params.allowedMcpTools.length > 0
20
+ ? { allowed_mcp_tools: params.allowedMcpTools }
21
+ : {}),
18
22
  stop_condition: params.stopCondition,
19
23
  repo_root: params.repoRoot,
20
24
  artifacts_dir: params.artifactsDir,
package/dist/cli.js CHANGED
@@ -935,12 +935,19 @@ async function renderSemanticReviewStep(params) {
935
935
  stepKind: "dispatch_review",
936
936
  status: "ready",
937
937
  runId: activeReviewRun.run_id,
938
- allowedCommands: [
939
- "auditor_merge_and_ingest",
940
- "auditor_continue_audit",
941
- mergeCommand,
942
- continueCommand,
943
- ],
938
+ allowedCommands: [mergeCommand, continueCommand],
939
+ allowedMcpTools: ["auditor_merge_and_ingest", "auditor_continue_audit"],
940
+ progress: {
941
+ summary: `Dispatching ${dispatch.packet_count} review packet(s) covering ` +
942
+ `${dispatch.task_count} task(s) in waves of ${dispatch.wave_size}` +
943
+ (dispatch.skipped_task_count > 0
944
+ ? `; ${dispatch.skipped_task_count} task(s) already completed.`
945
+ : "."),
946
+ pending_packets: dispatch.packet_count,
947
+ pending_tasks: dispatch.task_count,
948
+ completed_tasks: dispatch.skipped_task_count,
949
+ wave_size: dispatch.wave_size,
950
+ },
944
951
  stopCondition: "Dispatch every packet, run merge-and-ingest once, then run next-step.",
945
952
  repoRoot: root,
946
953
  artifactPaths: {
@@ -87,6 +87,14 @@ export function scheduleWave(options) {
87
87
  : computeMaxSafeConcurrency(quotaStateEntry, halfLifeHours);
88
88
  waveSize = Math.min(waveSize, learnedCap);
89
89
  }
90
+ else if (hostConcurrencyLimit !== null) {
91
+ // The host explicitly reported its active-subagent capacity. That is a
92
+ // real concurrency signal, so it supersedes the conservative
93
+ // unknown-provider fallback (which exists only when we have no signal at
94
+ // all). Leaving waveSize untouched here lets applyHostConcurrencyLimit()
95
+ // below enforce the reported limit as the hard ceiling, while any RPM/TPM
96
+ // caps applied above still bind.
97
+ }
90
98
  else {
91
99
  const providerType = classifyProvider(providerName);
92
100
  const fallbackCap = providerType === "local"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auditor-lambda",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
4
4
  "private": false,
5
5
  "description": "Portable hybrid code-auditing framework for arbitrary repositories.",
6
6
  "type": "module",
@@ -25,16 +25,22 @@ monorepo root). From the monorepo root use:
25
25
  node packages/audit-code/audit-code.mjs ensure --quiet
26
26
  ```
27
27
 
28
- Then ask the backend for exactly one next step:
28
+ Then ask the backend for exactly one next step. This host can dispatch review
29
+ subagents in parallel (via the `Agent`/`task` tool), so report that capacity on
30
+ every `next-step` call — otherwise the backend assumes it cannot parallelize and
31
+ sizes dispatch waves to one packet at a time:
29
32
 
30
33
  ```bash
31
- audit-code next-step
34
+ audit-code next-step --host-max-active-subagents 4
32
35
  ```
33
36
 
37
+ `4` is a safe default for this host; raise it for more parallelism or lower it
38
+ under rate-limit pressure. The backend's learned quota adapts from there.
39
+
34
40
  When developing `auditor-lambda` itself, from the monorepo root use:
35
41
 
36
42
  ```bash
37
- node packages/audit-code/audit-code.mjs next-step
43
+ node packages/audit-code/audit-code.mjs next-step --host-max-active-subagents 4
38
44
  ```
39
45
 
40
46
  Read the returned JSON only far enough to find `prompt_path`, then read and
@@ -47,7 +53,8 @@ Use MCP tools only as a compatibility adapter when direct shell access to
47
53
  `continue_audit` tools return the same one-step contract; they are not a
48
54
  separate orchestration path.
49
55
 
50
- When a step prompt tells you to continue, run `audit-code next-step` again and
51
- follow only the newly returned `prompt_path`.
56
+ When a step prompt tells you to continue, run
57
+ `audit-code next-step --host-max-active-subagents 4` again and follow only the
58
+ newly returned `prompt_path`.
52
59
 
53
60
  Stop when the current step prompt tells you to stop.