Files
reporting-governance-plugin/scripts/long_task_governor_wrapper.mjs

159 lines
5.3 KiB
JavaScript

#!/usr/bin/env node
import fs from 'fs';
function parseArgs(argv) {
const args = { input: '', pretty: true };
for (let i = 2; i < argv.length; i += 1) {
const arg = argv[i];
if (arg === '--input') args.input = argv[++i] || '';
else if (arg === '--compact') args.pretty = false;
}
return args;
}
function readInput(path) {
if (!path || path === '-') return fs.readFileSync(0, 'utf8');
return fs.readFileSync(path, 'utf8');
}
function normalizeRequest(raw) {
const data = JSON.parse(raw);
return {
requestText: data.requestText || '',
hasFilesOrSystems: Boolean(data.hasFilesOrSystems),
needsWaiting: Boolean(data.needsWaiting),
needsSubagent: Boolean(data.needsSubagent),
needsOwnerDecision: Boolean(data.needsOwnerDecision),
canReplyNow: Boolean(data.canReplyNow),
taskName: data.taskName || 'Untitled long-task',
currentStep: data.currentStep || 'Classifying request',
nextStep: data.nextStep || 'Define next actionable step',
nextReportCondition: data.nextReportCondition || 'After next meaningful milestone',
waitingOn: data.waitingOn || 'none',
blocker: data.blocker || 'none',
checkpointTrigger: data.checkpointTrigger || '',
externalizedTrigger: data.externalizedTrigger || '',
triggerKind: data.triggerKind || '',
};
}
function inferFromRequestText(input) {
const text = (input.requestText || '').toLowerCase();
const inferred = { ...input };
if (!input.canReplyNow && /\b(can( not|'t)? use|check|inspect|investigate|review|verify|fix|debug|analyze|analyse|compare|deploy|run)\b/.test(text)) {
inferred.hasFilesOrSystems = true;
}
if (!input.needsOwnerDecision && /\b(accept|reject|approve|decision|choose|pick|verdict)\b/.test(text)) {
inferred.needsOwnerDecision = true;
}
if (!input.needsWaiting && /\b(wait|later|after|async|background|follow up|follow-up)\b/.test(text)) {
inferred.needsWaiting = true;
}
if (!input.needsSubagent && /\bsubagent\b/.test(text)) {
inferred.needsSubagent = true;
}
return inferred;
}
function classify(input) {
const classification = input.canReplyNow && !input.hasFilesOrSystems && !input.needsWaiting && !input.needsSubagent && !input.needsOwnerDecision
? 'general_chat'
: 'long_task';
const silentCandidate = classification === 'long_task' && (input.needsWaiting || input.needsSubagent || Boolean(input.checkpointTrigger));
const needsCheckpoint = classification === 'long_task';
return {
classification,
silentCandidate,
needsOwnerDecision: input.needsOwnerDecision,
needsCheckpoint,
needsSubagent: input.needsSubagent,
};
}
function bootstrapTaskState(input, classificationResult) {
if (classificationResult.classification !== 'long_task') return null;
return {
task_name: input.taskName,
status: input.blocker !== 'none' ? 'blocked' : (input.waitingOn !== 'none' ? 'waiting_user' : 'active'),
current_step: input.currentStep,
next_step: input.nextStep,
next_report_condition: input.nextReportCondition,
waiting_on: input.waitingOn,
blocker: input.blocker,
silent: classificationResult.silentCandidate,
};
}
function validateSilentLaunch(input, classificationResult) {
if (!classificationResult.silentCandidate) {
return {
ok: true,
reason: 'not a silent long-task',
recommendedFallback: 'none',
requiredNextAction: 'proceed_with_normal_long_task_flow',
};
}
if (!input.checkpointTrigger) {
return {
ok: false,
reason: 'missing first forced checkpoint trigger',
recommendedFallback: 'non_silent_follow_up',
requiredNextAction: 'define_first_checkpoint_trigger_before_silent_launch',
};
}
if (!input.externalizedTrigger) {
return {
ok: false,
reason: 'missing externalized checkpoint path',
recommendedFallback: 'non_silent_follow_up',
requiredNextAction: 'bind_externalized_checkpoint_path_or_abort_silent_launch',
};
}
return {
ok: true,
reason: `${input.triggerKind || 'externalized'} trigger is defined`,
recommendedFallback: 'none',
requiredNextAction: 'proceed_with_silent_launch',
};
}
function planHandoff(classificationResult) {
if (classificationResult.needsOwnerDecision) return { mode: 'button_path' };
return { mode: 'direct_reply' };
}
function main() {
const args = parseArgs(process.argv);
const raw = readInput(args.input);
const input = inferFromRequestText(normalizeRequest(raw));
const classificationResult = classify(input);
const taskRecord = bootstrapTaskState(input, classificationResult);
const silentLaunch = validateSilentLaunch(input, classificationResult);
const handoff = planHandoff(classificationResult);
const output = {
classification: classificationResult.classification,
silentCandidate: classificationResult.silentCandidate,
needsOwnerDecision: classificationResult.needsOwnerDecision,
needsCheckpoint: classificationResult.needsCheckpoint,
needsSubagent: classificationResult.needsSubagent,
taskRecord,
silentLaunchOk: silentLaunch.ok,
silentLaunchReason: silentLaunch.reason,
recommendedFallback: silentLaunch.recommendedFallback,
requiredNextAction: silentLaunch.requiredNextAction,
handoff,
};
process.stdout.write(JSON.stringify(output, null, args.pretty ? 2 : 0) + '\n');
}
main();