titan-agent 6.0.0 → 6.0.2

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/gateway/routes/agents.ts"],"sourcesContent":["/**\n * Agents Router (Lifecycle)\n *\n * Extracted from gateway/server.ts.\n * Consolidates autopilot, goals, and daemon routes.\n */\n\nimport { Router, type Request, type Response } from 'express';\nimport logger from '../../utils/logger.js';\nimport { setupSSEFlush } from '../../utils/sseFlush.js';\nimport { loadConfig } from '../../config/config.js';\n\n// Autopilot\nimport {\n initAutopilot,\n stopAutopilot,\n runAutopilotNow,\n getAutopilotStatus,\n getRunHistory,\n setAutopilotDryRun,\n} from '../../agent/autopilot.js';\n\n// Goals\nimport {\n listGoals,\n createGoal,\n getGoal,\n deleteGoal,\n updateGoal,\n completeSubtask,\n addSubtask,\n dedupeGoalsBulk,\n} from '../../agent/goals.js';\n\n// Daemon\nimport {\n getDaemonStatus,\n pauseDaemonManual,\n resumeDaemon,\n titanEvents,\n} from '../../agent/daemon.js';\n\nconst COMPONENT = 'AgentsRouter';\n\nconst DAEMON_SSE_EVENTS = ['daemon:started', 'daemon:stopped', 'daemon:paused', 'daemon:resumed',\n 'daemon:heartbeat', 'goal:subtask:ready', 'health:ollama:down',\n 'health:ollama:degraded', 'cron:stuck',\n 'initiative:start', 'initiative:complete', 'initiative:no_progress',\n 'initiative:tool_call', 'initiative:tool_result', 'initiative:round'];\n\nexport function createLifecycleRouter(): Router {\n const router = Router();\n\n // ── Autopilot ───────────────────────────────────────────────\n router.get('/autopilot/status', (_req, res) => {\n res.json(getAutopilotStatus());\n });\n\n router.get('/autopilot/history', (req, res) => {\n const limit = parseInt(req.query.limit as string, 10) || 30;\n res.json(getRunHistory(limit));\n });\n\n router.post('/autopilot/run', async (req, res) => {\n try {\n const dryRun = typeof req.body?.dryRun === 'boolean' ? req.body.dryRun : undefined;\n const result = await runAutopilotNow({ dryRun });\n res.json(result);\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.post('/autopilot/toggle', (req, res) => {\n try {\n const cfg = loadConfig();\n const enable = typeof req.body.enabled === 'boolean' ? req.body.enabled : !cfg.autopilot.enabled;\n const dryRun = typeof req.body.dryRun === 'boolean' ? req.body.dryRun : undefined;\n\n cfg.autopilot.enabled = enable;\n if (typeof dryRun === 'boolean') {\n (cfg.autopilot as Record<string, unknown>).dryRun = dryRun;\n setAutopilotDryRun(dryRun);\n }\n\n if (enable) {\n initAutopilot(cfg);\n } else {\n stopAutopilot();\n }\n const status = getAutopilotStatus();\n res.json({ enabled: enable, dryRun: status.dryRun });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n // ── Goals API ─────────────────────────────────────────────\n\n router.get('/goals', (_req, res) => {\n res.json({ goals: listGoals() });\n });\n\n router.post('/goals', (req, res) => {\n const { title, description, subtasks, priority, tags, force } = req.body;\n if (!title) { res.status(400).json({ error: 'title is required' }); return; }\n try {\n const goal = createGoal({\n title,\n description: description || '',\n subtasks: subtasks || [],\n priority,\n tags,\n force: !!force,\n });\n res.status(201).json({ goal });\n } catch (err) {\n res.status(429).json({ error: (err as Error).message });\n }\n });\n\n router.get('/goals/dedupe', (_req, res) => {\n const result = dedupeGoalsBulk();\n res.status(200).json({ success: true, ...result });\n });\n\n router.get('/goals/:id', (req, res) => {\n const goal = getGoal(req.params.id);\n if (!goal) { res.status(404).json({ error: 'Goal not found' }); return; }\n res.json({ goal });\n });\n\n router.delete('/goals/:id', (req, res) => {\n const deleted = deleteGoal(req.params.id);\n if (!deleted) { res.status(404).json({ error: 'Goal not found' }); return; }\n res.json({ deleted: true });\n });\n\n // v4.3.1: update a goal's top-level fields (status, priority, title, description, etc.).\n // Previously the only way to pause a stuck goal was to hand-edit ~/.titan/goals.json and\n // restart the gateway — which is what we did on Titan PC to clear 3 failed Upwork goals.\n // This endpoint closes that gap so the UI \"pause\" action works end-to-end.\n router.patch('/goals/:id', (req, res) => {\n const updated = updateGoal(req.params.id, req.body || {});\n if (!updated) { res.status(404).json({ error: 'Goal not found' }); return; }\n res.json({ goal: updated });\n });\n\n router.post('/goals/:id/subtasks', (req, res) => {\n const { title, description } = req.body;\n if (!title) { res.status(400).json({ error: 'title is required' }); return; }\n const subtask = addSubtask(req.params.id, title, description || '');\n if (!subtask) { res.status(404).json({ error: 'Goal not found' }); return; }\n res.status(201).json({ subtask });\n });\n\n router.post('/goals/:id/subtasks/:sid/complete', (req, res) => {\n const ok = completeSubtask(req.params.id, req.params.sid, req.body.result || 'Completed via UI');\n if (!ok) { res.status(404).json({ error: 'Goal or subtask not found' }); return; }\n res.json({ completed: true });\n });\n\n // v4.1: retry a failed subtask — resets status, clears error, zeros retries.\n router.post('/goals/:id/subtasks/:sid/retry', async (req, res) => {\n const { retrySubtask } = await import('../../agent/goals.js');\n const ok = retrySubtask(req.params.id, req.params.sid);\n if (!ok) { res.status(404).json({ error: 'Goal or subtask not found' }); return; }\n res.json({ retried: true });\n });\n\n // v4.1: edit a subtask's title/description.\n router.patch('/goals/:id/subtasks/:sid', async (req, res) => {\n const { updateSubtask } = await import('../../agent/goals.js');\n const { title, description } = req.body || {};\n const ok = updateSubtask(req.params.id, req.params.sid, { title, description });\n if (!ok) { res.status(404).json({ error: 'Goal or subtask not found' }); return; }\n res.json({ updated: true });\n });\n\n // ── Daemon API ────────────────────────────────────────────\n\n router.get('/daemon/status', (_req, res) => {\n res.json(getDaemonStatus());\n });\n\n router.post('/daemon/stop', (_req, res) => {\n pauseDaemonManual();\n res.json({ paused: true });\n });\n\n router.post('/daemon/resume', (_req, res) => {\n resumeDaemon();\n res.json({ resumed: true });\n });\n\n router.get('/daemon/stream', (req, res) => {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n });\n const sseWrite = setupSSEFlush(res);\n\n const onEvent = (event: string, data: unknown) => {\n try { sseWrite(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`); } catch { /* client gone */ }\n };\n\n const events = DAEMON_SSE_EVENTS;\n\n // Store per-client listener references so we only remove THIS client's listeners on disconnect\n const listeners = new Map<string, (data: unknown) => void>();\n for (const evt of events) {\n const handler = (data: unknown) => onEvent(evt, data);\n listeners.set(evt, handler);\n titanEvents.on(evt, handler);\n }\n\n const keepalive = setInterval(() => {\n try { sseWrite(': keepalive\\n\\n'); } catch { /* client gone */ }\n }, 15_000);\n\n req.on('close', () => {\n clearInterval(keepalive);\n for (const [evt, handler] of listeners) {\n titanEvents.removeListener(evt, handler);\n }\n });\n });\n\n return router;\n}\n"],"mappings":";AAOA,SAAS,cAA2C;AACpD,OAAO,YAAY;AACnB,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAG3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,MAAM,YAAY;AAElB,MAAM,oBAAoB;AAAA,EAAC;AAAA,EAAkB;AAAA,EAAkB;AAAA,EAAiB;AAAA,EAC9E;AAAA,EAAoB;AAAA,EAAsB;AAAA,EAC1C;AAAA,EAA0B;AAAA,EAC1B;AAAA,EAAoB;AAAA,EAAuB;AAAA,EAC3C;AAAA,EAAwB;AAAA,EAA0B;AAAkB;AAE/D,SAAS,wBAAgC;AAC9C,QAAM,SAAS,OAAO;AAGtB,SAAO,IAAI,qBAAqB,CAAC,MAAM,QAAQ;AAC7C,QAAI,KAAK,mBAAmB,CAAC;AAAA,EAC/B,CAAC;AAED,SAAO,IAAI,sBAAsB,CAAC,KAAK,QAAQ;AAC7C,UAAM,QAAQ,SAAS,IAAI,MAAM,OAAiB,EAAE,KAAK;AACzD,QAAI,KAAK,cAAc,KAAK,CAAC;AAAA,EAC/B,CAAC;AAED,SAAO,KAAK,kBAAkB,OAAO,KAAK,QAAQ;AAChD,QAAI;AACF,YAAM,SAAS,OAAO,IAAI,MAAM,WAAW,YAAY,IAAI,KAAK,SAAS;AACzE,YAAM,SAAS,MAAM,gBAAgB,EAAE,OAAO,CAAC;AAC/C,UAAI,KAAK,MAAM;AAAA,IACjB,SAAS,GAAG;AACV,aAAO,MAAM,WAAW,mBAAoB,EAAY,OAAO,EAAE;AAAG,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iEAAiE,CAAC;AAAA,IACtK;AAAA,EACF,CAAC;AAED,SAAO,KAAK,qBAAqB,CAAC,KAAK,QAAQ;AAC7C,QAAI;AACF,YAAM,MAAM,WAAW;AACvB,YAAM,SAAS,OAAO,IAAI,KAAK,YAAY,YAAY,IAAI,KAAK,UAAU,CAAC,IAAI,UAAU;AACzF,YAAM,SAAS,OAAO,IAAI,KAAK,WAAW,YAAY,IAAI,KAAK,SAAS;AAExE,UAAI,UAAU,UAAU;AACxB,UAAI,OAAO,WAAW,WAAW;AAC/B,QAAC,IAAI,UAAsC,SAAS;AACpD,2BAAmB,MAAM;AAAA,MAC3B;AAEA,UAAI,QAAQ;AACV,sBAAc,GAAG;AAAA,MACnB,OAAO;AACL,sBAAc;AAAA,MAChB;AACA,YAAM,SAAS,mBAAmB;AAClC,UAAI,KAAK,EAAE,SAAS,QAAQ,QAAQ,OAAO,OAAO,CAAC;AAAA,IACrD,SAAS,GAAG;AACV,aAAO,MAAM,WAAW,mBAAoB,EAAY,OAAO,EAAE;AAAG,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iEAAiE,CAAC;AAAA,IACtK;AAAA,EACF,CAAC;AAID,SAAO,IAAI,UAAU,CAAC,MAAM,QAAQ;AAClC,QAAI,KAAK,EAAE,OAAO,UAAU,EAAE,CAAC;AAAA,EACjC,CAAC;AAED,SAAO,KAAK,UAAU,CAAC,KAAK,QAAQ;AAClC,UAAM,EAAE,OAAO,aAAa,UAAU,UAAU,MAAM,MAAM,IAAI,IAAI;AACpE,QAAI,CAAC,OAAO;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAG;AAAA,IAAQ;AAC5E,QAAI;AACF,YAAM,OAAO,WAAW;AAAA,QACtB;AAAA,QACA,aAAa,eAAe;AAAA,QAC5B,UAAU,YAAY,CAAC;AAAA,QACvB;AAAA,QACA;AAAA,QACA,OAAO,CAAC,CAAC;AAAA,MACX,CAAC;AACD,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC;AAAA,IAC/B,SAAS,KAAK;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,IAAc,QAAQ,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAED,SAAO,IAAI,iBAAiB,CAAC,MAAM,QAAQ;AACzC,UAAM,SAAS,gBAAgB;AAC/B,QAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,MAAM,GAAG,OAAO,CAAC;AAAA,EACnD,CAAC;AAED,SAAO,IAAI,cAAc,CAAC,KAAK,QAAQ;AACrC,UAAM,OAAO,QAAQ,IAAI,OAAO,EAAE;AAClC,QAAI,CAAC,MAAM;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAG;AAAA,IAAQ;AACxE,QAAI,KAAK,EAAE,KAAK,CAAC;AAAA,EACnB,CAAC;AAED,SAAO,OAAO,cAAc,CAAC,KAAK,QAAQ;AACxC,UAAM,UAAU,WAAW,IAAI,OAAO,EAAE;AACxC,QAAI,CAAC,SAAS;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAG;AAAA,IAAQ;AAC3E,QAAI,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,EAC5B,CAAC;AAMD,SAAO,MAAM,cAAc,CAAC,KAAK,QAAQ;AACvC,UAAM,UAAU,WAAW,IAAI,OAAO,IAAI,IAAI,QAAQ,CAAC,CAAC;AACxD,QAAI,CAAC,SAAS;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAG;AAAA,IAAQ;AAC3E,QAAI,KAAK,EAAE,MAAM,QAAQ,CAAC;AAAA,EAC5B,CAAC;AAED,SAAO,KAAK,uBAAuB,CAAC,KAAK,QAAQ;AAC/C,UAAM,EAAE,OAAO,YAAY,IAAI,IAAI;AACnC,QAAI,CAAC,OAAO;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAG;AAAA,IAAQ;AAC5E,UAAM,UAAU,WAAW,IAAI,OAAO,IAAI,OAAO,eAAe,EAAE;AAClE,QAAI,CAAC,SAAS;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAG;AAAA,IAAQ;AAC3E,QAAI,OAAO,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC;AAAA,EAClC,CAAC;AAED,SAAO,KAAK,qCAAqC,CAAC,KAAK,QAAQ;AAC7D,UAAM,KAAK,gBAAgB,IAAI,OAAO,IAAI,IAAI,OAAO,KAAK,IAAI,KAAK,UAAU,kBAAkB;AAC/F,QAAI,CAAC,IAAI;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,4BAA4B,CAAC;AAAG;AAAA,IAAQ;AACjF,QAAI,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EAC9B,CAAC;AAGD,SAAO,KAAK,kCAAkC,OAAO,KAAK,QAAQ;AAChE,UAAM,EAAE,aAAa,IAAI,MAAM,OAAO,sBAAsB;AAC5D,UAAM,KAAK,aAAa,IAAI,OAAO,IAAI,IAAI,OAAO,GAAG;AACrD,QAAI,CAAC,IAAI;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,4BAA4B,CAAC;AAAG;AAAA,IAAQ;AACjF,QAAI,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,EAC5B,CAAC;AAGD,SAAO,MAAM,4BAA4B,OAAO,KAAK,QAAQ;AAC3D,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,sBAAsB;AAC7D,UAAM,EAAE,OAAO,YAAY,IAAI,IAAI,QAAQ,CAAC;AAC5C,UAAM,KAAK,cAAc,IAAI,OAAO,IAAI,IAAI,OAAO,KAAK,EAAE,OAAO,YAAY,CAAC;AAC9E,QAAI,CAAC,IAAI;AAAE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,4BAA4B,CAAC;AAAG;AAAA,IAAQ;AACjF,QAAI,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,EAC5B,CAAC;AAID,SAAO,IAAI,kBAAkB,CAAC,MAAM,QAAQ;AAC1C,QAAI,KAAK,gBAAgB,CAAC;AAAA,EAC5B,CAAC;AAED,SAAO,KAAK,gBAAgB,CAAC,MAAM,QAAQ;AACzC,sBAAkB;AAClB,QAAI,KAAK,EAAE,QAAQ,KAAK,CAAC;AAAA,EAC3B,CAAC;AAED,SAAO,KAAK,kBAAkB,CAAC,MAAM,QAAQ;AAC3C,iBAAa;AACb,QAAI,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,EAC5B,CAAC;AAED,SAAO,IAAI,kBAAkB,CAAC,KAAK,QAAQ;AACzC,QAAI,UAAU,KAAK;AAAA,MACjB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,YAAY;AAAA,MACZ,qBAAqB;AAAA,IACvB,CAAC;AACD,UAAM,WAAW,cAAc,GAAG;AAElC,UAAM,UAAU,CAAC,OAAe,SAAkB;AAChD,UAAI;AAAE,iBAAS,UAAU,KAAK;AAAA,QAAW,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA,CAAM;AAAA,MAAG,QAAQ;AAAA,MAAoB;AAAA,IACpG;AAEA,UAAM,SAAS;AAGf,UAAM,YAAY,oBAAI,IAAqC;AAC3D,eAAW,OAAO,QAAQ;AACxB,YAAM,UAAU,CAAC,SAAkB,QAAQ,KAAK,IAAI;AACpD,gBAAU,IAAI,KAAK,OAAO;AAC1B,kBAAY,GAAG,KAAK,OAAO;AAAA,IAC7B;AAEA,UAAM,YAAY,YAAY,MAAM;AAClC,UAAI;AAAE,iBAAS,iBAAiB;AAAA,MAAG,QAAQ;AAAA,MAAoB;AAAA,IACjE,GAAG,IAAM;AAET,QAAI,GAAG,SAAS,MAAM;AACpB,oBAAc,SAAS;AACvB,iBAAW,CAAC,KAAK,OAAO,KAAK,WAAW;AACtC,oBAAY,eAAe,KAAK,OAAO;AAAA,MACzC;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../src/gateway/routes/agents.ts"],"sourcesContent":["/**\n * Agents Router (Lifecycle)\n *\n * Extracted from gateway/server.ts.\n * Consolidates autopilot, goals, and daemon routes.\n */\n\nimport { Router, type Request, type Response } from 'express';\nimport logger from '../../utils/logger.js';\nimport { setupSSEFlush } from '../../utils/sseFlush.js';\nimport { loadConfig, saveConfig } from '../../config/config.js';\n\n// Autopilot\nimport {\n initAutopilot,\n stopAutopilot,\n runAutopilotNow,\n getAutopilotStatus,\n getRunHistory,\n setAutopilotDryRun,\n} from '../../agent/autopilot.js';\n\n// Goals\nimport {\n listGoals,\n createGoal,\n getGoal,\n deleteGoal,\n updateGoal,\n completeSubtask,\n addSubtask,\n dedupeGoalsBulk,\n} from '../../agent/goals.js';\n\n// Daemon\nimport {\n getDaemonStatus,\n pauseDaemonManual,\n resumeDaemon,\n titanEvents,\n} from '../../agent/daemon.js';\n\nconst COMPONENT = 'AgentsRouter';\n\nconst DAEMON_SSE_EVENTS = ['daemon:started', 'daemon:stopped', 'daemon:paused', 'daemon:resumed',\n 'daemon:heartbeat', 'goal:subtask:ready', 'health:ollama:down',\n 'health:ollama:degraded', 'cron:stuck',\n 'initiative:start', 'initiative:complete', 'initiative:no_progress',\n 'initiative:tool_call', 'initiative:tool_result', 'initiative:round'];\n\nexport function createLifecycleRouter(): Router {\n const router = Router();\n\n // ── Autopilot ───────────────────────────────────────────────\n router.get('/autopilot/status', (_req, res) => {\n res.json(getAutopilotStatus());\n });\n\n router.get('/autopilot/history', (req, res) => {\n const limit = parseInt(req.query.limit as string, 10) || 30;\n res.json(getRunHistory(limit));\n });\n\n router.post('/autopilot/run', async (req, res) => {\n try {\n const dryRun = typeof req.body?.dryRun === 'boolean' ? req.body.dryRun : undefined;\n const result = await runAutopilotNow({ dryRun });\n res.json(result);\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.post('/autopilot/toggle', (req, res) => {\n try {\n const cfg = loadConfig();\n const enable = typeof req.body.enabled === 'boolean' ? req.body.enabled : !cfg.autopilot.enabled;\n const dryRun = typeof req.body.dryRun === 'boolean' ? req.body.dryRun : undefined;\n\n cfg.autopilot.enabled = enable;\n if (typeof dryRun === 'boolean') {\n (cfg.autopilot as Record<string, unknown>).dryRun = dryRun;\n setAutopilotDryRun(dryRun);\n }\n\n // v6.0.2 — Persist the toggle to disk. Pre-fix this endpoint\n // mutated `cfg` (a reference to the in-memory cache) but never\n // called `saveConfig`, so the change vanished on the next\n // service restart. Result: a user could explicitly disable\n // autopilot, restart the service for any reason, and find the\n // daemon firing again on its 2am cron because the on-disk\n // `autopilot.enabled: true` was reloaded as authoritative.\n try {\n saveConfig(cfg);\n } catch (err) {\n // Persistence failure is loud — the in-memory toggle still\n // takes effect for this process, but the user needs to know\n // it won't survive a restart.\n logger.warn(COMPONENT, `Autopilot toggle saved in memory but FAILED to persist: ${(err as Error).message}`);\n }\n\n if (enable) {\n initAutopilot(cfg);\n } else {\n stopAutopilot();\n }\n const status = getAutopilotStatus();\n res.json({ enabled: enable, dryRun: status.dryRun, persisted: true });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n // ── Goals API ─────────────────────────────────────────────\n\n router.get('/goals', (_req, res) => {\n res.json({ goals: listGoals() });\n });\n\n router.post('/goals', (req, res) => {\n const { title, description, subtasks, priority, tags, force } = req.body;\n if (!title) { res.status(400).json({ error: 'title is required' }); return; }\n try {\n const goal = createGoal({\n title,\n description: description || '',\n subtasks: subtasks || [],\n priority,\n tags,\n force: !!force,\n });\n res.status(201).json({ goal });\n } catch (err) {\n res.status(429).json({ error: (err as Error).message });\n }\n });\n\n router.get('/goals/dedupe', (_req, res) => {\n const result = dedupeGoalsBulk();\n res.status(200).json({ success: true, ...result });\n });\n\n router.get('/goals/:id', (req, res) => {\n const goal = getGoal(req.params.id);\n if (!goal) { res.status(404).json({ error: 'Goal not found' }); return; }\n res.json({ goal });\n });\n\n router.delete('/goals/:id', (req, res) => {\n const deleted = deleteGoal(req.params.id);\n if (!deleted) { res.status(404).json({ error: 'Goal not found' }); return; }\n res.json({ deleted: true });\n });\n\n // v4.3.1: update a goal's top-level fields (status, priority, title, description, etc.).\n // Previously the only way to pause a stuck goal was to hand-edit ~/.titan/goals.json and\n // restart the gateway — which is what we did on Titan PC to clear 3 failed Upwork goals.\n // This endpoint closes that gap so the UI \"pause\" action works end-to-end.\n router.patch('/goals/:id', (req, res) => {\n const updated = updateGoal(req.params.id, req.body || {});\n if (!updated) { res.status(404).json({ error: 'Goal not found' }); return; }\n res.json({ goal: updated });\n });\n\n router.post('/goals/:id/subtasks', (req, res) => {\n const { title, description } = req.body;\n if (!title) { res.status(400).json({ error: 'title is required' }); return; }\n const subtask = addSubtask(req.params.id, title, description || '');\n if (!subtask) { res.status(404).json({ error: 'Goal not found' }); return; }\n res.status(201).json({ subtask });\n });\n\n router.post('/goals/:id/subtasks/:sid/complete', (req, res) => {\n const ok = completeSubtask(req.params.id, req.params.sid, req.body.result || 'Completed via UI');\n if (!ok) { res.status(404).json({ error: 'Goal or subtask not found' }); return; }\n res.json({ completed: true });\n });\n\n // v4.1: retry a failed subtask — resets status, clears error, zeros retries.\n router.post('/goals/:id/subtasks/:sid/retry', async (req, res) => {\n const { retrySubtask } = await import('../../agent/goals.js');\n const ok = retrySubtask(req.params.id, req.params.sid);\n if (!ok) { res.status(404).json({ error: 'Goal or subtask not found' }); return; }\n res.json({ retried: true });\n });\n\n // v4.1: edit a subtask's title/description.\n router.patch('/goals/:id/subtasks/:sid', async (req, res) => {\n const { updateSubtask } = await import('../../agent/goals.js');\n const { title, description } = req.body || {};\n const ok = updateSubtask(req.params.id, req.params.sid, { title, description });\n if (!ok) { res.status(404).json({ error: 'Goal or subtask not found' }); return; }\n res.json({ updated: true });\n });\n\n // ── Daemon API ────────────────────────────────────────────\n\n router.get('/daemon/status', (_req, res) => {\n res.json(getDaemonStatus());\n });\n\n router.post('/daemon/stop', (_req, res) => {\n pauseDaemonManual();\n res.json({ paused: true });\n });\n\n router.post('/daemon/resume', (_req, res) => {\n resumeDaemon();\n res.json({ resumed: true });\n });\n\n router.get('/daemon/stream', (req, res) => {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n });\n const sseWrite = setupSSEFlush(res);\n\n const onEvent = (event: string, data: unknown) => {\n try { sseWrite(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`); } catch { /* client gone */ }\n };\n\n const events = DAEMON_SSE_EVENTS;\n\n // Store per-client listener references so we only remove THIS client's listeners on disconnect\n const listeners = new Map<string, (data: unknown) => void>();\n for (const evt of events) {\n const handler = (data: unknown) => onEvent(evt, data);\n listeners.set(evt, handler);\n titanEvents.on(evt, handler);\n }\n\n const keepalive = setInterval(() => {\n try { sseWrite(': keepalive\\n\\n'); } catch { /* client gone */ }\n }, 15_000);\n\n req.on('close', () => {\n clearInterval(keepalive);\n for (const [evt, handler] of listeners) {\n titanEvents.removeListener(evt, handler);\n }\n });\n });\n\n return router;\n}\n"],"mappings":"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","names":[]}
@@ -4358,6 +4358,14 @@ ${err.stack || ""}`);
4358
4358
  Channels: ${Array.from(channels.values()).map((c) => `${c.displayName} (${c.getStatus().connected ? "\u2705" : "\u274C"})`).join(", ")}`);
4359
4359
  logger.info(COMPONENT, `Skills: ${getSkills().length} loaded`);
4360
4360
  logger.info(COMPONENT, `Tools: ${getRegisteredTools().length} registered`);
4361
+ try {
4362
+ void import("../providers/defaultModel.js").then(({ pickDefaultModel }) => {
4363
+ const pick = pickDefaultModel();
4364
+ logger.info(COMPONENT, `Default model: ${pick.model} (${pick.provider}) \u2014 ${pick.reason}`);
4365
+ }).catch(() => {
4366
+ });
4367
+ } catch {
4368
+ }
4361
4369
  try {
4362
4370
  const markerPath = join(homedir(), ".titan", "install-marker.json");
4363
4371
  if (fs.existsSync(markerPath)) {