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/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