aoaoe 1.0.0 → 2.0.0
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/README.md +23 -1
- package/dist/ab-reasoning.d.ts +42 -0
- package/dist/ab-reasoning.js +91 -0
- package/dist/alert-rules.d.ts +42 -0
- package/dist/alert-rules.js +94 -0
- package/dist/cli-completions.d.ts +24 -0
- package/dist/cli-completions.js +114 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +8 -1
- package/dist/fleet-federation.d.ts +34 -0
- package/dist/fleet-federation.js +55 -0
- package/dist/index.js +281 -1
- package/dist/input.d.ts +42 -0
- package/dist/input.js +130 -0
- package/dist/multi-reasoner.d.ts +36 -0
- package/dist/multi-reasoner.js +87 -0
- package/dist/output-archival.d.ts +23 -0
- package/dist/output-archival.js +72 -0
- package/dist/runbook-generator.d.ts +21 -0
- package/dist/runbook-generator.js +104 -0
- package/dist/service-generator.d.ts +32 -0
- package/dist/service-generator.js +132 -0
- package/dist/session-checkpoint.d.ts +55 -0
- package/dist/session-checkpoint.js +69 -0
- package/dist/session-replay.d.ts +25 -0
- package/dist/session-replay.js +103 -0
- package/dist/token-quota.d.ts +45 -0
- package/dist/token-quota.js +76 -0
- package/dist/workflow-chain.d.ts +33 -0
- package/dist/workflow-chain.js +69 -0
- package/dist/workflow-cost-forecast.d.ts +22 -0
- package/dist/workflow-cost-forecast.js +55 -0
- package/dist/workflow-engine.d.ts +48 -0
- package/dist/workflow-engine.js +91 -0
- package/dist/workflow-templates.d.ts +25 -0
- package/dist/workflow-templates.js +92 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -59,6 +59,20 @@ import { GraduationManager } from "./session-graduation.js";
|
|
|
59
59
|
import { filterThroughApproval, formatApprovalWorkflowStatus } from "./approval-workflow.js";
|
|
60
60
|
import { analyzeCompletedTasks, refineGoal, formatGoalRefinement } from "./goal-refiner.js";
|
|
61
61
|
import { generateHtmlReport, buildReportData } from "./fleet-export.js";
|
|
62
|
+
import { installService } from "./service-generator.js";
|
|
63
|
+
import { buildSessionReplay, formatReplay, summarizeReplay } from "./session-replay.js";
|
|
64
|
+
import { createWorkflowState, advanceWorkflow, formatWorkflow } from "./workflow-engine.js";
|
|
65
|
+
import { assignReasonerBackends, formatAssignments } from "./multi-reasoner.js";
|
|
66
|
+
import { TokenQuotaManager } from "./token-quota.js";
|
|
67
|
+
import { saveCheckpoint, loadCheckpoint, buildCheckpoint, formatCheckpointInfo, shouldRestoreCheckpoint } from "./session-checkpoint.js";
|
|
68
|
+
import { findWorkflowTemplate, instantiateWorkflow, formatWorkflowTemplateList } from "./workflow-templates.js";
|
|
69
|
+
import { ABReasoningTracker } from "./ab-reasoning.js";
|
|
70
|
+
import { forecastWorkflowCost, formatWorkflowCostForecast } from "./workflow-cost-forecast.js";
|
|
71
|
+
import { advanceChain, formatWorkflowChain } from "./workflow-chain.js";
|
|
72
|
+
import { aggregateFederation, formatFederationOverview } from "./fleet-federation.js";
|
|
73
|
+
import { formatArchiveList } from "./output-archival.js";
|
|
74
|
+
import { generateRunbooks, formatGeneratedRunbooks } from "./runbook-generator.js";
|
|
75
|
+
import { defaultAlertRules, evaluateAlertRules, formatAlertRules } from "./alert-rules.js";
|
|
62
76
|
import { buildLifecycleRecords, computeLifecycleStats, formatLifecycleStats } from "./lifecycle-analytics.js";
|
|
63
77
|
import { buildCostAttributions, computeCostReport, formatCostReport } from "./cost-attribution.js";
|
|
64
78
|
import { decomposeGoal, formatDecomposition } from "./goal-decomposer.js";
|
|
@@ -87,7 +101,7 @@ const AOAOE_DIR = join(homedir(), ".aoaoe"); // watch dir for wakeable sleep
|
|
|
87
101
|
const INPUT_FILE = join(AOAOE_DIR, "pending-input.txt"); // file IPC from chat.ts
|
|
88
102
|
const TASK_RECONCILE_EVERY_POLLS = 6;
|
|
89
103
|
async function main() {
|
|
90
|
-
const { overrides, help, version, register, testContext: isTestContext, runTest, showTasks, showTasksJson, runProgress, progressSince, progressJson, runHealth, healthJson, runSummary, runAdopt, adoptTemplate, showHistory, showStatus, runRunbook, runbookJson, runbookSection, runIncident, incidentSince, incidentLimit, incidentJson, incidentNdjson, incidentWatch, incidentChangesOnly, incidentHeartbeatSec, incidentIntervalMs, runSupervisor, supervisorAll, supervisorSince, supervisorLimit, supervisorJson, supervisorNdjson, supervisorWatch, supervisorChangesOnly, supervisorHeartbeatSec, supervisorIntervalMs, showConfig, configValidate, configDiff, notifyTest, runDoctor, runBackup, backupOutput, runRestore, restoreInput, runSync, syncAction, syncRemote, runWeb, webPort, runLogs, logsActions, logsGrep, logsCount, runExport, exportFormat, exportOutput, exportLast, runInit, initForce, runTaskCli: isTaskCli, runTail: isTail, tailFollow, tailCount, runStats: isStats, statsLast, runReplay: isReplay, replaySpeed, replayLast, registerTitle } = parseCliArgs(process.argv);
|
|
104
|
+
const { overrides, help, version, register, testContext: isTestContext, runTest, showTasks, showTasksJson, runProgress, progressSince, progressJson, runHealth, healthJson, runSummary, runAdopt, adoptTemplate, showHistory, showStatus, runRunbook, runbookJson, runbookSection, runIncident, incidentSince, incidentLimit, incidentJson, incidentNdjson, incidentWatch, incidentChangesOnly, incidentHeartbeatSec, incidentIntervalMs, runSupervisor, supervisorAll, supervisorSince, supervisorLimit, supervisorJson, supervisorNdjson, supervisorWatch, supervisorChangesOnly, supervisorHeartbeatSec, supervisorIntervalMs, showConfig, configValidate, configDiff, notifyTest, runDoctor, runBackup, backupOutput, runRestore, restoreInput, runSync, syncAction, syncRemote, runWeb, webPort, runLogs, logsActions, logsGrep, logsCount, runExport, exportFormat, exportOutput, exportLast, runInit, initForce, runTaskCli: isTaskCli, runTail: isTail, tailFollow, tailCount, runStats: isStats, statsLast, runReplay: isReplay, replaySpeed, replayLast, registerTitle, runService, runCompletions, completionsShell } = parseCliArgs(process.argv);
|
|
91
105
|
if (help) {
|
|
92
106
|
printHelp();
|
|
93
107
|
process.exit(0);
|
|
@@ -232,6 +246,18 @@ async function main() {
|
|
|
232
246
|
await runDoctorCheck();
|
|
233
247
|
return;
|
|
234
248
|
}
|
|
249
|
+
if (runService) {
|
|
250
|
+
const lines = installService({ workingDir: process.cwd() });
|
|
251
|
+
for (const l of lines)
|
|
252
|
+
console.log(l);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (runCompletions) {
|
|
256
|
+
const { generateCompletion } = await import("./cli-completions.js");
|
|
257
|
+
const shell = (completionsShell ?? "bash");
|
|
258
|
+
console.log(generateCompletion(shell));
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
235
261
|
if (runBackup) {
|
|
236
262
|
try {
|
|
237
263
|
const result = await createBackup(backupOutput);
|
|
@@ -551,6 +577,25 @@ async function main() {
|
|
|
551
577
|
const recoveryPlaybookManager = new RecoveryPlaybookManager();
|
|
552
578
|
const approvalQueue = new ApprovalQueue();
|
|
553
579
|
const graduationManager = new GraduationManager();
|
|
580
|
+
let activeWorkflow = null;
|
|
581
|
+
let activeWorkflowChain = null;
|
|
582
|
+
const tokenQuotaManager = new TokenQuotaManager();
|
|
583
|
+
const abReasoningTracker = new ABReasoningTracker(config.reasoner, "claude-code");
|
|
584
|
+
const alertRules = defaultAlertRules();
|
|
585
|
+
// checkpoint restore: load previous daemon state if available
|
|
586
|
+
if (shouldRestoreCheckpoint()) {
|
|
587
|
+
const cp = loadCheckpoint();
|
|
588
|
+
if (cp) {
|
|
589
|
+
// restore adaptive poll interval
|
|
590
|
+
if (cp.pollInterval && cp.pollInterval !== config.pollIntervalMs) {
|
|
591
|
+
// poll controller will naturally adjust, but log what was saved
|
|
592
|
+
}
|
|
593
|
+
const restoredSessions = Object.keys(cp.graduation).length;
|
|
594
|
+
if (restoredSessions > 0) {
|
|
595
|
+
audit("daemon_start", `restored checkpoint: ${restoredSessions} graduation states, cache ${cp.cacheStats.hits}/${cp.cacheStats.misses}`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
554
599
|
// audit: log daemon start
|
|
555
600
|
audit("daemon_start", `daemon started (v${pkg ?? "dev"}, reasoner=${config.reasoner})`);
|
|
556
601
|
const refreshTaskSupervisorState = (reason) => {
|
|
@@ -2420,6 +2465,151 @@ async function main() {
|
|
|
2420
2465
|
writeFileSync(filepath, html);
|
|
2421
2466
|
tui.log("system", `fleet report exported: ${filepath}`);
|
|
2422
2467
|
});
|
|
2468
|
+
// wire /service — generate systemd/launchd service file
|
|
2469
|
+
input.onService(() => {
|
|
2470
|
+
const lines = installService({ workingDir: process.cwd() });
|
|
2471
|
+
for (const l of lines)
|
|
2472
|
+
tui.log("system", l);
|
|
2473
|
+
});
|
|
2474
|
+
// wire /session-replay — session activity timeline replay
|
|
2475
|
+
input.onSessionReplay((target) => {
|
|
2476
|
+
const replay = buildSessionReplay(target);
|
|
2477
|
+
if (replay.events.length === 0) {
|
|
2478
|
+
tui.log("system", `replay: no events for "${target}" — run the daemon to generate audit data first`);
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2481
|
+
const summary = summarizeReplay(replay);
|
|
2482
|
+
for (const l of summary)
|
|
2483
|
+
tui.log("system", l);
|
|
2484
|
+
tui.log("system", "");
|
|
2485
|
+
const detailed = formatReplay(replay);
|
|
2486
|
+
for (const l of detailed)
|
|
2487
|
+
tui.log("system", l);
|
|
2488
|
+
});
|
|
2489
|
+
// wire /workflow — show active workflow state
|
|
2490
|
+
input.onWorkflow(() => {
|
|
2491
|
+
if (!activeWorkflow) {
|
|
2492
|
+
tui.log("system", "workflow: no active workflow (define one in aoaoe.tasks.json with workflow stages)");
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
2495
|
+
const lines = formatWorkflow(activeWorkflow);
|
|
2496
|
+
for (const l of lines)
|
|
2497
|
+
tui.log("system", l);
|
|
2498
|
+
});
|
|
2499
|
+
// wire /multi-reasoner — show reasoner assignments
|
|
2500
|
+
input.onMultiReasoner(() => {
|
|
2501
|
+
const sessions = tui.getSessions();
|
|
2502
|
+
const sessionInfos = sessions.map((s) => ({ title: s.title, template: undefined, difficultyScore: undefined }));
|
|
2503
|
+
const assignments = assignReasonerBackends(sessionInfos, { defaultBackend: config.reasoner });
|
|
2504
|
+
const lines = formatAssignments(assignments);
|
|
2505
|
+
for (const l of lines)
|
|
2506
|
+
tui.log("system", l);
|
|
2507
|
+
});
|
|
2508
|
+
// wire /token-quota — per-model token quotas
|
|
2509
|
+
input.onTokenQuota(() => {
|
|
2510
|
+
const lines = tokenQuotaManager.formatAll();
|
|
2511
|
+
for (const l of lines)
|
|
2512
|
+
tui.log("system", l);
|
|
2513
|
+
});
|
|
2514
|
+
// wire /checkpoint — show checkpoint info
|
|
2515
|
+
input.onCheckpoint(() => {
|
|
2516
|
+
const lines = formatCheckpointInfo();
|
|
2517
|
+
for (const l of lines)
|
|
2518
|
+
tui.log("system", l);
|
|
2519
|
+
});
|
|
2520
|
+
// wire /workflow-new — create workflow from template
|
|
2521
|
+
input.onWorkflowNew((args) => {
|
|
2522
|
+
const parts = args.split(/\s+/);
|
|
2523
|
+
const templateName = parts[0];
|
|
2524
|
+
const prefix = parts[1] ?? "wf";
|
|
2525
|
+
const template = findWorkflowTemplate(templateName);
|
|
2526
|
+
if (!template) {
|
|
2527
|
+
tui.log("system", `workflow-new: template "${templateName}" not found`);
|
|
2528
|
+
const lines = formatWorkflowTemplateList();
|
|
2529
|
+
for (const l of lines)
|
|
2530
|
+
tui.log("system", l);
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2533
|
+
const def = instantiateWorkflow(template, prefix);
|
|
2534
|
+
// show cost forecast before creating
|
|
2535
|
+
const forecast = forecastWorkflowCost(def);
|
|
2536
|
+
const forecastLines = formatWorkflowCostForecast(forecast);
|
|
2537
|
+
for (const l of forecastLines)
|
|
2538
|
+
tui.log("system", l);
|
|
2539
|
+
activeWorkflow = createWorkflowState(def);
|
|
2540
|
+
tui.log("+ action", `workflow "${def.name}" created from template "${templateName}" (${def.stages.length} stages)`);
|
|
2541
|
+
audit("task_created", `workflow created: ${def.name} from ${templateName}`, undefined, { stages: def.stages.length });
|
|
2542
|
+
const lines = formatWorkflow(activeWorkflow);
|
|
2543
|
+
for (const l of lines)
|
|
2544
|
+
tui.log("system", l);
|
|
2545
|
+
});
|
|
2546
|
+
// wire /ab-stats — A/B reasoning statistics
|
|
2547
|
+
input.onABStats(() => {
|
|
2548
|
+
const lines = abReasoningTracker.formatStats();
|
|
2549
|
+
for (const l of lines)
|
|
2550
|
+
tui.log("system", l);
|
|
2551
|
+
});
|
|
2552
|
+
// wire /workflow-chain — show active workflow chain
|
|
2553
|
+
input.onWorkflowChain(() => {
|
|
2554
|
+
if (!activeWorkflowChain) {
|
|
2555
|
+
tui.log("system", "workflow-chain: no active chain");
|
|
2556
|
+
return;
|
|
2557
|
+
}
|
|
2558
|
+
const lines = formatWorkflowChain(activeWorkflowChain);
|
|
2559
|
+
for (const l of lines)
|
|
2560
|
+
tui.log("system", l);
|
|
2561
|
+
});
|
|
2562
|
+
// wire /workflow-forecast — preview cost estimate for a template
|
|
2563
|
+
input.onWorkflowForecast((templateName) => {
|
|
2564
|
+
const template = findWorkflowTemplate(templateName);
|
|
2565
|
+
if (!template) {
|
|
2566
|
+
tui.log("system", `workflow-forecast: template "${templateName}" not found`);
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
const def = instantiateWorkflow(template, "preview");
|
|
2570
|
+
const forecast = forecastWorkflowCost(def);
|
|
2571
|
+
const lines = formatWorkflowCostForecast(forecast);
|
|
2572
|
+
for (const l of lines)
|
|
2573
|
+
tui.log("system", l);
|
|
2574
|
+
});
|
|
2575
|
+
// wire /federation — multi-host fleet overview
|
|
2576
|
+
input.onFederation(() => {
|
|
2577
|
+
// in practice, would fetch from configured peers; show local state as single peer
|
|
2578
|
+
const sessions = tui.getSessions();
|
|
2579
|
+
const tasks = taskManager?.tasks ?? [];
|
|
2580
|
+
const scores = sessions.map((s) => s.status === "working" || s.status === "running" ? 80 : s.status === "error" ? 20 : 50);
|
|
2581
|
+
const health = scores.length > 0 ? Math.round(scores.reduce((a, b) => a + b, 0) / scores.length) : 100;
|
|
2582
|
+
let cost = 0;
|
|
2583
|
+
for (const s of sessions) {
|
|
2584
|
+
const m = s.costStr?.match(/\$(\d+(?:\.\d+)?)/);
|
|
2585
|
+
if (m)
|
|
2586
|
+
cost += parseFloat(m[1]);
|
|
2587
|
+
}
|
|
2588
|
+
const localState = { peer: "local", sessions: sessions.length, activeTasks: tasks.filter((t) => t.status === "active").length, fleetHealth: health, totalCostUsd: cost, lastUpdatedAt: Date.now() };
|
|
2589
|
+
const overview = aggregateFederation([localState]);
|
|
2590
|
+
const lines = formatFederationOverview(overview);
|
|
2591
|
+
for (const l of lines)
|
|
2592
|
+
tui.log("system", l);
|
|
2593
|
+
});
|
|
2594
|
+
// wire /archives — show output archive list
|
|
2595
|
+
input.onArchives(() => {
|
|
2596
|
+
const lines = formatArchiveList();
|
|
2597
|
+
for (const l of lines)
|
|
2598
|
+
tui.log("system", l);
|
|
2599
|
+
});
|
|
2600
|
+
// wire /runbook-gen — generate runbooks from audit trail
|
|
2601
|
+
input.onRunbookGen(() => {
|
|
2602
|
+
const runbooks = generateRunbooks();
|
|
2603
|
+
const lines = formatGeneratedRunbooks(runbooks);
|
|
2604
|
+
for (const l of lines)
|
|
2605
|
+
tui.log("system", l);
|
|
2606
|
+
});
|
|
2607
|
+
// wire /alert-rules — show alert rules and their status
|
|
2608
|
+
input.onAlertRules(() => {
|
|
2609
|
+
const lines = formatAlertRules(alertRules);
|
|
2610
|
+
for (const l of lines)
|
|
2611
|
+
tui.log("system", l);
|
|
2612
|
+
});
|
|
2423
2613
|
input.onCostSummary(() => {
|
|
2424
2614
|
const sessions = tui.getSessions();
|
|
2425
2615
|
const summary = computeCostSummary(sessions, tui.getAllSessionCosts());
|
|
@@ -2881,6 +3071,27 @@ async function main() {
|
|
|
2881
3071
|
.then(() => reasoner?.shutdown())
|
|
2882
3072
|
.catch((err) => console.error(`[shutdown] error during cleanup: ${err}`))
|
|
2883
3073
|
.finally(() => {
|
|
3074
|
+
// save daemon state checkpoint before exit
|
|
3075
|
+
try {
|
|
3076
|
+
const cp = buildCheckpoint({
|
|
3077
|
+
graduation: Object.fromEntries([...(tui?.getSessions() ?? [])].map((s) => [s.title, {
|
|
3078
|
+
mode: graduationManager.getState(s.title)?.currentMode ?? "confirm",
|
|
3079
|
+
successes: graduationManager.getState(s.title)?.successfulActions ?? 0,
|
|
3080
|
+
failures: graduationManager.getState(s.title)?.failedActions ?? 0,
|
|
3081
|
+
rate: graduationManager.getState(s.title)?.successRate ?? 0,
|
|
3082
|
+
}])),
|
|
3083
|
+
escalation: {},
|
|
3084
|
+
velocitySamples: {},
|
|
3085
|
+
nudgeRecords: [],
|
|
3086
|
+
budgetSamples: {},
|
|
3087
|
+
cacheStats: { hits: observationCache.getStats().totalHits, misses: observationCache.getStats().totalMisses },
|
|
3088
|
+
slaHistory: [],
|
|
3089
|
+
pollInterval: adaptivePollController.intervalMs,
|
|
3090
|
+
});
|
|
3091
|
+
saveCheckpoint(cp);
|
|
3092
|
+
audit("daemon_stop", "daemon stopped, checkpoint saved");
|
|
3093
|
+
}
|
|
3094
|
+
catch { /* best-effort */ }
|
|
2884
3095
|
cleanupState();
|
|
2885
3096
|
process.exit(0);
|
|
2886
3097
|
});
|
|
@@ -3959,6 +4170,7 @@ async function main() {
|
|
|
3959
4170
|
escalationManager,
|
|
3960
4171
|
graduationManager,
|
|
3961
4172
|
approvalQueue,
|
|
4173
|
+
tokenQuotaManager,
|
|
3962
4174
|
pushSupervisorEvent,
|
|
3963
4175
|
refreshTaskSupervisorState,
|
|
3964
4176
|
});
|
|
@@ -4045,6 +4257,64 @@ async function main() {
|
|
|
4045
4257
|
}
|
|
4046
4258
|
}
|
|
4047
4259
|
}
|
|
4260
|
+
// workflow engine: advance active workflow based on task states
|
|
4261
|
+
if (activeWorkflow && taskManager && tui) {
|
|
4262
|
+
const wf = activeWorkflow; // capture before potential null assignment
|
|
4263
|
+
const taskStates = new Map(taskManager.tasks.map((t) => [t.sessionTitle, t.status]));
|
|
4264
|
+
const { actions: wfActions, completed } = advanceWorkflow(wf, taskStates);
|
|
4265
|
+
for (const a of wfActions) {
|
|
4266
|
+
if (a.type === "activate_task") {
|
|
4267
|
+
const task = taskManager.getTaskForSession(a.detail);
|
|
4268
|
+
if (task && task.status === "pending")
|
|
4269
|
+
task.status = "active";
|
|
4270
|
+
}
|
|
4271
|
+
tui.log("status", `workflow: ${a.type} — ${a.detail}`);
|
|
4272
|
+
audit("task_created", `workflow ${a.type}: ${a.detail}`, a.detail);
|
|
4273
|
+
}
|
|
4274
|
+
if (completed) {
|
|
4275
|
+
tui.log("+ action", `workflow "${wf.name}" completed`);
|
|
4276
|
+
activeWorkflow = null;
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
// workflow chain: advance cross-workflow dependencies
|
|
4280
|
+
if (activeWorkflowChain && tui) {
|
|
4281
|
+
const chain = activeWorkflowChain;
|
|
4282
|
+
const wfStates = new Map();
|
|
4283
|
+
const { activate, completed: chainDone, failed: chainFailed } = advanceChain(chain, wfStates);
|
|
4284
|
+
for (const name of activate) {
|
|
4285
|
+
tui.log("status", `workflow-chain: activating workflow "${name}"`);
|
|
4286
|
+
audit("task_created", `chain activated: ${name}`, name);
|
|
4287
|
+
}
|
|
4288
|
+
if (chainDone) {
|
|
4289
|
+
tui.log("+ action", `workflow chain "${chain.name}" completed`);
|
|
4290
|
+
activeWorkflowChain = null;
|
|
4291
|
+
}
|
|
4292
|
+
if (chainFailed) {
|
|
4293
|
+
tui.log("status", `workflow chain "${chain.name}" has failures`);
|
|
4294
|
+
}
|
|
4295
|
+
}
|
|
4296
|
+
// custom alert rules: evaluate fleet conditions per tick
|
|
4297
|
+
if (tui) {
|
|
4298
|
+
const sessions = tui.getSessions();
|
|
4299
|
+
const tasks = taskManager?.tasks ?? [];
|
|
4300
|
+
const scores = sessions.map((s) => s.status === "working" || s.status === "running" ? 80 : s.status === "error" ? 20 : 50);
|
|
4301
|
+
const fleetHealth = scores.length > 0 ? Math.round(scores.reduce((a, b) => a + b, 0) / scores.length) : 100;
|
|
4302
|
+
const activeSessions = sessions.filter((s) => s.status === "working" || s.status === "running").length;
|
|
4303
|
+
const errorSessions = sessions.filter((s) => s.status === "error").length;
|
|
4304
|
+
const stuckSessions = tasks.filter((t) => t.status === "active" && (t.stuckNudgeCount ?? 0) > 0).length;
|
|
4305
|
+
let hourlyCost = 0;
|
|
4306
|
+
for (const s of sessions) {
|
|
4307
|
+
const m = s.costStr?.match(/\$(\d+(?:\.\d+)?)/);
|
|
4308
|
+
if (m)
|
|
4309
|
+
hourlyCost += parseFloat(m[1]);
|
|
4310
|
+
}
|
|
4311
|
+
const alertCtx = { fleetHealth, activeSessions, errorSessions, totalCostUsd: hourlyCost, hourlyCostRate: hourlyCost, stuckSessions, idleMinutes: new Map() };
|
|
4312
|
+
const firedAlerts = evaluateAlertRules(alertRules, alertCtx);
|
|
4313
|
+
for (const alert of firedAlerts) {
|
|
4314
|
+
tui.log("status", `${alert.severity === "critical" ? "🚨" : alert.severity === "warning" ? "⚠" : "ℹ"} ALERT: ${alert.message}`);
|
|
4315
|
+
audit("session_error", `alert fired: ${alert.ruleName} — ${alert.message}`, undefined, { severity: alert.severity });
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4048
4318
|
// trust ladder: record stable tick or failure, sync mode if escalated
|
|
4049
4319
|
if (tui && decisionsThisTick > 0) {
|
|
4050
4320
|
if (actionsFail > 0) {
|
|
@@ -4182,6 +4452,14 @@ async function daemonTick(config, poller, reasoner, executor, reasonerConsole, p
|
|
|
4182
4452
|
init: () => reasoner.init(),
|
|
4183
4453
|
shutdown: () => reasoner.shutdown(),
|
|
4184
4454
|
decide: async (obs) => {
|
|
4455
|
+
// ── gate 0: per-model token quota — block if model quota exceeded ──
|
|
4456
|
+
if (intelligence?.tokenQuotaManager.isBlocked(config.reasoner)) {
|
|
4457
|
+
const status = intelligence.tokenQuotaManager.getStatus(config.reasoner);
|
|
4458
|
+
if (tui)
|
|
4459
|
+
tui.log("status", `⏸ token quota exceeded for ${config.reasoner}: ${status.reason}`);
|
|
4460
|
+
audit("reasoner_action", `token quota blocked: ${config.reasoner} — ${status.reason}`);
|
|
4461
|
+
return { actions: [{ action: "wait", reason: `token quota: ${status.reason}` }] };
|
|
4462
|
+
}
|
|
4185
4463
|
// ── gate 1: fleet rate limiter — block if over API spend limits ──
|
|
4186
4464
|
if (intelligence?.fleetRateLimiter.isBlocked()) {
|
|
4187
4465
|
const status = intelligence.fleetRateLimiter.getStatus();
|
|
@@ -4260,6 +4538,8 @@ async function daemonTick(config, poller, reasoner, executor, reasonerConsole, p
|
|
|
4260
4538
|
intelligence.reasonerCostTracker.recordCall("fleet", tokenEstimate, outputEstimate, reasonerDurationMs);
|
|
4261
4539
|
intelligence.fleetRateLimiter.recordCost(estimateCallCost(tokenEstimate, outputEstimate));
|
|
4262
4540
|
intelligence.observationCache.set(obsJson, r);
|
|
4541
|
+
// per-model token quota tracking
|
|
4542
|
+
intelligence.tokenQuotaManager.recordUsage(config.reasoner, tokenEstimate, outputEstimate);
|
|
4263
4543
|
// approval workflow: gate risky/low-confidence actions through approval queue
|
|
4264
4544
|
if (config.confirm || r.confidence === "low") {
|
|
4265
4545
|
const { immediate, queued } = filterThroughApproval(r, intelligence.approvalQueue);
|
package/dist/input.d.ts
CHANGED
|
@@ -131,6 +131,20 @@ export type AllocationHandler = () => void;
|
|
|
131
131
|
export type GraduationHandler = () => void;
|
|
132
132
|
export type RefineHandler = (target: string) => void;
|
|
133
133
|
export type ExportHandler = () => void;
|
|
134
|
+
export type ServiceHandler = () => void;
|
|
135
|
+
export type SessionReplayHandler = (target: string) => void;
|
|
136
|
+
export type WorkflowHandler = () => void;
|
|
137
|
+
export type MultiReasonerHandler = () => void;
|
|
138
|
+
export type TokenQuotaHandler = () => void;
|
|
139
|
+
export type CheckpointHandler = () => void;
|
|
140
|
+
export type WorkflowNewHandler = (template: string) => void;
|
|
141
|
+
export type ABStatsHandler = () => void;
|
|
142
|
+
export type WorkflowChainHandler = () => void;
|
|
143
|
+
export type WorkflowForecastHandler = (template: string) => void;
|
|
144
|
+
export type FederationHandler = () => void;
|
|
145
|
+
export type ArchivesHandler = () => void;
|
|
146
|
+
export type RunbookGenHandler = () => void;
|
|
147
|
+
export type AlertRulesHandler = () => void;
|
|
134
148
|
export interface MouseEvent {
|
|
135
149
|
button: number;
|
|
136
150
|
col: number;
|
|
@@ -443,6 +457,34 @@ export declare class InputReader {
|
|
|
443
457
|
onGraduation(handler: GraduationHandler): void;
|
|
444
458
|
onRefine(handler: RefineHandler): void;
|
|
445
459
|
onExport(handler: ExportHandler): void;
|
|
460
|
+
private serviceHandler;
|
|
461
|
+
private sessionReplayHandler;
|
|
462
|
+
private workflowHandler;
|
|
463
|
+
onService(handler: ServiceHandler): void;
|
|
464
|
+
onSessionReplay(handler: SessionReplayHandler): void;
|
|
465
|
+
onWorkflow(handler: WorkflowHandler): void;
|
|
466
|
+
private multiReasonerHandler;
|
|
467
|
+
private tokenQuotaHandler;
|
|
468
|
+
private checkpointHandler;
|
|
469
|
+
private workflowNewHandler;
|
|
470
|
+
onMultiReasoner(handler: MultiReasonerHandler): void;
|
|
471
|
+
onTokenQuota(handler: TokenQuotaHandler): void;
|
|
472
|
+
onCheckpoint(handler: CheckpointHandler): void;
|
|
473
|
+
onWorkflowNew(handler: WorkflowNewHandler): void;
|
|
474
|
+
private abStatsHandler;
|
|
475
|
+
private workflowChainHandler;
|
|
476
|
+
private workflowForecastHandler;
|
|
477
|
+
onABStats(handler: ABStatsHandler): void;
|
|
478
|
+
onWorkflowChain(handler: WorkflowChainHandler): void;
|
|
479
|
+
onWorkflowForecast(handler: WorkflowForecastHandler): void;
|
|
480
|
+
private federationHandler;
|
|
481
|
+
private archivesHandler;
|
|
482
|
+
private runbookGenHandler;
|
|
483
|
+
private alertRulesHandler;
|
|
484
|
+
onFederation(handler: FederationHandler): void;
|
|
485
|
+
onArchives(handler: ArchivesHandler): void;
|
|
486
|
+
onRunbookGen(handler: RunbookGenHandler): void;
|
|
487
|
+
onAlertRules(handler: AlertRulesHandler): void;
|
|
446
488
|
onFleetSearch(handler: FleetSearchHandler): void;
|
|
447
489
|
onNudgeStats(handler: NudgeStatsHandler): void;
|
|
448
490
|
onAllocation(handler: AllocationHandler): void;
|
package/dist/input.js
CHANGED
|
@@ -540,6 +540,34 @@ export class InputReader {
|
|
|
540
540
|
onGraduation(handler) { this.graduationHandler = handler; }
|
|
541
541
|
onRefine(handler) { this.refineHandler = handler; }
|
|
542
542
|
onExport(handler) { this.exportHandler = handler; }
|
|
543
|
+
serviceHandler = null;
|
|
544
|
+
sessionReplayHandler = null;
|
|
545
|
+
workflowHandler = null;
|
|
546
|
+
onService(handler) { this.serviceHandler = handler; }
|
|
547
|
+
onSessionReplay(handler) { this.sessionReplayHandler = handler; }
|
|
548
|
+
onWorkflow(handler) { this.workflowHandler = handler; }
|
|
549
|
+
multiReasonerHandler = null;
|
|
550
|
+
tokenQuotaHandler = null;
|
|
551
|
+
checkpointHandler = null;
|
|
552
|
+
workflowNewHandler = null;
|
|
553
|
+
onMultiReasoner(handler) { this.multiReasonerHandler = handler; }
|
|
554
|
+
onTokenQuota(handler) { this.tokenQuotaHandler = handler; }
|
|
555
|
+
onCheckpoint(handler) { this.checkpointHandler = handler; }
|
|
556
|
+
onWorkflowNew(handler) { this.workflowNewHandler = handler; }
|
|
557
|
+
abStatsHandler = null;
|
|
558
|
+
workflowChainHandler = null;
|
|
559
|
+
workflowForecastHandler = null;
|
|
560
|
+
onABStats(handler) { this.abStatsHandler = handler; }
|
|
561
|
+
onWorkflowChain(handler) { this.workflowChainHandler = handler; }
|
|
562
|
+
onWorkflowForecast(handler) { this.workflowForecastHandler = handler; }
|
|
563
|
+
federationHandler = null;
|
|
564
|
+
archivesHandler = null;
|
|
565
|
+
runbookGenHandler = null;
|
|
566
|
+
alertRulesHandler = null;
|
|
567
|
+
onFederation(handler) { this.federationHandler = handler; }
|
|
568
|
+
onArchives(handler) { this.archivesHandler = handler; }
|
|
569
|
+
onRunbookGen(handler) { this.runbookGenHandler = handler; }
|
|
570
|
+
onAlertRules(handler) { this.alertRulesHandler = handler; }
|
|
543
571
|
onFleetSearch(handler) { this.fleetSearchHandler = handler; }
|
|
544
572
|
onNudgeStats(handler) { this.nudgeStatsHandler = handler; }
|
|
545
573
|
onAllocation(handler) { this.allocationHandler = handler; }
|
|
@@ -2364,6 +2392,108 @@ ${BOLD}other:${RESET}
|
|
|
2364
2392
|
else
|
|
2365
2393
|
console.error(`${DIM}export not available (no TUI)${RESET}`);
|
|
2366
2394
|
break;
|
|
2395
|
+
case "/service":
|
|
2396
|
+
if (this.serviceHandler)
|
|
2397
|
+
this.serviceHandler();
|
|
2398
|
+
else
|
|
2399
|
+
console.error(`${DIM}service not available (no TUI)${RESET}`);
|
|
2400
|
+
break;
|
|
2401
|
+
case "/session-replay": {
|
|
2402
|
+
const rpArg = line.slice("/session-replay".length).trim();
|
|
2403
|
+
if (!rpArg) {
|
|
2404
|
+
console.error(`${DIM}usage: /session-replay <session-name>${RESET}`);
|
|
2405
|
+
break;
|
|
2406
|
+
}
|
|
2407
|
+
if (this.sessionReplayHandler)
|
|
2408
|
+
this.sessionReplayHandler(rpArg);
|
|
2409
|
+
else
|
|
2410
|
+
console.error(`${DIM}session-replay not available (no TUI)${RESET}`);
|
|
2411
|
+
break;
|
|
2412
|
+
}
|
|
2413
|
+
case "/workflow":
|
|
2414
|
+
if (this.workflowHandler)
|
|
2415
|
+
this.workflowHandler();
|
|
2416
|
+
else
|
|
2417
|
+
console.error(`${DIM}workflow not available (no TUI)${RESET}`);
|
|
2418
|
+
break;
|
|
2419
|
+
case "/multi-reasoner":
|
|
2420
|
+
if (this.multiReasonerHandler)
|
|
2421
|
+
this.multiReasonerHandler();
|
|
2422
|
+
else
|
|
2423
|
+
console.error(`${DIM}multi-reasoner not available (no TUI)${RESET}`);
|
|
2424
|
+
break;
|
|
2425
|
+
case "/token-quota":
|
|
2426
|
+
if (this.tokenQuotaHandler)
|
|
2427
|
+
this.tokenQuotaHandler();
|
|
2428
|
+
else
|
|
2429
|
+
console.error(`${DIM}token-quota not available (no TUI)${RESET}`);
|
|
2430
|
+
break;
|
|
2431
|
+
case "/checkpoint":
|
|
2432
|
+
if (this.checkpointHandler)
|
|
2433
|
+
this.checkpointHandler();
|
|
2434
|
+
else
|
|
2435
|
+
console.error(`${DIM}checkpoint not available (no TUI)${RESET}`);
|
|
2436
|
+
break;
|
|
2437
|
+
case "/workflow-new": {
|
|
2438
|
+
const wnArg = line.slice("/workflow-new".length).trim();
|
|
2439
|
+
if (!wnArg) {
|
|
2440
|
+
console.error(`${DIM}usage: /workflow-new <template> <prefix>${RESET}`);
|
|
2441
|
+
break;
|
|
2442
|
+
}
|
|
2443
|
+
if (this.workflowNewHandler)
|
|
2444
|
+
this.workflowNewHandler(wnArg);
|
|
2445
|
+
else
|
|
2446
|
+
console.error(`${DIM}workflow-new not available (no TUI)${RESET}`);
|
|
2447
|
+
break;
|
|
2448
|
+
}
|
|
2449
|
+
case "/ab-stats":
|
|
2450
|
+
if (this.abStatsHandler)
|
|
2451
|
+
this.abStatsHandler();
|
|
2452
|
+
else
|
|
2453
|
+
console.error(`${DIM}ab-stats not available (no TUI)${RESET}`);
|
|
2454
|
+
break;
|
|
2455
|
+
case "/workflow-chain":
|
|
2456
|
+
if (this.workflowChainHandler)
|
|
2457
|
+
this.workflowChainHandler();
|
|
2458
|
+
else
|
|
2459
|
+
console.error(`${DIM}workflow-chain not available (no TUI)${RESET}`);
|
|
2460
|
+
break;
|
|
2461
|
+
case "/workflow-forecast": {
|
|
2462
|
+
const wfArg = line.slice("/workflow-forecast".length).trim();
|
|
2463
|
+
if (!wfArg) {
|
|
2464
|
+
console.error(`${DIM}usage: /workflow-forecast <template>${RESET}`);
|
|
2465
|
+
break;
|
|
2466
|
+
}
|
|
2467
|
+
if (this.workflowForecastHandler)
|
|
2468
|
+
this.workflowForecastHandler(wfArg);
|
|
2469
|
+
else
|
|
2470
|
+
console.error(`${DIM}workflow-forecast not available (no TUI)${RESET}`);
|
|
2471
|
+
break;
|
|
2472
|
+
}
|
|
2473
|
+
case "/federation":
|
|
2474
|
+
if (this.federationHandler)
|
|
2475
|
+
this.federationHandler();
|
|
2476
|
+
else
|
|
2477
|
+
console.error(`${DIM}federation not available (no TUI)${RESET}`);
|
|
2478
|
+
break;
|
|
2479
|
+
case "/archives":
|
|
2480
|
+
if (this.archivesHandler)
|
|
2481
|
+
this.archivesHandler();
|
|
2482
|
+
else
|
|
2483
|
+
console.error(`${DIM}archives not available (no TUI)${RESET}`);
|
|
2484
|
+
break;
|
|
2485
|
+
case "/runbook-gen":
|
|
2486
|
+
if (this.runbookGenHandler)
|
|
2487
|
+
this.runbookGenHandler();
|
|
2488
|
+
else
|
|
2489
|
+
console.error(`${DIM}runbook-gen not available (no TUI)${RESET}`);
|
|
2490
|
+
break;
|
|
2491
|
+
case "/alert-rules":
|
|
2492
|
+
if (this.alertRulesHandler)
|
|
2493
|
+
this.alertRulesHandler();
|
|
2494
|
+
else
|
|
2495
|
+
console.error(`${DIM}alert-rules not available (no TUI)${RESET}`);
|
|
2496
|
+
break;
|
|
2367
2497
|
case "/clear":
|
|
2368
2498
|
process.stderr.write("\x1b[2J\x1b[H");
|
|
2369
2499
|
break;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Observation, ReasonerResult } from "./types.js";
|
|
2
|
+
export type ReasonerBackend = "opencode" | "claude-code" | "gemini" | "custom";
|
|
3
|
+
export interface ReasonerAssignment {
|
|
4
|
+
sessionTitle: string;
|
|
5
|
+
backend: ReasonerBackend;
|
|
6
|
+
reason: string;
|
|
7
|
+
}
|
|
8
|
+
export interface MultiReasonerConfig {
|
|
9
|
+
defaultBackend: ReasonerBackend;
|
|
10
|
+
sessionOverrides: Record<string, ReasonerBackend>;
|
|
11
|
+
templateMappings: Record<string, ReasonerBackend>;
|
|
12
|
+
difficultyThreshold: number;
|
|
13
|
+
premiumBackend: ReasonerBackend;
|
|
14
|
+
economyBackend: ReasonerBackend;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Determine which reasoner backend to use for each session.
|
|
18
|
+
*/
|
|
19
|
+
export declare function assignReasonerBackends(sessions: Array<{
|
|
20
|
+
title: string;
|
|
21
|
+
template?: string;
|
|
22
|
+
difficultyScore?: number;
|
|
23
|
+
}>, config?: Partial<MultiReasonerConfig>): ReasonerAssignment[];
|
|
24
|
+
/**
|
|
25
|
+
* Split an observation into per-backend groups for routing.
|
|
26
|
+
*/
|
|
27
|
+
export declare function routeObservation(observation: Observation, assignments: ReasonerAssignment[]): Map<ReasonerBackend, Observation>;
|
|
28
|
+
/**
|
|
29
|
+
* Merge results from multiple reasoner backends into a single result.
|
|
30
|
+
*/
|
|
31
|
+
export declare function mergeReasonerResults(results: ReasonerResult[]): ReasonerResult;
|
|
32
|
+
/**
|
|
33
|
+
* Format assignments for TUI display.
|
|
34
|
+
*/
|
|
35
|
+
export declare function formatAssignments(assignments: ReasonerAssignment[]): string[];
|
|
36
|
+
//# sourceMappingURL=multi-reasoner.d.ts.map
|