auditor-lambda 0.6.4 → 0.6.5

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.
@@ -1,4 +1,4 @@
1
- import type { SessionConfig, DispatchModelHint } from "@audit-tools/shared";
1
+ import type { ProviderRateLimits, SessionConfig, DispatchModelHint } from "@audit-tools/shared";
2
2
  import type { ArtifactBundle } from "../io/artifacts.js";
3
3
  import type { AuditTask } from "../types.js";
4
4
  export declare const LARGE_FILE_PACKET_TARGET_LINES = 2500;
@@ -76,5 +76,6 @@ export declare function prepareDispatchArtifacts(params: {
76
76
  root?: string;
77
77
  sessionConfig?: SessionConfig;
78
78
  hostModel?: string | null;
79
+ queryLimits?: (model: string | null) => Promise<ProviderRateLimits | null>;
79
80
  hostActiveSubagentLimit?: number | null;
80
81
  }): Promise<PrepareDispatchResult>;
@@ -2,12 +2,13 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { existsSync } from "node:fs";
3
3
  import { isAbsolute, join, relative, resolve } from "node:path";
4
4
  import { isFileMissingError, readJsonFile, writeJsonFile } from "@audit-tools/shared";
5
+ import { buildQuotaSource } from "@audit-tools/shared/quota/compositeQuotaSource";
5
6
  import { loadArtifactBundle } from "../io/artifacts.js";
6
7
  import { orderTasksForPacketReview, buildReviewPackets, sizeIndexFromManifest, } from "../orchestrator/reviewPackets.js";
7
8
  import { buildFileAnchorSummary } from "../orchestrator/fileAnchors.js";
8
9
  import { resolveFreshSessionProviderName } from "../providers/index.js";
9
10
  import { loadSessionConfig } from "../supervisor/sessionConfig.js";
10
- import { scheduleWave, buildProviderModelKey, readQuotaState, resolveHostActiveSubagentLimit, lookupDiscoveredLimits, } from "../quota/index.js";
11
+ import { scheduleWave, buildProviderModelKey, readQuotaState, resolveHostActiveSubagentLimit, lookupDiscoveredLimits, mergeDiscoveredLimits, } from "../quota/index.js";
11
12
  import { taskResultPath, packetPromptPath, artifactNameForId, toBase64Url, fromBase64Url, getFlag, } from "./args.js";
12
13
  export const LARGE_FILE_PACKET_TARGET_LINES = 2500;
13
14
  export const SMALL_MODEL_HINT_MAX_LINES = 500;
@@ -462,7 +463,15 @@ export async function prepareDispatchArtifacts(params) {
462
463
  explicitLimit: params.hostActiveSubagentLimit,
463
464
  sessionConfig,
464
465
  });
466
+ const providerLimits = await params.queryLimits?.(hostModel)
467
+ .then((r) => r ? { ...r, source: "provider_query" } : null)
468
+ .catch(() => null)
469
+ ?? null;
465
470
  const dispatchCachedLimits = await lookupDiscoveredLimits(quotaProviderKey).catch(() => null);
471
+ const discoveredLimits = mergeDiscoveredLimits(providerLimits, dispatchCachedLimits);
472
+ const halfLifeHours = sessionConfig.quota?.empirical_half_life_hours ?? 24;
473
+ const quotaSource = buildQuotaSource({ halfLifeHours });
474
+ const quotaSourceSnapshot = await quotaSource.queryCurrentUsage(quotaProviderKey).catch(() => null);
466
475
  const waveSchedule = scheduleWave({
467
476
  providerName: quotaProviderName,
468
477
  sessionConfig,
@@ -471,7 +480,8 @@ export async function prepareDispatchArtifacts(params) {
471
480
  estimatedSlotTokens: perPacketTokens,
472
481
  quotaStateEntry,
473
482
  hostConcurrencyLimit,
474
- discoveredLimits: dispatchCachedLimits,
483
+ discoveredLimits,
484
+ quotaSourceSnapshot,
475
485
  });
476
486
  const dispatchQuota = {
477
487
  contract_version: "audit-code-dispatch-quota/v1alpha2",
package/dist/cli.js CHANGED
@@ -12,6 +12,7 @@ import { buildRuntimeValidationTasks, } from "./orchestrator/runtimeValidation.j
12
12
  import { initializeCoverageFromPlan } from "./orchestrator/planning.js";
13
13
  import { loadArtifactBundle, writeCoreArtifacts, promoteFinalAuditReport, AUDIT_REPORT_FILENAME, } from "./io/artifacts.js";
14
14
  import { isFileMissingError, readJsonFile, writeJsonFile, prefixValidationIssues, RunLogger } from "@audit-tools/shared";
15
+ import { buildQuotaSource } from "@audit-tools/shared/quota/compositeQuotaSource";
15
16
  import { validateArtifactBundle } from "./validation/artifacts.js";
16
17
  import { validateAuditResults, formatAuditResultIssues, } from "./validation/auditResults.js";
17
18
  import { validateConfiguredProviderEnvironment, validateSessionConfig, } from "./validation/sessionConfig.js";
@@ -35,7 +36,7 @@ import { renderWorkerPrompt } from "./prompts/renderWorkerPrompt.js";
35
36
  import { estimateTaskGroupTokens, sizeIndexFromManifest, } from "./orchestrator/reviewPackets.js";
36
37
  import { LOCAL_SUBPROCESS_PROVIDER_NAME } from "./providers/constants.js";
37
38
  import { runAuditCodeMcpServer } from "./mcp/server.js";
38
- import { scheduleWave, buildProviderModelKey, readQuotaState, recordWaveOutcome, resolveLimits, resolveHostActiveSubagentLimit, probeProvider, computeMaxSafeConcurrency, getQuotaStatePath, detectRateLimitError, computeCooldownUntil, runSlidingWindow, LearnedQuotaSource, CompositeQuotaSource, lookupDiscoveredLimits, updateDiscoveredLimits, mergeDiscoveredLimits, getHeaderExtractorForProvider, setQuotaStateDir, } from "./quota/index.js";
39
+ import { scheduleWave, buildProviderModelKey, readQuotaState, recordWaveOutcome, resolveLimits, resolveHostActiveSubagentLimit, probeProvider, computeMaxSafeConcurrency, getQuotaStatePath, detectRateLimitError, computeCooldownUntil, runSlidingWindow, lookupDiscoveredLimits, updateDiscoveredLimits, mergeDiscoveredLimits, getHeaderExtractorForProvider, setQuotaStateDir, } from "./quota/index.js";
39
40
  // Re-exports from extracted modules
40
41
  export { resolveHostDispatchCapability, DIRECT_CLI_DEFAULTS, getFlag, hasFlag, getOptionalBooleanFlag, getArtifactsDir, getRootDir, getBatchResultsDir, getMaxRuns, getAgentBatchSize, getParallelWorkers, getTimeoutMs, chunkArray, getUiMode, looksLikeCliFlag, countLines, warnIfNotGitRepo, } from "./cli/args.js";
41
42
  import { DIRECT_CLI_DEFAULTS, getFlag, hasFlag, getOptionalBooleanFlag, fromBase64Url, renderCommand, summarizeLaunchExit, taskResultPath, readStdinText, getArtifactsDir, getRootDir, warnIfNotGitRepo, getBatchResultsDir, getMaxRuns, getAgentBatchSize, getParallelWorkers, getTimeoutMs, getExplicitProvider, getHostModel, getHostMaxActiveSubagents, getQuotaProbeMode, resolveRunProviderName, chunkArray, getUiMode, looksLikeCliFlag, resolveHostDispatchCapability, countLines, listBatchResultFiles, } from "./cli/args.js";
@@ -915,11 +916,16 @@ async function renderSemanticReviewStep(params) {
915
916
  },
916
917
  });
917
918
  }
919
+ const sessionConfig = await loadSessionConfig(artifactsDir).catch(() => ({}));
920
+ const provider = createFreshSessionProvider(undefined, sessionConfig);
918
921
  const dispatch = await prepareDispatchArtifacts({
919
922
  packageRoot,
920
923
  runId: activeReviewRun.run_id,
921
924
  artifactsDir,
922
925
  root,
926
+ sessionConfig,
927
+ hostModel: sessionConfig.block_quota?.host_model ?? null,
928
+ queryLimits: provider.queryLimits?.bind(provider),
923
929
  hostActiveSubagentLimit: params.hostMaxActiveSubagents,
924
930
  });
925
931
  const mergeCommand = mergeAndIngestCommand(artifactsDir, activeReviewRun.run_id);
@@ -1473,7 +1479,7 @@ async function cmdRunToCompletion(argv) {
1473
1479
  const cachedLimits = await lookupDiscoveredLimits(providerModelKey).catch(() => null);
1474
1480
  const discoveredLimits = mergeDiscoveredLimits(providerLimits, cachedLimits);
1475
1481
  const halfLifeHours = sessionConfig.quota?.empirical_half_life_hours ?? 24;
1476
- const quotaSource = new CompositeQuotaSource([new LearnedQuotaSource(halfLifeHours)]);
1482
+ const quotaSource = buildQuotaSource({ halfLifeHours });
1477
1483
  const quotaSourceSnapshot = await quotaSource.queryCurrentUsage(providerModelKey).catch(() => null);
1478
1484
  const hostConcurrencyLimit = resolveHostActiveSubagentLimit({
1479
1485
  sessionConfig,
@@ -2155,12 +2161,18 @@ async function cmdPrepareDispatch(argv) {
2155
2161
  const runId = getFlag(argv, "--run-id");
2156
2162
  if (!runId)
2157
2163
  throw new Error("prepare-dispatch requires --run-id <run_id>");
2164
+ const artifactsDir = getArtifactsDir(argv);
2165
+ const sessionConfig = await loadSessionConfig(artifactsDir).catch(() => ({}));
2166
+ const provider = createFreshSessionProvider(getExplicitProvider(argv), sessionConfig);
2167
+ const hostModel = getHostModel(argv) ?? sessionConfig.block_quota?.host_model ?? null;
2158
2168
  const result = await prepareDispatchArtifacts({
2159
2169
  packageRoot,
2160
2170
  runId,
2161
- artifactsDir: getArtifactsDir(argv),
2171
+ artifactsDir,
2162
2172
  root: getFlag(argv, "--root") ? getRootDir(argv) : undefined,
2163
- hostModel: getHostModel(argv),
2173
+ sessionConfig,
2174
+ hostModel,
2175
+ queryLimits: provider.queryLimits?.bind(provider),
2164
2176
  hostActiveSubagentLimit: getHostMaxActiveSubagents(argv),
2165
2177
  });
2166
2178
  console.log(JSON.stringify(result, null, 2));
@@ -2958,7 +2970,7 @@ async function cmdQuota(argv) {
2958
2970
  explicitLimit: getHostMaxActiveSubagents(argv),
2959
2971
  sessionConfig,
2960
2972
  });
2961
- const quotaSource = new CompositeQuotaSource([new LearnedQuotaSource(halfLifeHours)]);
2973
+ const quotaSource = buildQuotaSource({ halfLifeHours });
2962
2974
  const quotaSourceSnapshot = await quotaSource.queryCurrentUsage(providerModelKey).catch(() => null);
2963
2975
  const queryDiscoveredLimits = await lookupDiscoveredLimits(providerModelKey).catch(() => null);
2964
2976
  const waveSchedule = scheduleWave({
@@ -22,8 +22,11 @@ function commandExists(command) {
22
22
  return result.status === 0;
23
23
  }
24
24
  export function resolveFreshSessionProviderName(name, sessionConfig = {}, options = {}) {
25
- const requestedProvider = name ?? sessionConfig.provider ?? "local-subprocess";
26
- if (requestedProvider !== "auto") {
25
+ const requestedProvider = name ?? sessionConfig.provider;
26
+ const shouldAutoDetect = requestedProvider === undefined ||
27
+ requestedProvider === "auto" ||
28
+ (name === undefined && requestedProvider === "local-subprocess");
29
+ if (!shouldAutoDetect) {
27
30
  return requestedProvider;
28
31
  }
29
32
  const env = options.env ?? process.env;
@@ -65,8 +68,12 @@ export function resolveFreshSessionProviderName(name, sessionConfig = {}, option
65
68
  export function createFreshSessionProvider(name, sessionConfig = {}) {
66
69
  const providerName = resolveFreshSessionProviderName(name, sessionConfig);
67
70
  const opentoken = sessionConfig.opentoken ?? {};
71
+ const requestedProvider = name ?? sessionConfig.provider;
72
+ const autoDetectionRequested = requestedProvider === undefined ||
73
+ requestedProvider === "auto" ||
74
+ (name === undefined && requestedProvider === "local-subprocess");
68
75
  if (providerName === "local-subprocess" &&
69
- (name ?? sessionConfig.provider) === "auto") {
76
+ autoDetectionRequested) {
70
77
  process.stderr.write("audit-code: auto provider resolved to local-subprocess — no capable agent provider detected. " +
71
78
  "Agent tasks will require manual dispatch. Configure claude-code, opencode, or subprocess-template " +
72
79
  "in session-config.json to automate them.\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auditor-lambda",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "private": false,
5
5
  "description": "Portable hybrid code-auditing framework for arbitrary repositories.",
6
6
  "type": "module",
@@ -5,6 +5,7 @@ import { mkdirSync, existsSync, readFileSync, writeFileSync } from 'fs';
5
5
  import { fileURLToPath } from 'url';
6
6
 
7
7
  const pkgRoot = dirname(dirname(fileURLToPath(import.meta.url)));
8
+ const packageVersion = JSON.parse(readFileSync(join(pkgRoot, 'package.json'), 'utf8')).version ?? '0.0.0';
8
9
  const promptSourceFile = join(pkgRoot, 'skills', 'audit-code', 'audit-code.prompt.md');
9
10
  const skillSourceFile = join(pkgRoot, 'skills', 'audit-code', 'SKILL.md');
10
11
  const codexOpenAiAgentSourceFile = join(pkgRoot, 'skills', 'audit-code', 'agents', 'openai.yaml');
@@ -330,6 +331,10 @@ function mergeOpenCodeGlobalConfig(existing) {
330
331
  };
331
332
  }
332
333
 
334
+ function claudePluginExternalDir() {
335
+ return join(homedir(), '.claude', 'plugins', 'marketplaces', 'claude-plugins-official', 'external_plugins', 'audit-code');
336
+ }
337
+
333
338
  function claudeDesktopConfigPath() {
334
339
  if (process.platform === 'win32') {
335
340
  return join(process.env.APPDATA || join(homedir(), 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
@@ -459,6 +464,44 @@ try {
459
464
  console.warn(`audit-code: could not install Antigravity plugin (${err.message})`);
460
465
  }
461
466
 
467
+ // Install Claude Desktop plugin so /audit-code appears in the slash-command menu
468
+ // Claude Desktop reads external plugins from ~/.claude/plugins/marketplaces/claude-plugins-official/external_plugins/
469
+ const claudePluginDir = claudePluginExternalDir();
470
+ const claudePluginManifestPath = join(claudePluginDir, '.claude-plugin', 'plugin.json');
471
+ const claudePluginCommandPath = join(claudePluginDir, 'commands', 'audit-code.md');
472
+ const claudePluginSkillPath = join(claudePluginDir, 'skills', 'audit-code', 'SKILL.md');
473
+ try {
474
+ const manifest = {
475
+ name: 'audit-code',
476
+ description: 'Autonomous local-loop code auditing workflow',
477
+ version: packageVersion,
478
+ author: {
479
+ name: 'auditor-lambda',
480
+ url: 'https://github.com/OhOkThisIsFine/auditor-lambda',
481
+ },
482
+ homepage: 'https://github.com/OhOkThisIsFine/auditor-lambda',
483
+ repository: 'https://github.com/OhOkThisIsFine/auditor-lambda',
484
+ license: 'MIT',
485
+ keywords: ['audit', 'code-audit', 'static-analysis', 'orchestration'],
486
+ };
487
+ const manifestAction = writeGeneratedFile(
488
+ claudePluginManifestPath,
489
+ Buffer.from(JSON.stringify(manifest, null, 2) + '\n'),
490
+ );
491
+ console.log(`audit-code: ${manifestAction} Claude Desktop plugin manifest at ${claudePluginManifestPath}`);
492
+
493
+ const commandAction = writeGeneratedFile(claudePluginCommandPath, promptSource);
494
+ console.log(`audit-code: ${commandAction} Claude Desktop plugin command at ${claudePluginCommandPath}`);
495
+
496
+ const skillAction = writeGeneratedFile(claudePluginSkillPath, skillSource);
497
+ console.log(`audit-code: ${skillAction} Claude Desktop plugin skill at ${claudePluginSkillPath}`);
498
+
499
+ console.log(`audit-code: restart Claude Desktop for /audit-code to appear in the slash-command menu`);
500
+ } catch (err) {
501
+ console.warn(`audit-code: could not install Claude Desktop plugin (${err.message})`);
502
+ console.warn(` Plugin directory: ${claudePluginDir}`);
503
+ }
504
+
462
505
  // Register auditor MCP server with Claude Desktop so /audit-code appears in its slash-command menu
463
506
  const claudeDesktopConfig = claudeDesktopConfigPath();
464
507
  try {