auditor-lambda 0.3.26 → 0.3.27
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 +158 -69
- package/dist/cli.js +23 -3
- package/package.json +1 -1
- package/scripts/postinstall.mjs +1 -3
|
@@ -399,21 +399,122 @@ async function writeGeneratedMarkdown(targetPath, content) {
|
|
|
399
399
|
};
|
|
400
400
|
}
|
|
401
401
|
|
|
402
|
-
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
402
|
+
function looksLikeAuditCodeSkill(content) {
|
|
403
|
+
const normalized = normalizeNewlines(content);
|
|
404
|
+
return (
|
|
405
|
+
/^name:\s*audit-code\b/mu.test(normalized)
|
|
406
|
+
|| normalized.includes('Conversation-first autonomous code auditing workflow for the /audit-code command.')
|
|
407
|
+
|| normalized.includes('The canonical entrypoint is `/audit-code` in conversation.')
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function looksLikeAuditCodePrompt(content) {
|
|
412
|
+
const normalized = normalizeNewlines(content);
|
|
413
|
+
return (
|
|
414
|
+
normalized.includes('# `/audit-code`')
|
|
415
|
+
&& (
|
|
416
|
+
normalized.includes('audit-code orchestrator')
|
|
417
|
+
|| normalized.includes('Autonomous local loop code auditing')
|
|
418
|
+
|| normalized.includes('Conversation-first autonomous code auditing workflow')
|
|
419
|
+
)
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function looksLikeAuditCodeInterfaceMetadata(content) {
|
|
424
|
+
const normalized = normalizeNewlines(content);
|
|
425
|
+
return (
|
|
426
|
+
normalized.includes('audit-code')
|
|
427
|
+
&& (
|
|
428
|
+
normalized.includes('display_name:')
|
|
429
|
+
|| normalized.includes('short_description:')
|
|
430
|
+
|| normalized.includes('default_prompt:')
|
|
431
|
+
)
|
|
432
|
+
&& (
|
|
433
|
+
normalized.includes('/audit-code')
|
|
434
|
+
|| normalized.includes('Start /audit-code')
|
|
435
|
+
)
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
async function buildLegacyAuditCodeSurfaceTargets(root) {
|
|
440
|
+
const targets = [
|
|
441
|
+
{
|
|
442
|
+
host: 'codex',
|
|
443
|
+
surface: 'skill',
|
|
444
|
+
path: join(root, '.codex', 'skills', 'audit-code', 'SKILL.md'),
|
|
445
|
+
matches: looksLikeAuditCodeSkill,
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
host: 'codex',
|
|
449
|
+
surface: 'prompt',
|
|
450
|
+
path: join(root, '.codex', 'skills', 'audit-code', 'audit-code.prompt.md'),
|
|
451
|
+
matches: looksLikeAuditCodePrompt,
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
host: 'opencode',
|
|
455
|
+
surface: 'command',
|
|
456
|
+
path: join(root, '.opencode', 'commands', 'audit-code.md'),
|
|
457
|
+
matches: looksLikeAuditCodePrompt,
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
host: 'opencode',
|
|
461
|
+
surface: 'skill',
|
|
462
|
+
path: join(root, '.opencode', 'skills', 'audit-code', 'SKILL.md'),
|
|
463
|
+
matches: looksLikeAuditCodeSkill,
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
host: 'opencode',
|
|
467
|
+
surface: 'prompt',
|
|
468
|
+
path: join(root, '.opencode', 'skills', 'audit-code', 'audit-code.prompt.md'),
|
|
469
|
+
matches: looksLikeAuditCodePrompt,
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
host: 'claude',
|
|
473
|
+
surface: 'command',
|
|
474
|
+
path: join(root, '.claude', 'commands', 'audit-code.md'),
|
|
475
|
+
matches: looksLikeAuditCodePrompt,
|
|
476
|
+
},
|
|
477
|
+
];
|
|
478
|
+
|
|
479
|
+
const codexAgentDir = join(root, '.codex', 'skills', 'audit-code', 'agents');
|
|
480
|
+
const codexAgentEntries = await readdir(codexAgentDir).catch(() => []);
|
|
481
|
+
for (const entry of codexAgentEntries) {
|
|
482
|
+
targets.push({
|
|
483
|
+
host: 'codex',
|
|
484
|
+
surface: 'interface-metadata',
|
|
485
|
+
path: join(codexAgentDir, entry),
|
|
486
|
+
matches: looksLikeAuditCodeInterfaceMetadata,
|
|
487
|
+
});
|
|
406
488
|
}
|
|
407
489
|
|
|
408
|
-
|
|
409
|
-
|
|
490
|
+
return targets;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async function findLegacyAuditCodeSurfaceFiles(root) {
|
|
494
|
+
const matches = [];
|
|
495
|
+
for (const target of await buildLegacyAuditCodeSurfaceTargets(root)) {
|
|
496
|
+
const existing = await readTextIfExists(target.path);
|
|
497
|
+
if (existing !== null && target.matches(existing)) {
|
|
498
|
+
matches.push(target.path);
|
|
499
|
+
}
|
|
410
500
|
}
|
|
501
|
+
return matches;
|
|
502
|
+
}
|
|
411
503
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
504
|
+
async function removeLegacyAuditCodeSurfaceFiles(root) {
|
|
505
|
+
const removed = [];
|
|
506
|
+
for (const target of await buildLegacyAuditCodeSurfaceTargets(root)) {
|
|
507
|
+
const existing = await readTextIfExists(target.path);
|
|
508
|
+
if (existing === null || !target.matches(existing)) {
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
await unlink(target.path);
|
|
512
|
+
removed.push({
|
|
513
|
+
path: target.path,
|
|
514
|
+
mode: 'removed',
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return removed;
|
|
417
518
|
}
|
|
418
519
|
|
|
419
520
|
async function writeGeneratedJson(targetPath, value) {
|
|
@@ -608,9 +709,7 @@ const OPENCODE_MCP_COMMAND_TEMPLATE = [
|
|
|
608
709
|
'Use the auditor MCP tools as the primary interface to the audit workflow.',
|
|
609
710
|
'',
|
|
610
711
|
'1. Call `auditor_start_audit` to initialize and receive the first step.',
|
|
611
|
-
'2.
|
|
612
|
-
' - If `step_kind` is `"capability_check"`: immediately call `auditor_report_capability` with `can_dispatch_subagents: true` and `can_select_subagent_model: true`. Do not run shell commands or inspect prompt_content for this step.',
|
|
613
|
-
' - Otherwise: read `prompt_content` and follow it.',
|
|
712
|
+
'2. Read `prompt_content` in the response and follow it.',
|
|
614
713
|
'3. When a step completes (not blocked), call `auditor_continue_audit` to advance.',
|
|
615
714
|
'4. Stop when the step instructions say to stop.',
|
|
616
715
|
'',
|
|
@@ -620,18 +719,9 @@ const OPENCODE_MCP_COMMAND_TEMPLATE = [
|
|
|
620
719
|
].join('\n');
|
|
621
720
|
|
|
622
721
|
function renderOpenCodeProjectConfig(_root) {
|
|
623
|
-
const launcher = `.audit-code/install/${MCP_LAUNCHER_FILENAME}`;
|
|
624
722
|
const auditPermission = renderOpenCodePermissionConfig();
|
|
625
723
|
return {
|
|
626
724
|
$schema: 'https://opencode.ai/config.json',
|
|
627
|
-
mcp: {
|
|
628
|
-
auditor: {
|
|
629
|
-
type: 'local',
|
|
630
|
-
command: ['node', launcher],
|
|
631
|
-
enabled: true,
|
|
632
|
-
timeout: 10000,
|
|
633
|
-
},
|
|
634
|
-
},
|
|
635
725
|
permission: auditPermission,
|
|
636
726
|
agent: {
|
|
637
727
|
auditor: {
|
|
@@ -803,14 +893,13 @@ function assertOpenCodeAuditPermissionConfig(permissionConfig, label) {
|
|
|
803
893
|
|
|
804
894
|
function buildMergedOpenCodeProjectConfig(existing, root) {
|
|
805
895
|
const generated = renderOpenCodeProjectConfig(root);
|
|
896
|
+
const mergedMcp = objectValue(existing.mcp);
|
|
897
|
+
delete mergedMcp.auditor;
|
|
806
898
|
return {
|
|
807
899
|
...existing,
|
|
808
900
|
$schema: existing.$schema ?? generated.$schema,
|
|
809
901
|
command: removeManagedOpenCodeCommand(existing.command),
|
|
810
|
-
mcp:
|
|
811
|
-
...objectValue(existing.mcp),
|
|
812
|
-
auditor: generated.mcp.auditor,
|
|
813
|
-
},
|
|
902
|
+
mcp: mergedMcp,
|
|
814
903
|
permission: {
|
|
815
904
|
...mergeOpenCodePermissionConfig(existing.permission, generated.permission),
|
|
816
905
|
external_directory: { '*': 'allow' },
|
|
@@ -1857,6 +1946,18 @@ async function verifyInstalledBootstrap(argv) {
|
|
|
1857
1946
|
};
|
|
1858
1947
|
});
|
|
1859
1948
|
|
|
1949
|
+
await collectVerifyCheck(generalChecks, 'legacy_local_surfaces', async () => {
|
|
1950
|
+
const legacySurfaces = await findLegacyAuditCodeSurfaceFiles(root);
|
|
1951
|
+
if (legacySurfaces.length > 0) {
|
|
1952
|
+
throw new Error(
|
|
1953
|
+
`Legacy local /audit-code surfaces are still present: ${legacySurfaces.join(', ')}. Run "audit-code install" from ${root}.`,
|
|
1954
|
+
);
|
|
1955
|
+
}
|
|
1956
|
+
return {
|
|
1957
|
+
summary: 'No legacy local /audit-code command or skill surfaces were found.',
|
|
1958
|
+
};
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1860
1961
|
await collectVerifyCheck(generalChecks, 'shared_launcher_file', async () => {
|
|
1861
1962
|
const launcher = await readFile(assetPaths.mcpLauncherPath, 'utf8');
|
|
1862
1963
|
if (!launcher.includes('Unable to locate an audit-code executable')) {
|
|
@@ -2005,23 +2106,16 @@ async function verifyInstalledBootstrap(argv) {
|
|
|
2005
2106
|
case 'opencode':
|
|
2006
2107
|
await collectVerifyCheck(checks, 'opencode_config', async () => {
|
|
2007
2108
|
const config = await readJson(assetPaths.opencodeConfigPath, 'OpenCode project config');
|
|
2008
|
-
const mcpCommand = config?.mcp?.auditor?.command;
|
|
2009
|
-
if (!Array.isArray(mcpCommand) || mcpCommand[0] !== 'node') {
|
|
2010
|
-
throw new Error('OpenCode config must set mcp.auditor.command as a Node command array.');
|
|
2011
|
-
}
|
|
2012
|
-
if (!mcpCommand[1]?.includes(MCP_LAUNCHER_FILENAME)) {
|
|
2013
|
-
throw new Error(`OpenCode config must reference ${MCP_LAUNCHER_FILENAME}, got ${mcpCommand[1] ?? 'missing'}.`);
|
|
2014
|
-
}
|
|
2015
|
-
if (config?.mcp?.auditor?.type !== 'local') {
|
|
2016
|
-
throw new Error(`OpenCode config must set mcp.auditor.type to "local", got ${config?.mcp?.auditor?.type ?? 'missing'}.`);
|
|
2017
|
-
}
|
|
2018
2109
|
if (config?.command?.['audit-code']) {
|
|
2019
2110
|
throw new Error('OpenCode project config must not define command["audit-code"]; the slash command is global npm-installed state. Run "audit-code install --host opencode" to remove the stale local command.');
|
|
2020
2111
|
}
|
|
2112
|
+
if (config?.mcp?.auditor) {
|
|
2113
|
+
throw new Error('OpenCode project config must not define mcp.auditor; the MCP server is supplied by the global npm-installed config. Run "audit-code install --host opencode" to remove the stale project-level MCP entry.');
|
|
2114
|
+
}
|
|
2021
2115
|
assertOpenCodeAuditPermissionConfig(config?.permission, 'permission');
|
|
2022
2116
|
assertOpenCodeAuditPermissionConfig(config?.agent?.auditor?.permission, 'agent.auditor.permission');
|
|
2023
2117
|
return {
|
|
2024
|
-
summary: 'OpenCode project config has MCP server and
|
|
2118
|
+
summary: 'OpenCode project config has audit permissions; MCP server and /audit-code command are supplied by the global npm-installed config.',
|
|
2025
2119
|
path: assetPaths.opencodeConfigPath,
|
|
2026
2120
|
};
|
|
2027
2121
|
});
|
|
@@ -2149,7 +2243,11 @@ async function detectBootstrapRefreshReason(root, host) {
|
|
|
2149
2243
|
);
|
|
2150
2244
|
|
|
2151
2245
|
if (hostCatalog.has('codex') && (assetPaths.codexSkillPath || assetPaths.codexPromptPath)) {
|
|
2152
|
-
return '
|
|
2246
|
+
return 'legacy_local_audit_code_surface';
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
if ((await findLegacyAuditCodeSurfaceFiles(root)).length > 0) {
|
|
2250
|
+
return 'legacy_local_audit_code_surface';
|
|
2153
2251
|
}
|
|
2154
2252
|
|
|
2155
2253
|
for (const hostKey of getInstallHostKeys(host)) {
|
|
@@ -2199,6 +2297,9 @@ async function detectBootstrapRefreshReason(root, host) {
|
|
|
2199
2297
|
if (opencodeConfig?.command?.['audit-code']) {
|
|
2200
2298
|
return 'stale_host_asset:opencode:local_command';
|
|
2201
2299
|
}
|
|
2300
|
+
if (opencodeConfig?.mcp?.auditor) {
|
|
2301
|
+
return 'stale_host_asset:opencode:project_mcp';
|
|
2302
|
+
}
|
|
2202
2303
|
try {
|
|
2203
2304
|
assertOpenCodeAuditPermissionConfig(opencodeConfig?.permission, 'permission');
|
|
2204
2305
|
assertOpenCodeAuditPermissionConfig(opencodeConfig?.agent?.auditor?.permission, 'agent.auditor.permission');
|
|
@@ -2384,22 +2485,7 @@ async function installBootstrap(argv, options = {}) {
|
|
|
2384
2485
|
);
|
|
2385
2486
|
}
|
|
2386
2487
|
|
|
2387
|
-
|
|
2388
|
-
const legacyCodexSkillRemoval = await removeGeneratedMarkdownIfMatches(
|
|
2389
|
-
join(root, '.codex', 'skills', 'audit-code', 'SKILL.md'),
|
|
2390
|
-
skillSource,
|
|
2391
|
-
);
|
|
2392
|
-
if (legacyCodexSkillRemoval) {
|
|
2393
|
-
results.push(legacyCodexSkillRemoval);
|
|
2394
|
-
}
|
|
2395
|
-
const legacyCodexPromptRemoval = await removeGeneratedMarkdownIfMatches(
|
|
2396
|
-
join(root, '.codex', 'skills', 'audit-code', 'audit-code.prompt.md'),
|
|
2397
|
-
promptSource,
|
|
2398
|
-
);
|
|
2399
|
-
if (legacyCodexPromptRemoval) {
|
|
2400
|
-
results.push(legacyCodexPromptRemoval);
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2488
|
+
results.push(...await removeLegacyAuditCodeSurfaceFiles(root));
|
|
2403
2489
|
|
|
2404
2490
|
if (profile.writeCodex) {
|
|
2405
2491
|
results.push(
|
|
@@ -2447,19 +2533,6 @@ async function installBootstrap(argv, options = {}) {
|
|
|
2447
2533
|
}
|
|
2448
2534
|
|
|
2449
2535
|
if (profile.writeOpenCode) {
|
|
2450
|
-
// Remove legacy command/skill/prompt files unconditionally — these paths are exclusively
|
|
2451
|
-
// owned by the auditor-lambda installer and keeping any version of them causes OpenCode to
|
|
2452
|
-
// load the wrong slash command regardless of prompt content.
|
|
2453
|
-
for (const legacyPath of [
|
|
2454
|
-
join(root, '.opencode', 'commands', 'audit-code.md'),
|
|
2455
|
-
join(root, '.opencode', 'skills', 'audit-code', 'SKILL.md'),
|
|
2456
|
-
join(root, '.opencode', 'skills', 'audit-code', 'audit-code.prompt.md'),
|
|
2457
|
-
]) {
|
|
2458
|
-
if (await fileExists(legacyPath)) {
|
|
2459
|
-
await unlink(legacyPath);
|
|
2460
|
-
results.push({ path: legacyPath, mode: 'removed' });
|
|
2461
|
-
}
|
|
2462
|
-
}
|
|
2463
2536
|
results.push(
|
|
2464
2537
|
await writeMergedGeneratedJson(
|
|
2465
2538
|
assetPaths.opencodeConfigPath,
|
|
@@ -2625,6 +2698,22 @@ async function runDistCommand(commandName, argv, { ensureArtifactsDir = false }
|
|
|
2625
2698
|
await run(nodeExecutable(), [distEntry, commandName, ...commandArgs]);
|
|
2626
2699
|
}
|
|
2627
2700
|
|
|
2701
|
+
async function runDistCommandInline(commandName, argv) {
|
|
2702
|
+
const commandArgs = [...argv];
|
|
2703
|
+
const rootValue = resolve(getFlag(commandArgs, '--root') ?? '.');
|
|
2704
|
+
const artifactsDir = resolve(getFlag(commandArgs, '--artifacts-dir') ?? join(rootValue, '.audit-artifacts'));
|
|
2705
|
+
|
|
2706
|
+
setDefaultFlag(commandArgs, '--root', rootValue);
|
|
2707
|
+
setDefaultFlag(commandArgs, '--artifacts-dir', artifactsDir);
|
|
2708
|
+
|
|
2709
|
+
await mkdir(artifactsDir, { recursive: true });
|
|
2710
|
+
await ensureBuilt();
|
|
2711
|
+
|
|
2712
|
+
const distUrl = new URL(`file:///${distEntry.replace(/\\/g, '/')}`);
|
|
2713
|
+
const cli = await import(distUrl.href);
|
|
2714
|
+
await cli.runCli([process.execPath, distEntry, commandName, ...commandArgs]);
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2628
2717
|
export async function runAuditCodeWrapper({
|
|
2629
2718
|
usageName,
|
|
2630
2719
|
argv = process.argv.slice(2),
|
|
@@ -2683,7 +2772,7 @@ export async function runAuditCodeWrapper({
|
|
|
2683
2772
|
}
|
|
2684
2773
|
|
|
2685
2774
|
if (argv[0] === 'mcp') {
|
|
2686
|
-
await
|
|
2775
|
+
await runDistCommandInline('mcp', argv.slice(1));
|
|
2687
2776
|
return;
|
|
2688
2777
|
}
|
|
2689
2778
|
|
package/dist/cli.js
CHANGED
|
@@ -2238,8 +2238,18 @@ async function prepareDispatchArtifacts(params) {
|
|
|
2238
2238
|
const lensDefsPath = join(packageRoot, "dispatch", "lens-definitions.json");
|
|
2239
2239
|
const lensDefs = await readJsonFile(lensDefsPath);
|
|
2240
2240
|
await mkdir(taskResultsDir, { recursive: true });
|
|
2241
|
-
|
|
2242
|
-
const
|
|
2241
|
+
// On resume: skip tasks whose result files already exist from a prior dispatch.
|
|
2242
|
+
const priorResultTaskIds = new Set();
|
|
2243
|
+
for (const task of tasks) {
|
|
2244
|
+
if (existsSync(taskResultPath(taskResultsDir, task.task_id))) {
|
|
2245
|
+
priorResultTaskIds.add(task.task_id);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
const dispatchTasks = priorResultTaskIds.size > 0
|
|
2249
|
+
? tasks.filter((task) => !priorResultTaskIds.has(task.task_id))
|
|
2250
|
+
: tasks;
|
|
2251
|
+
const lineIndex = Object.fromEntries(dispatchTasks.flatMap((task) => Object.entries(task.file_line_counts ?? {})));
|
|
2252
|
+
const orderedTasks = orderTasksForPacketReview(dispatchTasks, {
|
|
2243
2253
|
graphBundle: bundle.graph_bundle,
|
|
2244
2254
|
lineIndex,
|
|
2245
2255
|
});
|
|
@@ -2258,6 +2268,15 @@ async function prepareDispatchArtifacts(params) {
|
|
|
2258
2268
|
}
|
|
2259
2269
|
const plan = [];
|
|
2260
2270
|
const resultMapEntries = [];
|
|
2271
|
+
for (const task of tasks) {
|
|
2272
|
+
if (priorResultTaskIds.has(task.task_id)) {
|
|
2273
|
+
resultMapEntries.push({
|
|
2274
|
+
packet_id: "__prior_dispatch__",
|
|
2275
|
+
task_id: task.task_id,
|
|
2276
|
+
result_path: taskResultPath(taskResultsDir, task.task_id),
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2261
2280
|
let largestPacketId = null;
|
|
2262
2281
|
let largestLines = 0;
|
|
2263
2282
|
let largestEstimatedTokens = 0;
|
|
@@ -2473,7 +2492,7 @@ async function prepareDispatchArtifacts(params) {
|
|
|
2473
2492
|
providerName: quotaProviderName,
|
|
2474
2493
|
sessionConfig,
|
|
2475
2494
|
hostModel,
|
|
2476
|
-
requestedConcurrency: sessionConfig.parallel_workers ??
|
|
2495
|
+
requestedConcurrency: sessionConfig.parallel_workers ?? plan.length,
|
|
2477
2496
|
estimatedPacketTokens: avgPacketTokens,
|
|
2478
2497
|
quotaStateEntry,
|
|
2479
2498
|
});
|
|
@@ -2518,6 +2537,7 @@ async function prepareDispatchArtifacts(params) {
|
|
|
2518
2537
|
dispatch_quota_path: dispatchQuotaPath,
|
|
2519
2538
|
packet_count: plan.length,
|
|
2520
2539
|
task_count: orderedTasks.length,
|
|
2540
|
+
skipped_task_count: priorResultTaskIds.size,
|
|
2521
2541
|
largest_packet: largestPacketId
|
|
2522
2542
|
? {
|
|
2523
2543
|
packet_id: largestPacketId,
|
package/package.json
CHANGED
package/scripts/postinstall.mjs
CHANGED
|
@@ -283,9 +283,7 @@ const OPENCODE_MCP_COMMAND_TEMPLATE = [
|
|
|
283
283
|
'Use the auditor MCP tools as the primary interface to the audit workflow.',
|
|
284
284
|
'',
|
|
285
285
|
'1. Call `auditor_start_audit` to initialize and receive the first step.',
|
|
286
|
-
'2.
|
|
287
|
-
' - If `step_kind` is `"capability_check"`: immediately call `auditor_report_capability` with `can_dispatch_subagents: true` and `can_select_subagent_model: true`. Do not run shell commands or inspect prompt_content for this step.',
|
|
288
|
-
' - Otherwise: read `prompt_content` and follow it.',
|
|
286
|
+
'2. Read `prompt_content` in the response and follow it.',
|
|
289
287
|
'3. When a step completes (not blocked), call `auditor_continue_audit` to advance.',
|
|
290
288
|
'4. Stop when the step instructions say to stop.',
|
|
291
289
|
'',
|