titan-agent 6.1.0-beta.2 → 6.1.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/agent/goalDriver.js +31 -0
- package/dist/agent/goalDriver.js.map +1 -1
- package/dist/agent/missionLifecycle.js +1 -1
- package/dist/agent/missionLifecycle.js.map +1 -1
- package/dist/utils/constants.js +1 -1
- package/dist/utils/constants.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
|
|
47
47
|
## 🪵 NEW in v6.1 — Mission Chat + Desk view
|
|
48
48
|
|
|
49
|
-
> Status: `v6.1.0-beta.
|
|
49
|
+
> Status: `v6.1.0-beta.4` — live on npm at `@latest` and `@beta`. Alpha series concluded with `6.1.0-alpha.57`; the schema promoted to beta once features stabilized.
|
|
50
50
|
> Install with `npm i -g titan-agent` (or `npm update -g titan-agent`).
|
|
51
51
|
> The v6.0 "Presence" feature set below still applies — v6.1.0 layers
|
|
52
52
|
> a beautiful new surface on top of it.
|
package/dist/agent/goalDriver.js
CHANGED
|
@@ -366,11 +366,13 @@ ${baseTask}` : baseTask;
|
|
|
366
366
|
...result.artifacts.map((a) => a.ref)
|
|
367
367
|
])];
|
|
368
368
|
if (result.status === "done") {
|
|
369
|
+
subState.consecutiveNeedsInfoCount = 0;
|
|
369
370
|
state.phase = "verifying";
|
|
370
371
|
appendHistory(state, "verifying", `Spawn returned done with ${result.artifacts.length} artifact(s), confidence ${result.confidence.toFixed(2)}`);
|
|
371
372
|
subState.lastSpawnResult = result;
|
|
372
373
|
} else if (result.status === "failed") {
|
|
373
374
|
subState.lastError = result.reasoning || "failed";
|
|
375
|
+
subState.consecutiveNeedsInfoCount = 0;
|
|
374
376
|
state.phase = "iterating";
|
|
375
377
|
appendHistory(state, "iterating", `Spawn returned failed: ${subState.lastError.slice(0, 120)}`);
|
|
376
378
|
} else if (result.status === "needs_info" || result.status === "blocked") {
|
|
@@ -379,6 +381,35 @@ ${baseTask}` : baseTask;
|
|
|
379
381
|
const attemptCount = subState.attempts;
|
|
380
382
|
const lastErr = subState.lastError;
|
|
381
383
|
const rawQuestion = result.questions[0] ?? "";
|
|
384
|
+
subState.consecutiveNeedsInfoCount = (subState.consecutiveNeedsInfoCount || 0) + 1;
|
|
385
|
+
const trimmedQuestion = rawQuestion.trim();
|
|
386
|
+
const needsInfoCount = subState.consecutiveNeedsInfoCount;
|
|
387
|
+
if (trimmedQuestion.length === 0 && attemptCount >= 2) {
|
|
388
|
+
try {
|
|
389
|
+
const { failSubtask } = await import("./goals.js");
|
|
390
|
+
failSubtask(goal.id, next.id, `Specialist ${specialistName} returned needs_info with an empty question on attempt ${attemptCount}. No actionable signal \u2014 failing subtask to break the loop.`);
|
|
391
|
+
} catch {
|
|
392
|
+
}
|
|
393
|
+
subState.lastError = `Failed: empty needs_info on attempt ${attemptCount} (specialist ${specialistName})`;
|
|
394
|
+
state.currentSubtaskId = void 0;
|
|
395
|
+
state.phase = "delegating";
|
|
396
|
+
appendHistory(state, "delegating", `Empty needs_info fail on ${next.id} (attempt ${attemptCount})`);
|
|
397
|
+
subState.pendingSpawn = void 0;
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (needsInfoCount >= 3) {
|
|
401
|
+
try {
|
|
402
|
+
const { failSubtask } = await import("./goals.js");
|
|
403
|
+
failSubtask(goal.id, next.id, `Specialist ${specialistName} returned needs_info ${needsInfoCount}\xD7 in a row on this subtask. Forcing failure to break the loop. Last question: "${trimmedQuestion.slice(0, 160)}"`);
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
406
|
+
subState.lastError = `Failed: ${needsInfoCount} consecutive needs_info returns`;
|
|
407
|
+
state.currentSubtaskId = void 0;
|
|
408
|
+
state.phase = "delegating";
|
|
409
|
+
appendHistory(state, "delegating", `Consecutive needs_info fail on ${next.id} (${needsInfoCount}\xD7)`);
|
|
410
|
+
subState.pendingSpawn = void 0;
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
382
413
|
if (subState.commitOverride) {
|
|
383
414
|
subState.commitOverride = false;
|
|
384
415
|
try {
|
|
@@ -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 { isGenericQuestion, forceCommitDirective } from './questionQuality.js';\nimport { onGoalCompleted, onGoalFailed, onGoalBlocked } from './somaFeedback.js';\nimport type { Goal, Subtask } from './goals.js';\nimport { emitAgentEvent } from './agentEvents.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 // v6.1.0-alpha.1 — emit a structured \"agent_spawn\" event on the shared\n // bus so listeners (Mission Chat lifecycle bridge, the SSE dashboard\n // tail, the Agent Watcher) know who's starting on what. Includes the\n // goalId so listeners can correlate without having to peer-into driver\n // state.\n try {\n emitAgentEvent({\n type: 'agent_spawn',\n agentId: strategy.specialist,\n agentName: strategy.specialist,\n timestamp: Date.now(),\n data: {\n goalId: goal.id,\n subtaskId: next.id,\n subtaskTitle: next.title,\n subtaskKind: subState.kind,\n attempt: subState.attempts,\n },\n });\n } catch { /* event bus failure should never block a spawn */ }\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 // v6.1.0-alpha.32 — if the goal looks like a document deliverable,\n // prepend a hard FORMAT directive to the task description. Belt\n // and suspenders with the specialist system prompt's HTML_REPORT_\n // GUIDANCE: even strong system-prompt rules can be drowned by a\n // long context, but a directive at the top of the per-task user\n // message is the LAST thing the LLM sees before producing output.\n const baseTask = `${next.title}\\n\\n${next.description}${strategy.promptAdjustment ?? ''}`;\n const documentLike = /\\b(essay|report|briefing|writeup|write-up|summary|article|document|memo|paper|analysis|whitepaper|brief)\\b/i.test(goal.title)\n || /\\b(essay|report|briefing|writeup|summary|article)\\b/i.test(next.title);\n const taskWithFormat = documentLike\n ? `OUTPUT FORMAT — REQUIRED:\\nThe user wants a real document they can view with images, charts, and styling. You MUST deliver this as a single self-contained .html file (write_file with a path ending in .html, e.g. \"${slugForGoal(goal.title)}.html\"). Do NOT write .md or .markdown — those can't show images or charts. See the OUTPUT FORMAT FOR DOCUMENTS section of your system prompt for the structure.\\n\\n──────────\\n\\n${baseTask}`\n : baseTask;\n const result = await structuredSpawn({\n specialistId: strategy.specialist,\n task: taskWithFormat,\n modelOverride: strategy.modelOverride,\n toolAllowlist: routeForKind(subState.kind).toolAllowlist,\n maxRounds: strategy.maxRounds,\n // v6.1.0-alpha.41 — thread goalId so subAgent's tool_call\n // events carry data.goalId and the lifecycle bridge can\n // correlate them back to the mission room.\n goalId: goal.id,\n });\n const durationMs = Date.now() - startMs;\n\n // v6.1.0-alpha.1 — emit the result on the agent bus too. The Mission\n // Chat bridge turns this into a chat message; the SSE dashboard tail\n // logs it for replay. We always emit, even on failed/needs_info\n // status, so the UI can show the specialist's reasoning regardless.\n try {\n emitAgentEvent({\n type: 'agent_done',\n agentId: strategy.specialist,\n agentName: strategy.specialist,\n timestamp: Date.now(),\n data: {\n goalId: goal.id,\n subtaskId: next.id,\n subtaskTitle: next.title,\n status: result.status,\n reasoning: result.reasoning,\n artifactCount: result.artifacts.length,\n artifacts: result.artifacts.map(a => ({ ref: a.ref, type: a.type })),\n toolsUsed: result.toolsUsed ?? [],\n tokensUsed: result.tokensUsed ?? 0,\n costUsd: result.costUsd ?? 0,\n durationMs,\n // v6.1.0-alpha.4 — include the resolved model so the\n // Mission Chat UI can show \"ran on X\" when the user\n // expands a bubble. Use modelOverride if set, otherwise\n // the specialist's id (UI maps id → friendly model name\n // via the registry when needed).\n model: strategy.modelOverride ?? strategy.specialist,\n },\n });\n } catch { /* event bus failure should never affect driver state */ }\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 // 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 // v6.1.0-alpha.51 — commit-override short-circuit. If the\n // previous block was auto-rejected (15-min timeout) we set\n // commitOverride. The specialist had its one free re-try with\n // a \"commit, don't re-ask\" directive in lastError; if it STILL\n // came back with needs_info, the question is structurally\n // unanswerable from current context. File the subtask as\n // committed (best-effort done) rather than re-blocking — this\n // breaks the \"Auto-rejected... What should the specialist do\n // next?\" infinite ask loop Tony reported.\n if (subState.commitOverride) {\n subState.commitOverride = false; // one-shot\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, `Auto-committed after timeout: specialist still needs info but no human is available. Original question: \"${rawQuestion.slice(0, 160)}\"`);\n } catch { /* ok */ }\n subState.lastError = `Auto-committed after timeout. Last unresolved question: ${rawQuestion.slice(0, 160)}`;\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Commit-override applied on ${next.id} — no re-block after timeout`);\n subState.pendingSpawn = undefined;\n return;\n }\n\n // v6.1.0-alpha.51 — dedupe repeated questions. If the\n // specialist has already asked this same question (by\n // fingerprint) before on this subtask, don't file the\n // approval again. Convert to a forced commit instead so\n // the user isn't pestered with the same prompt 3 times.\n const rawFp = rawQuestion ? fingerprintBlockedQuestion(rawQuestion) : '';\n const askedBefore = !!rawFp && (subState.askedQuestionFingerprints || []).includes(rawFp);\n if (askedBefore) {\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, `Repeat-question dedupe: specialist asked the same question (\"${rawQuestion.slice(0, 120)}\") twice. Marking subtask failed to avoid pestering the human.`);\n } catch { /* ok */ }\n subState.lastError = `Repeat question detected — failed subtask. Last question: ${rawQuestion.slice(0, 160)}`;\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Repeat-question dedupe on ${next.id}`);\n subState.pendingSpawn = undefined;\n return;\n }\n if (rawFp) {\n subState.askedQuestionFingerprints = [\n ...(subState.askedQuestionFingerprints || []),\n rawFp,\n ].slice(-8);\n }\n\n // v6.1.0-alpha.54 — generic-question pre-pass. Tony asked\n // not to be pinged with low-info questions (\"What should\n // I focus on?\", \"Please clarify\", etc.). isGenericQuestion\n // classifies the rawQuestion; if generic AND we haven't\n // already rejected one for this subtask, we set\n // `lastError` to a forceCommitDirective and bounce back\n // to delegating — the specialist gets one free retry with\n // explicit \"commit your best interpretation OR consult a\n // teammate\" guidance. If the specialist STILL returns\n // generic on the retry, we fail the subtask rather than\n // burn Tony's attention. Specific questions (with a\n // decision marker, named option, year, URL, quoted name,\n // etc.) pass through to the existing approval flow.\n if (isGenericQuestion(rawQuestion)) {\n if (!subState.genericQuestionRejected) {\n subState.genericQuestionRejected = true;\n subState.lastError = forceCommitDirective(rawQuestion, goal.title);\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Generic question rejected — forcing commit retry on ${next.id}`);\n subState.pendingSpawn = undefined;\n return;\n }\n // Second generic question in a row → fail the subtask\n // instead of looping. Don't ship to Tony either way.\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, `Two generic questions in a row from ${specialistName}. Last: \"${rawQuestion.slice(0, 160)}\". The specialist couldn't commit on its own after a force-retry — marking subtask failed rather than escalating a low-info question to the human.`);\n } catch { /* ok */ }\n subState.lastError = `Failed after two generic questions. Last: ${rawQuestion.slice(0, 160)}`;\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Generic-question fail on ${next.id} after force-retry`);\n subState.pendingSpawn = undefined;\n return;\n }\n\n state.phase = 'blocked';\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 && !/TIMEOUT_DIRECTIVE/.test(lastErr)) {\n // v6.1.0-alpha.51 — guard against propagating internal\n // commit-override directive into the user-facing question.\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\n // Bug #3 fix — v6.1.0-alpha.43: when the subtask we just\n // verified was the LAST remaining pending subtask, jump\n // straight to 'reporting' rather than back through\n // 'delegating'. Going through delegating left the goal\n // stuck in 'active' forever when the scheduler stopped\n // driving it after this tick.\n const goalSubs = goal.subtasks || [];\n const allResolved = goalSubs.every(\n s => s.status === 'done' || s.status === 'failed' || s.status === 'skipped',\n );\n if (allResolved) {\n state.phase = 'verifying'; // next tick → whole-goal verify → reporting\n appendHistory(state, 'verifying', 'Last subtask resolved — final whole-goal verify');\n } else {\n state.phase = 'delegating';\n }\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 // v6.1.0-alpha.7 — broadcast on titanEvents so Mission Chat (and any\n // future listener) knows the goal finished. Without this, the mission\n // room sat in 'working' state with no completion message and looked\n // stuck even though the goal driver had returned status=completed in\n // goals.json. Bridge in missionLifecycle.ts maps this to a \"Mission\n // complete\" system message + status=done in the room.\n try {\n const { titanEvents } = await import('./daemon.js');\n titanEvents.emit('goal:completed', {\n goalId: goal.id,\n title: goal.title,\n durationMs,\n tokensUsed: state.budget.tokensUsed,\n costUsd: state.budget.costUsd,\n specialistsUsed,\n subtaskCount: Object.keys(state.subtaskStates).length,\n });\n } catch { /* event bus failure never blocks driver state transitions */ }\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 // Bug #6 fix — v6.1.0-alpha.43: if the approval is still\n // \"pending\" after 15 minutes, the human is unlikely to answer\n // soon (asleep, away, etc.). Auto-reject it so the goal\n // doesn't stay blocked forever. The specialist will retry\n // with a fresh attempt rather than spinning.\n const STALE_PENDING_MS = 15 * 60 * 1000;\n if (approval.status === 'pending' && sinceMs > STALE_PENDING_MS) {\n try {\n const { rejectApproval } = await import('./commandPost.js');\n rejectApproval(approvalId, 'auto:timeout', `Auto-rejected after ${Math.round(sinceMs / 60000)}min with no human response`);\n } catch { /* ok */ }\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 // v6.1.0-alpha.51 — the previous wording (\"Retry with\n // your best interpretation. What should the specialist\n // do next?\") would propagate into the next richQuestion\n // (line ~543) and surface verbatim to the user as a\n // pending question, creating an infinite ask loop. Rephrase\n // as a directive that does NOT read like a user-question,\n // and set commitOverride so the next needs_info short-\n // circuits to a commit/fail instead of filing another\n // approval.\n sub.lastError = 'TIMEOUT_DIRECTIVE: previous block timed out (no human within 15min). Commit to your best-effort interpretation now and proceed — do NOT ask another question. If no reasonable assumption is possible, return status=done with a \"could not complete: <reason>\" note in reasoning.';\n sub.commitOverride = true;\n }\n state.blockedReason = undefined;\n state.phase = 'iterating';\n appendHistory(state, 'iterating', `Stale pending approval ${approvalId} auto-rejected after ${Math.round(sinceMs / 60000)}min — iterating with commit-override`);\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 // v6.1.0-alpha.7 — same broadcast pattern as completion. The Mission\n // Chat bridge translates this to a \"Couldn't finish this one.\" system\n // message + status=failed so the user sees closure instead of an\n // idle team strip.\n try {\n const { titanEvents } = await import('./daemon.js');\n titanEvents.emit('goal:failed', {\n goalId: goal.id,\n title: goal.title,\n durationMs,\n tokensUsed: state.budget.tokensUsed,\n costUsd: state.budget.costUsd,\n retries: state.budget.totalRetries,\n });\n } catch { /* event bus failure never blocks driver state transitions */ }\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\n/**\n * v6.1.0-alpha.32 — turn a goal title into a reasonable filename\n * stem the LLM can use as a default for write_file. Lowercase,\n * kebab-case, only alphanumerics + dashes, capped at 40 chars.\n * Example: \"Write a 3 page essay about Martin Luther King\" →\n * \"write-a-3-page-essay-about-martin-l\".\n */\nfunction slugForGoal(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 40)\n || 'report';\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"]}
|
|
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 { isGenericQuestion, forceCommitDirective } from './questionQuality.js';\nimport { onGoalCompleted, onGoalFailed, onGoalBlocked } from './somaFeedback.js';\nimport type { Goal, Subtask } from './goals.js';\nimport { emitAgentEvent } from './agentEvents.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 // v6.1.0-alpha.1 — emit a structured \"agent_spawn\" event on the shared\n // bus so listeners (Mission Chat lifecycle bridge, the SSE dashboard\n // tail, the Agent Watcher) know who's starting on what. Includes the\n // goalId so listeners can correlate without having to peer-into driver\n // state.\n try {\n emitAgentEvent({\n type: 'agent_spawn',\n agentId: strategy.specialist,\n agentName: strategy.specialist,\n timestamp: Date.now(),\n data: {\n goalId: goal.id,\n subtaskId: next.id,\n subtaskTitle: next.title,\n subtaskKind: subState.kind,\n attempt: subState.attempts,\n },\n });\n } catch { /* event bus failure should never block a spawn */ }\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 // v6.1.0-alpha.32 — if the goal looks like a document deliverable,\n // prepend a hard FORMAT directive to the task description. Belt\n // and suspenders with the specialist system prompt's HTML_REPORT_\n // GUIDANCE: even strong system-prompt rules can be drowned by a\n // long context, but a directive at the top of the per-task user\n // message is the LAST thing the LLM sees before producing output.\n const baseTask = `${next.title}\\n\\n${next.description}${strategy.promptAdjustment ?? ''}`;\n const documentLike = /\\b(essay|report|briefing|writeup|write-up|summary|article|document|memo|paper|analysis|whitepaper|brief)\\b/i.test(goal.title)\n || /\\b(essay|report|briefing|writeup|summary|article)\\b/i.test(next.title);\n const taskWithFormat = documentLike\n ? `OUTPUT FORMAT — REQUIRED:\\nThe user wants a real document they can view with images, charts, and styling. You MUST deliver this as a single self-contained .html file (write_file with a path ending in .html, e.g. \"${slugForGoal(goal.title)}.html\"). Do NOT write .md or .markdown — those can't show images or charts. See the OUTPUT FORMAT FOR DOCUMENTS section of your system prompt for the structure.\\n\\n──────────\\n\\n${baseTask}`\n : baseTask;\n const result = await structuredSpawn({\n specialistId: strategy.specialist,\n task: taskWithFormat,\n modelOverride: strategy.modelOverride,\n toolAllowlist: routeForKind(subState.kind).toolAllowlist,\n maxRounds: strategy.maxRounds,\n // v6.1.0-alpha.41 — thread goalId so subAgent's tool_call\n // events carry data.goalId and the lifecycle bridge can\n // correlate them back to the mission room.\n goalId: goal.id,\n });\n const durationMs = Date.now() - startMs;\n\n // v6.1.0-alpha.1 — emit the result on the agent bus too. The Mission\n // Chat bridge turns this into a chat message; the SSE dashboard tail\n // logs it for replay. We always emit, even on failed/needs_info\n // status, so the UI can show the specialist's reasoning regardless.\n try {\n emitAgentEvent({\n type: 'agent_done',\n agentId: strategy.specialist,\n agentName: strategy.specialist,\n timestamp: Date.now(),\n data: {\n goalId: goal.id,\n subtaskId: next.id,\n subtaskTitle: next.title,\n status: result.status,\n reasoning: result.reasoning,\n artifactCount: result.artifacts.length,\n artifacts: result.artifacts.map(a => ({ ref: a.ref, type: a.type })),\n toolsUsed: result.toolsUsed ?? [],\n tokensUsed: result.tokensUsed ?? 0,\n costUsd: result.costUsd ?? 0,\n durationMs,\n // v6.1.0-alpha.4 — include the resolved model so the\n // Mission Chat UI can show \"ran on X\" when the user\n // expands a bubble. Use modelOverride if set, otherwise\n // the specialist's id (UI maps id → friendly model name\n // via the registry when needed).\n model: strategy.modelOverride ?? strategy.specialist,\n },\n });\n } catch { /* event bus failure should never affect driver state */ }\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 subState.consecutiveNeedsInfoCount = 0; // reset on non-needs_info\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 subState.consecutiveNeedsInfoCount = 0; // reset on non-needs_info\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 // 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 // v6.1.0-beta.4 — defensive consecutive-needs_info counter.\n // Reproduces the mzzbajnj bug: a specialist returns\n // `needs_info` with EMPTY question string, the\n // genericQuestionRejected→fail path *should* terminate it on\n // attempt 2, but in practice the loop ran 17 minutes before\n // Tony cancelled. Belt-and-suspenders guard independent of\n // genericQuestionRejected/commitOverride/dedupe state: count\n // consecutive needs_info, force-fail at threshold regardless\n // of any other flag. This guarantees termination even if a\n // future refactor breaks one of the other guards.\n subState.consecutiveNeedsInfoCount = (subState.consecutiveNeedsInfoCount || 0) + 1;\n const trimmedQuestion = rawQuestion.trim();\n const needsInfoCount = subState.consecutiveNeedsInfoCount;\n // Empty/whitespace question + attempts >= 2 → fail\n // immediately. An empty needs_info on a retry is a sure\n // sign the specialist has nothing to say.\n if (trimmedQuestion.length === 0 && attemptCount >= 2) {\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, `Specialist ${specialistName} returned needs_info with an empty question on attempt ${attemptCount}. No actionable signal — failing subtask to break the loop.`);\n } catch { /* ok */ }\n subState.lastError = `Failed: empty needs_info on attempt ${attemptCount} (specialist ${specialistName})`;\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Empty needs_info fail on ${next.id} (attempt ${attemptCount})`);\n subState.pendingSpawn = undefined;\n return;\n }\n // Three consecutive needs_info on this subtask → fail\n // regardless of question content. Forward progress check.\n if (needsInfoCount >= 3) {\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, `Specialist ${specialistName} returned needs_info ${needsInfoCount}× in a row on this subtask. Forcing failure to break the loop. Last question: \"${trimmedQuestion.slice(0, 160)}\"`);\n } catch { /* ok */ }\n subState.lastError = `Failed: ${needsInfoCount} consecutive needs_info returns`;\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Consecutive needs_info fail on ${next.id} (${needsInfoCount}×)`);\n subState.pendingSpawn = undefined;\n return;\n }\n\n // v6.1.0-alpha.51 — commit-override short-circuit. If the\n // previous block was auto-rejected (15-min timeout) we set\n // commitOverride. The specialist had its one free re-try with\n // a \"commit, don't re-ask\" directive in lastError; if it STILL\n // came back with needs_info, the question is structurally\n // unanswerable from current context. File the subtask as\n // committed (best-effort done) rather than re-blocking — this\n // breaks the \"Auto-rejected... What should the specialist do\n // next?\" infinite ask loop Tony reported.\n if (subState.commitOverride) {\n subState.commitOverride = false; // one-shot\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, `Auto-committed after timeout: specialist still needs info but no human is available. Original question: \"${rawQuestion.slice(0, 160)}\"`);\n } catch { /* ok */ }\n subState.lastError = `Auto-committed after timeout. Last unresolved question: ${rawQuestion.slice(0, 160)}`;\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Commit-override applied on ${next.id} — no re-block after timeout`);\n subState.pendingSpawn = undefined;\n return;\n }\n\n // v6.1.0-alpha.51 — dedupe repeated questions. If the\n // specialist has already asked this same question (by\n // fingerprint) before on this subtask, don't file the\n // approval again. Convert to a forced commit instead so\n // the user isn't pestered with the same prompt 3 times.\n const rawFp = rawQuestion ? fingerprintBlockedQuestion(rawQuestion) : '';\n const askedBefore = !!rawFp && (subState.askedQuestionFingerprints || []).includes(rawFp);\n if (askedBefore) {\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, `Repeat-question dedupe: specialist asked the same question (\"${rawQuestion.slice(0, 120)}\") twice. Marking subtask failed to avoid pestering the human.`);\n } catch { /* ok */ }\n subState.lastError = `Repeat question detected — failed subtask. Last question: ${rawQuestion.slice(0, 160)}`;\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Repeat-question dedupe on ${next.id}`);\n subState.pendingSpawn = undefined;\n return;\n }\n if (rawFp) {\n subState.askedQuestionFingerprints = [\n ...(subState.askedQuestionFingerprints || []),\n rawFp,\n ].slice(-8);\n }\n\n // v6.1.0-alpha.54 — generic-question pre-pass. Tony asked\n // not to be pinged with low-info questions (\"What should\n // I focus on?\", \"Please clarify\", etc.). isGenericQuestion\n // classifies the rawQuestion; if generic AND we haven't\n // already rejected one for this subtask, we set\n // `lastError` to a forceCommitDirective and bounce back\n // to delegating — the specialist gets one free retry with\n // explicit \"commit your best interpretation OR consult a\n // teammate\" guidance. If the specialist STILL returns\n // generic on the retry, we fail the subtask rather than\n // burn Tony's attention. Specific questions (with a\n // decision marker, named option, year, URL, quoted name,\n // etc.) pass through to the existing approval flow.\n if (isGenericQuestion(rawQuestion)) {\n if (!subState.genericQuestionRejected) {\n subState.genericQuestionRejected = true;\n subState.lastError = forceCommitDirective(rawQuestion, goal.title);\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Generic question rejected — forcing commit retry on ${next.id}`);\n subState.pendingSpawn = undefined;\n return;\n }\n // Second generic question in a row → fail the subtask\n // instead of looping. Don't ship to Tony either way.\n try {\n const { failSubtask } = await import('./goals.js');\n failSubtask(goal.id, next.id, `Two generic questions in a row from ${specialistName}. Last: \"${rawQuestion.slice(0, 160)}\". The specialist couldn't commit on its own after a force-retry — marking subtask failed rather than escalating a low-info question to the human.`);\n } catch { /* ok */ }\n subState.lastError = `Failed after two generic questions. Last: ${rawQuestion.slice(0, 160)}`;\n state.currentSubtaskId = undefined;\n state.phase = 'delegating';\n appendHistory(state, 'delegating', `Generic-question fail on ${next.id} after force-retry`);\n subState.pendingSpawn = undefined;\n return;\n }\n\n state.phase = 'blocked';\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 && !/TIMEOUT_DIRECTIVE/.test(lastErr)) {\n // v6.1.0-alpha.51 — guard against propagating internal\n // commit-override directive into the user-facing question.\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\n // Bug #3 fix — v6.1.0-alpha.43: when the subtask we just\n // verified was the LAST remaining pending subtask, jump\n // straight to 'reporting' rather than back through\n // 'delegating'. Going through delegating left the goal\n // stuck in 'active' forever when the scheduler stopped\n // driving it after this tick.\n const goalSubs = goal.subtasks || [];\n const allResolved = goalSubs.every(\n s => s.status === 'done' || s.status === 'failed' || s.status === 'skipped',\n );\n if (allResolved) {\n state.phase = 'verifying'; // next tick → whole-goal verify → reporting\n appendHistory(state, 'verifying', 'Last subtask resolved — final whole-goal verify');\n } else {\n state.phase = 'delegating';\n }\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 // v6.1.0-alpha.7 — broadcast on titanEvents so Mission Chat (and any\n // future listener) knows the goal finished. Without this, the mission\n // room sat in 'working' state with no completion message and looked\n // stuck even though the goal driver had returned status=completed in\n // goals.json. Bridge in missionLifecycle.ts maps this to a \"Mission\n // complete\" system message + status=done in the room.\n try {\n const { titanEvents } = await import('./daemon.js');\n titanEvents.emit('goal:completed', {\n goalId: goal.id,\n title: goal.title,\n durationMs,\n tokensUsed: state.budget.tokensUsed,\n costUsd: state.budget.costUsd,\n specialistsUsed,\n subtaskCount: Object.keys(state.subtaskStates).length,\n });\n } catch { /* event bus failure never blocks driver state transitions */ }\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 // Bug #6 fix — v6.1.0-alpha.43: if the approval is still\n // \"pending\" after 15 minutes, the human is unlikely to answer\n // soon (asleep, away, etc.). Auto-reject it so the goal\n // doesn't stay blocked forever. The specialist will retry\n // with a fresh attempt rather than spinning.\n const STALE_PENDING_MS = 15 * 60 * 1000;\n if (approval.status === 'pending' && sinceMs > STALE_PENDING_MS) {\n try {\n const { rejectApproval } = await import('./commandPost.js');\n rejectApproval(approvalId, 'auto:timeout', `Auto-rejected after ${Math.round(sinceMs / 60000)}min with no human response`);\n } catch { /* ok */ }\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 // v6.1.0-alpha.51 — the previous wording (\"Retry with\n // your best interpretation. What should the specialist\n // do next?\") would propagate into the next richQuestion\n // (line ~543) and surface verbatim to the user as a\n // pending question, creating an infinite ask loop. Rephrase\n // as a directive that does NOT read like a user-question,\n // and set commitOverride so the next needs_info short-\n // circuits to a commit/fail instead of filing another\n // approval.\n sub.lastError = 'TIMEOUT_DIRECTIVE: previous block timed out (no human within 15min). Commit to your best-effort interpretation now and proceed — do NOT ask another question. If no reasonable assumption is possible, return status=done with a \"could not complete: <reason>\" note in reasoning.';\n sub.commitOverride = true;\n }\n state.blockedReason = undefined;\n state.phase = 'iterating';\n appendHistory(state, 'iterating', `Stale pending approval ${approvalId} auto-rejected after ${Math.round(sinceMs / 60000)}min — iterating with commit-override`);\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 // v6.1.0-alpha.7 — same broadcast pattern as completion. The Mission\n // Chat bridge translates this to a \"Couldn't finish this one.\" system\n // message + status=failed so the user sees closure instead of an\n // idle team strip.\n try {\n const { titanEvents } = await import('./daemon.js');\n titanEvents.emit('goal:failed', {\n goalId: goal.id,\n title: goal.title,\n durationMs,\n tokensUsed: state.budget.tokensUsed,\n costUsd: state.budget.costUsd,\n retries: state.budget.totalRetries,\n });\n } catch { /* event bus failure never blocks driver state transitions */ }\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\n/**\n * v6.1.0-alpha.32 — turn a goal title into a reasonable filename\n * stem the LLM can use as a default for write_file. Lowercase,\n * kebab-case, only alphanumerics + dashes, capped at 40 chars.\n * Example: \"Write a 3 page essay about Martin Luther King\" →\n * \"write-a-3-page-essay-about-martin-l\".\n */\nfunction slugForGoal(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 40)\n || 'report';\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"]}
|
|
@@ -187,7 +187,7 @@ function ensureGlobalBusBridge() {
|
|
|
187
187
|
const summary = toolsUsed.length > 0 ? `Done \u2014 used ${toolsUsed.slice(0, 3).join(", ")}.` : `Done.`;
|
|
188
188
|
postAgentMessage(mission.id, agentId, summary, actions.length > 0 ? actions : void 0, meta, sourcesArg);
|
|
189
189
|
}
|
|
190
|
-
setMemberState(mission.id, agentId, "idle",
|
|
190
|
+
setMemberState(mission.id, agentId, "idle", "standing by");
|
|
191
191
|
const tokens = typeof data.tokensUsed === "number" ? data.tokensUsed : 0;
|
|
192
192
|
const cost = typeof data.costUsd === "number" ? data.costUsd : 0;
|
|
193
193
|
if (tokens > 0 || cost > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/agent/missionLifecycle.ts"],"sourcesContent":["/**\n * TITAN — Mission lifecycle adapter (v6.1.0)\n *\n * Connects a freshly-created Mission Room to the existing goal driver\n * + Command Post pipeline. When a mission is created:\n *\n * 1. Create a Goal (in the goals subsystem) with title = goal text,\n * tagged with the mission id and play id so the bridge below can\n * filter events back to the right room.\n * 2. Subscribe to the agent message bus so each specialist's tool\n * calls / answers / progress messages get rewritten as a chat\n * message in the mission thread.\n * 3. Subscribe to the Command Post approval queue so blocking\n * questions filed against this mission's goal show up as inline\n * question messages in the chat.\n *\n * The bridge is one-way (driver → mission room). User actions (replies\n * to questions, status toggles) go through the mission router and\n * propagate to the goal driver / Command Post via existing APIs.\n *\n * Idempotent: subscribing a second mission doesn't double-fire. We\n * track per-mission unsubscribe functions in `lifecycles`.\n */\nimport { readFileSync, existsSync, statSync } from 'fs';\nimport logger from '../utils/logger.js';\nimport { createGoal } from './goals.js';\nimport {\n postAgentMessage,\n postSystemMessage,\n setMemberState,\n appendMemberActivity,\n setStatus,\n raiseQuestion,\n recordCost,\n ensureMember,\n getMission,\n getMissionByGoalId,\n listMissions,\n setLinkedGoal,\n setLinkedIssue,\n updateArtifact,\n type MissionRoom,\n type MissionStatus,\n} from './missionRoom.js';\nimport { onAgentEvent, type AgentEvent } from './agentEvents.js';\nimport { createIssue, updateIssue, addIssueComment } from './commandPost.js';\n\n// v6.1.0-alpha.57 — track in-flight notebook fills per mission so a\n// second agent_done can cancel the prior typewriter (rather than the\n// two of them racing on `updateArtifact`).\nconst notebookFillTimers = new Map<string, NodeJS.Timeout>();\n\n/**\n * Convert HTML to plain text suitable for the lined-paper notebook\n * view. The notebook component renders `content.split('\\n').filter`\n * and slices to 10 lines per page — it wants prose, not markup.\n *\n * - Strip <script>/<style> blocks (including content)\n * - Convert <br>/<p>/<h1-6>/<li>/<tr> to line breaks\n * - Strip every other tag\n * - Decode the 5 standard entities + numeric refs\n * - Collapse runs of 3+ blank lines to 2\n */\nfunction htmlToNotebookText(html: string): string {\n if (!html) return '';\n let s = html;\n s = s.replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script>/gi, '');\n s = s.replace(/<style\\b[^>]*>[\\s\\S]*?<\\/style>/gi, '');\n s = s.replace(/<head\\b[^>]*>[\\s\\S]*?<\\/head>/gi, '');\n s = s.replace(/<(?:br|hr)\\s*\\/?>/gi, '\\n');\n s = s.replace(/<\\/(?:p|h[1-6]|li|tr|div|section|article|figure|figcaption|blockquote)\\s*>/gi, '\\n\\n');\n s = s.replace(/<[^>]+>/g, '');\n s = s.replace(/ /gi, ' ')\n .replace(/&/gi, '&')\n .replace(/</gi, '<')\n .replace(/>/gi, '>')\n .replace(/"/gi, '\"')\n .replace(/'/gi, \"'\")\n .replace(/&#(\\d+);/g, (_m, n) => String.fromCharCode(Number(n)));\n s = s.replace(/[ \\t]+/g, ' ');\n s = s.replace(/\\n{3,}/g, '\\n\\n');\n return s.trim();\n}\n\n/**\n * Stream plain text into a mission's artifact buffer in small\n * chunks. Each chunk fires an `artifact_updated` SSE event so the\n * DocumentPaper notebook fills in like someone writing into it.\n *\n * - Default chunk = 40 chars every 80ms → ~500 chars/sec, feels\n * human-paced (faster than typing, slower than dumping)\n * - Cap total fill time at ~6s so a 3-page essay doesn't take\n * forever; long content scales chunk size up\n * - Fire-and-forget — caller doesn't await, but we expose the\n * promise so tests can await on it\n * - Cancelable: a second call for the same mission cancels the\n * prior animation\n */\nfunction streamFillNotebook(missionId: string, agentId: string, plainText: string): void {\n if (!plainText) return;\n // Cancel any in-flight fill for this mission.\n const prior = notebookFillTimers.get(missionId);\n if (prior) clearInterval(prior);\n\n const total = plainText.length;\n // Scale chunk size so total fill time is roughly 3–6s.\n const TARGET_MS = 4000;\n const TICK_MS = 80;\n const ticks = Math.max(20, Math.ceil(TARGET_MS / TICK_MS));\n const chunkChars = Math.max(20, Math.ceil(total / ticks));\n let offset = 0;\n\n const id = setInterval(() => {\n offset = Math.min(total, offset + chunkChars);\n const slice = plainText.slice(0, offset);\n try {\n updateArtifact(missionId, agentId, slice);\n } catch (err) {\n // Mission may have been deleted mid-stream.\n logger.debug(COMPONENT, `streamFillNotebook stopped on ${missionId}: ${(err as Error).message}`);\n clearInterval(id);\n notebookFillTimers.delete(missionId);\n return;\n }\n if (offset >= total) {\n clearInterval(id);\n notebookFillTimers.delete(missionId);\n }\n }, TICK_MS);\n notebookFillTimers.set(missionId, id);\n}\n\n/**\n * If `agent_done` brought a file artifact (HTML/markdown/text the\n * Writer just produced), read it and start the notebook fill so the\n * user watches the document appear on the lined paper. Defensive on\n * everything — bad file, missing file, oversize file all silently\n * skip rather than crashing the lifecycle bridge.\n */\nfunction maybeFillNotebookFromFileArtifact(\n missionId: string,\n agentId: string,\n artifacts: Array<{ type: string; ref: string }>,\n): void {\n const file = artifacts.find(\n (a) => a.type === 'file' && typeof a.ref === 'string' && a.ref.length > 0,\n );\n if (!file) return;\n try {\n if (!existsSync(file.ref)) return;\n const stat = statSync(file.ref);\n // Cap at 1 MB to avoid loading huge accidentally-large files\n // into memory + streaming them as a 25-minute animation.\n if (stat.size > 1024 * 1024) return;\n const raw = readFileSync(file.ref, 'utf-8');\n const isHtml = /\\.html?$/i.test(file.ref) || /<html[\\s>]/i.test(raw);\n const plain = isHtml ? htmlToNotebookText(raw) : raw;\n if (plain.trim().length === 0) return;\n streamFillNotebook(missionId, agentId, plain);\n } catch (err) {\n logger.debug(COMPONENT, `maybeFillNotebookFromFileArtifact skipped: ${(err as Error).message}`);\n }\n}\n\nconst COMPONENT = 'MissionLifecycle';\n\n// Per-mission cleanup handlers. When a mission completes, we tear down\n// these subscriptions so the bus doesn't leak listeners.\nconst lifecycles = new Map<string, Array<() => void>>();\n\n/**\n * v6.1.0-alpha.1 — a SINGLE global subscription to the shared\n * agentEvents bus. Every mission piggybacks off this; we filter by\n * goalId at dispatch time. This is much cheaper than N per-mission\n * subscriptions (which is what alpha.0's broken bridge attempted) and\n * survives the case where the goal driver routes to a specialist that\n * isn't on the predicted Plays team — we add them dynamically.\n */\nlet globalBusUnsub: (() => void) | null = null;\nfunction ensureGlobalBusBridge(): void {\n if (globalBusUnsub) return;\n globalBusUnsub = onAgentEvent((ev: AgentEvent) => {\n // The goalDriver-emitted events carry data.goalId; events from\n // ad-hoc spawns (CLI, channels) don't, and we silently ignore\n // those — they aren't part of a mission.\n const data = ev.data ?? {};\n const goalId = typeof data.goalId === 'string' ? data.goalId : undefined;\n if (!goalId) return;\n const mission = getMissionByGoalId(goalId);\n if (!mission) return;\n const agentId = (ev.agentId ?? ev.agentName ?? '').toLowerCase();\n if (!agentId) return;\n try {\n switch (ev.type) {\n case 'agent_spawn': {\n ensureMember(mission.id, agentId);\n const subtaskTitle = typeof data.subtaskTitle === 'string' ? data.subtaskTitle : 'something';\n setMemberState(mission.id, agentId, 'working', shortenActivity(subtaskTitle));\n break;\n }\n case 'tool_call': {\n const name = typeof data.name === 'string' ? data.name : 'tool';\n ensureMember(mission.id, agentId);\n setMemberState(mission.id, agentId, 'working', shortenActivity(`running ${name}`));\n // v6.1.0-alpha.31 — also push a live activity sticky\n // to the desk for research-y tool calls. Tony's\n // direct ask: \"I liked the way we had it before\n // when the Agents put sticky notes on the desk\n // when working, with their research data so I can\n // see what they are doing.\" The activityLog\n // accumulates here per-agent and the canvas reads\n // it into draggable sticky notes.\n try {\n const args = (typeof data.args === 'object' && data.args !== null)\n ? (data.args as Record<string, unknown>)\n : {};\n const sticky = buildActivitySticky(name, args);\n if (sticky) {\n appendMemberActivity(mission.id, agentId, sticky);\n }\n } catch (err) {\n logger.debug(COMPONENT, `Activity sticky build threw: ${(err as Error).message}`);\n }\n break;\n }\n case 'tool_end': {\n // Don't transition state — the next tool_call or agent_done\n // will overwrite. Just no-op (avoids flicker).\n break;\n }\n case 'agent_done': {\n ensureMember(mission.id, agentId);\n const rawReasoning = typeof data.reasoning === 'string' && data.reasoning.trim().length > 0\n ? data.reasoning.trim()\n : null;\n // v6.1.0-alpha.12 — pluck the specialist's concrete\n // artifacts (URLs visited, files written, facts\n // memorized) so the chat can render them as clickable\n // sources. Pre-alpha.12 these were silently discarded —\n // a \"Successfully researched\" reasoning summary made it\n // to chat but the actual URLs/files behind it did not,\n // so the user couldn't follow up on anything.\n const sources: Array<{ type: 'url' | 'file' | 'fact' | 'report'; ref: string; description?: string }> = [];\n if (Array.isArray(data.artifacts)) {\n for (const raw of data.artifacts) {\n if (!raw || typeof raw !== 'object') continue; // skip null/undefined/primitive\n const a = raw as Record<string, unknown>;\n const t = typeof a.type === 'string' ? a.type : '';\n const ref = typeof a.ref === 'string' ? a.ref : '';\n if (!ref) continue;\n if (t !== 'url' && t !== 'file' && t !== 'fact' && t !== 'report') continue;\n sources.push({\n type: t as 'url' | 'file' | 'fact' | 'report',\n ref,\n description: typeof a.description === 'string' ? a.description : undefined,\n });\n if (sources.length >= 12) break;\n }\n }\n // v6.1.0-alpha.57 — live notebook fill. If the\n // specialist produced a file artifact, stream its\n // plain-text contents into the mission's\n // `room.artifact.content` buffer in small chunks\n // (~4s total). The DocumentPaper component on the\n // canvas watches that buffer and re-renders as it\n // grows, so the user sees the document fill in\n // line-by-line like the AI is writing in a\n // notebook. Fire-and-forget; safe if the file is\n // missing / too large / unreadable.\n if (sources.length > 0) {\n maybeFillNotebookFromFileArtifact(\n mission.id,\n agentId,\n sources.map((s) => ({ type: s.type, ref: s.ref })),\n );\n }\n\n // v6.1.0-alpha.7 — scrub internal-error stack traces.\n // When a spawn fails, the `reasoning` field can be the\n // raw error chain: \"Parser could not extract JSON...\n // Sub-agent error: All providers failed: HTTP 429\n // <html>...\" That's pure jargon for users. Detect it\n // and replace with the friendly fallback. The original\n // text is preserved on `meta.failureDetail` so power\n // users can still see it via click-to-expand.\n const reasoning = looksLikeInternalErrorTrace(rawReasoning)\n ? null\n : rawReasoning;\n const toolsUsed = Array.isArray(data.toolsUsed)\n ? (data.toolsUsed as string[]).slice(0, 6)\n : [];\n const actions = toolsUsed.map(t => ({ name: 'used', detail: t }));\n const status = typeof data.status === 'string' ? data.status : 'done';\n // v6.1.0-alpha.4 — pass the rich context as the message\n // `meta` field. The chat UI hides it by default and\n // surfaces it when the user clicks the bubble — keeps\n // the thread readable while making \"what actually\n // happened here\" one tap away.\n //\n // v6.1.0-alpha.7 — if the raw reasoning was a scrubbed\n // internal-error trace, stash it on `meta.failureDetail`\n // so power users can still see it via click-to-expand.\n // The chat surface stays clean.\n const meta: Record<string, unknown> = {\n subtaskTitle: typeof data.subtaskTitle === 'string' ? data.subtaskTitle : undefined,\n status,\n durationMs: typeof data.durationMs === 'number' ? data.durationMs : undefined,\n tokensUsed: typeof data.tokensUsed === 'number' ? data.tokensUsed : undefined,\n costUsd: typeof data.costUsd === 'number' ? data.costUsd : undefined,\n model: typeof data.model === 'string' ? data.model : undefined,\n };\n if (rawReasoning && rawReasoning !== reasoning) {\n meta.failureDetail = rawReasoning;\n }\n // v6.1.0-alpha.1 — every agent_done emits SOMETHING into\n // the chat. The pre-fix path returned nothing when\n // `reasoning` was empty, which is common on cloud\n // specialists that return JSON-only responses (their\n // reasoning field is empty because the *artifact* is\n // the value). The chat shouldn't go silent.\n // v6.1.0-alpha.12 — also pass `sources` so URLs +\n // files the specialist worked with render clickably.\n const sourcesArg = sources.length > 0 ? sources : undefined;\n if (reasoning) {\n postAgentMessage(mission.id, agentId, reasoning, actions.length > 0 ? actions : undefined, meta, sourcesArg);\n } else if (status === 'failed') {\n postAgentMessage(\n mission.id,\n agentId,\n `I ran into trouble on this one and couldn't finish — handing back to the team.`,\n actions.length > 0 ? actions : undefined,\n meta,\n sourcesArg,\n );\n } else if (status === 'needs_info' || status === 'blocked') {\n postAgentMessage(\n mission.id,\n agentId,\n `I have a quick question before I can finish — see below.`,\n actions.length > 0 ? actions : undefined,\n meta,\n sourcesArg,\n );\n } else {\n // status === 'done' with empty reasoning. Common with\n // JSON-only specialists. Don't go silent — say something\n // honest about what they did.\n const summary = toolsUsed.length > 0\n ? `Done — used ${toolsUsed.slice(0, 3).join(', ')}.`\n : `Done.`;\n postAgentMessage(mission.id, agentId, summary, actions.length > 0 ? actions : undefined, meta, sourcesArg);\n }\n setMemberState(mission.id, agentId, 'idle', undefined);\n const tokens = typeof data.tokensUsed === 'number' ? data.tokensUsed : 0;\n const cost = typeof data.costUsd === 'number' ? data.costUsd : 0;\n if (tokens > 0 || cost > 0) {\n recordCost(mission.id, tokens, cost);\n }\n break;\n }\n }\n } catch (err) {\n logger.debug(COMPONENT, `Bridge dispatch threw for ${mission.id}/${ev.type}: ${(err as Error).message}`);\n }\n });\n logger.info(COMPONENT, 'Global agent-event bridge attached (goalId → mission room dispatch)');\n}\n\n/** The wire that the gateway / missions router calls when a mission is\n * created. Returns the linked goal id. */\nexport async function startMissionWork(mission: MissionRoom): Promise<string | null> {\n try {\n // Make sure the global event bridge is alive. Idempotent.\n ensureGlobalBusBridge();\n\n // Tag the goal with the mission + play id so message-bus event\n // bridges can filter \"which mission did this belong to.\"\n const goal = createGoal({\n title: mission.goal,\n description: `Mission ${mission.id}` + (mission.playId ? ` (play: ${mission.playId})` : ''),\n tags: [\n `mission:${mission.id}`,\n mission.playId ? `play:${mission.playId}` : 'play:generic',\n ],\n // v6.1.0-alpha.5 — Mission Chat goals are user-initiated. The\n // autonomous-creation rate limit (10/hour) + active-goals cap\n // exists to throttle runaway self-mod / self-repair loops, not\n // to throttle the user typing into the chat. Bypass it so\n // missions never silently fail with \"Couldn't start the team:\n // rate limited\" after the user's 11th request in an hour.\n force: true,\n });\n // v6.1.0-alpha.1 — link the goal id INSIDE startMissionWork so any\n // event the goal driver fires (even before the missions router gets\n // back to set it) can resolve the mission via getMissionByGoalId.\n // The router's redundant setLinkedGoal call is now a no-op safety\n // net rather than the source of truth.\n setLinkedGoal(mission.id, goal.id);\n\n // v6.1.0-alpha.10 — auto-create a Command Post issue for this\n // mission. The issue becomes the durable audit trail (every\n // significant lifecycle event mirrors as a comment) and the\n // Command Post Issues panel + Mission Chat / Canvas surface the\n // same record. Failure to create the issue should never block\n // the mission itself — it's audit, not behavior.\n let issueIdent: string | undefined;\n try {\n const issue = createIssue({\n title: mission.goal,\n description: `Mission ${mission.id}` + (mission.playId ? ` · play: ${mission.playId}` : '') +\n `\\n\\nLinked goal: ${goal.id}` +\n `\\n\\nTeam: ${mission.team.map(t => t.name).join(', ') || '(forming)'}`,\n priority: 'medium',\n createdByUser: 'mission-chat',\n goalId: goal.id,\n });\n setLinkedIssue(mission.id, issue.id);\n issueIdent = issue.identifier;\n updateIssue(issue.id, { status: 'in_progress' });\n addIssueComment(\n issue.id,\n `Mission opened with team: ${mission.team.map(t => t.name).join(', ') || '(forming)'}.`,\n { user: 'mission-chat' },\n );\n } catch (issueErr) {\n logger.warn(COMPONENT, `Issue link skipped for mission ${mission.id}: ${(issueErr as Error).message}`);\n }\n logger.info(\n COMPONENT,\n `Mission ${mission.id} linked to goal ${goal.id}` +\n (issueIdent ? ` and issue ${issueIdent}` : '') +\n ` (play=${mission.playId})`,\n );\n\n // Mark every Plays-predicted member as \"ready\" so the team strip\n // lights up immediately. The goal driver routes for real, and the\n // global bridge will narrow each member's `currentActivity` (and\n // ADD new members if the driver picks specialists Plays didn't\n // predict).\n for (const member of mission.team) {\n setMemberState(mission.id, member.agentId, 'idle', 'standing by');\n }\n\n // The approval bridge + goal-lifecycle bridge are both\n // per-mission — they filter by goalId, so we register them\n // scoped to the goal we just created.\n const cleanups: Array<() => void> = [];\n cleanups.push(await wireApprovalBridge(mission.id, goal.id));\n cleanups.push(await wireGoalLifecycleBridge(mission.id, goal.id));\n lifecycles.set(mission.id, cleanups);\n\n return goal.id;\n } catch (err) {\n logger.error(COMPONENT, `Failed to start mission ${mission.id}: ${(err as Error).message}`);\n setStatus(mission.id, 'failed', `Couldn't start the team: ${(err as Error).message}`);\n return null;\n }\n}\n\n/** Called when the UI posts a user message into a mission. Two paths:\n *\n * 1. **Mission is live** (working/blocked/paused/forming) — broadcast\n * the user's note to every registered specialist mailbox so the\n * next agent loop picks it up. Same behavior as alpha.1+.\n *\n * 2. **Mission is terminal** (done/failed) — v6.1.0-alpha.13: reopen\n * the mission with this message as a new direction. Creates a\n * fresh Goal, re-wires the lifecycle bridges, flips status back\n * to 'working', posts a \"Picking this back up…\" system note,\n * mirrors to the linked Command Post issue. The DriverScheduler\n * picks up the new active goal on its next tick (~10s).\n */\nexport async function handleUserMessage(missionId: string, content: string): Promise<void> {\n const room = getMission(missionId);\n if (!room) return;\n const isTerminal = room.status === 'done' || room.status === 'failed';\n if (isTerminal) {\n await reopenMissionWithFollowUp(missionId, content);\n return;\n }\n // For v1 we use the existing messageBus. We don't know which\n // specialist is \"current\" — broadcast to every registered member's\n // mailbox so whichever one is in-flight picks the note up at the\n // start of its next round. Mailbox names match the specialist ids.\n //\n // v6.1.0-alpha.1 — only dispatch to mailboxes that are actually\n // REGISTERED right now. Specialists register their mailbox at spawn\n // time and unregister when done. If no team member is mid-spawn,\n // there's no mailbox to deliver to — that's fine, the user's note\n // is already in the chat thread and the goal driver will see it\n // when it next decides which subtask to schedule. Sending to an\n // unregistered mailbox would emit a noisy warn for every team\n // member every time.\n try {\n const { sendMessage, hasMailbox } = await import('./messageBus.js');\n const recipients = room.team.map(t => t.agentId);\n let delivered = 0;\n for (const to of recipients) {\n if (!hasMailbox(to)) continue; // skip non-registered to avoid noisy warns\n const result = sendMessage('user', to, content, { priority: 'urgent' });\n if (result) delivered++;\n }\n if (delivered === 0) {\n logger.debug(COMPONENT, `User message recorded in chat; no specialist mailboxes were live to receive it (team will pick up at next subtask).`);\n }\n } catch (err) {\n logger.debug(COMPONENT, `messageBus dispatch skipped: ${(err as Error).message}`);\n }\n}\n\n/**\n * v6.1.0-alpha.13 — reopen a terminal mission with a follow-up direction.\n *\n * Creates a brand-new Goal with the user's new content as the title.\n * The mission keeps its id + history; the chat thread continues\n * organically; the team strip keeps showing previous helpers (and\n * picks up new ones automatically as the new goal spawns specialists\n * via the dynamic-team logic from alpha.1).\n *\n * Re-wires the per-mission bridges (approval bridge, goal-lifecycle\n * bridge) since the previous bridges were torn down when the mission\n * first reached its terminal state. The agent-event bridge is global\n * and looks up missions by goalId at dispatch time — it picks up the\n * new goal automatically without re-registration.\n */\nasync function reopenMissionWithFollowUp(missionId: string, newContent: string): Promise<void> {\n const room = getMission(missionId);\n if (!room) return;\n try {\n ensureGlobalBusBridge();\n const newGoal = createGoal({\n title: newContent,\n description:\n `Follow-up on mission ${missionId} (continued from previous goal ${room.goalId ?? '?'}).\\n\\n` +\n `Original mission: \"${room.goal}\"\\n\\n` +\n `User wants: ${newContent}`,\n tags: [\n `mission:${missionId}`,\n room.playId ? `play:${room.playId}` : 'play:generic',\n 'mission-followup',\n ],\n // Same rationale as startMissionWork — user-initiated, not\n // autonomous; bypass the 10-goals-per-hour rate limit.\n force: true,\n });\n setLinkedGoal(missionId, newGoal.id);\n // Tear down any stale bridges (in case completion teardown\n // didn't run yet) and wire fresh ones for the new goal.\n teardownMissionWork(missionId);\n const cleanups: Array<() => void> = [];\n cleanups.push(await wireApprovalBridge(missionId, newGoal.id));\n cleanups.push(await wireGoalLifecycleBridge(missionId, newGoal.id));\n lifecycles.set(missionId, cleanups);\n\n // Wake the team back up in the team strip.\n for (const member of room.team) {\n setMemberState(missionId, member.agentId, 'idle', 'getting back on it');\n }\n\n // Chat surface: explain what just happened.\n postSystemMessage(\n missionId,\n 'Picking this back up — the team is taking another swing with your new direction.',\n 'mission_resumed',\n );\n setStatus(missionId, 'working');\n\n // Mirror to the linked Command Post issue.\n if (room.issueId) {\n try {\n updateIssue(room.issueId, { status: 'in_progress' });\n addIssueComment(\n room.issueId,\n `User picked the mission back up: \"${newContent.slice(0, 200)}${newContent.length > 200 ? '…' : ''}\"`,\n { user: 'mission-chat' },\n );\n } catch { /* mirror failure never blocks the reopen */ }\n }\n\n logger.info(\n COMPONENT,\n `Mission ${missionId} reopened — new goal ${newGoal.id} linked (was: ${room.goalId ?? 'unset'})`,\n );\n } catch (err) {\n logger.error(COMPONENT, `Reopen failed for mission ${missionId}: ${(err as Error).message}`);\n // Surface the failure to the user instead of going silent.\n try {\n postSystemMessage(\n missionId,\n `Couldn't pick this back up: ${(err as Error).message}. Try starting a fresh mission instead.`,\n 'mission_resume_failed',\n );\n } catch { /* ok */ }\n }\n}\n\n/** Called when the UI toggles status (pause / resume). */\nexport async function handleStatusChange(missionId: string, status: MissionStatus): Promise<void> {\n if (status !== 'paused' && status !== 'working') return;\n try {\n // Look up the linked goal and toggle its driver state via the\n // existing user-controls surface. We import dynamically so the\n // lifecycle module doesn't have a top-level goal-driver\n // dependency (matches the rest of the file's pattern).\n const room = (await import('./missionRoom.js')).getMission(missionId);\n if (!room?.goalId) return;\n const driver = await import('./goalDriver.js');\n if (status === 'paused' && typeof driver.pauseDriver === 'function') {\n driver.pauseDriver(room.goalId);\n } else if (status === 'working' && typeof driver.resumeDriverControl === 'function') {\n driver.resumeDriverControl(room.goalId);\n }\n } catch (err) {\n logger.debug(COMPONENT, `Couldn't toggle driver state for ${missionId}: ${(err as Error).message}`);\n }\n}\n\n/** Tear down a mission's bridges. Called when the goal completes,\n * fails, or the mission is deleted. */\nexport function teardownMissionWork(missionId: string): void {\n const cleanups = lifecycles.get(missionId);\n if (!cleanups) return;\n for (const fn of cleanups) {\n try { fn(); }\n catch (err) { logger.debug(COMPONENT, `Cleanup threw: ${(err as Error).message}`); }\n }\n lifecycles.delete(missionId);\n}\n\n/**\n * v6.1.0-alpha.25 — re-attach event/approval/goal-lifecycle bridges\n * for missions that were already in-flight when the service was\n * restarted.\n *\n * **The bug this fixes (caught 2026-05-13):**\n *\n * The lifecycle bridges (`ensureGlobalBusBridge`, `wireApprovalBridge`,\n * `wireGoalLifecycleBridge`) live as in-memory subscriptions on the\n * agent-event bus + Command Post approval store + titanEvents bus.\n * They're attached only from `startMissionWork()` (new missions) and\n * `reopenMissionWithFollowUp()` (user reopens a stopped mission).\n *\n * On a service restart, those module-level subscriptions are gone.\n * Missions on disk in `status: working | forming | blocked` whose\n * goal driver keeps running (driver state is persisted; spawns\n * continue from where they left off) silently emit events into the\n * void — no listener catches them. From the user's POV the desk\n * shows \"Writer is working\" but no agent_done message ever lands,\n * the artifact paper stays blank, the cost stays $0.00.\n *\n * Hit Tony with the MLK essay mission on 2026-05-13 21:17 PT: the\n * spawn started seconds after the alpha.24 deploy restarted the\n * service. Writer talked to Ollama for 25s, returned needs_info,\n * and the lifecycle dropped the event because no bridge was\n * attached for that mission's goalId.\n *\n * **The fix:**\n *\n * Call this on server bootstrap. It scans every persisted mission,\n * and for each one still in a non-terminal status with a linked\n * `goalId`:\n * 1. Calls `ensureGlobalBusBridge()` (idempotent — only attaches\n * the global agent-event listener once).\n * 2. Wires the per-mission approval + goal-lifecycle bridges and\n * tracks the cleanups in the lifecycles map so\n * `teardownMissionWork()` still works.\n *\n * Returns the count of missions re-attached for logging.\n */\nexport async function reattachMissionBridgesOnStartup(): Promise<number> {\n let count = 0;\n let scanned = 0;\n try {\n const missions = listMissions();\n for (const m of missions) {\n scanned += 1;\n // Skip missions that have reached terminal state — their\n // driver is dormant, no events expected.\n if (m.status === 'done' || m.status === 'failed') continue;\n // Skip missions never linked to a goal.\n if (!m.goalId) continue;\n // Skip if we've already wired this mission this process\n // (defensive — shouldn't happen on startup but harmless).\n if (lifecycles.has(m.id)) continue;\n try {\n ensureGlobalBusBridge();\n const cleanups: Array<() => void> = [];\n cleanups.push(await wireApprovalBridge(m.id, m.goalId));\n cleanups.push(await wireGoalLifecycleBridge(m.id, m.goalId));\n lifecycles.set(m.id, cleanups);\n count += 1;\n logger.info(COMPONENT, `Re-attached lifecycle bridges for mission ${m.id} (goal ${m.goalId}, status ${m.status})`);\n } catch (err) {\n logger.warn(COMPONENT, `Re-attach failed for mission ${m.id}: ${(err as Error).message}`);\n }\n }\n if (count > 0) {\n logger.info(COMPONENT, `Startup re-attach complete — ${count}/${scanned} mission(s) wired back to the bus`);\n } else if (scanned > 0) {\n logger.debug(COMPONENT, `Startup re-attach — ${scanned} mission(s) scanned, none needed re-wiring`);\n }\n } catch (err) {\n logger.warn(COMPONENT, `Startup re-attach scan threw: ${(err as Error).message}`);\n }\n return count;\n}\n\n// ── Issue mirror ───────────────────────────────────────────────────\n//\n// v6.1.0-alpha.10 — mirror big mission lifecycle events to the linked\n// Command Post issue. Keeps the issue useful as the durable audit\n// trail without spamming it with every chat message (chat thread\n// already serves that purpose). What we mirror:\n//\n// - Mission start (in startMissionWork): \"Mission opened with team: …\"\n// - Question raised: `Sage asked: \"<question>\"`\n// - Question answered: `User answered: \"<answer>\"`\n// - Mission complete / failed: the same one-liner posted to chat,\n// plus an issue status update (done / cancelled).\n//\n// Every individual agent_message is NOT mirrored — too noisy.\n// Subtask-grain events stay in the chat thread.\n\ninterface IssueMirror {\n status?: 'in_progress' | 'in_review' | 'done' | 'blocked' | 'cancelled';\n comment?: string;\n}\n\nfunction mirrorToIssue(missionId: string, mirror: IssueMirror): void {\n try {\n const room = getMission(missionId);\n const issueId = room?.issueId;\n if (!issueId) return;\n if (mirror.comment) {\n addIssueComment(issueId, mirror.comment, { user: 'mission-chat' });\n }\n if (mirror.status) {\n updateIssue(issueId, { status: mirror.status });\n }\n } catch (err) {\n logger.debug(COMPONENT, `Issue mirror skipped: ${(err as Error).message}`);\n }\n}\n\n// ── Bridges ────────────────────────────────────────────────────────\n\n/**\n * Subscribe to goal-lifecycle events on the titanEvents bus and\n * translate goal completion / failure into:\n * 1. a system message in the mission thread (\"Mission complete.\" or\n * \"Couldn't finish this one — here's what we have so far.\")\n * 2. a status transition (working → done | failed)\n *\n * Without this, the mission room sat in 'working' indefinitely after\n * the underlying goal completed — the chat showed an idle team strip\n * and no closure message, which looked like the mission was stuck even\n * when it had successfully finished.\n */\nasync function wireGoalLifecycleBridge(missionId: string, goalId: string): Promise<() => void> {\n const { titanEvents } = await import('./daemon.js');\n const completedHandler = (data: unknown) => {\n try {\n const d = (data ?? {}) as Record<string, unknown>;\n if (d.goalId !== goalId) return;\n const durationMs = typeof d.durationMs === 'number' ? d.durationMs : 0;\n const seconds = Math.max(1, Math.round(durationMs / 1000));\n const specialistsUsed = Array.isArray(d.specialistsUsed) ? (d.specialistsUsed as string[]) : [];\n const namesList = specialistsUsed.length > 0\n ? ` with help from ${specialistsUsed.slice(0, 5).join(', ')}`\n : '';\n const completionLine = `Mission complete in ${seconds}s${namesList}.`;\n postSystemMessage(missionId, completionLine, 'mission_complete');\n setStatus(missionId, 'done');\n // v6.1.0-alpha.10 — mirror to the linked Command Post issue.\n mirrorToIssue(missionId, { status: 'done', comment: completionLine });\n // Mission reached a terminal state — tear down its bridges so\n // we don't leak event-bus listeners. Defer one tick so any\n // remaining synchronous work inside this handler chain runs\n // before the bridges go away.\n setImmediate(() => teardownMissionWork(missionId));\n } catch (err) {\n logger.debug(COMPONENT, `goal:completed handler threw: ${(err as Error).message}`);\n }\n };\n const failedHandler = (data: unknown) => {\n try {\n const d = (data ?? {}) as Record<string, unknown>;\n if (d.goalId !== goalId) return;\n const retries = typeof d.retries === 'number' ? d.retries : 0;\n const failureLine = retries > 0\n ? `Couldn't finish this one after ${retries} retry attempt(s). Take a look at what we did manage and tell me what to try next.`\n : `Couldn't finish this one. Take a look at what we did manage and tell me what to try next.`;\n postSystemMessage(missionId, failureLine, 'mission_failed');\n setStatus(missionId, 'failed');\n mirrorToIssue(missionId, { status: 'cancelled', comment: failureLine });\n setImmediate(() => teardownMissionWork(missionId));\n } catch (err) {\n logger.debug(COMPONENT, `goal:failed handler threw: ${(err as Error).message}`);\n }\n };\n titanEvents.on('goal:completed', completedHandler);\n titanEvents.on('goal:failed', failedHandler);\n return () => {\n titanEvents.off('goal:completed', completedHandler);\n titanEvents.off('goal:failed', failedHandler);\n };\n}\n\n/** Subscribe to the Command Post approval queue and translate any\n * approval filed against this mission's goal into an inline question\n * message. v6.1.0-alpha.1 — uses the real titanEvents bus\n * ('commandpost:approval:created' event added in commandPost.ts).\n * Returns an unsubscribe. */\nasync function wireApprovalBridge(missionId: string, goalId: string): Promise<() => void> {\n const { titanEvents } = await import('./daemon.js');\n const handler = (approval: CPApprovalLike) => {\n try {\n // Filter: only approvals tied to this mission's goal.\n const payload = (approval.payload ?? {}) as Record<string, unknown>;\n if (payload.goalId !== goalId) return;\n const agentId = String(payload.specialist ?? payload.subtaskKind ?? approval.requestedBy ?? 'sage').toLowerCase();\n const rawContent = String(\n payload.question\n ?? payload.allQuestions\n ?? approval.title\n ?? approval.reason\n ?? `${approval.type} approval needed`\n );\n // Strip the goalDriver boilerplate prefix (alpha.5).\n const stripped = stripDriverBoilerplate(rawContent);\n // v6.1.0-alpha.29 — when stripping leaves a generic fallback\n // (or anything ≤ that fallback's information density), enrich\n // the question with payload context so the user actually knows\n // what's happening. Pre-alpha.29 the user saw a bare \"I need\n // more direction to keep going. What should I focus on?\" with\n // no clue that Writer was stuck on the MLK essay because the\n // structured-output reformat failed.\n const content = enrichBlockedQuestion(stripped, payload);\n const quickReplies = Array.isArray(payload.quickReplies)\n ? (payload.quickReplies as string[]).slice(0, 4)\n : defaultQuickReplies(approval);\n raiseQuestion({\n missionId,\n agentId,\n content,\n approvalId: approval.id,\n quickReplies,\n });\n // v6.1.0-alpha.10 — mirror to issue audit trail.\n mirrorToIssue(missionId, {\n comment: `${agentId} asked: \"${content.slice(0, 200)}${content.length > 200 ? '…' : ''}\"`,\n status: 'blocked',\n });\n } catch (err) {\n logger.debug(COMPONENT, `Approval bridge handler threw: ${(err as Error).message}`);\n }\n };\n titanEvents.on('commandpost:approval:created', handler);\n return () => titanEvents.off('commandpost:approval:created', handler);\n}\n\n/**\n * v6.1.0-alpha.29 — when stripDriverBoilerplate leaves us with the\n * generic fallback (\"I need more direction to keep going. What should\n * I focus on?\"), the user has zero signal about WHY the agent is\n * stuck or what they were trying to do. The approval payload carries\n * the missing context (subtaskTitle, specialist, lastError, attempts);\n * this composer weaves it into a friendlier question.\n *\n * Decision matrix:\n * 1. If `content` already reads like a specific question from the\n * specialist (>= 30 chars AND not equal to the generic fallback),\n * leave it alone — the specialist gave us something real.\n * 2. Otherwise build a contextual fallback from `payload`:\n * \"<Specialist> was working on <subtaskTitle> (attempt N).\n * Last hurdle: <lastError, scrubbed>.\n * What should they focus on?\"\n * Empty fields are dropped gracefully so a missing `lastError`\n * doesn't produce \"Last hurdle: undefined.\"\n * 3. Internal-error traces in `lastError` (Parser could not extract\n * JSON, HTTP 429, <!doctype html>, etc.) get scrubbed via\n * `looksLikeInternalErrorTrace` — they're not useful to the user\n * and reading them as \"what went wrong\" would be misleading.\n */\nfunction enrichBlockedQuestion(stripped: string, payload: Record<string, unknown>): string {\n const GENERIC = 'I need more direction to keep going. What should I focus on?';\n const isGeneric = stripped === GENERIC || stripped.trim().length < 30;\n if (!isGeneric) return stripped;\n\n const specialist = typeof payload.specialist === 'string' ? capitalize(payload.specialist) : 'Your helper';\n const subtaskTitle = typeof payload.subtaskTitle === 'string' ? payload.subtaskTitle : null;\n const attempts = typeof payload.attempts === 'number' ? payload.attempts : null;\n const rawLastError = typeof payload.lastError === 'string' ? payload.lastError : null;\n // Scrub internal traces — they're noise.\n const lastError = rawLastError && !looksLikeInternalErrorTrace(rawLastError)\n ? rawLastError.slice(0, 200).trim()\n : null;\n\n const parts: string[] = [];\n if (subtaskTitle) {\n parts.push(`${specialist} is working on \"${shortenLine(subtaskTitle, 80)}\"`);\n } else {\n parts.push(`${specialist} is working on your mission`);\n }\n if (attempts && attempts > 1) {\n parts[parts.length - 1] += ` (attempt ${attempts})`;\n }\n parts[parts.length - 1] += ' but got stuck.';\n\n if (lastError) {\n parts.push(`Last hurdle: ${lastError}${rawLastError && rawLastError.length > 200 ? '…' : ''}`);\n } else if (rawLastError) {\n // We scrubbed it as an internal trace — give the user a heads-up\n // it exists without dumping the trace itself.\n parts.push(`There was a technical hiccup the team couldn't recover from on their own.`);\n }\n\n parts.push(`What should they focus on? Pick a reply below — or type your own.`);\n return parts.join(' ');\n}\n\nfunction capitalize(s: string): string {\n if (!s) return s;\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nfunction shortenLine(s: string, max: number): string {\n if (s.length <= max) return s;\n return s.slice(0, max - 1).trimEnd() + '…';\n}\n\n/**\n * Strip the goalDriver's auto-generated boilerplate from blocked-approval\n * question text so it reads like a person's question. The driver wraps\n * the specialist's actual question (or a fallback) with internal context\n * (subtask title, attempt count, specialist id) that's noise to the user.\n *\n * Patterns matched:\n * - `Goal \"...\" is stuck on subtask \"...\" (attempt N, specialist: X).\\n\\n`\n * - `Goal \"...\" — subtask \"...\" failed after N attempt(s) with specialist X.\\n\\n`\n * - `Goal \"...\" — subtask \"...\" is blocked after N attempt(s) with specialist X. The specialist could not complete the task and needs guidance on how to proceed.`\n */\nexport function stripDriverBoilerplate(text: string): string {\n if (!text || typeof text !== 'string') return text ?? '';\n let cleaned = text;\n // Pattern 1: prefix → \"Goal '...' is stuck on subtask '...' (...).\\n\\n<real question>\"\n cleaned = cleaned.replace(\n /^Goal\\s+\"[^\"]*\"\\s+is\\s+stuck\\s+on\\s+subtask\\s+\"[^\"]*\"\\s+\\([^)]*\\)\\.\\s*\\n+/i,\n '',\n );\n // Pattern 2: prefix → \"Goal '...' — subtask '...' failed after N attempt(s) with specialist X.\\n\\nError: ...\"\n cleaned = cleaned.replace(\n /^Goal\\s+\"[^\"]*\"\\s+—\\s+subtask\\s+\"[^\"]*\"\\s+failed\\s+after\\s+\\d+\\s+attempt\\(s\\)\\s+with\\s+specialist\\s+\\S+\\.\\s*\\n+/i,\n '',\n );\n // Pattern 3: fallback (no real question, just the driver's placeholder)\n cleaned = cleaned.replace(\n /^Goal\\s+\"[^\"]*\"\\s+—\\s+subtask\\s+\"[^\"]*\"\\s+is\\s+blocked\\s+after\\s+\\d+\\s+attempt\\(s\\)\\s+with\\s+specialist\\s+\\S+\\.\\s*The\\s+specialist\\s+could\\s+not\\s+complete\\s+the\\s+task\\s+and\\s+needs\\s+guidance\\s+on\\s+how\\s+to\\s+proceed\\.?\\s*/i,\n '',\n );\n cleaned = cleaned.trim();\n // If we stripped everything, fall back to a friendly default — the\n // user shouldn't see an empty question.\n if (cleaned.length === 0) {\n return 'I need more direction to keep going. What should I focus on?';\n }\n return cleaned;\n}\n\n/**\n * Detect internal error-chain text that should NOT be shown to the user\n * as the specialist's \"reasoning.\" Examples seen in the wild post-alpha.5:\n *\n * `Parser could not extract JSON from specialist response. Raw (200 chars):\n * Sub-agent error: All providers failed: Provider ollama/glm-5:cloud\n * failed: [HTTP 429] Ollama error (429): <!doctype html>...`\n *\n * The bridge scrubs these strings and falls through to the friendly\n * fallback (\"I ran into trouble on this one and couldn't finish — handing\n * back to the team.\") rather than dumping a stack trace into the chat.\n * The raw text is preserved on the message's `meta.failureDetail` so it\n * stays available via the click-to-expand details panel.\n */\nexport function looksLikeInternalErrorTrace(text: string | null | undefined): boolean {\n if (!text || typeof text !== 'string') return false;\n const markers = [\n /Parser could not extract JSON/i,\n /Sub-agent error:/i,\n /All providers failed/i,\n /HTTP\\s+\\d{3}.*Ollama error/i,\n /Circuit breaker OPEN for/i,\n /<!doctype html>/i,\n /\\bToo Many Requests\\b/i,\n ];\n return markers.some(re => re.test(text));\n}\n\n/** Sensible default reply set for the common approval kinds. */\nfunction defaultQuickReplies(approval: CPApprovalLike): string[] {\n const payloadKind = (approval.payload as Record<string, unknown> | undefined)?.kind;\n if (payloadKind === 'driver_blocked') return ['Use your best judgment', 'Pause for me', 'Try a different angle'];\n if (payloadKind === 'self_repair') return ['Approve fix', 'Skip', 'Tell me more'];\n return ['Approve', 'Skip'];\n}\n\n// ── Types shared with the commandPost module ───────────────────────\n//\n// We don't import the full CPApproval shape because that would couple\n// the mission lifecycle to internal commandPost types that may reshape.\n// Duck-type only what we need.\n\ninterface CPApprovalLike {\n id: string;\n type?: string;\n title?: string;\n reason?: string;\n requestedBy?: string;\n payload?: Record<string, unknown>;\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\n/**\n * Bound the activity string that flows into MissionMember.currentActivity.\n *\n * v6.1.0-alpha.14 hotfix — was capping at 80 chars, which truncated mid-word\n * on long user prompts (\"Scout I want you to research AI Agents and how they\n * help bus…\") and there was no way to read the full text from the chat\n * surface. The 80-char cap was a holdover from when this was rendered in a\n * tiny tooltip; the actual typing pill in the chat has plenty of room.\n *\n * Bumped to 240 so even long goal prompts come through readable. The cap\n * exists only to bound the JSON payload size flowing through the bus and\n * SSE stream; the UI applies its own visual treatment (wrap + max-width +\n * title attribute) so even this length renders cleanly.\n */\n/**\n * v6.1.0-alpha.31 — turn a raw `tool_call` event into a friendly\n * sticky-note entry for the agent's activityLog. Returns null if the\n * tool isn't one we want to surface (silent background tools like\n * `memory_store`, `system_info`, etc. shouldn't clutter the desk).\n *\n * Each known research/work tool maps to:\n * - icon: a single emoji that reads at sticky-note scale\n * - activity: one-line summary (\"searched\", \"fetched\", \"wrote\")\n * - detail: the most informative argument value, truncated\n *\n * Unknown tools fall through to a generic \"used <name>\" — visible but\n * lightweight, so the user still sees activity even for tools we\n * haven't explicitly mapped.\n */\nfunction buildActivitySticky(\n name: string,\n args: Record<string, unknown>,\n): { icon: string; activity: string; detail?: string } | null {\n const pickStr = (...keys: string[]): string | undefined => {\n for (const k of keys) {\n const v = args[k];\n if (typeof v === 'string' && v.trim().length > 0) return v.trim();\n }\n return undefined;\n };\n const truncate = (s: string, max: number) => s.length <= max ? s : s.slice(0, max - 1).trimEnd() + '…';\n\n switch (name) {\n case 'web_search':\n case 'browser_search': {\n const query = pickStr('query', 'q', 'search');\n return { icon: '🔍', activity: 'searched the web', detail: query ? truncate(query, 100) : undefined };\n }\n case 'web_fetch':\n case 'browse_url':\n case 'web_read': {\n const url = pickStr('url', 'href');\n return { icon: '🌐', activity: 'read a page', detail: url ? truncate(url, 100) : undefined };\n }\n case 'web_browse_llm':\n case 'browser': {\n const target = pickStr('url', 'task');\n return { icon: '🌐', activity: 'browsed', detail: target ? truncate(target, 100) : undefined };\n }\n case 'write_file':\n case 'append_file':\n case 'edit_file':\n case 'apply_patch': {\n const path = pickStr('path', 'file', 'filename');\n return { icon: '✍️', activity: 'wrote a file', detail: path ? truncate(path, 100) : undefined };\n }\n case 'read_file': {\n const path = pickStr('path', 'file', 'filename');\n return { icon: '📖', activity: 'read a file', detail: path ? truncate(path, 100) : undefined };\n }\n case 'list_dir': {\n const path = pickStr('path', 'dir');\n return { icon: '📂', activity: 'listed a folder', detail: path ? truncate(path, 100) : undefined };\n }\n case 'shell':\n case 'exec':\n case 'code_exec':\n case 'execute_code': {\n const cmd = pickStr('command', 'cmd', 'code');\n return { icon: '⚙️', activity: 'ran a command', detail: cmd ? truncate(cmd, 100) : undefined };\n }\n case 'memory':\n case 'graph_remember':\n case 'memory_store': {\n const fact = pickStr('content', 'fact', 'value', 'key');\n return { icon: '💡', activity: 'memorized', detail: fact ? truncate(fact, 100) : undefined };\n }\n case 'graph_search':\n case 'graph_recall':\n case 'rag_search':\n case 'kb_search': {\n const q = pickStr('query', 'q');\n return { icon: '🧠', activity: 'recalled', detail: q ? truncate(q, 100) : undefined };\n }\n case 'screenshot':\n case 'browser_screenshot': {\n return { icon: '📷', activity: 'took a screenshot' };\n }\n case 'generate_image':\n case 'edit_image':\n case 'image_gen': {\n const prompt = pickStr('prompt', 'description');\n return { icon: '🎨', activity: 'drew an image', detail: prompt ? truncate(prompt, 100) : undefined };\n }\n case 'analyze_image':\n case 'vision': {\n return { icon: '👁️', activity: 'looked at an image' };\n }\n case 'github_repos':\n case 'github_issues':\n case 'github_prs':\n case 'github_commits':\n case 'github_files': {\n return { icon: '🐙', activity: name.replace('github_', 'checked GitHub '), detail: pickStr('repo', 'owner') };\n }\n // Silent / housekeeping tools — don't surface, keeps the desk tidy.\n case 'system_info':\n case 'current_model':\n case 'sessions_list':\n case 'sessions_history':\n case 'list_uploads':\n case 'list_active_widgets':\n case 'goal_list':\n case 'list_personas':\n case 'get_persona':\n case 'list_spaces':\n case 'interaction_log':\n case 'feedback_submit':\n return null;\n default:\n // Unknown tool — surface a generic sticky so the user still\n // sees the agent is doing something. Avoid noisy parameters.\n return { icon: '🛠️', activity: `used ${name}` };\n }\n}\n\nfunction shortenActivity(s: string | undefined): string | undefined {\n if (!s) return undefined;\n const trimmed = s.trim();\n if (trimmed.length <= 240) return trimmed;\n return trimmed.slice(0, 237).trimEnd() + '…';\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/agent/missionLifecycle.ts"],"sourcesContent":["/**\n * TITAN — Mission lifecycle adapter (v6.1.0)\n *\n * Connects a freshly-created Mission Room to the existing goal driver\n * + Command Post pipeline. When a mission is created:\n *\n * 1. Create a Goal (in the goals subsystem) with title = goal text,\n * tagged with the mission id and play id so the bridge below can\n * filter events back to the right room.\n * 2. Subscribe to the agent message bus so each specialist's tool\n * calls / answers / progress messages get rewritten as a chat\n * message in the mission thread.\n * 3. Subscribe to the Command Post approval queue so blocking\n * questions filed against this mission's goal show up as inline\n * question messages in the chat.\n *\n * The bridge is one-way (driver → mission room). User actions (replies\n * to questions, status toggles) go through the mission router and\n * propagate to the goal driver / Command Post via existing APIs.\n *\n * Idempotent: subscribing a second mission doesn't double-fire. We\n * track per-mission unsubscribe functions in `lifecycles`.\n */\nimport { readFileSync, existsSync, statSync } from 'fs';\nimport logger from '../utils/logger.js';\nimport { createGoal } from './goals.js';\nimport {\n postAgentMessage,\n postSystemMessage,\n setMemberState,\n appendMemberActivity,\n setStatus,\n raiseQuestion,\n recordCost,\n ensureMember,\n getMission,\n getMissionByGoalId,\n listMissions,\n setLinkedGoal,\n setLinkedIssue,\n updateArtifact,\n type MissionRoom,\n type MissionStatus,\n} from './missionRoom.js';\nimport { onAgentEvent, type AgentEvent } from './agentEvents.js';\nimport { createIssue, updateIssue, addIssueComment } from './commandPost.js';\n\n// v6.1.0-alpha.57 — track in-flight notebook fills per mission so a\n// second agent_done can cancel the prior typewriter (rather than the\n// two of them racing on `updateArtifact`).\nconst notebookFillTimers = new Map<string, NodeJS.Timeout>();\n\n/**\n * Convert HTML to plain text suitable for the lined-paper notebook\n * view. The notebook component renders `content.split('\\n').filter`\n * and slices to 10 lines per page — it wants prose, not markup.\n *\n * - Strip <script>/<style> blocks (including content)\n * - Convert <br>/<p>/<h1-6>/<li>/<tr> to line breaks\n * - Strip every other tag\n * - Decode the 5 standard entities + numeric refs\n * - Collapse runs of 3+ blank lines to 2\n */\nfunction htmlToNotebookText(html: string): string {\n if (!html) return '';\n let s = html;\n s = s.replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script>/gi, '');\n s = s.replace(/<style\\b[^>]*>[\\s\\S]*?<\\/style>/gi, '');\n s = s.replace(/<head\\b[^>]*>[\\s\\S]*?<\\/head>/gi, '');\n s = s.replace(/<(?:br|hr)\\s*\\/?>/gi, '\\n');\n s = s.replace(/<\\/(?:p|h[1-6]|li|tr|div|section|article|figure|figcaption|blockquote)\\s*>/gi, '\\n\\n');\n s = s.replace(/<[^>]+>/g, '');\n s = s.replace(/ /gi, ' ')\n .replace(/&/gi, '&')\n .replace(/</gi, '<')\n .replace(/>/gi, '>')\n .replace(/"/gi, '\"')\n .replace(/'/gi, \"'\")\n .replace(/&#(\\d+);/g, (_m, n) => String.fromCharCode(Number(n)));\n s = s.replace(/[ \\t]+/g, ' ');\n s = s.replace(/\\n{3,}/g, '\\n\\n');\n return s.trim();\n}\n\n/**\n * Stream plain text into a mission's artifact buffer in small\n * chunks. Each chunk fires an `artifact_updated` SSE event so the\n * DocumentPaper notebook fills in like someone writing into it.\n *\n * - Default chunk = 40 chars every 80ms → ~500 chars/sec, feels\n * human-paced (faster than typing, slower than dumping)\n * - Cap total fill time at ~6s so a 3-page essay doesn't take\n * forever; long content scales chunk size up\n * - Fire-and-forget — caller doesn't await, but we expose the\n * promise so tests can await on it\n * - Cancelable: a second call for the same mission cancels the\n * prior animation\n */\nfunction streamFillNotebook(missionId: string, agentId: string, plainText: string): void {\n if (!plainText) return;\n // Cancel any in-flight fill for this mission.\n const prior = notebookFillTimers.get(missionId);\n if (prior) clearInterval(prior);\n\n const total = plainText.length;\n // Scale chunk size so total fill time is roughly 3–6s.\n const TARGET_MS = 4000;\n const TICK_MS = 80;\n const ticks = Math.max(20, Math.ceil(TARGET_MS / TICK_MS));\n const chunkChars = Math.max(20, Math.ceil(total / ticks));\n let offset = 0;\n\n const id = setInterval(() => {\n offset = Math.min(total, offset + chunkChars);\n const slice = plainText.slice(0, offset);\n try {\n updateArtifact(missionId, agentId, slice);\n } catch (err) {\n // Mission may have been deleted mid-stream.\n logger.debug(COMPONENT, `streamFillNotebook stopped on ${missionId}: ${(err as Error).message}`);\n clearInterval(id);\n notebookFillTimers.delete(missionId);\n return;\n }\n if (offset >= total) {\n clearInterval(id);\n notebookFillTimers.delete(missionId);\n }\n }, TICK_MS);\n notebookFillTimers.set(missionId, id);\n}\n\n/**\n * If `agent_done` brought a file artifact (HTML/markdown/text the\n * Writer just produced), read it and start the notebook fill so the\n * user watches the document appear on the lined paper. Defensive on\n * everything — bad file, missing file, oversize file all silently\n * skip rather than crashing the lifecycle bridge.\n */\nfunction maybeFillNotebookFromFileArtifact(\n missionId: string,\n agentId: string,\n artifacts: Array<{ type: string; ref: string }>,\n): void {\n const file = artifacts.find(\n (a) => a.type === 'file' && typeof a.ref === 'string' && a.ref.length > 0,\n );\n if (!file) return;\n try {\n if (!existsSync(file.ref)) return;\n const stat = statSync(file.ref);\n // Cap at 1 MB to avoid loading huge accidentally-large files\n // into memory + streaming them as a 25-minute animation.\n if (stat.size > 1024 * 1024) return;\n const raw = readFileSync(file.ref, 'utf-8');\n const isHtml = /\\.html?$/i.test(file.ref) || /<html[\\s>]/i.test(raw);\n const plain = isHtml ? htmlToNotebookText(raw) : raw;\n if (plain.trim().length === 0) return;\n streamFillNotebook(missionId, agentId, plain);\n } catch (err) {\n logger.debug(COMPONENT, `maybeFillNotebookFromFileArtifact skipped: ${(err as Error).message}`);\n }\n}\n\nconst COMPONENT = 'MissionLifecycle';\n\n// Per-mission cleanup handlers. When a mission completes, we tear down\n// these subscriptions so the bus doesn't leak listeners.\nconst lifecycles = new Map<string, Array<() => void>>();\n\n/**\n * v6.1.0-alpha.1 — a SINGLE global subscription to the shared\n * agentEvents bus. Every mission piggybacks off this; we filter by\n * goalId at dispatch time. This is much cheaper than N per-mission\n * subscriptions (which is what alpha.0's broken bridge attempted) and\n * survives the case where the goal driver routes to a specialist that\n * isn't on the predicted Plays team — we add them dynamically.\n */\nlet globalBusUnsub: (() => void) | null = null;\nfunction ensureGlobalBusBridge(): void {\n if (globalBusUnsub) return;\n globalBusUnsub = onAgentEvent((ev: AgentEvent) => {\n // The goalDriver-emitted events carry data.goalId; events from\n // ad-hoc spawns (CLI, channels) don't, and we silently ignore\n // those — they aren't part of a mission.\n const data = ev.data ?? {};\n const goalId = typeof data.goalId === 'string' ? data.goalId : undefined;\n if (!goalId) return;\n const mission = getMissionByGoalId(goalId);\n if (!mission) return;\n const agentId = (ev.agentId ?? ev.agentName ?? '').toLowerCase();\n if (!agentId) return;\n try {\n switch (ev.type) {\n case 'agent_spawn': {\n ensureMember(mission.id, agentId);\n const subtaskTitle = typeof data.subtaskTitle === 'string' ? data.subtaskTitle : 'something';\n setMemberState(mission.id, agentId, 'working', shortenActivity(subtaskTitle));\n break;\n }\n case 'tool_call': {\n const name = typeof data.name === 'string' ? data.name : 'tool';\n ensureMember(mission.id, agentId);\n setMemberState(mission.id, agentId, 'working', shortenActivity(`running ${name}`));\n // v6.1.0-alpha.31 — also push a live activity sticky\n // to the desk for research-y tool calls. Tony's\n // direct ask: \"I liked the way we had it before\n // when the Agents put sticky notes on the desk\n // when working, with their research data so I can\n // see what they are doing.\" The activityLog\n // accumulates here per-agent and the canvas reads\n // it into draggable sticky notes.\n try {\n const args = (typeof data.args === 'object' && data.args !== null)\n ? (data.args as Record<string, unknown>)\n : {};\n const sticky = buildActivitySticky(name, args);\n if (sticky) {\n appendMemberActivity(mission.id, agentId, sticky);\n }\n } catch (err) {\n logger.debug(COMPONENT, `Activity sticky build threw: ${(err as Error).message}`);\n }\n break;\n }\n case 'tool_end': {\n // Don't transition state — the next tool_call or agent_done\n // will overwrite. Just no-op (avoids flicker).\n break;\n }\n case 'agent_done': {\n ensureMember(mission.id, agentId);\n const rawReasoning = typeof data.reasoning === 'string' && data.reasoning.trim().length > 0\n ? data.reasoning.trim()\n : null;\n // v6.1.0-alpha.12 — pluck the specialist's concrete\n // artifacts (URLs visited, files written, facts\n // memorized) so the chat can render them as clickable\n // sources. Pre-alpha.12 these were silently discarded —\n // a \"Successfully researched\" reasoning summary made it\n // to chat but the actual URLs/files behind it did not,\n // so the user couldn't follow up on anything.\n const sources: Array<{ type: 'url' | 'file' | 'fact' | 'report'; ref: string; description?: string }> = [];\n if (Array.isArray(data.artifacts)) {\n for (const raw of data.artifacts) {\n if (!raw || typeof raw !== 'object') continue; // skip null/undefined/primitive\n const a = raw as Record<string, unknown>;\n const t = typeof a.type === 'string' ? a.type : '';\n const ref = typeof a.ref === 'string' ? a.ref : '';\n if (!ref) continue;\n if (t !== 'url' && t !== 'file' && t !== 'fact' && t !== 'report') continue;\n sources.push({\n type: t as 'url' | 'file' | 'fact' | 'report',\n ref,\n description: typeof a.description === 'string' ? a.description : undefined,\n });\n if (sources.length >= 12) break;\n }\n }\n // v6.1.0-alpha.57 — live notebook fill. If the\n // specialist produced a file artifact, stream its\n // plain-text contents into the mission's\n // `room.artifact.content` buffer in small chunks\n // (~4s total). The DocumentPaper component on the\n // canvas watches that buffer and re-renders as it\n // grows, so the user sees the document fill in\n // line-by-line like the AI is writing in a\n // notebook. Fire-and-forget; safe if the file is\n // missing / too large / unreadable.\n if (sources.length > 0) {\n maybeFillNotebookFromFileArtifact(\n mission.id,\n agentId,\n sources.map((s) => ({ type: s.type, ref: s.ref })),\n );\n }\n\n // v6.1.0-alpha.7 — scrub internal-error stack traces.\n // When a spawn fails, the `reasoning` field can be the\n // raw error chain: \"Parser could not extract JSON...\n // Sub-agent error: All providers failed: HTTP 429\n // <html>...\" That's pure jargon for users. Detect it\n // and replace with the friendly fallback. The original\n // text is preserved on `meta.failureDetail` so power\n // users can still see it via click-to-expand.\n const reasoning = looksLikeInternalErrorTrace(rawReasoning)\n ? null\n : rawReasoning;\n const toolsUsed = Array.isArray(data.toolsUsed)\n ? (data.toolsUsed as string[]).slice(0, 6)\n : [];\n const actions = toolsUsed.map(t => ({ name: 'used', detail: t }));\n const status = typeof data.status === 'string' ? data.status : 'done';\n // v6.1.0-alpha.4 — pass the rich context as the message\n // `meta` field. The chat UI hides it by default and\n // surfaces it when the user clicks the bubble — keeps\n // the thread readable while making \"what actually\n // happened here\" one tap away.\n //\n // v6.1.0-alpha.7 — if the raw reasoning was a scrubbed\n // internal-error trace, stash it on `meta.failureDetail`\n // so power users can still see it via click-to-expand.\n // The chat surface stays clean.\n const meta: Record<string, unknown> = {\n subtaskTitle: typeof data.subtaskTitle === 'string' ? data.subtaskTitle : undefined,\n status,\n durationMs: typeof data.durationMs === 'number' ? data.durationMs : undefined,\n tokensUsed: typeof data.tokensUsed === 'number' ? data.tokensUsed : undefined,\n costUsd: typeof data.costUsd === 'number' ? data.costUsd : undefined,\n model: typeof data.model === 'string' ? data.model : undefined,\n };\n if (rawReasoning && rawReasoning !== reasoning) {\n meta.failureDetail = rawReasoning;\n }\n // v6.1.0-alpha.1 — every agent_done emits SOMETHING into\n // the chat. The pre-fix path returned nothing when\n // `reasoning` was empty, which is common on cloud\n // specialists that return JSON-only responses (their\n // reasoning field is empty because the *artifact* is\n // the value). The chat shouldn't go silent.\n // v6.1.0-alpha.12 — also pass `sources` so URLs +\n // files the specialist worked with render clickably.\n const sourcesArg = sources.length > 0 ? sources : undefined;\n if (reasoning) {\n postAgentMessage(mission.id, agentId, reasoning, actions.length > 0 ? actions : undefined, meta, sourcesArg);\n } else if (status === 'failed') {\n postAgentMessage(\n mission.id,\n agentId,\n `I ran into trouble on this one and couldn't finish — handing back to the team.`,\n actions.length > 0 ? actions : undefined,\n meta,\n sourcesArg,\n );\n } else if (status === 'needs_info' || status === 'blocked') {\n postAgentMessage(\n mission.id,\n agentId,\n `I have a quick question before I can finish — see below.`,\n actions.length > 0 ? actions : undefined,\n meta,\n sourcesArg,\n );\n } else {\n // status === 'done' with empty reasoning. Common with\n // JSON-only specialists. Don't go silent — say something\n // honest about what they did.\n const summary = toolsUsed.length > 0\n ? `Done — used ${toolsUsed.slice(0, 3).join(', ')}.`\n : `Done.`;\n postAgentMessage(mission.id, agentId, summary, actions.length > 0 ? actions : undefined, meta, sourcesArg);\n }\n // v6.1.0-beta.4 — explicitly reset currentActivity\n // to a calm string. Passing `undefined` here used to\n // leave stale strings like \"running download_image\"\n // visible on the team strip long after the agent\n // went idle. The mzzbajnj writer stuck-on-string bug.\n setMemberState(mission.id, agentId, 'idle', 'standing by');\n const tokens = typeof data.tokensUsed === 'number' ? data.tokensUsed : 0;\n const cost = typeof data.costUsd === 'number' ? data.costUsd : 0;\n if (tokens > 0 || cost > 0) {\n recordCost(mission.id, tokens, cost);\n }\n break;\n }\n }\n } catch (err) {\n logger.debug(COMPONENT, `Bridge dispatch threw for ${mission.id}/${ev.type}: ${(err as Error).message}`);\n }\n });\n logger.info(COMPONENT, 'Global agent-event bridge attached (goalId → mission room dispatch)');\n}\n\n/** The wire that the gateway / missions router calls when a mission is\n * created. Returns the linked goal id. */\nexport async function startMissionWork(mission: MissionRoom): Promise<string | null> {\n try {\n // Make sure the global event bridge is alive. Idempotent.\n ensureGlobalBusBridge();\n\n // Tag the goal with the mission + play id so message-bus event\n // bridges can filter \"which mission did this belong to.\"\n const goal = createGoal({\n title: mission.goal,\n description: `Mission ${mission.id}` + (mission.playId ? ` (play: ${mission.playId})` : ''),\n tags: [\n `mission:${mission.id}`,\n mission.playId ? `play:${mission.playId}` : 'play:generic',\n ],\n // v6.1.0-alpha.5 — Mission Chat goals are user-initiated. The\n // autonomous-creation rate limit (10/hour) + active-goals cap\n // exists to throttle runaway self-mod / self-repair loops, not\n // to throttle the user typing into the chat. Bypass it so\n // missions never silently fail with \"Couldn't start the team:\n // rate limited\" after the user's 11th request in an hour.\n force: true,\n });\n // v6.1.0-alpha.1 — link the goal id INSIDE startMissionWork so any\n // event the goal driver fires (even before the missions router gets\n // back to set it) can resolve the mission via getMissionByGoalId.\n // The router's redundant setLinkedGoal call is now a no-op safety\n // net rather than the source of truth.\n setLinkedGoal(mission.id, goal.id);\n\n // v6.1.0-alpha.10 — auto-create a Command Post issue for this\n // mission. The issue becomes the durable audit trail (every\n // significant lifecycle event mirrors as a comment) and the\n // Command Post Issues panel + Mission Chat / Canvas surface the\n // same record. Failure to create the issue should never block\n // the mission itself — it's audit, not behavior.\n let issueIdent: string | undefined;\n try {\n const issue = createIssue({\n title: mission.goal,\n description: `Mission ${mission.id}` + (mission.playId ? ` · play: ${mission.playId}` : '') +\n `\\n\\nLinked goal: ${goal.id}` +\n `\\n\\nTeam: ${mission.team.map(t => t.name).join(', ') || '(forming)'}`,\n priority: 'medium',\n createdByUser: 'mission-chat',\n goalId: goal.id,\n });\n setLinkedIssue(mission.id, issue.id);\n issueIdent = issue.identifier;\n updateIssue(issue.id, { status: 'in_progress' });\n addIssueComment(\n issue.id,\n `Mission opened with team: ${mission.team.map(t => t.name).join(', ') || '(forming)'}.`,\n { user: 'mission-chat' },\n );\n } catch (issueErr) {\n logger.warn(COMPONENT, `Issue link skipped for mission ${mission.id}: ${(issueErr as Error).message}`);\n }\n logger.info(\n COMPONENT,\n `Mission ${mission.id} linked to goal ${goal.id}` +\n (issueIdent ? ` and issue ${issueIdent}` : '') +\n ` (play=${mission.playId})`,\n );\n\n // Mark every Plays-predicted member as \"ready\" so the team strip\n // lights up immediately. The goal driver routes for real, and the\n // global bridge will narrow each member's `currentActivity` (and\n // ADD new members if the driver picks specialists Plays didn't\n // predict).\n for (const member of mission.team) {\n setMemberState(mission.id, member.agentId, 'idle', 'standing by');\n }\n\n // The approval bridge + goal-lifecycle bridge are both\n // per-mission — they filter by goalId, so we register them\n // scoped to the goal we just created.\n const cleanups: Array<() => void> = [];\n cleanups.push(await wireApprovalBridge(mission.id, goal.id));\n cleanups.push(await wireGoalLifecycleBridge(mission.id, goal.id));\n lifecycles.set(mission.id, cleanups);\n\n return goal.id;\n } catch (err) {\n logger.error(COMPONENT, `Failed to start mission ${mission.id}: ${(err as Error).message}`);\n setStatus(mission.id, 'failed', `Couldn't start the team: ${(err as Error).message}`);\n return null;\n }\n}\n\n/** Called when the UI posts a user message into a mission. Two paths:\n *\n * 1. **Mission is live** (working/blocked/paused/forming) — broadcast\n * the user's note to every registered specialist mailbox so the\n * next agent loop picks it up. Same behavior as alpha.1+.\n *\n * 2. **Mission is terminal** (done/failed) — v6.1.0-alpha.13: reopen\n * the mission with this message as a new direction. Creates a\n * fresh Goal, re-wires the lifecycle bridges, flips status back\n * to 'working', posts a \"Picking this back up…\" system note,\n * mirrors to the linked Command Post issue. The DriverScheduler\n * picks up the new active goal on its next tick (~10s).\n */\nexport async function handleUserMessage(missionId: string, content: string): Promise<void> {\n const room = getMission(missionId);\n if (!room) return;\n const isTerminal = room.status === 'done' || room.status === 'failed';\n if (isTerminal) {\n await reopenMissionWithFollowUp(missionId, content);\n return;\n }\n // For v1 we use the existing messageBus. We don't know which\n // specialist is \"current\" — broadcast to every registered member's\n // mailbox so whichever one is in-flight picks the note up at the\n // start of its next round. Mailbox names match the specialist ids.\n //\n // v6.1.0-alpha.1 — only dispatch to mailboxes that are actually\n // REGISTERED right now. Specialists register their mailbox at spawn\n // time and unregister when done. If no team member is mid-spawn,\n // there's no mailbox to deliver to — that's fine, the user's note\n // is already in the chat thread and the goal driver will see it\n // when it next decides which subtask to schedule. Sending to an\n // unregistered mailbox would emit a noisy warn for every team\n // member every time.\n try {\n const { sendMessage, hasMailbox } = await import('./messageBus.js');\n const recipients = room.team.map(t => t.agentId);\n let delivered = 0;\n for (const to of recipients) {\n if (!hasMailbox(to)) continue; // skip non-registered to avoid noisy warns\n const result = sendMessage('user', to, content, { priority: 'urgent' });\n if (result) delivered++;\n }\n if (delivered === 0) {\n logger.debug(COMPONENT, `User message recorded in chat; no specialist mailboxes were live to receive it (team will pick up at next subtask).`);\n }\n } catch (err) {\n logger.debug(COMPONENT, `messageBus dispatch skipped: ${(err as Error).message}`);\n }\n}\n\n/**\n * v6.1.0-alpha.13 — reopen a terminal mission with a follow-up direction.\n *\n * Creates a brand-new Goal with the user's new content as the title.\n * The mission keeps its id + history; the chat thread continues\n * organically; the team strip keeps showing previous helpers (and\n * picks up new ones automatically as the new goal spawns specialists\n * via the dynamic-team logic from alpha.1).\n *\n * Re-wires the per-mission bridges (approval bridge, goal-lifecycle\n * bridge) since the previous bridges were torn down when the mission\n * first reached its terminal state. The agent-event bridge is global\n * and looks up missions by goalId at dispatch time — it picks up the\n * new goal automatically without re-registration.\n */\nasync function reopenMissionWithFollowUp(missionId: string, newContent: string): Promise<void> {\n const room = getMission(missionId);\n if (!room) return;\n try {\n ensureGlobalBusBridge();\n const newGoal = createGoal({\n title: newContent,\n description:\n `Follow-up on mission ${missionId} (continued from previous goal ${room.goalId ?? '?'}).\\n\\n` +\n `Original mission: \"${room.goal}\"\\n\\n` +\n `User wants: ${newContent}`,\n tags: [\n `mission:${missionId}`,\n room.playId ? `play:${room.playId}` : 'play:generic',\n 'mission-followup',\n ],\n // Same rationale as startMissionWork — user-initiated, not\n // autonomous; bypass the 10-goals-per-hour rate limit.\n force: true,\n });\n setLinkedGoal(missionId, newGoal.id);\n // Tear down any stale bridges (in case completion teardown\n // didn't run yet) and wire fresh ones for the new goal.\n teardownMissionWork(missionId);\n const cleanups: Array<() => void> = [];\n cleanups.push(await wireApprovalBridge(missionId, newGoal.id));\n cleanups.push(await wireGoalLifecycleBridge(missionId, newGoal.id));\n lifecycles.set(missionId, cleanups);\n\n // Wake the team back up in the team strip.\n for (const member of room.team) {\n setMemberState(missionId, member.agentId, 'idle', 'getting back on it');\n }\n\n // Chat surface: explain what just happened.\n postSystemMessage(\n missionId,\n 'Picking this back up — the team is taking another swing with your new direction.',\n 'mission_resumed',\n );\n setStatus(missionId, 'working');\n\n // Mirror to the linked Command Post issue.\n if (room.issueId) {\n try {\n updateIssue(room.issueId, { status: 'in_progress' });\n addIssueComment(\n room.issueId,\n `User picked the mission back up: \"${newContent.slice(0, 200)}${newContent.length > 200 ? '…' : ''}\"`,\n { user: 'mission-chat' },\n );\n } catch { /* mirror failure never blocks the reopen */ }\n }\n\n logger.info(\n COMPONENT,\n `Mission ${missionId} reopened — new goal ${newGoal.id} linked (was: ${room.goalId ?? 'unset'})`,\n );\n } catch (err) {\n logger.error(COMPONENT, `Reopen failed for mission ${missionId}: ${(err as Error).message}`);\n // Surface the failure to the user instead of going silent.\n try {\n postSystemMessage(\n missionId,\n `Couldn't pick this back up: ${(err as Error).message}. Try starting a fresh mission instead.`,\n 'mission_resume_failed',\n );\n } catch { /* ok */ }\n }\n}\n\n/** Called when the UI toggles status (pause / resume). */\nexport async function handleStatusChange(missionId: string, status: MissionStatus): Promise<void> {\n if (status !== 'paused' && status !== 'working') return;\n try {\n // Look up the linked goal and toggle its driver state via the\n // existing user-controls surface. We import dynamically so the\n // lifecycle module doesn't have a top-level goal-driver\n // dependency (matches the rest of the file's pattern).\n const room = (await import('./missionRoom.js')).getMission(missionId);\n if (!room?.goalId) return;\n const driver = await import('./goalDriver.js');\n if (status === 'paused' && typeof driver.pauseDriver === 'function') {\n driver.pauseDriver(room.goalId);\n } else if (status === 'working' && typeof driver.resumeDriverControl === 'function') {\n driver.resumeDriverControl(room.goalId);\n }\n } catch (err) {\n logger.debug(COMPONENT, `Couldn't toggle driver state for ${missionId}: ${(err as Error).message}`);\n }\n}\n\n/** Tear down a mission's bridges. Called when the goal completes,\n * fails, or the mission is deleted. */\nexport function teardownMissionWork(missionId: string): void {\n const cleanups = lifecycles.get(missionId);\n if (!cleanups) return;\n for (const fn of cleanups) {\n try { fn(); }\n catch (err) { logger.debug(COMPONENT, `Cleanup threw: ${(err as Error).message}`); }\n }\n lifecycles.delete(missionId);\n}\n\n/**\n * v6.1.0-alpha.25 — re-attach event/approval/goal-lifecycle bridges\n * for missions that were already in-flight when the service was\n * restarted.\n *\n * **The bug this fixes (caught 2026-05-13):**\n *\n * The lifecycle bridges (`ensureGlobalBusBridge`, `wireApprovalBridge`,\n * `wireGoalLifecycleBridge`) live as in-memory subscriptions on the\n * agent-event bus + Command Post approval store + titanEvents bus.\n * They're attached only from `startMissionWork()` (new missions) and\n * `reopenMissionWithFollowUp()` (user reopens a stopped mission).\n *\n * On a service restart, those module-level subscriptions are gone.\n * Missions on disk in `status: working | forming | blocked` whose\n * goal driver keeps running (driver state is persisted; spawns\n * continue from where they left off) silently emit events into the\n * void — no listener catches them. From the user's POV the desk\n * shows \"Writer is working\" but no agent_done message ever lands,\n * the artifact paper stays blank, the cost stays $0.00.\n *\n * Hit Tony with the MLK essay mission on 2026-05-13 21:17 PT: the\n * spawn started seconds after the alpha.24 deploy restarted the\n * service. Writer talked to Ollama for 25s, returned needs_info,\n * and the lifecycle dropped the event because no bridge was\n * attached for that mission's goalId.\n *\n * **The fix:**\n *\n * Call this on server bootstrap. It scans every persisted mission,\n * and for each one still in a non-terminal status with a linked\n * `goalId`:\n * 1. Calls `ensureGlobalBusBridge()` (idempotent — only attaches\n * the global agent-event listener once).\n * 2. Wires the per-mission approval + goal-lifecycle bridges and\n * tracks the cleanups in the lifecycles map so\n * `teardownMissionWork()` still works.\n *\n * Returns the count of missions re-attached for logging.\n */\nexport async function reattachMissionBridgesOnStartup(): Promise<number> {\n let count = 0;\n let scanned = 0;\n try {\n const missions = listMissions();\n for (const m of missions) {\n scanned += 1;\n // Skip missions that have reached terminal state — their\n // driver is dormant, no events expected.\n if (m.status === 'done' || m.status === 'failed') continue;\n // Skip missions never linked to a goal.\n if (!m.goalId) continue;\n // Skip if we've already wired this mission this process\n // (defensive — shouldn't happen on startup but harmless).\n if (lifecycles.has(m.id)) continue;\n try {\n ensureGlobalBusBridge();\n const cleanups: Array<() => void> = [];\n cleanups.push(await wireApprovalBridge(m.id, m.goalId));\n cleanups.push(await wireGoalLifecycleBridge(m.id, m.goalId));\n lifecycles.set(m.id, cleanups);\n count += 1;\n logger.info(COMPONENT, `Re-attached lifecycle bridges for mission ${m.id} (goal ${m.goalId}, status ${m.status})`);\n } catch (err) {\n logger.warn(COMPONENT, `Re-attach failed for mission ${m.id}: ${(err as Error).message}`);\n }\n }\n if (count > 0) {\n logger.info(COMPONENT, `Startup re-attach complete — ${count}/${scanned} mission(s) wired back to the bus`);\n } else if (scanned > 0) {\n logger.debug(COMPONENT, `Startup re-attach — ${scanned} mission(s) scanned, none needed re-wiring`);\n }\n } catch (err) {\n logger.warn(COMPONENT, `Startup re-attach scan threw: ${(err as Error).message}`);\n }\n return count;\n}\n\n// ── Issue mirror ───────────────────────────────────────────────────\n//\n// v6.1.0-alpha.10 — mirror big mission lifecycle events to the linked\n// Command Post issue. Keeps the issue useful as the durable audit\n// trail without spamming it with every chat message (chat thread\n// already serves that purpose). What we mirror:\n//\n// - Mission start (in startMissionWork): \"Mission opened with team: …\"\n// - Question raised: `Sage asked: \"<question>\"`\n// - Question answered: `User answered: \"<answer>\"`\n// - Mission complete / failed: the same one-liner posted to chat,\n// plus an issue status update (done / cancelled).\n//\n// Every individual agent_message is NOT mirrored — too noisy.\n// Subtask-grain events stay in the chat thread.\n\ninterface IssueMirror {\n status?: 'in_progress' | 'in_review' | 'done' | 'blocked' | 'cancelled';\n comment?: string;\n}\n\nfunction mirrorToIssue(missionId: string, mirror: IssueMirror): void {\n try {\n const room = getMission(missionId);\n const issueId = room?.issueId;\n if (!issueId) return;\n if (mirror.comment) {\n addIssueComment(issueId, mirror.comment, { user: 'mission-chat' });\n }\n if (mirror.status) {\n updateIssue(issueId, { status: mirror.status });\n }\n } catch (err) {\n logger.debug(COMPONENT, `Issue mirror skipped: ${(err as Error).message}`);\n }\n}\n\n// ── Bridges ────────────────────────────────────────────────────────\n\n/**\n * Subscribe to goal-lifecycle events on the titanEvents bus and\n * translate goal completion / failure into:\n * 1. a system message in the mission thread (\"Mission complete.\" or\n * \"Couldn't finish this one — here's what we have so far.\")\n * 2. a status transition (working → done | failed)\n *\n * Without this, the mission room sat in 'working' indefinitely after\n * the underlying goal completed — the chat showed an idle team strip\n * and no closure message, which looked like the mission was stuck even\n * when it had successfully finished.\n */\nasync function wireGoalLifecycleBridge(missionId: string, goalId: string): Promise<() => void> {\n const { titanEvents } = await import('./daemon.js');\n const completedHandler = (data: unknown) => {\n try {\n const d = (data ?? {}) as Record<string, unknown>;\n if (d.goalId !== goalId) return;\n const durationMs = typeof d.durationMs === 'number' ? d.durationMs : 0;\n const seconds = Math.max(1, Math.round(durationMs / 1000));\n const specialistsUsed = Array.isArray(d.specialistsUsed) ? (d.specialistsUsed as string[]) : [];\n const namesList = specialistsUsed.length > 0\n ? ` with help from ${specialistsUsed.slice(0, 5).join(', ')}`\n : '';\n const completionLine = `Mission complete in ${seconds}s${namesList}.`;\n postSystemMessage(missionId, completionLine, 'mission_complete');\n setStatus(missionId, 'done');\n // v6.1.0-alpha.10 — mirror to the linked Command Post issue.\n mirrorToIssue(missionId, { status: 'done', comment: completionLine });\n // Mission reached a terminal state — tear down its bridges so\n // we don't leak event-bus listeners. Defer one tick so any\n // remaining synchronous work inside this handler chain runs\n // before the bridges go away.\n setImmediate(() => teardownMissionWork(missionId));\n } catch (err) {\n logger.debug(COMPONENT, `goal:completed handler threw: ${(err as Error).message}`);\n }\n };\n const failedHandler = (data: unknown) => {\n try {\n const d = (data ?? {}) as Record<string, unknown>;\n if (d.goalId !== goalId) return;\n const retries = typeof d.retries === 'number' ? d.retries : 0;\n const failureLine = retries > 0\n ? `Couldn't finish this one after ${retries} retry attempt(s). Take a look at what we did manage and tell me what to try next.`\n : `Couldn't finish this one. Take a look at what we did manage and tell me what to try next.`;\n postSystemMessage(missionId, failureLine, 'mission_failed');\n setStatus(missionId, 'failed');\n mirrorToIssue(missionId, { status: 'cancelled', comment: failureLine });\n setImmediate(() => teardownMissionWork(missionId));\n } catch (err) {\n logger.debug(COMPONENT, `goal:failed handler threw: ${(err as Error).message}`);\n }\n };\n titanEvents.on('goal:completed', completedHandler);\n titanEvents.on('goal:failed', failedHandler);\n return () => {\n titanEvents.off('goal:completed', completedHandler);\n titanEvents.off('goal:failed', failedHandler);\n };\n}\n\n/** Subscribe to the Command Post approval queue and translate any\n * approval filed against this mission's goal into an inline question\n * message. v6.1.0-alpha.1 — uses the real titanEvents bus\n * ('commandpost:approval:created' event added in commandPost.ts).\n * Returns an unsubscribe. */\nasync function wireApprovalBridge(missionId: string, goalId: string): Promise<() => void> {\n const { titanEvents } = await import('./daemon.js');\n const handler = (approval: CPApprovalLike) => {\n try {\n // Filter: only approvals tied to this mission's goal.\n const payload = (approval.payload ?? {}) as Record<string, unknown>;\n if (payload.goalId !== goalId) return;\n const agentId = String(payload.specialist ?? payload.subtaskKind ?? approval.requestedBy ?? 'sage').toLowerCase();\n const rawContent = String(\n payload.question\n ?? payload.allQuestions\n ?? approval.title\n ?? approval.reason\n ?? `${approval.type} approval needed`\n );\n // Strip the goalDriver boilerplate prefix (alpha.5).\n const stripped = stripDriverBoilerplate(rawContent);\n // v6.1.0-alpha.29 — when stripping leaves a generic fallback\n // (or anything ≤ that fallback's information density), enrich\n // the question with payload context so the user actually knows\n // what's happening. Pre-alpha.29 the user saw a bare \"I need\n // more direction to keep going. What should I focus on?\" with\n // no clue that Writer was stuck on the MLK essay because the\n // structured-output reformat failed.\n const content = enrichBlockedQuestion(stripped, payload);\n const quickReplies = Array.isArray(payload.quickReplies)\n ? (payload.quickReplies as string[]).slice(0, 4)\n : defaultQuickReplies(approval);\n raiseQuestion({\n missionId,\n agentId,\n content,\n approvalId: approval.id,\n quickReplies,\n });\n // v6.1.0-alpha.10 — mirror to issue audit trail.\n mirrorToIssue(missionId, {\n comment: `${agentId} asked: \"${content.slice(0, 200)}${content.length > 200 ? '…' : ''}\"`,\n status: 'blocked',\n });\n } catch (err) {\n logger.debug(COMPONENT, `Approval bridge handler threw: ${(err as Error).message}`);\n }\n };\n titanEvents.on('commandpost:approval:created', handler);\n return () => titanEvents.off('commandpost:approval:created', handler);\n}\n\n/**\n * v6.1.0-alpha.29 — when stripDriverBoilerplate leaves us with the\n * generic fallback (\"I need more direction to keep going. What should\n * I focus on?\"), the user has zero signal about WHY the agent is\n * stuck or what they were trying to do. The approval payload carries\n * the missing context (subtaskTitle, specialist, lastError, attempts);\n * this composer weaves it into a friendlier question.\n *\n * Decision matrix:\n * 1. If `content` already reads like a specific question from the\n * specialist (>= 30 chars AND not equal to the generic fallback),\n * leave it alone — the specialist gave us something real.\n * 2. Otherwise build a contextual fallback from `payload`:\n * \"<Specialist> was working on <subtaskTitle> (attempt N).\n * Last hurdle: <lastError, scrubbed>.\n * What should they focus on?\"\n * Empty fields are dropped gracefully so a missing `lastError`\n * doesn't produce \"Last hurdle: undefined.\"\n * 3. Internal-error traces in `lastError` (Parser could not extract\n * JSON, HTTP 429, <!doctype html>, etc.) get scrubbed via\n * `looksLikeInternalErrorTrace` — they're not useful to the user\n * and reading them as \"what went wrong\" would be misleading.\n */\nfunction enrichBlockedQuestion(stripped: string, payload: Record<string, unknown>): string {\n const GENERIC = 'I need more direction to keep going. What should I focus on?';\n const isGeneric = stripped === GENERIC || stripped.trim().length < 30;\n if (!isGeneric) return stripped;\n\n const specialist = typeof payload.specialist === 'string' ? capitalize(payload.specialist) : 'Your helper';\n const subtaskTitle = typeof payload.subtaskTitle === 'string' ? payload.subtaskTitle : null;\n const attempts = typeof payload.attempts === 'number' ? payload.attempts : null;\n const rawLastError = typeof payload.lastError === 'string' ? payload.lastError : null;\n // Scrub internal traces — they're noise.\n const lastError = rawLastError && !looksLikeInternalErrorTrace(rawLastError)\n ? rawLastError.slice(0, 200).trim()\n : null;\n\n const parts: string[] = [];\n if (subtaskTitle) {\n parts.push(`${specialist} is working on \"${shortenLine(subtaskTitle, 80)}\"`);\n } else {\n parts.push(`${specialist} is working on your mission`);\n }\n if (attempts && attempts > 1) {\n parts[parts.length - 1] += ` (attempt ${attempts})`;\n }\n parts[parts.length - 1] += ' but got stuck.';\n\n if (lastError) {\n parts.push(`Last hurdle: ${lastError}${rawLastError && rawLastError.length > 200 ? '…' : ''}`);\n } else if (rawLastError) {\n // We scrubbed it as an internal trace — give the user a heads-up\n // it exists without dumping the trace itself.\n parts.push(`There was a technical hiccup the team couldn't recover from on their own.`);\n }\n\n parts.push(`What should they focus on? Pick a reply below — or type your own.`);\n return parts.join(' ');\n}\n\nfunction capitalize(s: string): string {\n if (!s) return s;\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nfunction shortenLine(s: string, max: number): string {\n if (s.length <= max) return s;\n return s.slice(0, max - 1).trimEnd() + '…';\n}\n\n/**\n * Strip the goalDriver's auto-generated boilerplate from blocked-approval\n * question text so it reads like a person's question. The driver wraps\n * the specialist's actual question (or a fallback) with internal context\n * (subtask title, attempt count, specialist id) that's noise to the user.\n *\n * Patterns matched:\n * - `Goal \"...\" is stuck on subtask \"...\" (attempt N, specialist: X).\\n\\n`\n * - `Goal \"...\" — subtask \"...\" failed after N attempt(s) with specialist X.\\n\\n`\n * - `Goal \"...\" — subtask \"...\" is blocked after N attempt(s) with specialist X. The specialist could not complete the task and needs guidance on how to proceed.`\n */\nexport function stripDriverBoilerplate(text: string): string {\n if (!text || typeof text !== 'string') return text ?? '';\n let cleaned = text;\n // Pattern 1: prefix → \"Goal '...' is stuck on subtask '...' (...).\\n\\n<real question>\"\n cleaned = cleaned.replace(\n /^Goal\\s+\"[^\"]*\"\\s+is\\s+stuck\\s+on\\s+subtask\\s+\"[^\"]*\"\\s+\\([^)]*\\)\\.\\s*\\n+/i,\n '',\n );\n // Pattern 2: prefix → \"Goal '...' — subtask '...' failed after N attempt(s) with specialist X.\\n\\nError: ...\"\n cleaned = cleaned.replace(\n /^Goal\\s+\"[^\"]*\"\\s+—\\s+subtask\\s+\"[^\"]*\"\\s+failed\\s+after\\s+\\d+\\s+attempt\\(s\\)\\s+with\\s+specialist\\s+\\S+\\.\\s*\\n+/i,\n '',\n );\n // Pattern 3: fallback (no real question, just the driver's placeholder)\n cleaned = cleaned.replace(\n /^Goal\\s+\"[^\"]*\"\\s+—\\s+subtask\\s+\"[^\"]*\"\\s+is\\s+blocked\\s+after\\s+\\d+\\s+attempt\\(s\\)\\s+with\\s+specialist\\s+\\S+\\.\\s*The\\s+specialist\\s+could\\s+not\\s+complete\\s+the\\s+task\\s+and\\s+needs\\s+guidance\\s+on\\s+how\\s+to\\s+proceed\\.?\\s*/i,\n '',\n );\n cleaned = cleaned.trim();\n // If we stripped everything, fall back to a friendly default — the\n // user shouldn't see an empty question.\n if (cleaned.length === 0) {\n return 'I need more direction to keep going. What should I focus on?';\n }\n return cleaned;\n}\n\n/**\n * Detect internal error-chain text that should NOT be shown to the user\n * as the specialist's \"reasoning.\" Examples seen in the wild post-alpha.5:\n *\n * `Parser could not extract JSON from specialist response. Raw (200 chars):\n * Sub-agent error: All providers failed: Provider ollama/glm-5:cloud\n * failed: [HTTP 429] Ollama error (429): <!doctype html>...`\n *\n * The bridge scrubs these strings and falls through to the friendly\n * fallback (\"I ran into trouble on this one and couldn't finish — handing\n * back to the team.\") rather than dumping a stack trace into the chat.\n * The raw text is preserved on the message's `meta.failureDetail` so it\n * stays available via the click-to-expand details panel.\n */\nexport function looksLikeInternalErrorTrace(text: string | null | undefined): boolean {\n if (!text || typeof text !== 'string') return false;\n const markers = [\n /Parser could not extract JSON/i,\n /Sub-agent error:/i,\n /All providers failed/i,\n /HTTP\\s+\\d{3}.*Ollama error/i,\n /Circuit breaker OPEN for/i,\n /<!doctype html>/i,\n /\\bToo Many Requests\\b/i,\n ];\n return markers.some(re => re.test(text));\n}\n\n/** Sensible default reply set for the common approval kinds. */\nfunction defaultQuickReplies(approval: CPApprovalLike): string[] {\n const payloadKind = (approval.payload as Record<string, unknown> | undefined)?.kind;\n if (payloadKind === 'driver_blocked') return ['Use your best judgment', 'Pause for me', 'Try a different angle'];\n if (payloadKind === 'self_repair') return ['Approve fix', 'Skip', 'Tell me more'];\n return ['Approve', 'Skip'];\n}\n\n// ── Types shared with the commandPost module ───────────────────────\n//\n// We don't import the full CPApproval shape because that would couple\n// the mission lifecycle to internal commandPost types that may reshape.\n// Duck-type only what we need.\n\ninterface CPApprovalLike {\n id: string;\n type?: string;\n title?: string;\n reason?: string;\n requestedBy?: string;\n payload?: Record<string, unknown>;\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\n/**\n * Bound the activity string that flows into MissionMember.currentActivity.\n *\n * v6.1.0-alpha.14 hotfix — was capping at 80 chars, which truncated mid-word\n * on long user prompts (\"Scout I want you to research AI Agents and how they\n * help bus…\") and there was no way to read the full text from the chat\n * surface. The 80-char cap was a holdover from when this was rendered in a\n * tiny tooltip; the actual typing pill in the chat has plenty of room.\n *\n * Bumped to 240 so even long goal prompts come through readable. The cap\n * exists only to bound the JSON payload size flowing through the bus and\n * SSE stream; the UI applies its own visual treatment (wrap + max-width +\n * title attribute) so even this length renders cleanly.\n */\n/**\n * v6.1.0-alpha.31 — turn a raw `tool_call` event into a friendly\n * sticky-note entry for the agent's activityLog. Returns null if the\n * tool isn't one we want to surface (silent background tools like\n * `memory_store`, `system_info`, etc. shouldn't clutter the desk).\n *\n * Each known research/work tool maps to:\n * - icon: a single emoji that reads at sticky-note scale\n * - activity: one-line summary (\"searched\", \"fetched\", \"wrote\")\n * - detail: the most informative argument value, truncated\n *\n * Unknown tools fall through to a generic \"used <name>\" — visible but\n * lightweight, so the user still sees activity even for tools we\n * haven't explicitly mapped.\n */\nfunction buildActivitySticky(\n name: string,\n args: Record<string, unknown>,\n): { icon: string; activity: string; detail?: string } | null {\n const pickStr = (...keys: string[]): string | undefined => {\n for (const k of keys) {\n const v = args[k];\n if (typeof v === 'string' && v.trim().length > 0) return v.trim();\n }\n return undefined;\n };\n const truncate = (s: string, max: number) => s.length <= max ? s : s.slice(0, max - 1).trimEnd() + '…';\n\n switch (name) {\n case 'web_search':\n case 'browser_search': {\n const query = pickStr('query', 'q', 'search');\n return { icon: '🔍', activity: 'searched the web', detail: query ? truncate(query, 100) : undefined };\n }\n case 'web_fetch':\n case 'browse_url':\n case 'web_read': {\n const url = pickStr('url', 'href');\n return { icon: '🌐', activity: 'read a page', detail: url ? truncate(url, 100) : undefined };\n }\n case 'web_browse_llm':\n case 'browser': {\n const target = pickStr('url', 'task');\n return { icon: '🌐', activity: 'browsed', detail: target ? truncate(target, 100) : undefined };\n }\n case 'write_file':\n case 'append_file':\n case 'edit_file':\n case 'apply_patch': {\n const path = pickStr('path', 'file', 'filename');\n return { icon: '✍️', activity: 'wrote a file', detail: path ? truncate(path, 100) : undefined };\n }\n case 'read_file': {\n const path = pickStr('path', 'file', 'filename');\n return { icon: '📖', activity: 'read a file', detail: path ? truncate(path, 100) : undefined };\n }\n case 'list_dir': {\n const path = pickStr('path', 'dir');\n return { icon: '📂', activity: 'listed a folder', detail: path ? truncate(path, 100) : undefined };\n }\n case 'shell':\n case 'exec':\n case 'code_exec':\n case 'execute_code': {\n const cmd = pickStr('command', 'cmd', 'code');\n return { icon: '⚙️', activity: 'ran a command', detail: cmd ? truncate(cmd, 100) : undefined };\n }\n case 'memory':\n case 'graph_remember':\n case 'memory_store': {\n const fact = pickStr('content', 'fact', 'value', 'key');\n return { icon: '💡', activity: 'memorized', detail: fact ? truncate(fact, 100) : undefined };\n }\n case 'graph_search':\n case 'graph_recall':\n case 'rag_search':\n case 'kb_search': {\n const q = pickStr('query', 'q');\n return { icon: '🧠', activity: 'recalled', detail: q ? truncate(q, 100) : undefined };\n }\n case 'screenshot':\n case 'browser_screenshot': {\n return { icon: '📷', activity: 'took a screenshot' };\n }\n case 'generate_image':\n case 'edit_image':\n case 'image_gen': {\n const prompt = pickStr('prompt', 'description');\n return { icon: '🎨', activity: 'drew an image', detail: prompt ? truncate(prompt, 100) : undefined };\n }\n case 'analyze_image':\n case 'vision': {\n return { icon: '👁️', activity: 'looked at an image' };\n }\n case 'github_repos':\n case 'github_issues':\n case 'github_prs':\n case 'github_commits':\n case 'github_files': {\n return { icon: '🐙', activity: name.replace('github_', 'checked GitHub '), detail: pickStr('repo', 'owner') };\n }\n // Silent / housekeeping tools — don't surface, keeps the desk tidy.\n case 'system_info':\n case 'current_model':\n case 'sessions_list':\n case 'sessions_history':\n case 'list_uploads':\n case 'list_active_widgets':\n case 'goal_list':\n case 'list_personas':\n case 'get_persona':\n case 'list_spaces':\n case 'interaction_log':\n case 'feedback_submit':\n return null;\n default:\n // Unknown tool — surface a generic sticky so the user still\n // sees the agent is doing something. Avoid noisy parameters.\n return { icon: '🛠️', activity: `used ${name}` };\n }\n}\n\nfunction shortenActivity(s: string | undefined): string | undefined {\n if (!s) return undefined;\n const trimmed = s.trim();\n if (trimmed.length <= 240) return trimmed;\n return trimmed.slice(0, 237).trimEnd() + '…';\n}\n"],"mappings":"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","names":[]}
|
package/dist/utils/constants.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
|
-
const TITAN_VERSION = "6.1.0-beta.
|
|
4
|
+
const TITAN_VERSION = "6.1.0-beta.4";
|
|
5
5
|
const TITAN_CODENAME = "Living Canvas";
|
|
6
6
|
const TITAN_NAME = "TITAN";
|
|
7
7
|
const TITAN_FULL_NAME = "The Intelligent Task Automation Network";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/constants.ts"],"sourcesContent":["/**\n * TITAN Constants\n */\nimport { homedir } from 'os';\nimport { join } from 'path';\n\nexport const TITAN_VERSION = '6.1.0-beta.
|
|
1
|
+
{"version":3,"sources":["../../src/utils/constants.ts"],"sourcesContent":["/**\n * TITAN Constants\n */\nimport { homedir } from 'os';\nimport { join } from 'path';\n\nexport const TITAN_VERSION = '6.1.0-beta.4';\nexport const TITAN_CODENAME = 'Living Canvas';\nexport const TITAN_NAME = 'TITAN';\nexport const TITAN_FULL_NAME = 'The Intelligent Task Automation Network';\nexport const TITAN_ASCII_LOGO = `\n╔══════════════════════════════════════════════════════╗\n║ ║\n║ ████████╗██╗████████╗ █████╗ ███╗ ██╗ ║\n║ ██║ ██║ ██║ ██╔══██╗████╗ ██║ ║\n║ ██║ ██║ ██║ ███████║██╔██╗ ██║ ║\n║ ██║ ██║ ██║ ██╔══██║██║╚██╗██║ ║\n║ ██║ ██║ ██║ ██║ ██║██║ ╚████║ ║\n║ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ║\n║ ║\n║ The Intelligent Task Automation Network ║\n║ v${TITAN_VERSION} • by Tony Elliott ║\n╚══════════════════════════════════════════════════════╝`;\n\n// Paths\n// Hunt Finding #03 (2026-04-14): honor TITAN_HOME env var if set.\n// Previously this was hardcoded to `~/.titan`, which meant:\n// - Docker containers couldn't override the config path\n// - Shared machines couldn't isolate per-user state\n// - Test fixtures couldn't run against an isolated home\n// - The systemd unit's `Environment=TITAN_HOME=...` was silently ignored\n// The env var is read once at module load (constants are resolved at import time).\n// If TITAN_HOME starts with `~/`, expand it to the user's home dir.\nfunction resolveTitanHome(): string {\n const envHome = process.env.TITAN_HOME;\n if (envHome && envHome.trim().length > 0) {\n const trimmed = envHome.trim();\n if (trimmed.startsWith('~/')) {\n return join(homedir(), trimmed.slice(2));\n }\n if (trimmed === '~') {\n return homedir();\n }\n return trimmed;\n }\n return join(homedir(), '.titan');\n}\nexport const TITAN_HOME = resolveTitanHome();\nexport const TITAN_CONFIG_PATH = join(TITAN_HOME, 'titan.json');\nexport const TITAN_DB_PATH = join(TITAN_HOME, 'titan.db');\nexport const TITAN_WORKSPACE = join(TITAN_HOME, 'workspace');\nexport const TITAN_SKILLS_DIR = join(TITAN_WORKSPACE, 'skills');\nexport const TITAN_LOGS_DIR = join(TITAN_HOME, 'logs');\nexport const TITAN_MEMORY_DIR = join(TITAN_HOME, 'memory');\n\n// Workspace prompt files (injected into agent context)\nexport const AGENTS_MD = join(TITAN_WORKSPACE, 'AGENTS.md');\nexport const SOUL_MD = join(TITAN_WORKSPACE, 'SOUL.md');\nexport const TOOLS_MD = join(TITAN_WORKSPACE, 'TOOLS.md');\nexport const TITAN_MD_FILENAME = 'TITAN.md';\nexport const AUTOPILOT_MD = join(TITAN_HOME, 'AUTOPILOT.md');\nexport const AUTOPILOT_RUNS_PATH = join(TITAN_HOME, 'autopilot-runs.jsonl');\nexport const TITAN_CREDENTIALS_DIR = join(TITAN_HOME, 'credentials');\n\n// Income & lead tracking\nexport const INCOME_LEDGER_PATH = join(TITAN_HOME, 'income-ledger.jsonl');\nexport const FREELANCE_LEADS_PATH = join(TITAN_HOME, 'freelance-leads.jsonl');\nexport const FREELANCE_PROFILE_PATH = join(TITAN_HOME, 'freelance-profile.json');\nexport const LEADS_PATH = join(TITAN_HOME, 'leads.jsonl');\nexport const TELEMETRY_EVENTS_PATH = join(TITAN_HOME, 'telemetry-events.jsonl');\nexport const SOMADRIVE_STATE_PATH = join(TITAN_HOME, 'soma-drive-state.json');\nexport const ACTIVITY_LOG_PATH = join(TITAN_HOME, 'activity-log.jsonl');\n\n// v6.1.0 — Mission Chat\nexport const MISSIONS_DIR = join(TITAN_HOME, 'missions');\nexport const PLAYS_DIR = join(TITAN_HOME, 'plays');\n\n// Gateway defaults\nexport const DEFAULT_GATEWAY_HOST = '0.0.0.0';\nexport const DEFAULT_GATEWAY_PORT = 48420;\nexport const DEFAULT_WEB_PORT = 48421;\n\n// Agent defaults\n// v6.0.1 — Hardcoded floor only. The actual default at runtime is picked\n// by `getDefaultModelId()` in `src/providers/defaultModel.ts`, which\n// detects the user's available API keys (Anthropic / OpenAI / Google /\n// OpenRouter) and selects accordingly. Falls back to local Ollama when\n// no cloud keys are set. This constant is the last-resort fallback for\n// callsites that don't go through the schema thunk.\nexport const DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\n/** v5.4.1: User-preference ceiling. Providers clamp per-model via\n * clampMaxTokens() so this can be high without causing 400s on\n * capped endpoints (e.g. Claude Sonnet 4 8K, Cohere 4K). */\nexport const DEFAULT_MAX_TOKENS = 200000;\nexport const DEFAULT_TEMPERATURE = 0.7;\nexport const MAX_CONTEXT_MESSAGES = 50;\nexport const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n\n// Security\nexport const DEFAULT_SANDBOX_MODE = 'host';\n/** Default allowed tools. Empty = allow ALL registered tools.\n * Use security.deniedTools to block specific tools instead. */\nexport const ALLOWED_TOOLS_DEFAULT: string[] = [];\nexport const DENIED_TOOLS_DEFAULT: string[] = [];\n"],"mappings":";AAGA,SAAS,eAAe;AACxB,SAAS,YAAY;AAEd,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AACvB,MAAM,aAAa;AACnB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAW1B,aAAa;AAAA;AAYnB,SAAS,mBAA2B;AAChC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,WAAW,QAAQ,KAAK,EAAE,SAAS,GAAG;AACtC,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,QAAQ,WAAW,IAAI,GAAG;AAC1B,aAAO,KAAK,QAAQ,GAAG,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC3C;AACA,QAAI,YAAY,KAAK;AACjB,aAAO,QAAQ;AAAA,IACnB;AACA,WAAO;AAAA,EACX;AACA,SAAO,KAAK,QAAQ,GAAG,QAAQ;AACnC;AACO,MAAM,aAAa,iBAAiB;AACpC,MAAM,oBAAoB,KAAK,YAAY,YAAY;AACvD,MAAM,gBAAgB,KAAK,YAAY,UAAU;AACjD,MAAM,kBAAkB,KAAK,YAAY,WAAW;AACpD,MAAM,mBAAmB,KAAK,iBAAiB,QAAQ;AACvD,MAAM,iBAAiB,KAAK,YAAY,MAAM;AAC9C,MAAM,mBAAmB,KAAK,YAAY,QAAQ;AAGlD,MAAM,YAAY,KAAK,iBAAiB,WAAW;AACnD,MAAM,UAAU,KAAK,iBAAiB,SAAS;AAC/C,MAAM,WAAW,KAAK,iBAAiB,UAAU;AACjD,MAAM,oBAAoB;AAC1B,MAAM,eAAe,KAAK,YAAY,cAAc;AACpD,MAAM,sBAAsB,KAAK,YAAY,sBAAsB;AACnE,MAAM,wBAAwB,KAAK,YAAY,aAAa;AAG5D,MAAM,qBAAqB,KAAK,YAAY,qBAAqB;AACjE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,yBAAyB,KAAK,YAAY,wBAAwB;AACxE,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,MAAM,wBAAwB,KAAK,YAAY,wBAAwB;AACvE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,oBAAoB,KAAK,YAAY,oBAAoB;AAG/D,MAAM,eAAe,KAAK,YAAY,UAAU;AAChD,MAAM,YAAY,KAAK,YAAY,OAAO;AAG1C,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AASzB,MAAM,gBAAgB;AAItB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB,KAAK,KAAK;AAGrC,MAAM,uBAAuB;AAG7B,MAAM,wBAAkC,CAAC;AACzC,MAAM,uBAAiC,CAAC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "titan-agent",
|
|
3
|
-
"version": "6.1.0-beta.
|
|
3
|
+
"version": "6.1.0-beta.4",
|
|
4
4
|
"description": "TITAN — Autonomous AI agent framework with self-improvement, multi-agent orchestration, 36 LLM providers, 16 channel adapters, GPU VRAM management, mesh networking, LiveKit voice, TITAN-Soma homeostatic drives, and a React Mission Control dashboard. Open-source, TypeScript, MIT licensed.",
|
|
5
5
|
"author": "Tony Elliott (https://github.com/Djtony707)",
|
|
6
6
|
"repository": {
|