Add scheduler handoff runner for selected prompts

This commit is contained in:
thePR0M3TH3AN committed 2026-02-14 17:38:20 -05:00
1 parent aac32c83bd
commit 26b65f273f
3 files changed
+147 -2

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env node
import fs from 'node:fs/promises';
import path from 'node:path';
import { spawn } from 'node:child_process';
const REQUIRED_ENV = ['SCHEDULER_PROMPT_PATH', 'SCHEDULER_AGENT', 'SCHEDULER_CADENCE'];
function fail(message, code = 1) {
console.error(`[scheduler] ${message}`);
process.exit(code);
}
function resolveRunner(promptMarkdown) {
const customRunner = process.env.SCHEDULER_AGENT_RUNNER_COMMAND?.trim();
if (customRunner) {
return { command: 'bash', args: ['-lc', customRunner] };
}
const platform = (process.env.AGENT_PLATFORM || 'codex').trim().toLowerCase();
if (platform === 'codex') {
return { command: 'codex', args: ['exec', promptMarkdown] };
}
if (platform === 'claude') {
return { command: 'claude', args: ['-p', promptMarkdown] };
}
fail(`Unsupported AGENT_PLATFORM="${platform}". Set SCHEDULER_AGENT_RUNNER_COMMAND to override.`);
}
async function main() {
for (const key of REQUIRED_ENV) {
if (!process.env[key] || !String(process.env[key]).trim()) {
fail(`Missing required environment variable ${key}.`);
}
}
const promptPath = path.resolve(process.cwd(), process.env.SCHEDULER_PROMPT_PATH);
let promptMarkdown;
try {
promptMarkdown = await fs.readFile(promptPath, 'utf8');
} catch {
fail(`Unable to read scheduler prompt at ${promptPath}.`);
}
if (!promptMarkdown.trim()) {
fail(`Scheduler prompt at ${promptPath} is empty.`);
}
const runner = resolveRunner(promptMarkdown);
const child = spawn(runner.command, runner.args, {
cwd: process.cwd(),
env: {
...process.env,
SCHEDULER_PROMPT_PATH: promptPath,
SCHEDULER_PROMPT_MARKDOWN: promptMarkdown,
},
stdio: 'inherit',
});
child.on('error', (error) => {
if (error.code === 'ENOENT') {
fail(`Runner command not found: ${runner.command}`, 127);
}
fail(`Runner execution failed: ${error.message}`);
});
child.on('close', (code) => {
process.exit(code ?? 1);
});
}
main();
+71
View File
@@ -0,0 +1,71 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { spawn } from 'node:child_process';
const SCRIPT_PATH = path.resolve('scripts/agent/run-selected-prompt.mjs');
function runScript({ cwd, env }) {
return new Promise((resolve) => {
const child = spawn(process.execPath, [SCRIPT_PATH], {
cwd,
env,
stdio: ['ignore', 'pipe', 'pipe'],
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (chunk) => {
stdout += chunk.toString();
});
child.stderr.on('data', (chunk) => {
stderr += chunk.toString();
});
child.on('close', (code) => {
resolve({ code: code ?? 1, stdout, stderr });
});
});
}
test('runs selected scheduler prompt through configured runner command', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'run-selected-prompt-'));
const promptPath = path.join(root, 'prompt.md');
await fs.writeFile(promptPath, '# hello from prompt\n', 'utf8');
const result = await runScript({
cwd: root,
env: {
...process.env,
SCHEDULER_PROMPT_PATH: promptPath,
SCHEDULER_AGENT: 'agent-a',
SCHEDULER_CADENCE: 'daily',
SCHEDULER_AGENT_RUNNER_COMMAND: 'node -e "if(!process.env.SCHEDULER_PROMPT_MARKDOWN.includes(\'hello from prompt\')) process.exit(6)"',
},
});
assert.equal(result.code, 0, `stdout: ${result.stdout}\nstderr: ${result.stderr}`);
});
test('propagates non-zero runner exit code', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'run-selected-prompt-fail-'));
const promptPath = path.join(root, 'prompt.md');
await fs.writeFile(promptPath, '# will fail\n', 'utf8');
const result = await runScript({
cwd: root,
env: {
...process.env,
SCHEDULER_PROMPT_PATH: promptPath,
SCHEDULER_AGENT: 'agent-b',
SCHEDULER_CADENCE: 'weekly',
SCHEDULER_AGENT_RUNNER_COMMAND: 'node -e "process.exit(7)"',
},
});
assert.equal(result.code, 7, `stdout: ${result.stdout}\nstderr: ${result.stderr}`);
});
+2 -2
View File
@@ -19,8 +19,8 @@
"weekly": "bug-reproducer-agent"
},
"handoffCommandByCadence": {
"daily": "test -n \"$SCHEDULER_AGENT\" && test -n \"$SCHEDULER_CADENCE\" && test -n \"$SCHEDULER_PROMPT_PATH\" && test -f \"$SCHEDULER_PROMPT_PATH\" && echo \"HANDOFF_OK cadence=$SCHEDULER_CADENCE agent=$SCHEDULER_AGENT prompt=$SCHEDULER_PROMPT_PATH\"",
"weekly": "test -n \"$SCHEDULER_AGENT\" && test -n \"$SCHEDULER_CADENCE\" && test -n \"$SCHEDULER_PROMPT_PATH\" && test -f \"$SCHEDULER_PROMPT_PATH\" && echo \"HANDOFF_OK cadence=$SCHEDULER_CADENCE agent=$SCHEDULER_AGENT prompt=$SCHEDULER_PROMPT_PATH\""
"daily": "node scripts/agent/run-selected-prompt.mjs",
"weekly": "node scripts/agent/run-selected-prompt.mjs"
},
"memoryPolicyByCadence": {
"daily": {