titan-agent 6.0.1 → 6.0.3

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.
@@ -53,6 +53,79 @@ function appendHistory(state, phase, note) {
53
53
  state.history.push(event);
54
54
  if (state.history.length > 200) state.history = state.history.slice(-200);
55
55
  }
56
+ const GENERIC_APPROVAL_NOTES = /* @__PURE__ */ new Set([
57
+ "approved",
58
+ "approve",
59
+ "ok",
60
+ "okay",
61
+ "yes",
62
+ "y",
63
+ "go",
64
+ "proceed",
65
+ "do it",
66
+ "lgtm",
67
+ "sure",
68
+ "fine",
69
+ "continue",
70
+ "yeah",
71
+ "yep"
72
+ ]);
73
+ function composeApprovalGuidance(rawNote) {
74
+ const note = (rawNote ?? "").trim();
75
+ const isGeneric = note === "" || note.length <= 8 || GENERIC_APPROVAL_NOTES.has(note.toLowerCase());
76
+ if (isGeneric) {
77
+ return [
78
+ "The user approved this subtask to proceed but did NOT provide additional textual guidance.",
79
+ 'Interpretation: "go ahead with your best-effort interpretation of the original task."',
80
+ "Do NOT re-ask the same question that caused the block \u2014 pick a reasonable assumption from",
81
+ "the available context and proceed. If you genuinely cannot make a reasonable assumption,",
82
+ "complete what you can and report what was unresolved in your reply rather than blocking again."
83
+ ].join(" ");
84
+ }
85
+ return `The user's answer to the question that caused the previous block is: "${note}". Use this as authoritative guidance for the next attempt.`;
86
+ }
87
+ const DAEMON_SPAWN_TAG_MARKERS = [
88
+ /^soma:/i,
89
+ /^autopilot/i,
90
+ /^mission-auto$/i,
91
+ /^self-repair$/i,
92
+ /^self-mod$/i,
93
+ /^self-healing$/i,
94
+ /^self-modification$/i,
95
+ /^canary-eval$/i,
96
+ /^dreaming$/i,
97
+ /^pressure$/i
98
+ ];
99
+ function isDaemonSpawnedGoal(goal) {
100
+ const tags = goal.tags ?? [];
101
+ for (const t of tags) {
102
+ for (const re of DAEMON_SPAWN_TAG_MARKERS) {
103
+ if (re.test(t)) return true;
104
+ }
105
+ }
106
+ return false;
107
+ }
108
+ function fingerprintBlockedQuestion(q) {
109
+ return q.toLowerCase().replace(/attempt\s*\d+/g, "attempt N").replace(/\b\d{4}-\d{2}-\d{2}t[\d:.]+z?\b/g, "").replace(/\b\d+(\.\d+)?\b/g, "N").replace(/\s+/g, " ").trim().slice(0, 120);
110
+ }
111
+ const SAME_BLOCK_TWICE_WINDOW_MS = 60 * 60 * 1e3;
112
+ function shouldAutoCancelOnRecurringBlock(state, goal, blockKind, question, windowMs = SAME_BLOCK_TWICE_WINDOW_MS) {
113
+ if (!isDaemonSpawnedGoal(goal)) {
114
+ return false;
115
+ }
116
+ const fp = fingerprintBlockedQuestion(question);
117
+ const now = Date.now();
118
+ const history = state.blockHistory ?? [];
119
+ const cutoff = now - windowMs;
120
+ const recurrence = history.find((ev) => {
121
+ if (ev.fingerprint !== fp) return false;
122
+ const t = Date.parse(ev.at);
123
+ return Number.isFinite(t) && t >= cutoff;
124
+ });
125
+ const next = [...history, { at: new Date(now).toISOString(), fingerprint: fp, kind: blockKind }];
126
+ state.blockHistory = next.slice(-16);
127
+ return !!recurrence;
128
+ }
56
129
  function isInfrastructureFailure(error) {
57
130
  if (!error) return false;
58
131
  const e = error.toLowerCase();
@@ -497,8 +570,7 @@ async function tickBlocked(goal, state) {
497
570
  const currentId = state.currentSubtaskId;
498
571
  if (currentId) {
499
572
  const subState = state.subtaskStates[currentId];
500
- const note = approval.decisionNote || "Approved";
501
- subState.lastError = `Previous attempt needed info; human provided: "${note}". Try again using this.`;
573
+ subState.lastError = composeApprovalGuidance(approval.decisionNote);
502
574
  }
503
575
  state.blockedReason = void 0;
504
576
  state.phase = "delegating";
@@ -618,6 +690,34 @@ async function pickNextReadySubtask(goal, state) {
618
690
  return null;
619
691
  }
620
692
  async function fileBlockedApproval(state, goal, questions) {
693
+ const primaryQuestion = questions[0] ?? state.blockedReason?.question ?? "Specialist requires input";
694
+ const blockKind = state.blockedReason?.kind ?? "needs_info";
695
+ if (shouldAutoCancelOnRecurringBlock(state, goal, blockKind, primaryQuestion)) {
696
+ const tagSummary = (goal.tags || []).slice(0, 4).join(",") || "(no tags)";
697
+ logger.warn(
698
+ COMPONENT,
699
+ `Auto-cancelling daemon-spawned goal ${goal.id} ("${goal.title.slice(0, 60)}"): same block recurred within ${Math.round(SAME_BLOCK_TWICE_WINDOW_MS / 6e4)}min [tags=${tagSummary}, kind=${blockKind}]`
700
+ );
701
+ state.phase = "cancelled";
702
+ state.blockedReason = void 0;
703
+ state.userControls.cancelRequested = true;
704
+ appendHistory(
705
+ state,
706
+ "cancelled",
707
+ `Auto-cancelled: daemon-spawned goal hit the same block (${blockKind}) twice within the recurrence window.`
708
+ );
709
+ try {
710
+ const { updateGoal } = await import("./goals.js");
711
+ updateGoal(goal.id, { status: "failed" });
712
+ } catch (err) {
713
+ logger.warn(COMPONENT, `Could not flip goal ${goal.id} status after auto-cancel: ${err.message}`);
714
+ }
715
+ try {
716
+ await onGoalBlocked(goal, state);
717
+ } catch {
718
+ }
719
+ return;
720
+ }
621
721
  try {
622
722
  const { shouldCreateApproval } = await import("./notificationThrottle.js");
623
723
  if (!shouldCreateApproval(goal.id, "driver_blocked")) {
@@ -878,17 +978,22 @@ function _resetDriverStateForTests(goalId) {
878
978
  }
879
979
  }
880
980
  export {
981
+ SAME_BLOCK_TWICE_WINDOW_MS,
881
982
  _resetDriverStateForTests,
882
983
  cancelDriver,
984
+ composeApprovalGuidance,
883
985
  deleteDriverState,
884
986
  driveGoal,
987
+ fingerprintBlockedQuestion,
885
988
  forceUnblockDriver,
886
989
  getDriverState,
990
+ isDaemonSpawnedGoal,
887
991
  listActiveDrivers,
888
992
  listAllDrivers,
889
993
  pauseDriver,
890
994
  reprioritizeDriver,
891
995
  resumeDriverControl,
996
+ shouldAutoCancelOnRecurringBlock,
892
997
  sweepStaleDriverStates,
893
998
  tickDriver
894
999
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agent/goalDriver.ts"],"sourcesContent":["/**\n * TITAN — Goal Driver (v4.10.0-local, Phase A)\n *\n * Owns a goal from \"active\" to terminal (done | failed | cancelled).\n * Replaces the passive \"initiative picks one subtask per 5-min tick\"\n * model with an active phase state machine that drives subtasks through\n * specialists, verifies each one, retries on failure via fallbackChain,\n * and reports outcomes back to SOMA.\n *\n * Design principles:\n * - State is persisted to ~/.titan/driver-state/<goalId>.json after\n * every phase transition. Restart-safe.\n * - One tick = one phase transition. Scheduler loops ticks until the\n * driver reaches a terminal phase OR requires waiting (observing a\n * spawned specialist, or blocked on human).\n * - Every phase's state transition is logged to driver state history\n * for UI replay + debugging.\n * - Kill-switch / scope-lock / staging are handled by toolRunner —\n * the driver inherits that protection automatically.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, rmSync, renameSync } from 'fs';\nimport { join, dirname } from 'path';\nimport logger from '../utils/logger.js';\nimport { TITAN_HOME } from '../utils/constants.js';\nimport type {\n DriverPhase, DriverState, DriverSubtaskState, DriverHistoryEvent,\n} from './goalDriverTypes.js';\nimport type { SubtaskKind } from './subtaskTaxonomy.js';\nimport { classifyAll } from './subtaskTaxonomy.js';\nimport { routeForKind, pickAttempt } from './specialistRouter.js';\nimport { nextFallback } from './fallbackChain.js';\nimport { DEFAULT_BUDGET_CAPS, checkBudget, suggestDegradation, recordSpend } from './budgetEnforcer.js';\nimport { structuredSpawn } from './structuredSpawn.js';\nimport { verifyByKind } from './verifier.js';\nimport { onGoalCompleted, onGoalFailed, onGoalBlocked } from './somaFeedback.js';\nimport type { Goal, Subtask } from './goals.js';\n\nconst COMPONENT = 'GoalDriver';\nconst STATE_DIR = join(TITAN_HOME, 'driver-state');\n\n// ── Storage ──────────────────────────────────────────────────────\n\nfunction ensureStateDir(): void {\n try { mkdirSync(STATE_DIR, { recursive: true }); } catch { /* ok */ }\n}\n\nfunction statePath(goalId: string): string {\n return join(STATE_DIR, `${goalId}.json`);\n}\n\nfunction loadState(goalId: string): DriverState | null {\n const path = statePath(goalId);\n if (!existsSync(path)) return null;\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf-8')) as DriverState;\n if (parsed.schemaVersion !== 1) {\n logger.warn(COMPONENT, `State for ${goalId} has unknown schemaVersion=${parsed.schemaVersion} — ignoring`);\n return null;\n }\n return parsed;\n } catch (err) {\n logger.warn(COMPONENT, `Could not parse state for ${goalId}: ${(err as Error).message}`);\n return null;\n }\n}\n\nfunction saveState(state: DriverState): void {\n ensureStateDir();\n state.lastTickAt = new Date().toISOString();\n const path = statePath(state.goalId);\n try {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path + '.tmp', JSON.stringify(state, null, 2));\n // Atomic rename so partial writes never surface\n // (Windows rename-over-existing is OK on Node 22+)\n renameSync(path + '.tmp', path);\n } catch (err) {\n logger.warn(COMPONENT, `Could not persist state for ${state.goalId}: ${(err as Error).message}`);\n }\n}\n\nfunction appendHistory(state: DriverState, phase: DriverPhase, note: string): void {\n const event: DriverHistoryEvent = { at: new Date().toISOString(), phase, note };\n state.history.push(event);\n if (state.history.length > 200) state.history = state.history.slice(-200);\n}\n\n// ── Infrastructure failure detection ──────────────────────────────\n\n/**\n * Detect systematic infrastructure failures that warrant human escalation.\n * Returns true if the error indicates all specialists are failing to produce\n * structured JSON output (thinking patterns, parse errors).\n */\nfunction isInfrastructureFailure(error: string | undefined): boolean {\n if (!error) return false;\n const e = error.toLowerCase();\n // JSON parse failures from structuredSpawn\n const parseFailurePatterns = [\n 'parser could not extract json',\n 'no json block found',\n 'json.parse failure',\n 'prose-fallback:thinking',\n 'thinking prose instead of structured json',\n ];\n return parseFailurePatterns.some(p => e.includes(p));\n}\n\n// ── Init / creation ──────────────────────────────────────────────\n\nfunction freshDriverState(goal: Goal): DriverState {\n const subtaskStates: Record<string, DriverSubtaskState> = {};\n const kinds = classifyAll(goal.subtasks || []);\n for (const sub of goal.subtasks || []) {\n subtaskStates[sub.id] = {\n kind: kinds[sub.id] ?? 'analysis',\n attempts: 0,\n // v4.10.0-local (post-deploy): 5 = full ladder depth\n // (primary + 4 fallbacks). Ensures the claude-code MAX-plan\n // tier is reachable before the subtask gives up. Goal-level\n // maxRetries: 10 remains as the cross-subtask backstop.\n maxAttempts: 5,\n artifacts: [],\n };\n }\n const now = new Date().toISOString();\n return {\n schemaVersion: 1,\n goalId: goal.id,\n phase: 'planning',\n startedAt: now,\n lastTickAt: now,\n budget: { tokensUsed: 0, costUsd: 0, elapsedMs: 0, totalRetries: 0 },\n budgetCaps: { ...DEFAULT_BUDGET_CAPS },\n userControls: {\n paused: false,\n cancelRequested: false,\n priority: (goal.priority as 1 | 2 | 3 | 4 | 5) ?? 3,\n },\n subtaskStates,\n history: [{ at: now, phase: 'planning', note: `Driver started for \"${goal.title}\"` }],\n };\n}\n\n// ── Phase transitions (one tick = one transition) ───────────────\n\nasync function tickPlanning(goal: Goal, state: DriverState): Promise<void> {\n // Ensure subtasks exist (the proposer usually creates them; if a goal\n // came without any, we classify based on title and create a single\n // `analysis` subtask as a placeholder).\n if (!goal.subtasks || goal.subtasks.length === 0) {\n appendHistory(state, 'planning', 'No subtasks — creating single analysis subtask from title');\n try {\n const { addSubtask } = await import('./goals.js');\n addSubtask(goal.id, goal.title, goal.description);\n } catch (err) {\n logger.warn(COMPONENT, `Could not add default subtask to ${goal.id}: ${(err as Error).message}`);\n }\n }\n\n // v4.10.0-local (post-deploy, Fix 9): classify lazily and persist\n // once. Previously this called classifyAll unconditionally on every\n // planning pass, but only persisted new entries — if taxonomy rules\n // changed between boots, restored drivers kept their stale kinds\n // silently. Only classify when we actually need to create a new\n // subtask state entry.\n let kinds: Record<string, ReturnType<typeof classifyAll>[string]> | null = null;\n for (const sub of goal.subtasks || []) {\n if (!state.subtaskStates[sub.id]) {\n if (!kinds) kinds = classifyAll(goal.subtasks || []);\n state.subtaskStates[sub.id] = {\n kind: kinds[sub.id] ?? 'analysis',\n attempts: 0,\n maxAttempts: 5, // per Fix B — matches ladder depth\n artifacts: [],\n };\n }\n }\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Planned: ${Object.keys(state.subtaskStates).length} subtasks classified`);\n}\n\nasync function tickDelegating(goal: Goal, state: DriverState): Promise<void> {\n // Find the next ready subtask (dependencies satisfied, not already done/failed)\n const next = await pickNextReadySubtask(goal, state);\n if (!next) {\n // Re-fetch goal so the allResolved check sees any subtasks that\n // pickNextReadySubtask just marked as failed (durable deadlock\n // recovery from Fix C). Falls back to the stale reference on\n // import failure — worst case we deadlock once more and recover\n // next tick.\n let freshGoal: Goal = goal;\n try {\n const { getGoal } = await import('./goals.js');\n freshGoal = getGoal(goal.id) || goal;\n } catch { /* ok */ }\n // No more ready subtasks — either all done or all blocked on deps.\n // Check if everything's done or failed:\n const allResolved = (freshGoal.subtasks || []).every(\n s => s.status === 'done' || s.status === 'failed' || s.status === 'skipped',\n );\n if (allResolved) {\n // v4.10.0-local (post-deploy): clear currentSubtaskId so\n // tickVerifying takes the whole-goal-verify branch\n // (line 316). Otherwise it keeps trying to verify the\n // last-touched subtask (which may be stale or terminal)\n // and oscillates verifying → iterating → delegating.\n state.currentSubtaskId = undefined;\n state.phase = 'verifying';\n appendHistory(state, 'verifying', 'All subtasks resolved — running final verification');\n } else {\n // Dependencies blocking — pause-block for human\n state.phase = 'blocked';\n state.blockedReason = {\n question: `Goal \"${goal.title}\" is deadlocked. All pending subtasks have unresolved dependencies and none are ready to run. Please review the subtask order and dependencies.`,\n approvalId: '', // filled below if we file an approval\n sinceAt: new Date().toISOString(),\n kind: 'dep_deadlock',\n };\n appendHistory(state, 'blocked', 'Dependency deadlock');\n }\n return;\n }\n\n state.currentSubtaskId = next.id;\n const subState = state.subtaskStates[next.id];\n subState.attempts += 1;\n\n // v4.10.0-local (post-deploy, Fix B): per-subtask attempt cap. A single\n // unlucky subtask can no longer consume the whole goal's retry budget.\n // We fail fast on THIS subtask and let the driver move to the next one.\n const cap = subState.maxAttempts ?? 5;\n if (subState.attempts > cap) {\n subState.verificationResult = {\n passed: false,\n reason: `Per-subtask cap exceeded (${cap})`,\n verifier: 'budget',\n };\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, subState.lastError || `per-subtask cap exceeded (${cap} attempts)`);\n } catch { /* ok */ }\n state.currentSubtaskId = undefined;\n state.phase = 'delegating'; // next tick picks up the next ready subtask\n appendHistory(state, 'delegating', `Subtask ${next.id} exceeded per-subtask cap (${cap}) — moved on`);\n return;\n }\n\n // Pass the per-subtask cap — NOT goal-level maxRetries — to\n // nextFallback. The ladder has 5 tiers; matching means we can reach\n // the final (claude-code MAX) tier before declaring exhaustion.\n const strategy = nextFallback(subState.kind, subState.attempts - 1, subState.lastError, cap);\n if (!strategy) {\n // v4.10.0-local fix: Check if exhaustion is due to infrastructure failure\n // (systematic JSON parse errors). If so, escalate to human instead of\n // silently failing the subtask.\n if (isInfrastructureFailure(subState.lastError)) {\n state.phase = 'escalated';\n state.blockedReason = {\n question: `Goal \"${goal.title}\" — all specialists failed to produce valid output after ${goal.subtasks?.length ?? 0} subtask(s). This usually means the model is misconfigured, the task is too vague, or the specialist doesn't have the right tools. Please review the goal description or switch the model tier.`,\n approvalId: '',\n sinceAt: new Date().toISOString(),\n kind: 'infrastructure_failure',\n };\n appendHistory(state, 'escalated', `Subtask ${next.id}: infrastructure failure — all specialists failed JSON output`);\n await fileBlockedApproval(state, goal, [state.blockedReason.question]);\n return;\n }\n\n // Exhausted retries for this subtask (normal failure)\n subState.verificationResult = {\n passed: false,\n reason: 'Max retries exhausted',\n verifier: 'budget',\n };\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, 'max-retries-exhausted');\n } catch { /* ok */ }\n state.currentSubtaskId = undefined;\n state.phase = 'delegating'; // try next subtask\n appendHistory(state, 'delegating', `Subtask ${next.id} exhausted retries — moved on`);\n return;\n }\n\n subState.specialist = strategy.specialist;\n subState.pendingSpawn = {\n attemptedAt: new Date().toISOString(),\n specialist: strategy.specialist,\n };\n state.phase = 'observing';\n appendHistory(\n state,\n 'observing',\n `Spawning ${strategy.specialist} for subtask \"${next.title}\" (kind=${subState.kind}, attempt ${subState.attempts})`,\n );\n\n // Actually fire the spawn — runs in-tick, driver waits for the return.\n // (Future: async wakeup path so driver can observe multiple concurrent\n // spawns; for now one at a time per goal.)\n try {\n const startMs = Date.now();\n const result = await structuredSpawn({\n specialistId: strategy.specialist,\n task: `${next.title}\\n\\n${next.description}${strategy.promptAdjustment ?? ''}`,\n modelOverride: strategy.modelOverride,\n toolAllowlist: routeForKind(subState.kind).toolAllowlist,\n maxRounds: strategy.maxRounds,\n });\n const durationMs = Date.now() - startMs;\n recordSpend(state, {\n elapsedMs: durationMs,\n tokens: result.tokensUsed ?? 0,\n costUsd: result.costUsd ?? 0,\n });\n\n // Store artifacts\n subState.artifacts = [...new Set([\n ...subState.artifacts,\n ...result.artifacts.map(a => a.ref),\n ])];\n\n // Decide phase based on spawn status\n if (result.status === 'done') {\n state.phase = 'verifying';\n appendHistory(state, 'verifying', `Spawn returned done with ${result.artifacts.length} artifact(s), confidence ${result.confidence.toFixed(2)}`);\n // Stash the spawn result so verifying can read it\n (subState as DriverSubtaskState & { lastSpawnResult?: unknown }).lastSpawnResult = result;\n } else if (result.status === 'failed') {\n subState.lastError = result.reasoning || 'failed';\n state.phase = 'iterating';\n appendHistory(state, 'iterating', `Spawn returned failed: ${subState.lastError.slice(0, 120)}`);\n } else if (result.status === 'needs_info' || result.status === 'blocked') {\n state.phase = 'blocked';\n // v4.14.0: build a rich, contextual blocked reason instead of\n // the generic \"Specialist requires input\" that tells the user\n // nothing about what actually went wrong.\n const subtaskTitle = next.title || 'Unnamed subtask';\n const specialistName = strategy.specialist || subState.specialist || 'specialist';\n const attemptCount = subState.attempts;\n const lastErr = subState.lastError;\n const rawQuestion = result.questions[0] ?? '';\n\n let richQuestion: string;\n if (rawQuestion && rawQuestion.length > 10 && !rawQuestion.toLowerCase().includes('specialist requires')) {\n // The specialist gave a real question — use it but wrap with context\n richQuestion = `Goal \"${goal.title}\" is stuck on subtask \"${subtaskTitle}\" (attempt ${attemptCount}, specialist: ${specialistName}).\\n\\n${rawQuestion}`;\n } else if (lastErr) {\n richQuestion = `Goal \"${goal.title}\" — subtask \"${subtaskTitle}\" failed after ${attemptCount} attempt(s) with specialist ${specialistName}.\\n\\nError: ${lastErr.slice(0, 200)}\\n\\nWhat should the specialist do next?`;\n } else {\n richQuestion = `Goal \"${goal.title}\" — subtask \"${subtaskTitle}\" is blocked after ${attemptCount} attempt(s) with specialist ${specialistName}. The specialist could not complete the task and needs guidance on how to proceed.`;\n }\n\n state.blockedReason = {\n question: richQuestion,\n approvalId: '',\n sinceAt: new Date().toISOString(),\n kind: 'needs_info',\n };\n appendHistory(state, 'blocked', `Spawn needs info: ${richQuestion.slice(0, 120)}`);\n await fileBlockedApproval(state, goal, [richQuestion, ...result.questions.slice(1)]);\n }\n subState.pendingSpawn = undefined;\n } catch (err) {\n const msg = (err as Error).message;\n subState.lastError = msg;\n state.phase = 'iterating';\n appendHistory(state, 'iterating', `Spawn threw: ${msg.slice(0, 120)}`);\n }\n}\n\nasync function tickObserving(goal: Goal, state: DriverState): Promise<void> {\n // In Phase A, spawns are sync (await structuredSpawn completes in tickDelegating).\n // This phase exists for future async-wakeup integration; for now it just\n // advances based on whatever subState said after the spawn.\n //\n // v4.10.0-local (post-deploy, Fix 8): be a no-op unless there's a\n // pending spawn waiting for a wakeup. Previously this unconditionally\n // transitioned to iterating, which tickIterating treated as a failure\n // and burned an attempt on goals that never actually spawned. Now the\n // observing phase only advances when there's a spawn to observe.\n void goal;\n const currentId = state.currentSubtaskId;\n const subState = currentId ? state.subtaskStates[currentId] : undefined;\n if (!subState?.pendingSpawn) {\n // Nothing to observe — bounce back to delegating without counting\n // as a retry.\n state.phase = 'delegating';\n return;\n }\n state.phase = 'iterating';\n appendHistory(state, 'iterating', 'Observe tick with no spawn progress — iterating');\n}\n\nasync function tickIterating(goal: Goal, state: DriverState): Promise<void> {\n const currentId = state.currentSubtaskId;\n if (!currentId) {\n state.phase = 'delegating';\n return;\n }\n const subState = state.subtaskStates[currentId];\n\n // v4.10.0-local (post-deploy, Fix A): forward-progress detector.\n // If the same lastError (first 80 chars) repeats 3 times in a row,\n // retrying isn't producing new information — fail this subtask and\n // move on. Prevents the verifying↔iterating→delegating oscillation\n // observed on stuck drivers. Uses a semantic signal (error text)\n // rather than phase-pattern detection, which false-positives on\n // legitimate multi-phase verify passes.\n const fingerprint = (subState.lastError || '').slice(0, 80).toLowerCase().trim();\n if (fingerprint) {\n if (fingerprint === subState.lastErrorFingerprint) {\n subState.consecutiveIdenticalErrors = (subState.consecutiveIdenticalErrors || 0) + 1;\n } else {\n subState.consecutiveIdenticalErrors = 1;\n subState.lastErrorFingerprint = fingerprint;\n }\n if ((subState.consecutiveIdenticalErrors ?? 0) >= 3) {\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, currentId, `Stuck loop: same error 3× in a row: ${fingerprint}`);\n } catch { /* ok */ }\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Broke stall loop on ${currentId} (same error 3×)`);\n return;\n }\n }\n\n // v4.10.0-local (post-deploy, Fix 8): do NOT increment totalRetries\n // here. Per-subtask attempts are counted in tickDelegating. Counting\n // again here caused double-billing: one failed spawn consumed 2 budget\n // units. Retry budget is meant to track subtask-transition retries,\n // not individual spawn retries.\n const check = checkBudget(state);\n if (check.status === 'exceeded') {\n const suggestion = suggestDegradation(state);\n if (suggestion === 'ask_human') {\n state.phase = 'blocked';\n state.blockedReason = {\n question: `Goal \"${goal.title}\" — budget exceeded (${check.message}). The driver has used ${state.budget.costUsd.toFixed(2)} USD so far. Continue with extended budget, de-scope, or cancel?`,\n approvalId: '',\n sinceAt: new Date().toISOString(),\n kind: 'budget_exceeded',\n };\n appendHistory(state, 'blocked', check.message);\n await fileBlockedApproval(state, goal, [state.blockedReason.question]);\n return;\n }\n }\n\n if (subState.attempts >= state.budgetCaps.maxRetries) {\n // Gap 2 (plan-this-logical-ocean): before giving up on this subtask,\n // consult the bounded continuation counter. If the per-subtask cap\n // hasn't been hit (max 2, persisted to disk so restarts can't\n // bypass), halve attempts and let it try again. This is the\n // \"plan_only\" signal — spawns kept producing plans/output that\n // wouldn't verify. Two extra cycles across a restart boundary is\n // the cheapest escape from the verifying↔iterating oscillation that\n // the existing stuck-loop detector can't catch (different errors\n // each time, but no real progress).\n const { shouldContinue } = await import('./runContinuations.js');\n const continuationKey = `${goal.id}:${currentId}`;\n if (shouldContinue(continuationKey, 'plan_only')) {\n const before = subState.attempts;\n subState.attempts = Math.max(0, Math.floor(subState.attempts / 2));\n subState.consecutiveIdenticalErrors = 0;\n subState.lastErrorFingerprint = undefined;\n appendHistory(state, 'delegating', `Continuation granted on ${currentId}: attempts halved ${before} → ${subState.attempts}`);\n state.phase = 'delegating';\n return;\n }\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, currentId, subState.lastError || 'max retries');\n } catch { /* ok */ }\n appendHistory(state, 'delegating', `Subtask ${currentId} failed after ${subState.attempts} attempts`);\n state.phase = 'delegating';\n return;\n }\n\n // Back to delegating to try the next fallback\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Iterating on subtask ${currentId} — attempt ${subState.attempts + 1}`);\n}\n\nasync function tickVerifying(goal: Goal, state: DriverState): Promise<void> {\n const currentId = state.currentSubtaskId;\n if (!currentId) {\n // Whole-goal verify — check per-subtask results against the goal\n // file's subtask statuses. A subtask passes the whole-goal check\n // iff EITHER (a) its verificationResult.passed === true, OR\n // (b) the goal file reports it as done/skipped (completed through\n // an external path, e.g. human marked it done).\n //\n // v4.10.0-local (post-deploy, Fix 8): previous check was\n // `passed !== false`, which treated subtasks that never got\n // verified at all (verificationResult undefined) as passing\n // vacuously. Now we require an explicit pass signal OR an\n // explicit terminal status on the goal-side subtask.\n const goalSubs = goal.subtasks || [];\n const subById = new Map(goalSubs.map(s => [s.id, s]));\n const allPassed = Object.entries(state.subtaskStates).every(([subId, subState]) => {\n if (subState.verificationResult?.passed === true) return true;\n const goalSub = subById.get(subId);\n if (goalSub?.status === 'done' || goalSub?.status === 'skipped') return true;\n return false;\n });\n state.phase = allPassed ? 'reporting' : 'failed';\n appendHistory(state, state.phase, allPassed ? 'All subtasks verified' : 'Some subtasks failed verification');\n return;\n }\n const subState = state.subtaskStates[currentId];\n const subtask = (goal.subtasks || []).find(s => s.id === currentId);\n if (!subtask) {\n state.phase = 'delegating';\n return;\n }\n const lastSpawn = (subState as DriverSubtaskState & { lastSpawnResult?: unknown }).lastSpawnResult;\n if (!lastSpawn) {\n // No spawn result to verify against — iterate\n state.phase = 'iterating';\n appendHistory(state, 'iterating', 'Verify with no spawn result — iterating');\n return;\n }\n\n const verifyResult = await verifyByKind({\n kind: subState.kind,\n subtask,\n spawnResult: lastSpawn as Parameters<typeof verifyByKind>[0]['spawnResult'],\n });\n subState.verificationResult = verifyResult;\n\n if (verifyResult.passed) {\n try {\n const { completeSubtask } = await import('./goals.js');\n // v4.10.0-local polish: store the SPAWN'S actual output\n // (reasoning + artifacts summary), not the verifier's pass\n // message. Prior behavior stored \"Analysis 171 chars, conf 0.95\"\n // in the subtask.result field, losing the actual content.\n const spawn = lastSpawn as Parameters<typeof verifyByKind>[0]['spawnResult'];\n const artifactSummary = spawn.artifacts.length > 0\n ? `\\n\\nArtifacts:\\n${spawn.artifacts.map(a => ` - [${a.type}] ${a.ref}${a.description ? ` — ${a.description}` : ''}`).join('\\n')}`\n : '';\n const contentToSave = (spawn.reasoning || spawn.rawResponse || verifyResult.reason) + artifactSummary;\n completeSubtask(goal.id, currentId, contentToSave);\n } catch { /* ok */ }\n appendHistory(state, 'delegating', `Subtask ${currentId} verified: ${verifyResult.reason.slice(0, 120)}`);\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n } else {\n subState.lastError = `Verification failed: ${verifyResult.reason}`;\n appendHistory(state, 'iterating', `Verification failed: ${verifyResult.reason.slice(0, 120)}`);\n state.phase = 'iterating';\n }\n}\n\nasync function tickReporting(goal: Goal, state: DriverState): Promise<void> {\n const durationMs = Date.now() - new Date(state.startedAt).getTime();\n const specialistsUsed = [...new Set(\n Object.values(state.subtaskStates)\n .map(s => s.specialist)\n .filter((x): x is string => !!x),\n )];\n state.retrospective = {\n success: true,\n durationMs,\n tokensUsed: state.budget.tokensUsed,\n costUsd: state.budget.costUsd,\n lessonsLearned: [\n `Completed ${Object.keys(state.subtaskStates).length} subtasks across ${specialistsUsed.length} specialist(s) in ${Math.round(durationMs / 1000)}s`,\n ],\n specialistsUsed,\n };\n try {\n const { updateGoal } = await import('./goals.js');\n updateGoal(goal.id, { status: 'completed' });\n } catch { /* ok */ }\n state.phase = 'done';\n appendHistory(state, 'done', `Goal completed: ${state.retrospective.lessonsLearned[0]}`);\n try { await onGoalCompleted(goal, state); } catch { /* ok */ }\n // v4.10.0-local (Phase B): record retrospective for future goal learning\n try {\n const { saveRetrospective } = await import('./retrospectives.js');\n await saveRetrospective(goal, state);\n } catch { /* ok */ }\n\n // Fire episode\n try {\n const { recordEpisode } = await import('../memory/episodic.js');\n recordEpisode({\n kind: 'goal_completed',\n summary: `Driver completed goal \"${goal.title}\" (${Math.round(durationMs / 1000)}s, ${Object.keys(state.subtaskStates).length} subtasks, specialists: ${specialistsUsed.join(', ')})`,\n detail: state.history.slice(-10).map(h => `${h.at} ${h.phase}: ${h.note}`).join('\\n'),\n tags: ['goal-driver', 'goal_completed', goal.id, ...(goal.tags || [])],\n });\n } catch { /* ok */ }\n}\n\nasync function tickBlocked(goal: Goal, state: DriverState): Promise<void> {\n // v4.10.0-local (post-deploy, Fix F): auto-unblock stale blocked states.\n // The block was either never backed by an approval (approvalId empty —\n // bookkeeping bug) or the approval no longer exists (deleted / TTL'd).\n // After 10 minutes of sitting idle with no live approval, retry the\n // subtask rather than sitting forever waiting for a human who has no\n // way to resolve this. Uses forceUnblockDriver so the recovery is\n // consistent with the manual API path.\n const approvalId = state.blockedReason?.approvalId;\n const sinceAt = state.blockedReason?.sinceAt;\n const sinceMs = sinceAt ? Date.now() - new Date(sinceAt).getTime() : 0;\n const STALE_MS = 10 * 60 * 1000;\n if (sinceMs > STALE_MS) {\n let approval: { status?: string } | null = null;\n if (approvalId) {\n try {\n const { getApproval } = await import('./commandPost.js');\n approval = getApproval(approvalId) as { status?: string } | null;\n } catch { /* ok */ }\n }\n if (!approvalId || !approval) {\n // Unblock in-place on the shared state reference. We can't\n // call forceUnblockDriver here because it does its own\n // loadState/saveState cycle, and tickDriver's outer saveState\n // would then overwrite those changes with our stale local\n // state. Mirror forceUnblockDriver's logic here.\n logger.info(COMPONENT, `Auto-unblocking stale blocked state for ${goal.id} (approvalId=${approvalId || 'empty'}, age=${Math.round(sinceMs / 60000)}min)`);\n const note = `stale block auto-recovered (age ${Math.round(sinceMs / 60000)}min)`;\n const wasBudget = state.blockedReason?.kind === 'budget_exceeded'\n || /budget.*exceed|retries exceed/i.test(state.blockedReason?.question || '');\n if (wasBudget) {\n state.budget.totalRetries = Math.floor(state.budget.totalRetries / 2);\n }\n const currentId = state.currentSubtaskId;\n if (currentId && state.subtaskStates[currentId]) {\n const sub = state.subtaskStates[currentId];\n sub.attempts = Math.floor(sub.attempts / 2);\n sub.consecutiveIdenticalErrors = 0;\n }\n state.blockedReason = undefined;\n state.phase = 'iterating';\n appendHistory(state, 'iterating', `Force-unblocked: ${note}`);\n return;\n }\n }\n\n // Check if the blocking approval has been decided\n if (!approvalId) return; // nothing to unblock from\n try {\n const { getApproval } = await import('./commandPost.js');\n const approval = getApproval(approvalId);\n if (!approval) return;\n if (approval.status === 'pending') return; // still waiting\n if (approval.status === 'approved') {\n // Incorporate human answer into the current subtask's context\n const currentId = state.currentSubtaskId;\n if (currentId) {\n const subState = state.subtaskStates[currentId];\n const note = approval.decisionNote || 'Approved';\n subState.lastError = `Previous attempt needed info; human provided: \"${note}\". Try again using this.`;\n }\n state.blockedReason = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Unblocked by human (approval ${approvalId})`);\n return;\n }\n if (approval.status === 'rejected') {\n state.phase = 'failed';\n appendHistory(state, 'failed', `Goal rejected by human via approval ${approvalId}`);\n await onGoalFailed(goal, state, 'rejected by human');\n return;\n }\n } catch { /* ok */ }\n}\n\nasync function tickEscalated(goal: Goal, state: DriverState): Promise<void> {\n // v4.10.0-local: Escalated phase handles systematic infrastructure failures.\n // Similar to blocked, but specifically for JSON parse failures that indicate\n // model tier issues. Requires human intervention to fix infrastructure.\n\n const approvalId = state.blockedReason?.approvalId;\n const sinceAt = state.blockedReason?.sinceAt;\n const sinceMs = sinceAt ? Date.now() - new Date(sinceAt).getTime() : 0;\n\n // Check if the escalation approval has been decided\n if (!approvalId) return;\n try {\n const { getApproval } = await import('./commandPost.js');\n const approval = getApproval(approvalId);\n if (!approval) return;\n if (approval.status === 'pending') return; // still waiting\n\n if (approval.status === 'approved') {\n // Human acknowledged the infrastructure issue and wants to retry\n // Reset the subtask attempts to give it another go with potentially\n // new model configuration\n const currentId = state.currentSubtaskId;\n if (currentId) {\n const subState = state.subtaskStates[currentId];\n subState.attempts = 0; // Reset to allow fresh attempts\n subState.consecutiveIdenticalErrors = 0;\n subState.lastError = undefined;\n subState.lastErrorFingerprint = undefined;\n }\n state.blockedReason = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Escalation resolved by human — retrying subtask ${currentId} with fresh attempts`);\n return;\n }\n\n if (approval.status === 'rejected') {\n // Human decided to fail the goal rather than retry\n state.phase = 'failed';\n appendHistory(state, 'failed', `Infrastructure escalation rejected — goal failed`);\n await onGoalFailed(goal, state, 'infrastructure escalation rejected by human');\n return;\n }\n } catch { /* ok */ }\n}\n\nasync function tickFailed(goal: Goal, state: DriverState): Promise<void> {\n const durationMs = Date.now() - new Date(state.startedAt).getTime();\n state.retrospective = {\n success: false,\n durationMs,\n tokensUsed: state.budget.tokensUsed,\n costUsd: state.budget.costUsd,\n lessonsLearned: [\n `Goal failed after ${Math.round(durationMs / 1000)}s, ${state.budget.totalRetries} retries`,\n ],\n specialistsUsed: [...new Set(Object.values(state.subtaskStates).map(s => s.specialist).filter((x): x is string => !!x))],\n };\n try {\n const { updateGoal } = await import('./goals.js');\n updateGoal(goal.id, { status: 'failed' });\n } catch { /* ok */ }\n try { await onGoalFailed(goal, state, 'driver terminated in failed state'); } catch { /* ok */ }\n // Phase B: failed retrospectives are the most valuable — they teach us what to avoid\n try {\n const { saveRetrospective } = await import('./retrospectives.js');\n await saveRetrospective(goal, state);\n } catch { /* ok */ }\n try {\n const { recordEpisode } = await import('../memory/episodic.js');\n recordEpisode({\n kind: 'goal_failed',\n summary: `Driver failed goal \"${goal.title}\" after ${Math.round(durationMs / 1000)}s`,\n detail: state.history.slice(-15).map(h => `${h.at} ${h.phase}: ${h.note}`).join('\\n'),\n tags: ['goal-driver', 'goal_failed', goal.id, ...(goal.tags || [])],\n });\n } catch { /* ok */ }\n}\n\nasync function tickCancelled(goal: Goal, state: DriverState): Promise<void> {\n try {\n const { updateGoal } = await import('./goals.js');\n updateGoal(goal.id, { status: 'failed' });\n } catch { /* ok */ }\n appendHistory(state, 'cancelled', 'Cancelled by user');\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\nasync function pickNextReadySubtask(goal: Goal, state: DriverState): Promise<Subtask | null> {\n // v4.10.0-local (post-deploy, Fix C): synchronous deadlock recovery.\n // If a pending subtask has exhausted its attempts with a failed\n // verification, await failSubtask to persist the failure *before*\n // continuing. The previous async mutation was ephemeral — the next\n // tick's getGoal() call saw stale data and re-entered the deadlock\n // branch in tickDelegating. This version is durable.\n const cap = (id: string) => state.subtaskStates[id]?.maxAttempts ?? 5;\n for (const sub of goal.subtasks || []) {\n if (sub.status !== 'pending') continue;\n const subState = state.subtaskStates[sub.id];\n if (!subState) continue;\n const exhaustedAttempts = subState.attempts >= cap(sub.id)\n || subState.attempts >= state.budgetCaps.maxRetries;\n if (subState.verificationResult?.passed === false && exhaustedAttempts) {\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, sub.id, subState.lastError || 'max retries exceeded (deadlock recovery)');\n // failSubtask mutates the cached goalsCache in place, so our\n // `goal` reference (passed by the caller, loaded from the\n // same cache) now reflects status: 'failed'. Belt-and-braces:\n // also update our local object in case the cache was bypassed.\n sub.status = 'failed';\n } catch { /* ok — driver will re-try next tick */ }\n continue;\n }\n // Respect dependsOn: skip subtasks whose prerequisites are not completed\n const depsSatisfied = (sub.dependsOn ?? []).every(depId => {\n const dep = goal.subtasks?.find(s => s.id === depId);\n return dep?.status === 'done';\n });\n if (!depsSatisfied) continue;\n return sub;\n }\n return null;\n}\n\nasync function fileBlockedApproval(\n state: DriverState,\n goal: Goal,\n questions: string[],\n): Promise<void> {\n // v4.10.0-local (Phase B): throttle to 1 per (goalId, driver_blocked) per 5 min\n try {\n const { shouldCreateApproval } = await import('./notificationThrottle.js');\n if (!shouldCreateApproval(goal.id, 'driver_blocked')) {\n logger.debug(COMPONENT, `Throttled duplicate driver_blocked approval for goal ${goal.id}`);\n // Still record SOMA feedback since we DID get blocked\n try { await onGoalBlocked(goal, state); } catch { /* ok */ }\n return;\n }\n } catch { /* if throttle module unavailable, fall through */ }\n try {\n const { createApproval } = await import('./commandPost.js');\n const subState = state.currentSubtaskId ? state.subtaskStates[state.currentSubtaskId] : undefined;\n const subtaskTitle = goal.subtasks?.find(s => s.id === state.currentSubtaskId)?.title;\n const approval = createApproval({\n type: 'custom',\n requestedBy: 'goal-driver',\n payload: {\n kind: 'driver_blocked',\n goalId: goal.id,\n goalTitle: goal.title,\n question: questions[0] ?? state.blockedReason?.question ?? 'Specialist requires input',\n allQuestions: questions,\n blockedPhase: state.phase,\n currentSubtaskId: state.currentSubtaskId,\n subtaskKind: subState?.kind,\n subtaskTitle,\n specialist: subState?.specialist,\n attempts: subState?.attempts,\n lastError: subState?.lastError,\n urgency: 'high',\n },\n linkedIssueIds: [],\n });\n if (approval?.id && state.blockedReason) {\n state.blockedReason.approvalId = approval.id;\n }\n // Broadcast via SSE if broadcaster exists (throttled separately)\n try {\n const { shouldBroadcast } = await import('./notificationThrottle.js');\n if (shouldBroadcast('driver:blocked', goal.id)) {\n const g = globalThis as unknown as { __titan_sse_broadcast?: (topic: string, payload: unknown) => void };\n if (typeof g.__titan_sse_broadcast === 'function') {\n g.__titan_sse_broadcast('driver:blocked', {\n goalId: goal.id,\n goalTitle: goal.title,\n question: questions[0] ?? 'Specialist requires input',\n approvalId: approval?.id,\n });\n }\n }\n } catch { /* ok */ }\n } catch (err) {\n logger.warn(COMPONENT, `Could not file blocked-on-human approval: ${(err as Error).message}`);\n }\n // SOMA feedback: small hunger bump + slight purpose stall (via metricGuard)\n try { await onGoalBlocked(goal, state); } catch { /* ok */ }\n}\n\n// ── Main tick loop ──────────────────────────────────────────────\n\nexport async function tickDriver(goalId: string): Promise<DriverPhase> {\n const { getGoal } = await import('./goals.js');\n const goal = getGoal(goalId);\n if (!goal) return 'failed';\n\n let state = loadState(goalId);\n if (!state) {\n state = freshDriverState(goal);\n saveState(state);\n }\n\n // Respect user controls\n if (state.userControls.cancelRequested) {\n state.phase = 'cancelled';\n await tickCancelled(goal, state);\n saveState(state);\n return 'cancelled';\n }\n if (state.userControls.paused) return state.phase;\n\n // Kill switch inherits automatically — toolRunner gates writes,\n // spawn_agent early-returns. Drivers don't need to re-check.\n\n try {\n switch (state.phase) {\n case 'planning': await tickPlanning(goal, state); break;\n case 'delegating': await tickDelegating(goal, state); break;\n case 'observing': await tickObserving(goal, state); break;\n case 'iterating': await tickIterating(goal, state); break;\n case 'verifying': await tickVerifying(goal, state); break;\n case 'reporting': await tickReporting(goal, state); break;\n case 'blocked': await tickBlocked(goal, state); break;\n case 'escalated': await tickEscalated(goal, state); break;\n case 'failed': await tickFailed(goal, state); break;\n case 'done':\n case 'cancelled':\n break;\n }\n } catch (err) {\n logger.warn(COMPONENT, `Tick for ${goalId} threw in phase=${state.phase}: ${(err as Error).message}`);\n appendHistory(state, state.phase, `Tick threw: ${(err as Error).message.slice(0, 120)}`);\n }\n\n saveState(state);\n return state.phase;\n}\n\n/**\n * Drive a goal synchronously to a terminal or waiting state. Ticks in a\n * loop with a small pause between ticks so observing/blocked states give\n * the event loop time to breathe. Returns when the driver is terminal\n * (done/failed/cancelled) or waiting on a human (blocked).\n */\nexport async function driveGoal(goalId: string, maxTicks = 200): Promise<DriverPhase> {\n let last: DriverPhase = 'planning';\n for (let i = 0; i < maxTicks; i++) {\n last = await tickDriver(goalId);\n if (last === 'done' || last === 'failed' || last === 'cancelled' || last === 'blocked' || last === 'escalated') break;\n // Mini-delay between ticks to let IO + timers run\n await new Promise(res => setTimeout(res, 50));\n }\n return last;\n}\n\n// ── External API ────────────────────────────────────────────────\n\nexport function getDriverState(goalId: string): DriverState | null {\n return loadState(goalId);\n}\n\nexport function listActiveDrivers(): DriverState[] {\n ensureStateDir();\n if (!existsSync(STATE_DIR)) return [];\n const out: DriverState[] = [];\n for (const file of readdirSync(STATE_DIR)) {\n if (!file.endsWith('.json')) continue;\n const goalId = file.slice(0, -5);\n const s = loadState(goalId);\n if (s && s.phase !== 'done' && s.phase !== 'failed' && s.phase !== 'cancelled') out.push(s);\n }\n return out;\n}\n\nexport function listAllDrivers(): DriverState[] {\n ensureStateDir();\n if (!existsSync(STATE_DIR)) return [];\n const out: DriverState[] = [];\n for (const file of readdirSync(STATE_DIR)) {\n if (!file.endsWith('.json')) continue;\n const goalId = file.slice(0, -5);\n const s = loadState(goalId);\n if (s) out.push(s);\n }\n return out;\n}\n\nexport function pauseDriver(goalId: string): boolean {\n const s = loadState(goalId);\n if (!s) return false;\n s.userControls.paused = true;\n saveState(s);\n return true;\n}\n\nexport function resumeDriverControl(goalId: string): boolean {\n const s = loadState(goalId);\n if (!s) return false;\n s.userControls.paused = false;\n saveState(s);\n return true;\n}\n\n/**\n * Force a blocked driver back to iterating. Used when the block was\n * triggered by a machine-level failure (parse error, model glitch)\n * rather than a genuine human-answerable question, and the underlying\n * issue has been fixed (e.g. model swap).\n *\n * Also auto-rejects any pending approval the driver was blocked on so\n * it doesn't clutter the Approvals UI forever.\n */\nexport async function forceUnblockDriver(goalId: string, note = 'auto-unblock'): Promise<boolean> {\n const s = loadState(goalId);\n if (!s) return false;\n if (s.phase !== 'blocked') return false;\n\n // Auto-reject the bogus approval if one exists\n const approvalId = s.blockedReason?.approvalId;\n if (approvalId) {\n try {\n const { rejectApproval } = await import('./commandPost.js');\n rejectApproval(approvalId, 'force-unblock', `Auto-rejected by force-unblock: ${note}`);\n } catch { /* ok — approval might not exist anymore */ }\n }\n\n // If the block was budget-related, halve the retry counter so the\n // driver has headroom to retry with the underlying fix in place.\n // This is preferable to a full reset — it still records that the\n // goal has been difficult, just gives it another shot.\n const wasBudget = s.blockedReason?.kind === 'budget_exceeded'\n || /budget.*exceed|retries exceed/i.test(s.blockedReason?.question || '');\n if (wasBudget) {\n s.budget.totalRetries = Math.floor(s.budget.totalRetries / 2);\n appendHistory(s, 'iterating', `Force-unblock: budget halved (${s.budget.totalRetries} / ${s.budgetCaps.maxRetries} retries)`);\n }\n\n // v4.10.0-local (post-deploy, Fix F): also halve the per-subtask\n // attempt counter for the current subtask. Otherwise the subtask\n // re-hits its per-subtask cap immediately on resume (from Fix B)\n // and we just re-enter the blocked state next tick.\n const currentId = s.currentSubtaskId;\n if (currentId && s.subtaskStates[currentId]) {\n const sub = s.subtaskStates[currentId];\n const before = sub.attempts;\n sub.attempts = Math.floor(sub.attempts / 2);\n sub.consecutiveIdenticalErrors = 0; // reset stall detector too\n appendHistory(s, 'iterating', `Force-unblock: per-subtask attempts halved on ${currentId} (${before} → ${sub.attempts})`);\n }\n\n s.blockedReason = undefined;\n s.phase = 'iterating'; // iterating retries the current subtask with a fresh spawn\n appendHistory(s, 'iterating', `Force-unblocked: ${note}`);\n saveState(s);\n logger.info(COMPONENT, `Force-unblocked driver ${goalId}: ${note}${wasBudget ? ' (budget halved)' : ''}`);\n return true;\n}\n\nexport function cancelDriver(goalId: string): boolean {\n const s = loadState(goalId);\n if (!s) return false;\n s.userControls.cancelRequested = true;\n saveState(s);\n return true;\n}\n\n/**\n * Permanently remove a driver state file. Used by `resumeDriversAfterRestart`\n * to reap drivers whose goal no longer exists, and by the boot-time stale\n * sweep to cull drivers that haven't ticked in a long time.\n *\n * v5.5.31+. Before this, `cancelDriver` only flipped a `cancelRequested`\n * flag without deleting — meaning orphan driver files (e.g. tests pointing\n * at deleted goals) accumulated forever and the scheduler could re-tick\n * them. Audit 2026-05-08 found `mtime-cache-test.json` actively ticking\n * for 14h+ after the test that created it had finished.\n */\nexport function deleteDriverState(goalId: string): boolean {\n const path = statePath(goalId);\n if (!existsSync(path)) return false;\n try { rmSync(path, { force: true }); return true; }\n catch (err) { logger.warn(COMPONENT, `deleteDriverState ${goalId}: ${(err as Error).message}`); return false; }\n}\n\n/**\n * Sweep stale driver-state files. Returns count + ids of removed files.\n * A driver is considered stale if its `lastTickAt` is older than `maxAgeMs`\n * (default 24h). Called on gateway boot via `resumeDriversAfterRestart`.\n *\n * The audit found 5 driver-state files at 395+ hours old (16-17 days)\n * that resumeDriversAfterRestart kept \"cancelling\" via the flag but never\n * actually removing.\n */\nexport function sweepStaleDriverStates(maxAgeMs = 24 * 60 * 60 * 1000): { removed: number; ids: string[] } {\n ensureStateDir();\n const removed: string[] = [];\n const cutoff = Date.now() - maxAgeMs;\n try {\n const entries = readdirSync(STATE_DIR);\n for (const name of entries) {\n if (!name.endsWith('.json')) continue;\n const goalId = name.replace(/\\.json$/, '');\n const s = loadState(goalId);\n if (!s) continue;\n const lastTickMs = new Date(s.lastTickAt || s.startedAt).getTime();\n if (Number.isFinite(lastTickMs) && lastTickMs < cutoff) {\n if (deleteDriverState(goalId)) removed.push(goalId);\n }\n }\n } catch (err) {\n logger.warn(COMPONENT, `sweepStaleDriverStates: ${(err as Error).message}`);\n }\n return { removed: removed.length, ids: removed };\n}\n\nexport function reprioritizeDriver(goalId: string, priority: 1 | 2 | 3 | 4 | 5): boolean {\n const s = loadState(goalId);\n if (!s) return false;\n s.userControls.priority = priority;\n saveState(s);\n return true;\n}\n\nexport function _resetDriverStateForTests(goalId?: string): void {\n if (goalId) {\n try { rmSync(statePath(goalId), { force: true }); } catch { /* ok */ }\n } else {\n try { rmSync(STATE_DIR, { recursive: true, force: true }); } catch { /* ok */ }\n }\n}\n"],"mappings":";AAoBA,SAAS,YAAY,WAAW,cAAc,eAAe,aAAa,QAAQ,kBAAkB;AACpG,SAAS,MAAM,eAAe;AAC9B,OAAO,YAAY;AACnB,SAAS,kBAAkB;AAK3B,SAAS,mBAAmB;AAC5B,SAAS,oBAAiC;AAC1C,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB,aAAa,oBAAoB,mBAAmB;AAClF,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB,cAAc,qBAAqB;AAG7D,MAAM,YAAY;AAClB,MAAM,YAAY,KAAK,YAAY,cAAc;AAIjD,SAAS,iBAAuB;AAC5B,MAAI;AAAE,cAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,EAAG,QAAQ;AAAA,EAAW;AACxE;AAEA,SAAS,UAAU,QAAwB;AACvC,SAAO,KAAK,WAAW,GAAG,MAAM,OAAO;AAC3C;AAEA,SAAS,UAAU,QAAoC;AACnD,QAAM,OAAO,UAAU,MAAM;AAC7B,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACA,UAAM,SAAS,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;AACrD,QAAI,OAAO,kBAAkB,GAAG;AAC5B,aAAO,KAAK,WAAW,aAAa,MAAM,8BAA8B,OAAO,aAAa,kBAAa;AACzG,aAAO;AAAA,IACX;AACA,WAAO;AAAA,EACX,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,6BAA6B,MAAM,KAAM,IAAc,OAAO,EAAE;AACvF,WAAO;AAAA,EACX;AACJ;AAEA,SAAS,UAAU,OAA0B;AACzC,iBAAe;AACf,QAAM,cAAa,oBAAI,KAAK,GAAE,YAAY;AAC1C,QAAM,OAAO,UAAU,MAAM,MAAM;AACnC,MAAI;AACA,cAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,kBAAc,OAAO,QAAQ,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAG3D,eAAW,OAAO,QAAQ,IAAI;AAAA,EAClC,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,+BAA+B,MAAM,MAAM,KAAM,IAAc,OAAO,EAAE;AAAA,EACnG;AACJ;AAEA,SAAS,cAAc,OAAoB,OAAoB,MAAoB;AAC/E,QAAM,QAA4B,EAAE,KAAI,oBAAI,KAAK,GAAE,YAAY,GAAG,OAAO,KAAK;AAC9E,QAAM,QAAQ,KAAK,KAAK;AACxB,MAAI,MAAM,QAAQ,SAAS,IAAK,OAAM,UAAU,MAAM,QAAQ,MAAM,IAAI;AAC5E;AASA,SAAS,wBAAwB,OAAoC;AACjE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,IAAI,MAAM,YAAY;AAE5B,QAAM,uBAAuB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACA,SAAO,qBAAqB,KAAK,OAAK,EAAE,SAAS,CAAC,CAAC;AACvD;AAIA,SAAS,iBAAiB,MAAyB;AAC/C,QAAM,gBAAoD,CAAC;AAC3D,QAAM,QAAQ,YAAY,KAAK,YAAY,CAAC,CAAC;AAC7C,aAAW,OAAO,KAAK,YAAY,CAAC,GAAG;AACnC,kBAAc,IAAI,EAAE,IAAI;AAAA,MACpB,MAAM,MAAM,IAAI,EAAE,KAAK;AAAA,MACvB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,MAKV,aAAa;AAAA,MACb,WAAW,CAAC;AAAA,IAChB;AAAA,EACJ;AACA,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,SAAO;AAAA,IACH,eAAe;AAAA,IACf,QAAQ,KAAK;AAAA,IACb,OAAO;AAAA,IACP,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,QAAQ,EAAE,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,EAAE;AAAA,IACnE,YAAY,EAAE,GAAG,oBAAoB;AAAA,IACrC,cAAc;AAAA,MACV,QAAQ;AAAA,MACR,iBAAiB;AAAA,MACjB,UAAW,KAAK,YAAkC;AAAA,IACtD;AAAA,IACA;AAAA,IACA,SAAS,CAAC,EAAE,IAAI,KAAK,OAAO,YAAY,MAAM,uBAAuB,KAAK,KAAK,IAAI,CAAC;AAAA,EACxF;AACJ;AAIA,eAAe,aAAa,MAAY,OAAmC;AAIvE,MAAI,CAAC,KAAK,YAAY,KAAK,SAAS,WAAW,GAAG;AAC9C,kBAAc,OAAO,YAAY,gEAA2D;AAC5F,QAAI;AACA,YAAM,EAAE,WAAW,IAAI,MAAM,OAAO,YAAY;AAChD,iBAAW,KAAK,IAAI,KAAK,OAAO,KAAK,WAAW;AAAA,IACpD,SAAS,KAAK;AACV,aAAO,KAAK,WAAW,oCAAoC,KAAK,EAAE,KAAM,IAAc,OAAO,EAAE;AAAA,IACnG;AAAA,EACJ;AAQA,MAAI,QAAuE;AAC3E,aAAW,OAAO,KAAK,YAAY,CAAC,GAAG;AACnC,QAAI,CAAC,MAAM,cAAc,IAAI,EAAE,GAAG;AAC9B,UAAI,CAAC,MAAO,SAAQ,YAAY,KAAK,YAAY,CAAC,CAAC;AACnD,YAAM,cAAc,IAAI,EAAE,IAAI;AAAA,QAC1B,MAAM,MAAM,IAAI,EAAE,KAAK;AAAA,QACvB,UAAU;AAAA,QACV,aAAa;AAAA;AAAA,QACb,WAAW,CAAC;AAAA,MAChB;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,QAAQ;AACd,gBAAc,OAAO,cAAc,YAAY,OAAO,KAAK,MAAM,aAAa,EAAE,MAAM,sBAAsB;AAChH;AAEA,eAAe,eAAe,MAAY,OAAmC;AAEzE,QAAM,OAAO,MAAM,qBAAqB,MAAM,KAAK;AACnD,MAAI,CAAC,MAAM;AAMP,QAAI,YAAkB;AACtB,QAAI;AACA,YAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,YAAY;AAC7C,kBAAY,QAAQ,KAAK,EAAE,KAAK;AAAA,IACpC,QAAQ;AAAA,IAAW;AAGnB,UAAM,eAAe,UAAU,YAAY,CAAC,GAAG;AAAA,MAC3C,OAAK,EAAE,WAAW,UAAU,EAAE,WAAW,YAAY,EAAE,WAAW;AAAA,IACtE;AACA,QAAI,aAAa;AAMb,YAAM,mBAAmB;AACzB,YAAM,QAAQ;AACd,oBAAc,OAAO,aAAa,yDAAoD;AAAA,IAC1F,OAAO;AAEH,YAAM,QAAQ;AACd,YAAM,gBAAgB;AAAA,QAClB,UAAU,SAAS,KAAK,KAAK;AAAA,QAC7B,YAAY;AAAA;AAAA,QACZ,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,QAChC,MAAM;AAAA,MACV;AACA,oBAAc,OAAO,WAAW,qBAAqB;AAAA,IACzD;AACA;AAAA,EACJ;AAEA,QAAM,mBAAmB,KAAK;AAC9B,QAAM,WAAW,MAAM,cAAc,KAAK,EAAE;AAC5C,WAAS,YAAY;AAKrB,QAAM,MAAM,SAAS,eAAe;AACpC,MAAI,SAAS,WAAW,KAAK;AACzB,aAAS,qBAAqB;AAAA,MAC1B,QAAQ;AAAA,MACR,QAAQ,6BAA6B,GAAG;AAAA,MACxC,UAAU;AAAA,IACd;AACA,QAAI;AACA,YAAM,EAAE,YAAY,IAAI,MAAM,OAAO,YAAY;AACjD,kBAAY,KAAK,IAAI,KAAK,IAAI,SAAS,aAAa,6BAA6B,GAAG,YAAY;AAAA,IACpG,QAAQ;AAAA,IAAW;AACnB,UAAM,mBAAmB;AACzB,UAAM,QAAQ;AACd,kBAAc,OAAO,cAAc,WAAW,KAAK,EAAE,8BAA8B,GAAG,mBAAc;AACpG;AAAA,EACJ;AAKA,QAAM,WAAW,aAAa,SAAS,MAAM,SAAS,WAAW,GAAG,SAAS,WAAW,GAAG;AAC3F,MAAI,CAAC,UAAU;AAIX,QAAI,wBAAwB,SAAS,SAAS,GAAG;AAC7C,YAAM,QAAQ;AACd,YAAM,gBAAgB;AAAA,QAClB,UAAU,SAAS,KAAK,KAAK,iEAA4D,KAAK,UAAU,UAAU,CAAC;AAAA,QACnH,YAAY;AAAA,QACZ,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,QAChC,MAAM;AAAA,MACV;AACA,oBAAc,OAAO,aAAa,WAAW,KAAK,EAAE,oEAA+D;AACnH,YAAM,oBAAoB,OAAO,MAAM,CAAC,MAAM,cAAc,QAAQ,CAAC;AACrE;AAAA,IACJ;AAGA,aAAS,qBAAqB;AAAA,MAC1B,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,UAAU;AAAA,IACd;AACA,QAAI;AACA,YAAM,EAAE,YAAY,IAAI,MAAM,OAAO,YAAY;AACjD,kBAAY,KAAK,IAAI,KAAK,IAAI,uBAAuB;AAAA,IACzD,QAAQ;AAAA,IAAW;AACnB,UAAM,mBAAmB;AACzB,UAAM,QAAQ;AACd,kBAAc,OAAO,cAAc,WAAW,KAAK,EAAE,oCAA+B;AACpF;AAAA,EACJ;AAEA,WAAS,aAAa,SAAS;AAC/B,WAAS,eAAe;AAAA,IACpB,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,YAAY,SAAS;AAAA,EACzB;AACA,QAAM,QAAQ;AACd;AAAA,IACI;AAAA,IACA;AAAA,IACA,YAAY,SAAS,UAAU,iBAAiB,KAAK,KAAK,WAAW,SAAS,IAAI,aAAa,SAAS,QAAQ;AAAA,EACpH;AAKA,MAAI;AACA,UAAM,UAAU,KAAK,IAAI;AACzB,UAAM,SAAS,MAAM,gBAAgB;AAAA,MACjC,cAAc,SAAS;AAAA,MACvB,MAAM,GAAG,KAAK,KAAK;AAAA;AAAA,EAAO,KAAK,WAAW,GAAG,SAAS,oBAAoB,EAAE;AAAA,MAC5E,eAAe,SAAS;AAAA,MACxB,eAAe,aAAa,SAAS,IAAI,EAAE;AAAA,MAC3C,WAAW,SAAS;AAAA,IACxB,CAAC;AACD,UAAM,aAAa,KAAK,IAAI,IAAI;AAChC,gBAAY,OAAO;AAAA,MACf,WAAW;AAAA,MACX,QAAQ,OAAO,cAAc;AAAA,MAC7B,SAAS,OAAO,WAAW;AAAA,IAC/B,CAAC;AAGD,aAAS,YAAY,CAAC,GAAG,oBAAI,IAAI;AAAA,MAC7B,GAAG,SAAS;AAAA,MACZ,GAAG,OAAO,UAAU,IAAI,OAAK,EAAE,GAAG;AAAA,IACtC,CAAC,CAAC;AAGF,QAAI,OAAO,WAAW,QAAQ;AAC1B,YAAM,QAAQ;AACd,oBAAc,OAAO,aAAa,4BAA4B,OAAO,UAAU,MAAM,4BAA4B,OAAO,WAAW,QAAQ,CAAC,CAAC,EAAE;AAE/I,MAAC,SAAgE,kBAAkB;AAAA,IACvF,WAAW,OAAO,WAAW,UAAU;AACnC,eAAS,YAAY,OAAO,aAAa;AACzC,YAAM,QAAQ;AACd,oBAAc,OAAO,aAAa,0BAA0B,SAAS,UAAU,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,IAClG,WAAW,OAAO,WAAW,gBAAgB,OAAO,WAAW,WAAW;AACtE,YAAM,QAAQ;AAId,YAAM,eAAe,KAAK,SAAS;AACnC,YAAM,iBAAiB,SAAS,cAAc,SAAS,cAAc;AACrE,YAAM,eAAe,SAAS;AAC9B,YAAM,UAAU,SAAS;AACzB,YAAM,cAAc,OAAO,UAAU,CAAC,KAAK;AAE3C,UAAI;AACJ,UAAI,eAAe,YAAY,SAAS,MAAM,CAAC,YAAY,YAAY,EAAE,SAAS,qBAAqB,GAAG;AAEtG,uBAAe,SAAS,KAAK,KAAK,0BAA0B,YAAY,cAAc,YAAY,iBAAiB,cAAc;AAAA;AAAA,EAAS,WAAW;AAAA,MACzJ,WAAW,SAAS;AAChB,uBAAe,SAAS,KAAK,KAAK,qBAAgB,YAAY,kBAAkB,YAAY,+BAA+B,cAAc;AAAA;AAAA,SAAe,QAAQ,MAAM,GAAG,GAAG,CAAC;AAAA;AAAA;AAAA,MACjL,OAAO;AACH,uBAAe,SAAS,KAAK,KAAK,qBAAgB,YAAY,sBAAsB,YAAY,+BAA+B,cAAc;AAAA,MACjJ;AAEA,YAAM,gBAAgB;AAAA,QAClB,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,QAChC,MAAM;AAAA,MACV;AACA,oBAAc,OAAO,WAAW,qBAAqB,aAAa,MAAM,GAAG,GAAG,CAAC,EAAE;AACjF,YAAM,oBAAoB,OAAO,MAAM,CAAC,cAAc,GAAG,OAAO,UAAU,MAAM,CAAC,CAAC,CAAC;AAAA,IACvF;AACA,aAAS,eAAe;AAAA,EAC5B,SAAS,KAAK;AACV,UAAM,MAAO,IAAc;AAC3B,aAAS,YAAY;AACrB,UAAM,QAAQ;AACd,kBAAc,OAAO,aAAa,gBAAgB,IAAI,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,EACzE;AACJ;AAEA,eAAe,cAAc,MAAY,OAAmC;AAUxE,OAAK;AACL,QAAM,YAAY,MAAM;AACxB,QAAM,WAAW,YAAY,MAAM,cAAc,SAAS,IAAI;AAC9D,MAAI,CAAC,UAAU,cAAc;AAGzB,UAAM,QAAQ;AACd;AAAA,EACJ;AACA,QAAM,QAAQ;AACd,gBAAc,OAAO,aAAa,sDAAiD;AACvF;AAEA,eAAe,cAAc,MAAY,OAAmC;AACxE,QAAM,YAAY,MAAM;AACxB,MAAI,CAAC,WAAW;AACZ,UAAM,QAAQ;AACd;AAAA,EACJ;AACA,QAAM,WAAW,MAAM,cAAc,SAAS;AAS9C,QAAM,eAAe,SAAS,aAAa,IAAI,MAAM,GAAG,EAAE,EAAE,YAAY,EAAE,KAAK;AAC/E,MAAI,aAAa;AACb,QAAI,gBAAgB,SAAS,sBAAsB;AAC/C,eAAS,8BAA8B,SAAS,8BAA8B,KAAK;AAAA,IACvF,OAAO;AACH,eAAS,6BAA6B;AACtC,eAAS,uBAAuB;AAAA,IACpC;AACA,SAAK,SAAS,8BAA8B,MAAM,GAAG;AACjD,UAAI;AACA,cAAM,EAAE,YAAY,IAAI,MAAM,OAAO,YAAY;AACjD,oBAAY,KAAK,IAAI,WAAW,0CAAuC,WAAW,EAAE;AAAA,MACxF,QAAQ;AAAA,MAAW;AACnB,YAAM,mBAAmB;AACzB,YAAM,QAAQ;AACd,oBAAc,OAAO,cAAc,uBAAuB,SAAS,qBAAkB;AACrF;AAAA,IACJ;AAAA,EACJ;AAOA,QAAM,QAAQ,YAAY,KAAK;AAC/B,MAAI,MAAM,WAAW,YAAY;AAC7B,UAAM,aAAa,mBAAmB,KAAK;AAC3C,QAAI,eAAe,aAAa;AAC5B,YAAM,QAAQ;AACd,YAAM,gBAAgB;AAAA,QAClB,UAAU,SAAS,KAAK,KAAK,6BAAwB,MAAM,OAAO,0BAA0B,MAAM,OAAO,QAAQ,QAAQ,CAAC,CAAC;AAAA,QAC3H,YAAY;AAAA,QACZ,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,QAChC,MAAM;AAAA,MACV;AACA,oBAAc,OAAO,WAAW,MAAM,OAAO;AAC7C,YAAM,oBAAoB,OAAO,MAAM,CAAC,MAAM,cAAc,QAAQ,CAAC;AACrE;AAAA,IACJ;AAAA,EACJ;AAEA,MAAI,SAAS,YAAY,MAAM,WAAW,YAAY;AAUlD,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,uBAAuB;AAC/D,UAAM,kBAAkB,GAAG,KAAK,EAAE,IAAI,SAAS;AAC/C,QAAI,eAAe,iBAAiB,WAAW,GAAG;AAC9C,YAAM,SAAS,SAAS;AACxB,eAAS,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,WAAW,CAAC,CAAC;AACjE,eAAS,6BAA6B;AACtC,eAAS,uBAAuB;AAChC,oBAAc,OAAO,cAAc,2BAA2B,SAAS,qBAAqB,MAAM,WAAM,SAAS,QAAQ,EAAE;AAC3H,YAAM,QAAQ;AACd;AAAA,IACJ;AACA,QAAI;AACA,YAAM,EAAE,YAAY,IAAI,MAAM,OAAO,YAAY;AACjD,kBAAY,KAAK,IAAI,WAAW,SAAS,aAAa,aAAa;AAAA,IACvE,QAAQ;AAAA,IAAW;AACnB,kBAAc,OAAO,cAAc,WAAW,SAAS,iBAAiB,SAAS,QAAQ,WAAW;AACpG,UAAM,QAAQ;AACd;AAAA,EACJ;AAGA,QAAM,QAAQ;AACd,gBAAc,OAAO,cAAc,wBAAwB,SAAS,mBAAc,SAAS,WAAW,CAAC,EAAE;AAC7G;AAEA,eAAe,cAAc,MAAY,OAAmC;AACxE,QAAM,YAAY,MAAM;AACxB,MAAI,CAAC,WAAW;AAYZ,UAAM,WAAW,KAAK,YAAY,CAAC;AACnC,UAAM,UAAU,IAAI,IAAI,SAAS,IAAI,OAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACpD,UAAM,YAAY,OAAO,QAAQ,MAAM,aAAa,EAAE,MAAM,CAAC,CAAC,OAAOA,SAAQ,MAAM;AAC/E,UAAIA,UAAS,oBAAoB,WAAW,KAAM,QAAO;AACzD,YAAM,UAAU,QAAQ,IAAI,KAAK;AACjC,UAAI,SAAS,WAAW,UAAU,SAAS,WAAW,UAAW,QAAO;AACxE,aAAO;AAAA,IACX,CAAC;AACD,UAAM,QAAQ,YAAY,cAAc;AACxC,kBAAc,OAAO,MAAM,OAAO,YAAY,0BAA0B,mCAAmC;AAC3G;AAAA,EACJ;AACA,QAAM,WAAW,MAAM,cAAc,SAAS;AAC9C,QAAM,WAAW,KAAK,YAAY,CAAC,GAAG,KAAK,OAAK,EAAE,OAAO,SAAS;AAClE,MAAI,CAAC,SAAS;AACV,UAAM,QAAQ;AACd;AAAA,EACJ;AACA,QAAM,YAAa,SAAgE;AACnF,MAAI,CAAC,WAAW;AAEZ,UAAM,QAAQ;AACd,kBAAc,OAAO,aAAa,8CAAyC;AAC3E;AAAA,EACJ;AAEA,QAAM,eAAe,MAAM,aAAa;AAAA,IACpC,MAAM,SAAS;AAAA,IACf;AAAA,IACA,aAAa;AAAA,EACjB,CAAC;AACD,WAAS,qBAAqB;AAE9B,MAAI,aAAa,QAAQ;AACrB,QAAI;AACA,YAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,YAAY;AAKrD,YAAM,QAAQ;AACd,YAAM,kBAAkB,MAAM,UAAU,SAAS,IAC3C;AAAA;AAAA;AAAA,EAAmB,MAAM,UAAU,IAAI,OAAK,QAAQ,EAAE,IAAI,KAAK,EAAE,GAAG,GAAG,EAAE,cAAc,WAAM,EAAE,WAAW,KAAK,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC,KAC/H;AACN,YAAM,iBAAiB,MAAM,aAAa,MAAM,eAAe,aAAa,UAAU;AACtF,sBAAgB,KAAK,IAAI,WAAW,aAAa;AAAA,IACrD,QAAQ;AAAA,IAAW;AACnB,kBAAc,OAAO,cAAc,WAAW,SAAS,cAAc,aAAa,OAAO,MAAM,GAAG,GAAG,CAAC,EAAE;AACxG,UAAM,mBAAmB;AACzB,UAAM,QAAQ;AAAA,EAClB,OAAO;AACH,aAAS,YAAY,wBAAwB,aAAa,MAAM;AAChE,kBAAc,OAAO,aAAa,wBAAwB,aAAa,OAAO,MAAM,GAAG,GAAG,CAAC,EAAE;AAC7F,UAAM,QAAQ;AAAA,EAClB;AACJ;AAEA,eAAe,cAAc,MAAY,OAAmC;AACxE,QAAM,aAAa,KAAK,IAAI,IAAI,IAAI,KAAK,MAAM,SAAS,EAAE,QAAQ;AAClE,QAAM,kBAAkB,CAAC,GAAG,IAAI;AAAA,IAC5B,OAAO,OAAO,MAAM,aAAa,EAC5B,IAAI,OAAK,EAAE,UAAU,EACrB,OAAO,CAAC,MAAmB,CAAC,CAAC,CAAC;AAAA,EACvC,CAAC;AACD,QAAM,gBAAgB;AAAA,IAClB,SAAS;AAAA,IACT;AAAA,IACA,YAAY,MAAM,OAAO;AAAA,IACzB,SAAS,MAAM,OAAO;AAAA,IACtB,gBAAgB;AAAA,MACZ,aAAa,OAAO,KAAK,MAAM,aAAa,EAAE,MAAM,oBAAoB,gBAAgB,MAAM,qBAAqB,KAAK,MAAM,aAAa,GAAI,CAAC;AAAA,IACpJ;AAAA,IACA;AAAA,EACJ;AACA,MAAI;AACA,UAAM,EAAE,WAAW,IAAI,MAAM,OAAO,YAAY;AAChD,eAAW,KAAK,IAAI,EAAE,QAAQ,YAAY,CAAC;AAAA,EAC/C,QAAQ;AAAA,EAAW;AACnB,QAAM,QAAQ;AACd,gBAAc,OAAO,QAAQ,mBAAmB,MAAM,cAAc,eAAe,CAAC,CAAC,EAAE;AACvF,MAAI;AAAE,UAAM,gBAAgB,MAAM,KAAK;AAAA,EAAG,QAAQ;AAAA,EAAW;AAE7D,MAAI;AACA,UAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,qBAAqB;AAChE,UAAM,kBAAkB,MAAM,KAAK;AAAA,EACvC,QAAQ;AAAA,EAAW;AAGnB,MAAI;AACA,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,uBAAuB;AAC9D,kBAAc;AAAA,MACV,MAAM;AAAA,MACN,SAAS,0BAA0B,KAAK,KAAK,MAAM,KAAK,MAAM,aAAa,GAAI,CAAC,MAAM,OAAO,KAAK,MAAM,aAAa,EAAE,MAAM,2BAA2B,gBAAgB,KAAK,IAAI,CAAC;AAAA,MAClL,QAAQ,MAAM,QAAQ,MAAM,GAAG,EAAE,IAAI,OAAK,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,IAAI;AAAA,MACpF,MAAM,CAAC,eAAe,kBAAkB,KAAK,IAAI,GAAI,KAAK,QAAQ,CAAC,CAAE;AAAA,IACzE,CAAC;AAAA,EACL,QAAQ;AAAA,EAAW;AACvB;AAEA,eAAe,YAAY,MAAY,OAAmC;AAQtE,QAAM,aAAa,MAAM,eAAe;AACxC,QAAM,UAAU,MAAM,eAAe;AACrC,QAAM,UAAU,UAAU,KAAK,IAAI,IAAI,IAAI,KAAK,OAAO,EAAE,QAAQ,IAAI;AACrE,QAAM,WAAW,KAAK,KAAK;AAC3B,MAAI,UAAU,UAAU;AACpB,QAAI,WAAuC;AAC3C,QAAI,YAAY;AACZ,UAAI;AACA,cAAM,EAAE,YAAY,IAAI,MAAM,OAAO,kBAAkB;AACvD,mBAAW,YAAY,UAAU;AAAA,MACrC,QAAQ;AAAA,MAAW;AAAA,IACvB;AACA,QAAI,CAAC,cAAc,CAAC,UAAU;AAM1B,aAAO,KAAK,WAAW,2CAA2C,KAAK,EAAE,gBAAgB,cAAc,OAAO,SAAS,KAAK,MAAM,UAAU,GAAK,CAAC,MAAM;AACxJ,YAAM,OAAO,mCAAmC,KAAK,MAAM,UAAU,GAAK,CAAC;AAC3E,YAAM,YAAY,MAAM,eAAe,SAAS,qBACzC,iCAAiC,KAAK,MAAM,eAAe,YAAY,EAAE;AAChF,UAAI,WAAW;AACX,cAAM,OAAO,eAAe,KAAK,MAAM,MAAM,OAAO,eAAe,CAAC;AAAA,MACxE;AACA,YAAM,YAAY,MAAM;AACxB,UAAI,aAAa,MAAM,cAAc,SAAS,GAAG;AAC7C,cAAM,MAAM,MAAM,cAAc,SAAS;AACzC,YAAI,WAAW,KAAK,MAAM,IAAI,WAAW,CAAC;AAC1C,YAAI,6BAA6B;AAAA,MACrC;AACA,YAAM,gBAAgB;AACtB,YAAM,QAAQ;AACd,oBAAc,OAAO,aAAa,oBAAoB,IAAI,EAAE;AAC5D;AAAA,IACJ;AAAA,EACJ;AAGA,MAAI,CAAC,WAAY;AACjB,MAAI;AACA,UAAM,EAAE,YAAY,IAAI,MAAM,OAAO,kBAAkB;AACvD,UAAM,WAAW,YAAY,UAAU;AACvC,QAAI,CAAC,SAAU;AACf,QAAI,SAAS,WAAW,UAAW;AACnC,QAAI,SAAS,WAAW,YAAY;AAEhC,YAAM,YAAY,MAAM;AACxB,UAAI,WAAW;AACX,cAAM,WAAW,MAAM,cAAc,SAAS;AAC9C,cAAM,OAAO,SAAS,gBAAgB;AACtC,iBAAS,YAAY,kDAAkD,IAAI;AAAA,MAC/E;AACA,YAAM,gBAAgB;AACtB,YAAM,QAAQ;AACd,oBAAc,OAAO,cAAc,gCAAgC,UAAU,GAAG;AAChF;AAAA,IACJ;AACA,QAAI,SAAS,WAAW,YAAY;AAChC,YAAM,QAAQ;AACd,oBAAc,OAAO,UAAU,uCAAuC,UAAU,EAAE;AAClF,YAAM,aAAa,MAAM,OAAO,mBAAmB;AACnD;AAAA,IACJ;AAAA,EACJ,QAAQ;AAAA,EAAW;AACvB;AAEA,eAAe,cAAc,MAAY,OAAmC;AAKxE,QAAM,aAAa,MAAM,eAAe;AACxC,QAAM,UAAU,MAAM,eAAe;AACrC,QAAM,UAAU,UAAU,KAAK,IAAI,IAAI,IAAI,KAAK,OAAO,EAAE,QAAQ,IAAI;AAGrE,MAAI,CAAC,WAAY;AACjB,MAAI;AACA,UAAM,EAAE,YAAY,IAAI,MAAM,OAAO,kBAAkB;AACvD,UAAM,WAAW,YAAY,UAAU;AACvC,QAAI,CAAC,SAAU;AACf,QAAI,SAAS,WAAW,UAAW;AAEnC,QAAI,SAAS,WAAW,YAAY;AAIhC,YAAM,YAAY,MAAM;AACxB,UAAI,WAAW;AACX,cAAM,WAAW,MAAM,cAAc,SAAS;AAC9C,iBAAS,WAAW;AACpB,iBAAS,6BAA6B;AACtC,iBAAS,YAAY;AACrB,iBAAS,uBAAuB;AAAA,MACpC;AACA,YAAM,gBAAgB;AACtB,YAAM,QAAQ;AACd,oBAAc,OAAO,cAAc,wDAAmD,SAAS,sBAAsB;AACrH;AAAA,IACJ;AAEA,QAAI,SAAS,WAAW,YAAY;AAEhC,YAAM,QAAQ;AACd,oBAAc,OAAO,UAAU,uDAAkD;AACjF,YAAM,aAAa,MAAM,OAAO,6CAA6C;AAC7E;AAAA,IACJ;AAAA,EACJ,QAAQ;AAAA,EAAW;AACvB;AAEA,eAAe,WAAW,MAAY,OAAmC;AACrE,QAAM,aAAa,KAAK,IAAI,IAAI,IAAI,KAAK,MAAM,SAAS,EAAE,QAAQ;AAClE,QAAM,gBAAgB;AAAA,IAClB,SAAS;AAAA,IACT;AAAA,IACA,YAAY,MAAM,OAAO;AAAA,IACzB,SAAS,MAAM,OAAO;AAAA,IACtB,gBAAgB;AAAA,MACZ,qBAAqB,KAAK,MAAM,aAAa,GAAI,CAAC,MAAM,MAAM,OAAO,YAAY;AAAA,IACrF;AAAA,IACA,iBAAiB,CAAC,GAAG,IAAI,IAAI,OAAO,OAAO,MAAM,aAAa,EAAE,IAAI,OAAK,EAAE,UAAU,EAAE,OAAO,CAAC,MAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AAAA,EAC3H;AACA,MAAI;AACA,UAAM,EAAE,WAAW,IAAI,MAAM,OAAO,YAAY;AAChD,eAAW,KAAK,IAAI,EAAE,QAAQ,SAAS,CAAC;AAAA,EAC5C,QAAQ;AAAA,EAAW;AACnB,MAAI;AAAE,UAAM,aAAa,MAAM,OAAO,mCAAmC;AAAA,EAAG,QAAQ;AAAA,EAAW;AAE/F,MAAI;AACA,UAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,qBAAqB;AAChE,UAAM,kBAAkB,MAAM,KAAK;AAAA,EACvC,QAAQ;AAAA,EAAW;AACnB,MAAI;AACA,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,uBAAuB;AAC9D,kBAAc;AAAA,MACV,MAAM;AAAA,MACN,SAAS,uBAAuB,KAAK,KAAK,WAAW,KAAK,MAAM,aAAa,GAAI,CAAC;AAAA,MAClF,QAAQ,MAAM,QAAQ,MAAM,GAAG,EAAE,IAAI,OAAK,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,IAAI;AAAA,MACpF,MAAM,CAAC,eAAe,eAAe,KAAK,IAAI,GAAI,KAAK,QAAQ,CAAC,CAAE;AAAA,IACtE,CAAC;AAAA,EACL,QAAQ;AAAA,EAAW;AACvB;AAEA,eAAe,cAAc,MAAY,OAAmC;AACxE,MAAI;AACA,UAAM,EAAE,WAAW,IAAI,MAAM,OAAO,YAAY;AAChD,eAAW,KAAK,IAAI,EAAE,QAAQ,SAAS,CAAC;AAAA,EAC5C,QAAQ;AAAA,EAAW;AACnB,gBAAc,OAAO,aAAa,mBAAmB;AACzD;AAIA,eAAe,qBAAqB,MAAY,OAA6C;AAOzF,QAAM,MAAM,CAAC,OAAe,MAAM,cAAc,EAAE,GAAG,eAAe;AACpE,aAAW,OAAO,KAAK,YAAY,CAAC,GAAG;AACnC,QAAI,IAAI,WAAW,UAAW;AAC9B,UAAM,WAAW,MAAM,cAAc,IAAI,EAAE;AAC3C,QAAI,CAAC,SAAU;AACf,UAAM,oBAAoB,SAAS,YAAY,IAAI,IAAI,EAAE,KAClD,SAAS,YAAY,MAAM,WAAW;AAC7C,QAAI,SAAS,oBAAoB,WAAW,SAAS,mBAAmB;AACpE,UAAI;AACA,cAAM,EAAE,YAAY,IAAI,MAAM,OAAO,YAAY;AACjD,oBAAY,KAAK,IAAI,IAAI,IAAI,SAAS,aAAa,0CAA0C;AAK7F,YAAI,SAAS;AAAA,MACjB,QAAQ;AAAA,MAA0C;AAClD;AAAA,IACJ;AAEA,UAAM,iBAAiB,IAAI,aAAa,CAAC,GAAG,MAAM,WAAS;AACvD,YAAM,MAAM,KAAK,UAAU,KAAK,OAAK,EAAE,OAAO,KAAK;AACnD,aAAO,KAAK,WAAW;AAAA,IAC3B,CAAC;AACD,QAAI,CAAC,cAAe;AACpB,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAEA,eAAe,oBACX,OACA,MACA,WACa;AAEb,MAAI;AACA,UAAM,EAAE,qBAAqB,IAAI,MAAM,OAAO,2BAA2B;AACzE,QAAI,CAAC,qBAAqB,KAAK,IAAI,gBAAgB,GAAG;AAClD,aAAO,MAAM,WAAW,wDAAwD,KAAK,EAAE,EAAE;AAEzF,UAAI;AAAE,cAAM,cAAc,MAAM,KAAK;AAAA,MAAG,QAAQ;AAAA,MAAW;AAC3D;AAAA,IACJ;AAAA,EACJ,QAAQ;AAAA,EAAqD;AAC7D,MAAI;AACA,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,kBAAkB;AAC1D,UAAM,WAAW,MAAM,mBAAmB,MAAM,cAAc,MAAM,gBAAgB,IAAI;AACxF,UAAM,eAAe,KAAK,UAAU,KAAK,OAAK,EAAE,OAAO,MAAM,gBAAgB,GAAG;AAChF,UAAM,WAAW,eAAe;AAAA,MAC5B,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,QACb,WAAW,KAAK;AAAA,QAChB,UAAU,UAAU,CAAC,KAAK,MAAM,eAAe,YAAY;AAAA,QAC3D,cAAc;AAAA,QACd,cAAc,MAAM;AAAA,QACpB,kBAAkB,MAAM;AAAA,QACxB,aAAa,UAAU;AAAA,QACvB;AAAA,QACA,YAAY,UAAU;AAAA,QACtB,UAAU,UAAU;AAAA,QACpB,WAAW,UAAU;AAAA,QACrB,SAAS;AAAA,MACb;AAAA,MACA,gBAAgB,CAAC;AAAA,IACrB,CAAC;AACD,QAAI,UAAU,MAAM,MAAM,eAAe;AACrC,YAAM,cAAc,aAAa,SAAS;AAAA,IAC9C;AAEA,QAAI;AACA,YAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,2BAA2B;AACpE,UAAI,gBAAgB,kBAAkB,KAAK,EAAE,GAAG;AAC5C,cAAM,IAAI;AACV,YAAI,OAAO,EAAE,0BAA0B,YAAY;AAC/C,YAAE,sBAAsB,kBAAkB;AAAA,YACtC,QAAQ,KAAK;AAAA,YACb,WAAW,KAAK;AAAA,YAChB,UAAU,UAAU,CAAC,KAAK;AAAA,YAC1B,YAAY,UAAU;AAAA,UAC1B,CAAC;AAAA,QACL;AAAA,MACJ;AAAA,IACJ,QAAQ;AAAA,IAAW;AAAA,EACvB,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,6CAA8C,IAAc,OAAO,EAAE;AAAA,EAChG;AAEA,MAAI;AAAE,UAAM,cAAc,MAAM,KAAK;AAAA,EAAG,QAAQ;AAAA,EAAW;AAC/D;AAIA,eAAsB,WAAW,QAAsC;AACnE,QAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,YAAY;AAC7C,QAAM,OAAO,QAAQ,MAAM;AAC3B,MAAI,CAAC,KAAM,QAAO;AAElB,MAAI,QAAQ,UAAU,MAAM;AAC5B,MAAI,CAAC,OAAO;AACR,YAAQ,iBAAiB,IAAI;AAC7B,cAAU,KAAK;AAAA,EACnB;AAGA,MAAI,MAAM,aAAa,iBAAiB;AACpC,UAAM,QAAQ;AACd,UAAM,cAAc,MAAM,KAAK;AAC/B,cAAU,KAAK;AACf,WAAO;AAAA,EACX;AACA,MAAI,MAAM,aAAa,OAAQ,QAAO,MAAM;AAK5C,MAAI;AACA,YAAQ,MAAM,OAAO;AAAA,MACjB,KAAK;AAAe,cAAM,aAAa,MAAM,KAAK;AAAG;AAAA,MACrD,KAAK;AAAe,cAAM,eAAe,MAAM,KAAK;AAAG;AAAA,MACvD,KAAK;AAAe,cAAM,cAAc,MAAM,KAAK;AAAG;AAAA,MACtD,KAAK;AAAe,cAAM,cAAc,MAAM,KAAK;AAAG;AAAA,MACtD,KAAK;AAAe,cAAM,cAAc,MAAM,KAAK;AAAG;AAAA,MACtD,KAAK;AAAe,cAAM,cAAc,MAAM,KAAK;AAAG;AAAA,MACtD,KAAK;AAAe,cAAM,YAAY,MAAM,KAAK;AAAG;AAAA,MACpD,KAAK;AAAe,cAAM,cAAc,MAAM,KAAK;AAAG;AAAA,MACtD,KAAK;AAAe,cAAM,WAAW,MAAM,KAAK;AAAG;AAAA,MACnD,KAAK;AAAA,MACL,KAAK;AACD;AAAA,IACR;AAAA,EACJ,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,YAAY,MAAM,mBAAmB,MAAM,KAAK,KAAM,IAAc,OAAO,EAAE;AACpG,kBAAc,OAAO,MAAM,OAAO,eAAgB,IAAc,QAAQ,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,EAC3F;AAEA,YAAU,KAAK;AACf,SAAO,MAAM;AACjB;AAQA,eAAsB,UAAU,QAAgB,WAAW,KAA2B;AAClF,MAAI,OAAoB;AACxB,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AAC/B,WAAO,MAAM,WAAW,MAAM;AAC9B,QAAI,SAAS,UAAU,SAAS,YAAY,SAAS,eAAe,SAAS,aAAa,SAAS,YAAa;AAEhH,UAAM,IAAI,QAAQ,SAAO,WAAW,KAAK,EAAE,CAAC;AAAA,EAChD;AACA,SAAO;AACX;AAIO,SAAS,eAAe,QAAoC;AAC/D,SAAO,UAAU,MAAM;AAC3B;AAEO,SAAS,oBAAmC;AAC/C,iBAAe;AACf,MAAI,CAAC,WAAW,SAAS,EAAG,QAAO,CAAC;AACpC,QAAM,MAAqB,CAAC;AAC5B,aAAW,QAAQ,YAAY,SAAS,GAAG;AACvC,QAAI,CAAC,KAAK,SAAS,OAAO,EAAG;AAC7B,UAAM,SAAS,KAAK,MAAM,GAAG,EAAE;AAC/B,UAAM,IAAI,UAAU,MAAM;AAC1B,QAAI,KAAK,EAAE,UAAU,UAAU,EAAE,UAAU,YAAY,EAAE,UAAU,YAAa,KAAI,KAAK,CAAC;AAAA,EAC9F;AACA,SAAO;AACX;AAEO,SAAS,iBAAgC;AAC5C,iBAAe;AACf,MAAI,CAAC,WAAW,SAAS,EAAG,QAAO,CAAC;AACpC,QAAM,MAAqB,CAAC;AAC5B,aAAW,QAAQ,YAAY,SAAS,GAAG;AACvC,QAAI,CAAC,KAAK,SAAS,OAAO,EAAG;AAC7B,UAAM,SAAS,KAAK,MAAM,GAAG,EAAE;AAC/B,UAAM,IAAI,UAAU,MAAM;AAC1B,QAAI,EAAG,KAAI,KAAK,CAAC;AAAA,EACrB;AACA,SAAO;AACX;AAEO,SAAS,YAAY,QAAyB;AACjD,QAAM,IAAI,UAAU,MAAM;AAC1B,MAAI,CAAC,EAAG,QAAO;AACf,IAAE,aAAa,SAAS;AACxB,YAAU,CAAC;AACX,SAAO;AACX;AAEO,SAAS,oBAAoB,QAAyB;AACzD,QAAM,IAAI,UAAU,MAAM;AAC1B,MAAI,CAAC,EAAG,QAAO;AACf,IAAE,aAAa,SAAS;AACxB,YAAU,CAAC;AACX,SAAO;AACX;AAWA,eAAsB,mBAAmB,QAAgB,OAAO,gBAAkC;AAC9F,QAAM,IAAI,UAAU,MAAM;AAC1B,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,EAAE,UAAU,UAAW,QAAO;AAGlC,QAAM,aAAa,EAAE,eAAe;AACpC,MAAI,YAAY;AACZ,QAAI;AACA,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,kBAAkB;AAC1D,qBAAe,YAAY,iBAAiB,mCAAmC,IAAI,EAAE;AAAA,IACzF,QAAQ;AAAA,IAA8C;AAAA,EAC1D;AAMA,QAAM,YAAY,EAAE,eAAe,SAAS,qBACrC,iCAAiC,KAAK,EAAE,eAAe,YAAY,EAAE;AAC5E,MAAI,WAAW;AACX,MAAE,OAAO,eAAe,KAAK,MAAM,EAAE,OAAO,eAAe,CAAC;AAC5D,kBAAc,GAAG,aAAa,iCAAiC,EAAE,OAAO,YAAY,MAAM,EAAE,WAAW,UAAU,WAAW;AAAA,EAChI;AAMA,QAAM,YAAY,EAAE;AACpB,MAAI,aAAa,EAAE,cAAc,SAAS,GAAG;AACzC,UAAM,MAAM,EAAE,cAAc,SAAS;AACrC,UAAM,SAAS,IAAI;AACnB,QAAI,WAAW,KAAK,MAAM,IAAI,WAAW,CAAC;AAC1C,QAAI,6BAA6B;AACjC,kBAAc,GAAG,aAAa,iDAAiD,SAAS,KAAK,MAAM,WAAM,IAAI,QAAQ,GAAG;AAAA,EAC5H;AAEA,IAAE,gBAAgB;AAClB,IAAE,QAAQ;AACV,gBAAc,GAAG,aAAa,oBAAoB,IAAI,EAAE;AACxD,YAAU,CAAC;AACX,SAAO,KAAK,WAAW,0BAA0B,MAAM,KAAK,IAAI,GAAG,YAAY,qBAAqB,EAAE,EAAE;AACxG,SAAO;AACX;AAEO,SAAS,aAAa,QAAyB;AAClD,QAAM,IAAI,UAAU,MAAM;AAC1B,MAAI,CAAC,EAAG,QAAO;AACf,IAAE,aAAa,kBAAkB;AACjC,YAAU,CAAC;AACX,SAAO;AACX;AAaO,SAAS,kBAAkB,QAAyB;AACvD,QAAM,OAAO,UAAU,MAAM;AAC7B,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AAAE,WAAO,MAAM,EAAE,OAAO,KAAK,CAAC;AAAG,WAAO;AAAA,EAAM,SAC3C,KAAK;AAAE,WAAO,KAAK,WAAW,qBAAqB,MAAM,KAAM,IAAc,OAAO,EAAE;AAAG,WAAO;AAAA,EAAO;AAClH;AAWO,SAAS,uBAAuB,WAAW,KAAK,KAAK,KAAK,KAA0C;AACvG,iBAAe;AACf,QAAM,UAAoB,CAAC;AAC3B,QAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,MAAI;AACA,UAAM,UAAU,YAAY,SAAS;AACrC,eAAW,QAAQ,SAAS;AACxB,UAAI,CAAC,KAAK,SAAS,OAAO,EAAG;AAC7B,YAAM,SAAS,KAAK,QAAQ,WAAW,EAAE;AACzC,YAAM,IAAI,UAAU,MAAM;AAC1B,UAAI,CAAC,EAAG;AACR,YAAM,aAAa,IAAI,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,QAAQ;AACjE,UAAI,OAAO,SAAS,UAAU,KAAK,aAAa,QAAQ;AACpD,YAAI,kBAAkB,MAAM,EAAG,SAAQ,KAAK,MAAM;AAAA,MACtD;AAAA,IACJ;AAAA,EACJ,SAAS,KAAK;AACV,WAAO,KAAK,WAAW,2BAA4B,IAAc,OAAO,EAAE;AAAA,EAC9E;AACA,SAAO,EAAE,SAAS,QAAQ,QAAQ,KAAK,QAAQ;AACnD;AAEO,SAAS,mBAAmB,QAAgB,UAAsC;AACrF,QAAM,IAAI,UAAU,MAAM;AAC1B,MAAI,CAAC,EAAG,QAAO;AACf,IAAE,aAAa,WAAW;AAC1B,YAAU,CAAC;AACX,SAAO;AACX;AAEO,SAAS,0BAA0B,QAAuB;AAC7D,MAAI,QAAQ;AACR,QAAI;AAAE,aAAO,UAAU,MAAM,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,IAAG,QAAQ;AAAA,IAAW;AAAA,EACzE,OAAO;AACH,QAAI;AAAE,aAAO,WAAW,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,IAAG,QAAQ;AAAA,IAAW;AAAA,EAClF;AACJ;","names":["subState"]}
1
+ {"version":3,"sources":["../../src/agent/goalDriver.ts"],"sourcesContent":["/**\n * TITAN — Goal Driver (v4.10.0-local, Phase A)\n *\n * Owns a goal from \"active\" to terminal (done | failed | cancelled).\n * Replaces the passive \"initiative picks one subtask per 5-min tick\"\n * model with an active phase state machine that drives subtasks through\n * specialists, verifies each one, retries on failure via fallbackChain,\n * and reports outcomes back to SOMA.\n *\n * Design principles:\n * - State is persisted to ~/.titan/driver-state/<goalId>.json after\n * every phase transition. Restart-safe.\n * - One tick = one phase transition. Scheduler loops ticks until the\n * driver reaches a terminal phase OR requires waiting (observing a\n * spawned specialist, or blocked on human).\n * - Every phase's state transition is logged to driver state history\n * for UI replay + debugging.\n * - Kill-switch / scope-lock / staging are handled by toolRunner —\n * the driver inherits that protection automatically.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, rmSync, renameSync } from 'fs';\nimport { join, dirname } from 'path';\nimport logger from '../utils/logger.js';\nimport { TITAN_HOME } from '../utils/constants.js';\nimport type {\n DriverPhase, DriverState, DriverSubtaskState, DriverHistoryEvent,\n} from './goalDriverTypes.js';\nimport type { SubtaskKind } from './subtaskTaxonomy.js';\nimport { classifyAll } from './subtaskTaxonomy.js';\nimport { routeForKind, pickAttempt } from './specialistRouter.js';\nimport { nextFallback } from './fallbackChain.js';\nimport { DEFAULT_BUDGET_CAPS, checkBudget, suggestDegradation, recordSpend } from './budgetEnforcer.js';\nimport { structuredSpawn } from './structuredSpawn.js';\nimport { verifyByKind } from './verifier.js';\nimport { onGoalCompleted, onGoalFailed, onGoalBlocked } from './somaFeedback.js';\nimport type { Goal, Subtask } from './goals.js';\n\nconst COMPONENT = 'GoalDriver';\nconst STATE_DIR = join(TITAN_HOME, 'driver-state');\n\n// ── Storage ──────────────────────────────────────────────────────\n\nfunction ensureStateDir(): void {\n try { mkdirSync(STATE_DIR, { recursive: true }); } catch { /* ok */ }\n}\n\nfunction statePath(goalId: string): string {\n return join(STATE_DIR, `${goalId}.json`);\n}\n\nfunction loadState(goalId: string): DriverState | null {\n const path = statePath(goalId);\n if (!existsSync(path)) return null;\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf-8')) as DriverState;\n if (parsed.schemaVersion !== 1) {\n logger.warn(COMPONENT, `State for ${goalId} has unknown schemaVersion=${parsed.schemaVersion} — ignoring`);\n return null;\n }\n return parsed;\n } catch (err) {\n logger.warn(COMPONENT, `Could not parse state for ${goalId}: ${(err as Error).message}`);\n return null;\n }\n}\n\nfunction saveState(state: DriverState): void {\n ensureStateDir();\n state.lastTickAt = new Date().toISOString();\n const path = statePath(state.goalId);\n try {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path + '.tmp', JSON.stringify(state, null, 2));\n // Atomic rename so partial writes never surface\n // (Windows rename-over-existing is OK on Node 22+)\n renameSync(path + '.tmp', path);\n } catch (err) {\n logger.warn(COMPONENT, `Could not persist state for ${state.goalId}: ${(err as Error).message}`);\n }\n}\n\nfunction appendHistory(state: DriverState, phase: DriverPhase, note: string): void {\n const event: DriverHistoryEvent = { at: new Date().toISOString(), phase, note };\n state.history.push(event);\n if (state.history.length > 200) state.history = state.history.slice(-200);\n}\n\n// v6.0.3 — Approval-guidance composer. Distinguishes between a generic\n// \"yes proceed\" click vs real textual guidance, and synthesizes the right\n// follow-up directive for the specialist's next attempt.\n//\n// Generic markers (after trim + lowercase): \"approved\", \"approve\", \"ok\",\n// \"yes\", \"y\", \"go\", \"proceed\", \"do it\", \"lgtm\" — these mean \"go ahead,\n// make your best call.\" Real guidance is anything else >8 chars.\n//\n// Exported for the unit tests.\nconst GENERIC_APPROVAL_NOTES = new Set([\n 'approved', 'approve', 'ok', 'okay', 'yes', 'y', 'go', 'proceed',\n 'do it', 'lgtm', 'sure', 'fine', 'continue', 'yeah', 'yep',\n]);\n\nexport function composeApprovalGuidance(rawNote: string | undefined | null): string {\n const note = (rawNote ?? '').trim();\n const isGeneric = note === '' || note.length <= 8 || GENERIC_APPROVAL_NOTES.has(note.toLowerCase());\n if (isGeneric) {\n return [\n 'The user approved this subtask to proceed but did NOT provide additional textual guidance.',\n 'Interpretation: \"go ahead with your best-effort interpretation of the original task.\"',\n 'Do NOT re-ask the same question that caused the block — pick a reasonable assumption from',\n 'the available context and proceed. If you genuinely cannot make a reasonable assumption,',\n 'complete what you can and report what was unresolved in your reply rather than blocking again.',\n ].join(' ');\n }\n // Real textual guidance — pass it through as before, but reframe so\n // the specialist knows it is the user's explicit answer (not a prior\n // attempt's reasoning).\n return `The user's answer to the question that caused the previous block is: \"${note}\". Use this as authoritative guidance for the next attempt.`;\n}\n\n// ── Daemon-spawned auto-cancel guard (v6.0.3) ────────────────────\n//\n// When an autonomous producer (Soma pressure cycle, dreaming proposer,\n// self-repair daemon, autopilot, mission auto-decomposer) spins up a\n// goal and the goal hits the SAME blocking question twice within an\n// hour, we cancel it rather than file another approval. Rationale: the\n// question is structurally unanswerable by the specialist — re-asking\n// doesn't fix that, it just fills Tony's approval queue. A human-\n// authored goal stays in the queue forever (Tony will answer when he's\n// ready); only daemon goals get this auto-cancel.\n//\n// Detection is by tag — Goal has no `requestedBy` field, so we check\n// the canonical autonomous-tag set the producers attach.\n\nconst DAEMON_SPAWN_TAG_MARKERS = [\n /^soma:/i,\n /^autopilot/i,\n /^mission-auto$/i,\n /^self-repair$/i,\n /^self-mod$/i,\n /^self-healing$/i,\n /^self-modification$/i,\n /^canary-eval$/i,\n /^dreaming$/i,\n /^pressure$/i,\n];\n\n/** True if any of the goal's tags suggest an autonomous producer made it. */\nexport function isDaemonSpawnedGoal(goal: { tags?: string[] }): boolean {\n const tags = goal.tags ?? [];\n for (const t of tags) {\n for (const re of DAEMON_SPAWN_TAG_MARKERS) {\n if (re.test(t)) return true;\n }\n }\n return false;\n}\n\n/**\n * Normalize a blocking question into a stable fingerprint so trivial\n * formatting variations (whitespace, the rolling \"attempt N\" counter,\n * timestamp echoes) don't defeat the same-block-twice check.\n */\nexport function fingerprintBlockedQuestion(q: string): string {\n return q\n .toLowerCase()\n // Strip rolling counters / numbers / timestamps\n .replace(/attempt\\s*\\d+/g, 'attempt N')\n .replace(/\\b\\d{4}-\\d{2}-\\d{2}t[\\d:.]+z?\\b/g, '')\n .replace(/\\b\\d+(\\.\\d+)?\\b/g, 'N')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, 120);\n}\n\n/** Configurable in tests via the optional override; default 1h. */\nexport const SAME_BLOCK_TWICE_WINDOW_MS = 60 * 60 * 1000;\n\n/**\n * Check whether the next-block-to-be-filed is a recurrence of an\n * earlier one within the daemon-auto-cancel window. Mutates `state`\n * to append the new fingerprint regardless (so the next call can\n * still see it).\n *\n * Returns true if the goal should be auto-cancelled.\n */\nexport function shouldAutoCancelOnRecurringBlock(\n state: DriverState,\n goal: { tags?: string[] },\n blockKind: string,\n question: string,\n windowMs: number = SAME_BLOCK_TWICE_WINDOW_MS,\n): boolean {\n if (!isDaemonSpawnedGoal(goal)) {\n return false;\n }\n const fp = fingerprintBlockedQuestion(question);\n const now = Date.now();\n const history = state.blockHistory ?? [];\n const cutoff = now - windowMs;\n // Look for ANY prior block whose fingerprint matches and is within the window.\n const recurrence = history.find(ev => {\n if (ev.fingerprint !== fp) return false;\n const t = Date.parse(ev.at);\n return Number.isFinite(t) && t >= cutoff;\n });\n // Always record the new event (bounded to last 16 entries).\n const next: typeof history = [...history, { at: new Date(now).toISOString(), fingerprint: fp, kind: blockKind }];\n state.blockHistory = next.slice(-16);\n return !!recurrence;\n}\n\n// ── Infrastructure failure detection ──────────────────────────────\n\n/**\n * Detect systematic infrastructure failures that warrant human escalation.\n * Returns true if the error indicates all specialists are failing to produce\n * structured JSON output (thinking patterns, parse errors).\n */\nfunction isInfrastructureFailure(error: string | undefined): boolean {\n if (!error) return false;\n const e = error.toLowerCase();\n // JSON parse failures from structuredSpawn\n const parseFailurePatterns = [\n 'parser could not extract json',\n 'no json block found',\n 'json.parse failure',\n 'prose-fallback:thinking',\n 'thinking prose instead of structured json',\n ];\n return parseFailurePatterns.some(p => e.includes(p));\n}\n\n// ── Init / creation ──────────────────────────────────────────────\n\nfunction freshDriverState(goal: Goal): DriverState {\n const subtaskStates: Record<string, DriverSubtaskState> = {};\n const kinds = classifyAll(goal.subtasks || []);\n for (const sub of goal.subtasks || []) {\n subtaskStates[sub.id] = {\n kind: kinds[sub.id] ?? 'analysis',\n attempts: 0,\n // v4.10.0-local (post-deploy): 5 = full ladder depth\n // (primary + 4 fallbacks). Ensures the claude-code MAX-plan\n // tier is reachable before the subtask gives up. Goal-level\n // maxRetries: 10 remains as the cross-subtask backstop.\n maxAttempts: 5,\n artifacts: [],\n };\n }\n const now = new Date().toISOString();\n return {\n schemaVersion: 1,\n goalId: goal.id,\n phase: 'planning',\n startedAt: now,\n lastTickAt: now,\n budget: { tokensUsed: 0, costUsd: 0, elapsedMs: 0, totalRetries: 0 },\n budgetCaps: { ...DEFAULT_BUDGET_CAPS },\n userControls: {\n paused: false,\n cancelRequested: false,\n priority: (goal.priority as 1 | 2 | 3 | 4 | 5) ?? 3,\n },\n subtaskStates,\n history: [{ at: now, phase: 'planning', note: `Driver started for \"${goal.title}\"` }],\n };\n}\n\n// ── Phase transitions (one tick = one transition) ───────────────\n\nasync function tickPlanning(goal: Goal, state: DriverState): Promise<void> {\n // Ensure subtasks exist (the proposer usually creates them; if a goal\n // came without any, we classify based on title and create a single\n // `analysis` subtask as a placeholder).\n if (!goal.subtasks || goal.subtasks.length === 0) {\n appendHistory(state, 'planning', 'No subtasks — creating single analysis subtask from title');\n try {\n const { addSubtask } = await import('./goals.js');\n addSubtask(goal.id, goal.title, goal.description);\n } catch (err) {\n logger.warn(COMPONENT, `Could not add default subtask to ${goal.id}: ${(err as Error).message}`);\n }\n }\n\n // v4.10.0-local (post-deploy, Fix 9): classify lazily and persist\n // once. Previously this called classifyAll unconditionally on every\n // planning pass, but only persisted new entries — if taxonomy rules\n // changed between boots, restored drivers kept their stale kinds\n // silently. Only classify when we actually need to create a new\n // subtask state entry.\n let kinds: Record<string, ReturnType<typeof classifyAll>[string]> | null = null;\n for (const sub of goal.subtasks || []) {\n if (!state.subtaskStates[sub.id]) {\n if (!kinds) kinds = classifyAll(goal.subtasks || []);\n state.subtaskStates[sub.id] = {\n kind: kinds[sub.id] ?? 'analysis',\n attempts: 0,\n maxAttempts: 5, // per Fix B — matches ladder depth\n artifacts: [],\n };\n }\n }\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Planned: ${Object.keys(state.subtaskStates).length} subtasks classified`);\n}\n\nasync function tickDelegating(goal: Goal, state: DriverState): Promise<void> {\n // Find the next ready subtask (dependencies satisfied, not already done/failed)\n const next = await pickNextReadySubtask(goal, state);\n if (!next) {\n // Re-fetch goal so the allResolved check sees any subtasks that\n // pickNextReadySubtask just marked as failed (durable deadlock\n // recovery from Fix C). Falls back to the stale reference on\n // import failure — worst case we deadlock once more and recover\n // next tick.\n let freshGoal: Goal = goal;\n try {\n const { getGoal } = await import('./goals.js');\n freshGoal = getGoal(goal.id) || goal;\n } catch { /* ok */ }\n // No more ready subtasks — either all done or all blocked on deps.\n // Check if everything's done or failed:\n const allResolved = (freshGoal.subtasks || []).every(\n s => s.status === 'done' || s.status === 'failed' || s.status === 'skipped',\n );\n if (allResolved) {\n // v4.10.0-local (post-deploy): clear currentSubtaskId so\n // tickVerifying takes the whole-goal-verify branch\n // (line 316). Otherwise it keeps trying to verify the\n // last-touched subtask (which may be stale or terminal)\n // and oscillates verifying → iterating → delegating.\n state.currentSubtaskId = undefined;\n state.phase = 'verifying';\n appendHistory(state, 'verifying', 'All subtasks resolved — running final verification');\n } else {\n // Dependencies blocking — pause-block for human\n state.phase = 'blocked';\n state.blockedReason = {\n question: `Goal \"${goal.title}\" is deadlocked. All pending subtasks have unresolved dependencies and none are ready to run. Please review the subtask order and dependencies.`,\n approvalId: '', // filled below if we file an approval\n sinceAt: new Date().toISOString(),\n kind: 'dep_deadlock',\n };\n appendHistory(state, 'blocked', 'Dependency deadlock');\n }\n return;\n }\n\n state.currentSubtaskId = next.id;\n const subState = state.subtaskStates[next.id];\n subState.attempts += 1;\n\n // v4.10.0-local (post-deploy, Fix B): per-subtask attempt cap. A single\n // unlucky subtask can no longer consume the whole goal's retry budget.\n // We fail fast on THIS subtask and let the driver move to the next one.\n const cap = subState.maxAttempts ?? 5;\n if (subState.attempts > cap) {\n subState.verificationResult = {\n passed: false,\n reason: `Per-subtask cap exceeded (${cap})`,\n verifier: 'budget',\n };\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, subState.lastError || `per-subtask cap exceeded (${cap} attempts)`);\n } catch { /* ok */ }\n state.currentSubtaskId = undefined;\n state.phase = 'delegating'; // next tick picks up the next ready subtask\n appendHistory(state, 'delegating', `Subtask ${next.id} exceeded per-subtask cap (${cap}) — moved on`);\n return;\n }\n\n // Pass the per-subtask cap — NOT goal-level maxRetries — to\n // nextFallback. The ladder has 5 tiers; matching means we can reach\n // the final (claude-code MAX) tier before declaring exhaustion.\n const strategy = nextFallback(subState.kind, subState.attempts - 1, subState.lastError, cap);\n if (!strategy) {\n // v4.10.0-local fix: Check if exhaustion is due to infrastructure failure\n // (systematic JSON parse errors). If so, escalate to human instead of\n // silently failing the subtask.\n if (isInfrastructureFailure(subState.lastError)) {\n state.phase = 'escalated';\n state.blockedReason = {\n question: `Goal \"${goal.title}\" — all specialists failed to produce valid output after ${goal.subtasks?.length ?? 0} subtask(s). This usually means the model is misconfigured, the task is too vague, or the specialist doesn't have the right tools. Please review the goal description or switch the model tier.`,\n approvalId: '',\n sinceAt: new Date().toISOString(),\n kind: 'infrastructure_failure',\n };\n appendHistory(state, 'escalated', `Subtask ${next.id}: infrastructure failure — all specialists failed JSON output`);\n await fileBlockedApproval(state, goal, [state.blockedReason.question]);\n return;\n }\n\n // Exhausted retries for this subtask (normal failure)\n subState.verificationResult = {\n passed: false,\n reason: 'Max retries exhausted',\n verifier: 'budget',\n };\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, 'max-retries-exhausted');\n } catch { /* ok */ }\n state.currentSubtaskId = undefined;\n state.phase = 'delegating'; // try next subtask\n appendHistory(state, 'delegating', `Subtask ${next.id} exhausted retries — moved on`);\n return;\n }\n\n subState.specialist = strategy.specialist;\n subState.pendingSpawn = {\n attemptedAt: new Date().toISOString(),\n specialist: strategy.specialist,\n };\n state.phase = 'observing';\n appendHistory(\n state,\n 'observing',\n `Spawning ${strategy.specialist} for subtask \"${next.title}\" (kind=${subState.kind}, attempt ${subState.attempts})`,\n );\n\n // Actually fire the spawn — runs in-tick, driver waits for the return.\n // (Future: async wakeup path so driver can observe multiple concurrent\n // spawns; for now one at a time per goal.)\n try {\n const startMs = Date.now();\n const result = await structuredSpawn({\n specialistId: strategy.specialist,\n task: `${next.title}\\n\\n${next.description}${strategy.promptAdjustment ?? ''}`,\n modelOverride: strategy.modelOverride,\n toolAllowlist: routeForKind(subState.kind).toolAllowlist,\n maxRounds: strategy.maxRounds,\n });\n const durationMs = Date.now() - startMs;\n recordSpend(state, {\n elapsedMs: durationMs,\n tokens: result.tokensUsed ?? 0,\n costUsd: result.costUsd ?? 0,\n });\n\n // Store artifacts\n subState.artifacts = [...new Set([\n ...subState.artifacts,\n ...result.artifacts.map(a => a.ref),\n ])];\n\n // Decide phase based on spawn status\n if (result.status === 'done') {\n state.phase = 'verifying';\n appendHistory(state, 'verifying', `Spawn returned done with ${result.artifacts.length} artifact(s), confidence ${result.confidence.toFixed(2)}`);\n // Stash the spawn result so verifying can read it\n (subState as DriverSubtaskState & { lastSpawnResult?: unknown }).lastSpawnResult = result;\n } else if (result.status === 'failed') {\n subState.lastError = result.reasoning || 'failed';\n state.phase = 'iterating';\n appendHistory(state, 'iterating', `Spawn returned failed: ${subState.lastError.slice(0, 120)}`);\n } else if (result.status === 'needs_info' || result.status === 'blocked') {\n state.phase = 'blocked';\n // v4.14.0: build a rich, contextual blocked reason instead of\n // the generic \"Specialist requires input\" that tells the user\n // nothing about what actually went wrong.\n const subtaskTitle = next.title || 'Unnamed subtask';\n const specialistName = strategy.specialist || subState.specialist || 'specialist';\n const attemptCount = subState.attempts;\n const lastErr = subState.lastError;\n const rawQuestion = result.questions[0] ?? '';\n\n let richQuestion: string;\n if (rawQuestion && rawQuestion.length > 10 && !rawQuestion.toLowerCase().includes('specialist requires')) {\n // The specialist gave a real question — use it but wrap with context\n richQuestion = `Goal \"${goal.title}\" is stuck on subtask \"${subtaskTitle}\" (attempt ${attemptCount}, specialist: ${specialistName}).\\n\\n${rawQuestion}`;\n } else if (lastErr) {\n richQuestion = `Goal \"${goal.title}\" — subtask \"${subtaskTitle}\" failed after ${attemptCount} attempt(s) with specialist ${specialistName}.\\n\\nError: ${lastErr.slice(0, 200)}\\n\\nWhat should the specialist do next?`;\n } else {\n richQuestion = `Goal \"${goal.title}\" — subtask \"${subtaskTitle}\" is blocked after ${attemptCount} attempt(s) with specialist ${specialistName}. The specialist could not complete the task and needs guidance on how to proceed.`;\n }\n\n state.blockedReason = {\n question: richQuestion,\n approvalId: '',\n sinceAt: new Date().toISOString(),\n kind: 'needs_info',\n };\n appendHistory(state, 'blocked', `Spawn needs info: ${richQuestion.slice(0, 120)}`);\n await fileBlockedApproval(state, goal, [richQuestion, ...result.questions.slice(1)]);\n }\n subState.pendingSpawn = undefined;\n } catch (err) {\n const msg = (err as Error).message;\n subState.lastError = msg;\n state.phase = 'iterating';\n appendHistory(state, 'iterating', `Spawn threw: ${msg.slice(0, 120)}`);\n }\n}\n\nasync function tickObserving(goal: Goal, state: DriverState): Promise<void> {\n // In Phase A, spawns are sync (await structuredSpawn completes in tickDelegating).\n // This phase exists for future async-wakeup integration; for now it just\n // advances based on whatever subState said after the spawn.\n //\n // v4.10.0-local (post-deploy, Fix 8): be a no-op unless there's a\n // pending spawn waiting for a wakeup. Previously this unconditionally\n // transitioned to iterating, which tickIterating treated as a failure\n // and burned an attempt on goals that never actually spawned. Now the\n // observing phase only advances when there's a spawn to observe.\n void goal;\n const currentId = state.currentSubtaskId;\n const subState = currentId ? state.subtaskStates[currentId] : undefined;\n if (!subState?.pendingSpawn) {\n // Nothing to observe — bounce back to delegating without counting\n // as a retry.\n state.phase = 'delegating';\n return;\n }\n state.phase = 'iterating';\n appendHistory(state, 'iterating', 'Observe tick with no spawn progress — iterating');\n}\n\nasync function tickIterating(goal: Goal, state: DriverState): Promise<void> {\n const currentId = state.currentSubtaskId;\n if (!currentId) {\n state.phase = 'delegating';\n return;\n }\n const subState = state.subtaskStates[currentId];\n\n // v4.10.0-local (post-deploy, Fix A): forward-progress detector.\n // If the same lastError (first 80 chars) repeats 3 times in a row,\n // retrying isn't producing new information — fail this subtask and\n // move on. Prevents the verifying↔iterating→delegating oscillation\n // observed on stuck drivers. Uses a semantic signal (error text)\n // rather than phase-pattern detection, which false-positives on\n // legitimate multi-phase verify passes.\n const fingerprint = (subState.lastError || '').slice(0, 80).toLowerCase().trim();\n if (fingerprint) {\n if (fingerprint === subState.lastErrorFingerprint) {\n subState.consecutiveIdenticalErrors = (subState.consecutiveIdenticalErrors || 0) + 1;\n } else {\n subState.consecutiveIdenticalErrors = 1;\n subState.lastErrorFingerprint = fingerprint;\n }\n if ((subState.consecutiveIdenticalErrors ?? 0) >= 3) {\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, currentId, `Stuck loop: same error 3× in a row: ${fingerprint}`);\n } catch { /* ok */ }\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Broke stall loop on ${currentId} (same error 3×)`);\n return;\n }\n }\n\n // v4.10.0-local (post-deploy, Fix 8): do NOT increment totalRetries\n // here. Per-subtask attempts are counted in tickDelegating. Counting\n // again here caused double-billing: one failed spawn consumed 2 budget\n // units. Retry budget is meant to track subtask-transition retries,\n // not individual spawn retries.\n const check = checkBudget(state);\n if (check.status === 'exceeded') {\n const suggestion = suggestDegradation(state);\n if (suggestion === 'ask_human') {\n state.phase = 'blocked';\n state.blockedReason = {\n question: `Goal \"${goal.title}\" — budget exceeded (${check.message}). The driver has used ${state.budget.costUsd.toFixed(2)} USD so far. Continue with extended budget, de-scope, or cancel?`,\n approvalId: '',\n sinceAt: new Date().toISOString(),\n kind: 'budget_exceeded',\n };\n appendHistory(state, 'blocked', check.message);\n await fileBlockedApproval(state, goal, [state.blockedReason.question]);\n return;\n }\n }\n\n if (subState.attempts >= state.budgetCaps.maxRetries) {\n // Gap 2 (plan-this-logical-ocean): before giving up on this subtask,\n // consult the bounded continuation counter. If the per-subtask cap\n // hasn't been hit (max 2, persisted to disk so restarts can't\n // bypass), halve attempts and let it try again. This is the\n // \"plan_only\" signal — spawns kept producing plans/output that\n // wouldn't verify. Two extra cycles across a restart boundary is\n // the cheapest escape from the verifying↔iterating oscillation that\n // the existing stuck-loop detector can't catch (different errors\n // each time, but no real progress).\n const { shouldContinue } = await import('./runContinuations.js');\n const continuationKey = `${goal.id}:${currentId}`;\n if (shouldContinue(continuationKey, 'plan_only')) {\n const before = subState.attempts;\n subState.attempts = Math.max(0, Math.floor(subState.attempts / 2));\n subState.consecutiveIdenticalErrors = 0;\n subState.lastErrorFingerprint = undefined;\n appendHistory(state, 'delegating', `Continuation granted on ${currentId}: attempts halved ${before} → ${subState.attempts}`);\n state.phase = 'delegating';\n return;\n }\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, currentId, subState.lastError || 'max retries');\n } catch { /* ok */ }\n appendHistory(state, 'delegating', `Subtask ${currentId} failed after ${subState.attempts} attempts`);\n state.phase = 'delegating';\n return;\n }\n\n // Back to delegating to try the next fallback\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Iterating on subtask ${currentId} — attempt ${subState.attempts + 1}`);\n}\n\nasync function tickVerifying(goal: Goal, state: DriverState): Promise<void> {\n const currentId = state.currentSubtaskId;\n if (!currentId) {\n // Whole-goal verify — check per-subtask results against the goal\n // file's subtask statuses. A subtask passes the whole-goal check\n // iff EITHER (a) its verificationResult.passed === true, OR\n // (b) the goal file reports it as done/skipped (completed through\n // an external path, e.g. human marked it done).\n //\n // v4.10.0-local (post-deploy, Fix 8): previous check was\n // `passed !== false`, which treated subtasks that never got\n // verified at all (verificationResult undefined) as passing\n // vacuously. Now we require an explicit pass signal OR an\n // explicit terminal status on the goal-side subtask.\n const goalSubs = goal.subtasks || [];\n const subById = new Map(goalSubs.map(s => [s.id, s]));\n const allPassed = Object.entries(state.subtaskStates).every(([subId, subState]) => {\n if (subState.verificationResult?.passed === true) return true;\n const goalSub = subById.get(subId);\n if (goalSub?.status === 'done' || goalSub?.status === 'skipped') return true;\n return false;\n });\n state.phase = allPassed ? 'reporting' : 'failed';\n appendHistory(state, state.phase, allPassed ? 'All subtasks verified' : 'Some subtasks failed verification');\n return;\n }\n const subState = state.subtaskStates[currentId];\n const subtask = (goal.subtasks || []).find(s => s.id === currentId);\n if (!subtask) {\n state.phase = 'delegating';\n return;\n }\n const lastSpawn = (subState as DriverSubtaskState & { lastSpawnResult?: unknown }).lastSpawnResult;\n if (!lastSpawn) {\n // No spawn result to verify against — iterate\n state.phase = 'iterating';\n appendHistory(state, 'iterating', 'Verify with no spawn result — iterating');\n return;\n }\n\n const verifyResult = await verifyByKind({\n kind: subState.kind,\n subtask,\n spawnResult: lastSpawn as Parameters<typeof verifyByKind>[0]['spawnResult'],\n });\n subState.verificationResult = verifyResult;\n\n if (verifyResult.passed) {\n try {\n const { completeSubtask } = await import('./goals.js');\n // v4.10.0-local polish: store the SPAWN'S actual output\n // (reasoning + artifacts summary), not the verifier's pass\n // message. Prior behavior stored \"Analysis 171 chars, conf 0.95\"\n // in the subtask.result field, losing the actual content.\n const spawn = lastSpawn as Parameters<typeof verifyByKind>[0]['spawnResult'];\n const artifactSummary = spawn.artifacts.length > 0\n ? `\\n\\nArtifacts:\\n${spawn.artifacts.map(a => ` - [${a.type}] ${a.ref}${a.description ? ` — ${a.description}` : ''}`).join('\\n')}`\n : '';\n const contentToSave = (spawn.reasoning || spawn.rawResponse || verifyResult.reason) + artifactSummary;\n completeSubtask(goal.id, currentId, contentToSave);\n } catch { /* ok */ }\n appendHistory(state, 'delegating', `Subtask ${currentId} verified: ${verifyResult.reason.slice(0, 120)}`);\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n } else {\n subState.lastError = `Verification failed: ${verifyResult.reason}`;\n appendHistory(state, 'iterating', `Verification failed: ${verifyResult.reason.slice(0, 120)}`);\n state.phase = 'iterating';\n }\n}\n\nasync function tickReporting(goal: Goal, state: DriverState): Promise<void> {\n const durationMs = Date.now() - new Date(state.startedAt).getTime();\n const specialistsUsed = [...new Set(\n Object.values(state.subtaskStates)\n .map(s => s.specialist)\n .filter((x): x is string => !!x),\n )];\n state.retrospective = {\n success: true,\n durationMs,\n tokensUsed: state.budget.tokensUsed,\n costUsd: state.budget.costUsd,\n lessonsLearned: [\n `Completed ${Object.keys(state.subtaskStates).length} subtasks across ${specialistsUsed.length} specialist(s) in ${Math.round(durationMs / 1000)}s`,\n ],\n specialistsUsed,\n };\n try {\n const { updateGoal } = await import('./goals.js');\n updateGoal(goal.id, { status: 'completed' });\n } catch { /* ok */ }\n state.phase = 'done';\n appendHistory(state, 'done', `Goal completed: ${state.retrospective.lessonsLearned[0]}`);\n try { await onGoalCompleted(goal, state); } catch { /* ok */ }\n // v4.10.0-local (Phase B): record retrospective for future goal learning\n try {\n const { saveRetrospective } = await import('./retrospectives.js');\n await saveRetrospective(goal, state);\n } catch { /* ok */ }\n\n // Fire episode\n try {\n const { recordEpisode } = await import('../memory/episodic.js');\n recordEpisode({\n kind: 'goal_completed',\n summary: `Driver completed goal \"${goal.title}\" (${Math.round(durationMs / 1000)}s, ${Object.keys(state.subtaskStates).length} subtasks, specialists: ${specialistsUsed.join(', ')})`,\n detail: state.history.slice(-10).map(h => `${h.at} ${h.phase}: ${h.note}`).join('\\n'),\n tags: ['goal-driver', 'goal_completed', goal.id, ...(goal.tags || [])],\n });\n } catch { /* ok */ }\n}\n\nasync function tickBlocked(goal: Goal, state: DriverState): Promise<void> {\n // v4.10.0-local (post-deploy, Fix F): auto-unblock stale blocked states.\n // The block was either never backed by an approval (approvalId empty —\n // bookkeeping bug) or the approval no longer exists (deleted / TTL'd).\n // After 10 minutes of sitting idle with no live approval, retry the\n // subtask rather than sitting forever waiting for a human who has no\n // way to resolve this. Uses forceUnblockDriver so the recovery is\n // consistent with the manual API path.\n const approvalId = state.blockedReason?.approvalId;\n const sinceAt = state.blockedReason?.sinceAt;\n const sinceMs = sinceAt ? Date.now() - new Date(sinceAt).getTime() : 0;\n const STALE_MS = 10 * 60 * 1000;\n if (sinceMs > STALE_MS) {\n let approval: { status?: string } | null = null;\n if (approvalId) {\n try {\n const { getApproval } = await import('./commandPost.js');\n approval = getApproval(approvalId) as { status?: string } | null;\n } catch { /* ok */ }\n }\n if (!approvalId || !approval) {\n // Unblock in-place on the shared state reference. We can't\n // call forceUnblockDriver here because it does its own\n // loadState/saveState cycle, and tickDriver's outer saveState\n // would then overwrite those changes with our stale local\n // state. Mirror forceUnblockDriver's logic here.\n logger.info(COMPONENT, `Auto-unblocking stale blocked state for ${goal.id} (approvalId=${approvalId || 'empty'}, age=${Math.round(sinceMs / 60000)}min)`);\n const note = `stale block auto-recovered (age ${Math.round(sinceMs / 60000)}min)`;\n const wasBudget = state.blockedReason?.kind === 'budget_exceeded'\n || /budget.*exceed|retries exceed/i.test(state.blockedReason?.question || '');\n if (wasBudget) {\n state.budget.totalRetries = Math.floor(state.budget.totalRetries / 2);\n }\n const currentId = state.currentSubtaskId;\n if (currentId && state.subtaskStates[currentId]) {\n const sub = state.subtaskStates[currentId];\n sub.attempts = Math.floor(sub.attempts / 2);\n sub.consecutiveIdenticalErrors = 0;\n }\n state.blockedReason = undefined;\n state.phase = 'iterating';\n appendHistory(state, 'iterating', `Force-unblocked: ${note}`);\n return;\n }\n }\n\n // Check if the blocking approval has been decided\n if (!approvalId) return; // nothing to unblock from\n try {\n const { getApproval } = await import('./commandPost.js');\n const approval = getApproval(approvalId);\n if (!approval) return;\n if (approval.status === 'pending') return; // still waiting\n if (approval.status === 'approved') {\n // v6.0.3 — Incorporate the human's response into the current\n // subtask's context. The pre-v6.0.3 path composed\n // `Previous attempt needed info; human provided: \"${note}\".\n // Try again using this.` regardless of what the note was.\n //\n // When the user clicked \"Approve\" WITHOUT supplying actual\n // textual guidance via /api/command-post/approvals/:id/reply,\n // `decisionNote` was either undefined OR a generic verb like\n // \"Approved\" / \"OK\" / \"Yes\". The specialist would then try\n // to use the literal string \"Approved\" as the answer to a\n // question like \"What project should I document?\", fail\n // because that isn't actionable info, and re-block. Tony\n // would approve again, same loop. Approvals became useless\n // for `needs_info` blocks.\n //\n // Fix: detect generic/empty notes and synthesize a \"proceed\n // with best-effort interpretation; do NOT re-ask the same\n // question\" directive instead of feeding the action label.\n // When the note IS real textual guidance (>8 chars, not a\n // common approve-verb), pass it through as before.\n const currentId = state.currentSubtaskId;\n if (currentId) {\n const subState = state.subtaskStates[currentId];\n subState.lastError = composeApprovalGuidance(approval.decisionNote);\n }\n state.blockedReason = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Unblocked by human (approval ${approvalId})`);\n return;\n }\n if (approval.status === 'rejected') {\n state.phase = 'failed';\n appendHistory(state, 'failed', `Goal rejected by human via approval ${approvalId}`);\n await onGoalFailed(goal, state, 'rejected by human');\n return;\n }\n } catch { /* ok */ }\n}\n\nasync function tickEscalated(goal: Goal, state: DriverState): Promise<void> {\n // v4.10.0-local: Escalated phase handles systematic infrastructure failures.\n // Similar to blocked, but specifically for JSON parse failures that indicate\n // model tier issues. Requires human intervention to fix infrastructure.\n\n const approvalId = state.blockedReason?.approvalId;\n const sinceAt = state.blockedReason?.sinceAt;\n const sinceMs = sinceAt ? Date.now() - new Date(sinceAt).getTime() : 0;\n\n // Check if the escalation approval has been decided\n if (!approvalId) return;\n try {\n const { getApproval } = await import('./commandPost.js');\n const approval = getApproval(approvalId);\n if (!approval) return;\n if (approval.status === 'pending') return; // still waiting\n\n if (approval.status === 'approved') {\n // Human acknowledged the infrastructure issue and wants to retry\n // Reset the subtask attempts to give it another go with potentially\n // new model configuration\n const currentId = state.currentSubtaskId;\n if (currentId) {\n const subState = state.subtaskStates[currentId];\n subState.attempts = 0; // Reset to allow fresh attempts\n subState.consecutiveIdenticalErrors = 0;\n subState.lastError = undefined;\n subState.lastErrorFingerprint = undefined;\n }\n state.blockedReason = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Escalation resolved by human — retrying subtask ${currentId} with fresh attempts`);\n return;\n }\n\n if (approval.status === 'rejected') {\n // Human decided to fail the goal rather than retry\n state.phase = 'failed';\n appendHistory(state, 'failed', `Infrastructure escalation rejected — goal failed`);\n await onGoalFailed(goal, state, 'infrastructure escalation rejected by human');\n return;\n }\n } catch { /* ok */ }\n}\n\nasync function tickFailed(goal: Goal, state: DriverState): Promise<void> {\n const durationMs = Date.now() - new Date(state.startedAt).getTime();\n state.retrospective = {\n success: false,\n durationMs,\n tokensUsed: state.budget.tokensUsed,\n costUsd: state.budget.costUsd,\n lessonsLearned: [\n `Goal failed after ${Math.round(durationMs / 1000)}s, ${state.budget.totalRetries} retries`,\n ],\n specialistsUsed: [...new Set(Object.values(state.subtaskStates).map(s => s.specialist).filter((x): x is string => !!x))],\n };\n try {\n const { updateGoal } = await import('./goals.js');\n updateGoal(goal.id, { status: 'failed' });\n } catch { /* ok */ }\n try { await onGoalFailed(goal, state, 'driver terminated in failed state'); } catch { /* ok */ }\n // Phase B: failed retrospectives are the most valuable — they teach us what to avoid\n try {\n const { saveRetrospective } = await import('./retrospectives.js');\n await saveRetrospective(goal, state);\n } catch { /* ok */ }\n try {\n const { recordEpisode } = await import('../memory/episodic.js');\n recordEpisode({\n kind: 'goal_failed',\n summary: `Driver failed goal \"${goal.title}\" after ${Math.round(durationMs / 1000)}s`,\n detail: state.history.slice(-15).map(h => `${h.at} ${h.phase}: ${h.note}`).join('\\n'),\n tags: ['goal-driver', 'goal_failed', goal.id, ...(goal.tags || [])],\n });\n } catch { /* ok */ }\n}\n\nasync function tickCancelled(goal: Goal, state: DriverState): Promise<void> {\n try {\n const { updateGoal } = await import('./goals.js');\n updateGoal(goal.id, { status: 'failed' });\n } catch { /* ok */ }\n appendHistory(state, 'cancelled', 'Cancelled by user');\n}\n\n// ── Helpers ──────────────────────────────────────────────────────\n\nasync function pickNextReadySubtask(goal: Goal, state: DriverState): Promise<Subtask | null> {\n // v4.10.0-local (post-deploy, Fix C): synchronous deadlock recovery.\n // If a pending subtask has exhausted its attempts with a failed\n // verification, await failSubtask to persist the failure *before*\n // continuing. The previous async mutation was ephemeral — the next\n // tick's getGoal() call saw stale data and re-entered the deadlock\n // branch in tickDelegating. This version is durable.\n const cap = (id: string) => state.subtaskStates[id]?.maxAttempts ?? 5;\n for (const sub of goal.subtasks || []) {\n if (sub.status !== 'pending') continue;\n const subState = state.subtaskStates[sub.id];\n if (!subState) continue;\n const exhaustedAttempts = subState.attempts >= cap(sub.id)\n || subState.attempts >= state.budgetCaps.maxRetries;\n if (subState.verificationResult?.passed === false && exhaustedAttempts) {\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, sub.id, subState.lastError || 'max retries exceeded (deadlock recovery)');\n // failSubtask mutates the cached goalsCache in place, so our\n // `goal` reference (passed by the caller, loaded from the\n // same cache) now reflects status: 'failed'. Belt-and-braces:\n // also update our local object in case the cache was bypassed.\n sub.status = 'failed';\n } catch { /* ok — driver will re-try next tick */ }\n continue;\n }\n // Respect dependsOn: skip subtasks whose prerequisites are not completed\n const depsSatisfied = (sub.dependsOn ?? []).every(depId => {\n const dep = goal.subtasks?.find(s => s.id === depId);\n return dep?.status === 'done';\n });\n if (!depsSatisfied) continue;\n return sub;\n }\n return null;\n}\n\nasync function fileBlockedApproval(\n state: DriverState,\n goal: Goal,\n questions: string[],\n): Promise<void> {\n const primaryQuestion = questions[0] ?? state.blockedReason?.question ?? 'Specialist requires input';\n const blockKind = state.blockedReason?.kind ?? 'needs_info';\n\n // v6.0.3 — same-block-twice auto-cancel for daemon-spawned goals.\n // We do this BEFORE the throttle / approval-create path so a\n // recurring block on an autonomous goal collapses the loop instead\n // of throttling the surface signal.\n if (shouldAutoCancelOnRecurringBlock(state, goal, blockKind, primaryQuestion)) {\n const tagSummary = (goal.tags || []).slice(0, 4).join(',') || '(no tags)';\n logger.warn(\n COMPONENT,\n `Auto-cancelling daemon-spawned goal ${goal.id} (\"${goal.title.slice(0, 60)}\"): same block recurred within ${Math.round(SAME_BLOCK_TWICE_WINDOW_MS / 60000)}min [tags=${tagSummary}, kind=${blockKind}]`\n );\n state.phase = 'cancelled';\n state.blockedReason = undefined;\n state.userControls.cancelRequested = true;\n appendHistory(\n state,\n 'cancelled',\n `Auto-cancelled: daemon-spawned goal hit the same block (${blockKind}) twice within the recurrence window.`\n );\n try {\n // Goal model has no `cancelled` status — convention is to flip\n // to `failed` while the driver phase records `cancelled`. This\n // matches tickCancelled()'s own behavior.\n const { updateGoal } = await import('./goals.js');\n updateGoal(goal.id, { status: 'failed' });\n } catch (err) {\n logger.warn(COMPONENT, `Could not flip goal ${goal.id} status after auto-cancel: ${(err as Error).message}`);\n }\n try { await onGoalBlocked(goal, state); } catch { /* ok */ }\n return;\n }\n\n // v4.10.0-local (Phase B): throttle to 1 per (goalId, driver_blocked) per 5 min\n try {\n const { shouldCreateApproval } = await import('./notificationThrottle.js');\n if (!shouldCreateApproval(goal.id, 'driver_blocked')) {\n logger.debug(COMPONENT, `Throttled duplicate driver_blocked approval for goal ${goal.id}`);\n // Still record SOMA feedback since we DID get blocked\n try { await onGoalBlocked(goal, state); } catch { /* ok */ }\n return;\n }\n } catch { /* if throttle module unavailable, fall through */ }\n try {\n const { createApproval } = await import('./commandPost.js');\n const subState = state.currentSubtaskId ? state.subtaskStates[state.currentSubtaskId] : undefined;\n const subtaskTitle = goal.subtasks?.find(s => s.id === state.currentSubtaskId)?.title;\n const approval = createApproval({\n type: 'custom',\n requestedBy: 'goal-driver',\n payload: {\n kind: 'driver_blocked',\n goalId: goal.id,\n goalTitle: goal.title,\n question: questions[0] ?? state.blockedReason?.question ?? 'Specialist requires input',\n allQuestions: questions,\n blockedPhase: state.phase,\n currentSubtaskId: state.currentSubtaskId,\n subtaskKind: subState?.kind,\n subtaskTitle,\n specialist: subState?.specialist,\n attempts: subState?.attempts,\n lastError: subState?.lastError,\n urgency: 'high',\n },\n linkedIssueIds: [],\n });\n if (approval?.id && state.blockedReason) {\n state.blockedReason.approvalId = approval.id;\n }\n // Broadcast via SSE if broadcaster exists (throttled separately)\n try {\n const { shouldBroadcast } = await import('./notificationThrottle.js');\n if (shouldBroadcast('driver:blocked', goal.id)) {\n const g = globalThis as unknown as { __titan_sse_broadcast?: (topic: string, payload: unknown) => void };\n if (typeof g.__titan_sse_broadcast === 'function') {\n g.__titan_sse_broadcast('driver:blocked', {\n goalId: goal.id,\n goalTitle: goal.title,\n question: questions[0] ?? 'Specialist requires input',\n approvalId: approval?.id,\n });\n }\n }\n } catch { /* ok */ }\n } catch (err) {\n logger.warn(COMPONENT, `Could not file blocked-on-human approval: ${(err as Error).message}`);\n }\n // SOMA feedback: small hunger bump + slight purpose stall (via metricGuard)\n try { await onGoalBlocked(goal, state); } catch { /* ok */ }\n}\n\n// ── Main tick loop ──────────────────────────────────────────────\n\nexport async function tickDriver(goalId: string): Promise<DriverPhase> {\n const { getGoal } = await import('./goals.js');\n const goal = getGoal(goalId);\n if (!goal) return 'failed';\n\n let state = loadState(goalId);\n if (!state) {\n state = freshDriverState(goal);\n saveState(state);\n }\n\n // Respect user controls\n if (state.userControls.cancelRequested) {\n state.phase = 'cancelled';\n await tickCancelled(goal, state);\n saveState(state);\n return 'cancelled';\n }\n if (state.userControls.paused) return state.phase;\n\n // Kill switch inherits automatically — toolRunner gates writes,\n // spawn_agent early-returns. Drivers don't need to re-check.\n\n try {\n switch (state.phase) {\n case 'planning': await tickPlanning(goal, state); break;\n case 'delegating': await tickDelegating(goal, state); break;\n case 'observing': await tickObserving(goal, state); break;\n case 'iterating': await tickIterating(goal, state); break;\n case 'verifying': await tickVerifying(goal, state); break;\n case 'reporting': await tickReporting(goal, state); break;\n case 'blocked': await tickBlocked(goal, state); break;\n case 'escalated': await tickEscalated(goal, state); break;\n case 'failed': await tickFailed(goal, state); break;\n case 'done':\n case 'cancelled':\n break;\n }\n } catch (err) {\n logger.warn(COMPONENT, `Tick for ${goalId} threw in phase=${state.phase}: ${(err as Error).message}`);\n appendHistory(state, state.phase, `Tick threw: ${(err as Error).message.slice(0, 120)}`);\n }\n\n saveState(state);\n return state.phase;\n}\n\n/**\n * Drive a goal synchronously to a terminal or waiting state. Ticks in a\n * loop with a small pause between ticks so observing/blocked states give\n * the event loop time to breathe. Returns when the driver is terminal\n * (done/failed/cancelled) or waiting on a human (blocked).\n */\nexport async function driveGoal(goalId: string, maxTicks = 200): Promise<DriverPhase> {\n let last: DriverPhase = 'planning';\n for (let i = 0; i < maxTicks; i++) {\n last = await tickDriver(goalId);\n if (last === 'done' || last === 'failed' || last === 'cancelled' || last === 'blocked' || last === 'escalated') break;\n // Mini-delay between ticks to let IO + timers run\n await new Promise(res => setTimeout(res, 50));\n }\n return last;\n}\n\n// ── External API ────────────────────────────────────────────────\n\nexport function getDriverState(goalId: string): DriverState | null {\n return loadState(goalId);\n}\n\nexport function listActiveDrivers(): DriverState[] {\n ensureStateDir();\n if (!existsSync(STATE_DIR)) return [];\n const out: DriverState[] = [];\n for (const file of readdirSync(STATE_DIR)) {\n if (!file.endsWith('.json')) continue;\n const goalId = file.slice(0, -5);\n const s = loadState(goalId);\n if (s && s.phase !== 'done' && s.phase !== 'failed' && s.phase !== 'cancelled') out.push(s);\n }\n return out;\n}\n\nexport function listAllDrivers(): DriverState[] {\n ensureStateDir();\n if (!existsSync(STATE_DIR)) return [];\n const out: DriverState[] = [];\n for (const file of readdirSync(STATE_DIR)) {\n if (!file.endsWith('.json')) continue;\n const goalId = file.slice(0, -5);\n const s = loadState(goalId);\n if (s) out.push(s);\n }\n return out;\n}\n\nexport function pauseDriver(goalId: string): boolean {\n const s = loadState(goalId);\n if (!s) return false;\n s.userControls.paused = true;\n saveState(s);\n return true;\n}\n\nexport function resumeDriverControl(goalId: string): boolean {\n const s = loadState(goalId);\n if (!s) return false;\n s.userControls.paused = false;\n saveState(s);\n return true;\n}\n\n/**\n * Force a blocked driver back to iterating. Used when the block was\n * triggered by a machine-level failure (parse error, model glitch)\n * rather than a genuine human-answerable question, and the underlying\n * issue has been fixed (e.g. model swap).\n *\n * Also auto-rejects any pending approval the driver was blocked on so\n * it doesn't clutter the Approvals UI forever.\n */\nexport async function forceUnblockDriver(goalId: string, note = 'auto-unblock'): Promise<boolean> {\n const s = loadState(goalId);\n if (!s) return false;\n if (s.phase !== 'blocked') return false;\n\n // Auto-reject the bogus approval if one exists\n const approvalId = s.blockedReason?.approvalId;\n if (approvalId) {\n try {\n const { rejectApproval } = await import('./commandPost.js');\n rejectApproval(approvalId, 'force-unblock', `Auto-rejected by force-unblock: ${note}`);\n } catch { /* ok — approval might not exist anymore */ }\n }\n\n // If the block was budget-related, halve the retry counter so the\n // driver has headroom to retry with the underlying fix in place.\n // This is preferable to a full reset — it still records that the\n // goal has been difficult, just gives it another shot.\n const wasBudget = s.blockedReason?.kind === 'budget_exceeded'\n || /budget.*exceed|retries exceed/i.test(s.blockedReason?.question || '');\n if (wasBudget) {\n s.budget.totalRetries = Math.floor(s.budget.totalRetries / 2);\n appendHistory(s, 'iterating', `Force-unblock: budget halved (${s.budget.totalRetries} / ${s.budgetCaps.maxRetries} retries)`);\n }\n\n // v4.10.0-local (post-deploy, Fix F): also halve the per-subtask\n // attempt counter for the current subtask. Otherwise the subtask\n // re-hits its per-subtask cap immediately on resume (from Fix B)\n // and we just re-enter the blocked state next tick.\n const currentId = s.currentSubtaskId;\n if (currentId && s.subtaskStates[currentId]) {\n const sub = s.subtaskStates[currentId];\n const before = sub.attempts;\n sub.attempts = Math.floor(sub.attempts / 2);\n sub.consecutiveIdenticalErrors = 0; // reset stall detector too\n appendHistory(s, 'iterating', `Force-unblock: per-subtask attempts halved on ${currentId} (${before} → ${sub.attempts})`);\n }\n\n s.blockedReason = undefined;\n s.phase = 'iterating'; // iterating retries the current subtask with a fresh spawn\n appendHistory(s, 'iterating', `Force-unblocked: ${note}`);\n saveState(s);\n logger.info(COMPONENT, `Force-unblocked driver ${goalId}: ${note}${wasBudget ? ' (budget halved)' : ''}`);\n return true;\n}\n\nexport function cancelDriver(goalId: string): boolean {\n const s = loadState(goalId);\n if (!s) return false;\n s.userControls.cancelRequested = true;\n saveState(s);\n return true;\n}\n\n/**\n * Permanently remove a driver state file. Used by `resumeDriversAfterRestart`\n * to reap drivers whose goal no longer exists, and by the boot-time stale\n * sweep to cull drivers that haven't ticked in a long time.\n *\n * v5.5.31+. Before this, `cancelDriver` only flipped a `cancelRequested`\n * flag without deleting — meaning orphan driver files (e.g. tests pointing\n * at deleted goals) accumulated forever and the scheduler could re-tick\n * them. Audit 2026-05-08 found `mtime-cache-test.json` actively ticking\n * for 14h+ after the test that created it had finished.\n */\nexport function deleteDriverState(goalId: string): boolean {\n const path = statePath(goalId);\n if (!existsSync(path)) return false;\n try { rmSync(path, { force: true }); return true; }\n catch (err) { logger.warn(COMPONENT, `deleteDriverState ${goalId}: ${(err as Error).message}`); return false; }\n}\n\n/**\n * Sweep stale driver-state files. Returns count + ids of removed files.\n * A driver is considered stale if its `lastTickAt` is older than `maxAgeMs`\n * (default 24h). Called on gateway boot via `resumeDriversAfterRestart`.\n *\n * The audit found 5 driver-state files at 395+ hours old (16-17 days)\n * that resumeDriversAfterRestart kept \"cancelling\" via the flag but never\n * actually removing.\n */\nexport function sweepStaleDriverStates(maxAgeMs = 24 * 60 * 60 * 1000): { removed: number; ids: string[] } {\n ensureStateDir();\n const removed: string[] = [];\n const cutoff = Date.now() - maxAgeMs;\n try {\n const entries = readdirSync(STATE_DIR);\n for (const name of entries) {\n if (!name.endsWith('.json')) continue;\n const goalId = name.replace(/\\.json$/, '');\n const s = loadState(goalId);\n if (!s) continue;\n const lastTickMs = new Date(s.lastTickAt || s.startedAt).getTime();\n if (Number.isFinite(lastTickMs) && lastTickMs < cutoff) {\n if (deleteDriverState(goalId)) removed.push(goalId);\n }\n }\n } catch (err) {\n logger.warn(COMPONENT, `sweepStaleDriverStates: ${(err as Error).message}`);\n }\n return { removed: removed.length, ids: removed };\n}\n\nexport function reprioritizeDriver(goalId: string, priority: 1 | 2 | 3 | 4 | 5): boolean {\n const s = loadState(goalId);\n if (!s) return false;\n s.userControls.priority = priority;\n saveState(s);\n return true;\n}\n\nexport function _resetDriverStateForTests(goalId?: string): void {\n if (goalId) {\n try { rmSync(statePath(goalId), { force: true }); } catch { /* ok */ }\n } else {\n try { rmSync(STATE_DIR, { recursive: true, force: true }); } catch { /* ok */ }\n }\n}\n"],"mappings":"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","names":["subState"]}
@@ -200,7 +200,16 @@ ${(tags || []).join(" ")}`;
200
200
  const isSelfMod = matchedByText || matchedByTag;
201
201
  if (isSelfMod) {
202
202
  const cfg = loadConfig();
203
- const target = cfg.autonomy?.selfMod?.target || "/opt/TITAN";
203
+ const selfModBlock = cfg.autonomy?.selfMod;
204
+ const autoCreateGoals = selfModBlock?.autoCreateGoals === true;
205
+ if (!autoCreateGoals) {
206
+ logger.info(
207
+ COMPONENT,
208
+ `Dropping self-mod proposal "${title.slice(0, 60)}" \u2014 autonomy.selfMod.autoCreateGoals is disabled.`
209
+ );
210
+ return null;
211
+ }
212
+ const target = selfModBlock?.target || "/opt/TITAN";
204
213
  tags = tags ? [...tags] : [];
205
214
  if (!tagsLower.has("self-mod")) tags.push("self-mod");
206
215
  tags = tags.slice(0, 6);
@@ -397,6 +406,7 @@ function buildDefaultContext() {
397
406
  export {
398
407
  buildDefaultContext,
399
408
  generateGoalProposals,
409
+ normalizeProposal,
400
410
  remainingSlots
401
411
  };
402
412
  //# sourceMappingURL=goalProposer.js.map