titan-agent 6.0.0-beta.3 → 6.0.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.
Files changed (128) hide show
  1. package/dist/gateway/server.js +213 -0
  2. package/dist/gateway/server.js.map +1 -1
  3. package/dist/utils/constants.js +1 -1
  4. package/dist/utils/constants.js.map +1 -1
  5. package/package.json +1 -1
  6. package/ui/dist/assets/{AuditPanel-B6ByeY9s.js → AuditPanel-CPVcj_sn.js} +1 -1
  7. package/ui/dist/assets/{AutonomyPanel-DFbEUiXa.js → AutonomyPanel-DwOnaZWJ.js} +1 -1
  8. package/ui/dist/assets/{AutopilotPanel-DfY429Lo.js → AutopilotPanel-BQmYGQsc.js} +1 -1
  9. package/ui/dist/assets/{AutoresearchPanel-CuezuEdT.js → AutoresearchPanel-BRRcY5qr.js} +1 -1
  10. package/ui/dist/assets/{BackupPanel-CqbapnjY.js → BackupPanel-C51blwcY.js} +1 -1
  11. package/ui/dist/assets/{BrowserPanel-Dt5YewRB.js → BrowserPanel-ChSEMKKd.js} +1 -1
  12. package/ui/dist/assets/{CPActivity-CSnv3CqO.js → CPActivity-DEjzeQov.js} +1 -1
  13. package/ui/dist/assets/{CPAgentDetail-s4IJvapx.js → CPAgentDetail-DtZ87Opt.js} +1 -1
  14. package/ui/dist/assets/{CPAgents-VWubCERb.js → CPAgents-DZUmr3a-.js} +1 -1
  15. package/ui/dist/assets/{CPApprovals-rT9Lszle.js → CPApprovals-XuO0NR7S.js} +1 -1
  16. package/ui/dist/assets/{CPCosts-D9Smzdgc.js → CPCosts-Bezjuvzn.js} +1 -1
  17. package/ui/dist/assets/{CPDashboard-BHUoLz2x.js → CPDashboard-_IAIdSff.js} +1 -1
  18. package/ui/dist/assets/{CPFiles-gGieOziV.js → CPFiles-AITFlYn_.js} +1 -1
  19. package/ui/dist/assets/{CPGoals-DVnDs_V-.js → CPGoals-CQQYWMJD.js} +1 -1
  20. package/ui/dist/assets/{CPInbox-D7ogD-yT.js → CPInbox-CG1J6Gf-.js} +1 -1
  21. package/ui/dist/assets/{CPIssueDetail-BYC5HPiJ.js → CPIssueDetail-BryO7R5s.js} +1 -1
  22. package/ui/dist/assets/{CPIssues-peJUlaZe.js → CPIssues-BbFgqcN1.js} +1 -1
  23. package/ui/dist/assets/{CPLayout-B6dAtmzO.js → CPLayout-dsdLJtko.js} +3 -3
  24. package/ui/dist/assets/{CPOrg-Bf0V4v9D.js → CPOrg-BfwPVKpP.js} +1 -1
  25. package/ui/dist/assets/{CPRuns-CKJVqML1.js → CPRuns-CuHv9-eT.js} +1 -1
  26. package/ui/dist/assets/{CPSocial-DLqeJMzs.js → CPSocial-D_HXpPe_.js} +1 -1
  27. package/ui/dist/assets/{ChannelsPanel-COoqA79b.js → ChannelsPanel-JGjBm7Mb.js} +1 -1
  28. package/ui/dist/assets/{CheckpointsPanel-DZkPfYie.js → CheckpointsPanel-D1ZD2ELs.js} +1 -1
  29. package/ui/dist/assets/{CommandPostHub-Bztgzaq5.js → CommandPostHub-ckrtuPWP.js} +3 -3
  30. package/ui/dist/assets/{CronPanel-DX0JDeNF.js → CronPanel-CyvmHVno.js} +1 -1
  31. package/ui/dist/assets/{DataTable-CYoW9s8a.js → DataTable-D6s0Au-O.js} +1 -1
  32. package/ui/dist/assets/{DreamPanel-CvLxLUm_.js → DreamPanel-BVO4j6FH.js} +1 -1
  33. package/ui/dist/assets/{EmptyState-BVJs3cdU.js → EmptyState-ulMIMC-H.js} +1 -1
  34. package/ui/dist/assets/{EvalHarnessPanel-B3HkI4DS.js → EvalHarnessPanel-33IMVrPR.js} +1 -1
  35. package/ui/dist/assets/{EvalPanel-Bi2FLZzb.js → EvalPanel-BEUiBENA.js} +1 -1
  36. package/ui/dist/assets/{FilesPanel-SYm03-oB.js → FilesPanel-C_ZSnIeH.js} +1 -1
  37. package/ui/dist/assets/{FleetPanel-6JQMVl9S.js → FleetPanel-FTra3baZ.js} +1 -1
  38. package/ui/dist/assets/{HomelabPanel-D06cZR0f.js → HomelabPanel--LdphJX0.js} +1 -1
  39. package/ui/dist/assets/InfraView-a93xZCwF.js +2 -0
  40. package/ui/dist/assets/{InlineEditableField-jwqOGJoa.js → InlineEditableField-BYZd5PrH.js} +1 -1
  41. package/ui/dist/assets/{Input-KpZ5nc4A.js → Input-BhIm00dM.js} +1 -1
  42. package/ui/dist/assets/{IntegrationsPanel-BkCbBZHZ.js → IntegrationsPanel-Bh-JIsWK.js} +1 -1
  43. package/ui/dist/assets/IntelligenceView-CRbVnXjE.js +2 -0
  44. package/ui/dist/assets/{LearningPanel-azlN9qlH.js → LearningPanel-CuI-6kfI.js} +1 -1
  45. package/ui/dist/assets/{LogsPanel-BqZlM3Gy.js → LogsPanel-Df8YkzC6.js} +1 -1
  46. package/ui/dist/assets/{McpPanel-sgkhqXjs.js → McpPanel-T1lGJfzm.js} +1 -1
  47. package/ui/dist/assets/{MemoryGraphPanel-BUIazKR7.js → MemoryGraphPanel-aUAG4RcD.js} +1 -1
  48. package/ui/dist/assets/{MemoryWikiPanel-Bupaum4q.js → MemoryWikiPanel-CRk1tEpT.js} +1 -1
  49. package/ui/dist/assets/{MeshPanel-B82U1JCy.js → MeshPanel-DQ1-fwrq.js} +1 -1
  50. package/ui/dist/assets/{Modal-BkPE2Vkg.js → Modal-Dsq1jx9W.js} +1 -1
  51. package/ui/dist/assets/{NvidiaPanel-C9_2WQcw.js → NvidiaPanel-DZVC9cgj.js} +1 -1
  52. package/ui/dist/assets/{OrganismPanel-CRgtwXCk.js → OrganismPanel-D8EZ4IKx.js} +1 -1
  53. package/ui/dist/assets/{OverviewPanel-DoWSJHFa.js → OverviewPanel-BfChi02R.js} +1 -1
  54. package/ui/dist/assets/{PageHeader-Du2itazq.js → PageHeader-BL1Boc3n.js} +1 -1
  55. package/ui/dist/assets/{PersonaProfilesPanel-DmfaD0xB.js → PersonaProfilesPanel-B6mKG7_B.js} +1 -1
  56. package/ui/dist/assets/{PersonasPanel-DmTx0Liz.js → PersonasPanel-CMutcQl0.js} +1 -1
  57. package/ui/dist/assets/{RecipesPanel-CFKTNOVT.js → RecipesPanel-DqbRz9M3.js} +1 -1
  58. package/ui/dist/assets/{SecurityPanel-yO--atII.js → SecurityPanel-BsVyLspR.js} +1 -1
  59. package/ui/dist/assets/{SelfImprovePanel-D82p2MSW.js → SelfImprovePanel-BhvEyPmq.js} +1 -1
  60. package/ui/dist/assets/{SelfProposalsPanel-BVdwstVR.js → SelfProposalsPanel-B4xW1d04.js} +1 -1
  61. package/ui/dist/assets/{SessionsPanel-DSUYjyLs.js → SessionsPanel-xjaV5AiM.js} +1 -1
  62. package/ui/dist/assets/{SessionsTab-CLzmoMtf.js → SessionsTab-DrRdoB6j.js} +1 -1
  63. package/ui/dist/assets/{SettingsPanel-W5fWLJ0g.js → SettingsPanel-DYp3Pl5x.js} +1 -1
  64. package/ui/dist/assets/SettingsView-DxoNm2A6.js +2 -0
  65. package/ui/dist/assets/{SkeletonLoader-Cay6smRV.js → SkeletonLoader-C6CVf8CW.js} +1 -1
  66. package/ui/dist/assets/{SkillsPanel-suV_28aX.js → SkillsPanel-Dc1ecDcX.js} +1 -1
  67. package/ui/dist/assets/{SomaView-CxFduOGQ.js → SomaView-CUYebZkg.js} +1 -1
  68. package/ui/dist/assets/{StatCard-BYg3n_oA.js → StatCard-DkAdFSkO.js} +1 -1
  69. package/ui/dist/assets/{StatusBadge-B3u_B28U.js → StatusBadge-ecd7L8_K.js} +1 -1
  70. package/ui/dist/assets/{Tabs-Cj8gIxMR.js → Tabs-Cch3b_Z7.js} +1 -1
  71. package/ui/dist/assets/{TeamsPanel-b4Qx5KTi.js → TeamsPanel-DjmlVBhZ.js} +1 -1
  72. package/ui/dist/assets/{TelemetryPanel-CjN5WH2K.js → TelemetryPanel-B5vSSj6P.js} +1 -1
  73. package/ui/dist/assets/{TitanCanvas-CS7kRvsF.js → TitanCanvas-DzL7K-rk.js} +41 -41
  74. package/ui/dist/assets/{ToolsView-CGRNYkub.js → ToolsView-D0eCuM9b.js} +2 -2
  75. package/ui/dist/assets/{Tooltip-Ce8POzUQ.js → Tooltip-Ba5lbTyk.js} +1 -1
  76. package/ui/dist/assets/{TraceViewer-DUv0vZRq.js → TraceViewer-DRkm__ca.js} +1 -1
  77. package/ui/dist/assets/{TrainingPanel-DAO9LQM-.js → TrainingPanel-BI6SaYrn.js} +1 -1
  78. package/ui/dist/assets/{VoiceOverlay-CF0IwN0R.js → VoiceOverlay-Dr-VzZfY.js} +1 -1
  79. package/ui/dist/assets/{VramPanel-Cp4H9Xxl.js → VramPanel-Bywgmiaz.js} +1 -1
  80. package/ui/dist/assets/{WatchView-NFUf82q9.js → WatchView-BQA9czQ_.js} +1 -1
  81. package/ui/dist/assets/{WorkTab-B8ww1X4Y.js → WorkTab-BqXyP_vA.js} +1 -1
  82. package/ui/dist/assets/{WorkflowsPanel-BQMwhjCa.js → WorkflowsPanel-BhXm0zP_.js} +1 -1
  83. package/ui/dist/assets/{arrow-left-DIFIuRsJ.js → arrow-left-j58k0qV2.js} +1 -1
  84. package/ui/dist/assets/{briefcase-CAxB_U-8.js → briefcase-msy2nZHr.js} +1 -1
  85. package/ui/dist/assets/{chart-column-C2lpYDcZ.js → chart-column-CT3eWnwv.js} +1 -1
  86. package/ui/dist/assets/{check-F7ivHP88.js → check-DqAVE3Yc.js} +1 -1
  87. package/ui/dist/assets/{chevron-down-DKVRFEpi.js → chevron-down-CiorAwIj.js} +1 -1
  88. package/ui/dist/assets/{chevron-right-BHfhTPvA.js → chevron-right-rlDEUkrf.js} +1 -1
  89. package/ui/dist/assets/{chevron-up-BpBI-ayg.js → chevron-up-B7jDH3H0.js} +1 -1
  90. package/ui/dist/assets/{circle-check-big-OIVZenlI.js → circle-check-big-gX1JHrWK.js} +1 -1
  91. package/ui/dist/assets/{clock-Dy-1wXpd.js → clock-DAxdYN4L.js} +1 -1
  92. package/ui/dist/assets/{dollar-sign-BOplOhSO.js → dollar-sign-BtUG8CbV.js} +1 -1
  93. package/ui/dist/assets/{download-DEW3EXPL.js → download-BPACp-nA.js} +1 -1
  94. package/ui/dist/assets/{external-link-B5GBq_-Q.js → external-link-CHVhmhZH.js} +1 -1
  95. package/ui/dist/assets/{eye-off-r0IR_a6i.js → eye-off-amseTgDA.js} +1 -1
  96. package/ui/dist/assets/{folder-DMH6qMKv.js → folder-DLf2_Y4J.js} +1 -1
  97. package/ui/dist/assets/{funnel-s8SIZOyD.js → funnel-CZ6062y5.js} +1 -1
  98. package/ui/dist/assets/{git-branch-CF21vxD4.js → git-branch-DO4kkcq4.js} +1 -1
  99. package/ui/dist/assets/{index-C0phyJAs.js → index-DEGclTWM.js} +2 -2
  100. package/ui/dist/assets/{layers-CEQphI5n.js → layers-B7uW1DHk.js} +1 -1
  101. package/ui/dist/assets/{legacy-ByhfvWA8.js → legacy-B2mIqjOQ.js} +1 -1
  102. package/ui/dist/assets/{lightbulb-BdEQy6pp.js → lightbulb-2bMZBqGT.js} +1 -1
  103. package/ui/dist/assets/{list-todo-CCn69fwy.js → list-todo-CWbJVAZE.js} +1 -1
  104. package/ui/dist/assets/{loader-circle-BKWnHOOK.js → loader-circle-BJG8M8wL.js} +1 -1
  105. package/ui/dist/assets/{network-Comy2P5e.js → network-Te0orFjR.js} +1 -1
  106. package/ui/dist/assets/{pause-B7GkpUTy.js → pause-Dc728HXk.js} +1 -1
  107. package/ui/dist/assets/{play-D1ZUvkUu.js → play-BnOHuM93.js} +1 -1
  108. package/ui/dist/assets/{plug-CjDI6g3D.js → plug-RowS9ArB.js} +1 -1
  109. package/ui/dist/assets/{plus-Dzqxvvmw.js → plus-BiU9Od6R.js} +1 -1
  110. package/ui/dist/assets/{proxy-C_6aztI8.js → proxy-DNgwjdXa.js} +1 -1
  111. package/ui/dist/assets/{rotate-ccw-BYSbVQMg.js → rotate-ccw-DUqHGQ6w.js} +1 -1
  112. package/ui/dist/assets/{save-SKty-KeV.js → save-DJ18hq9J.js} +1 -1
  113. package/ui/dist/assets/{search-C7jlYumz.js → search-BYL2cmVY.js} +1 -1
  114. package/ui/dist/assets/{send-ChCVjnJo.js → send-BpR8W5bJ.js} +1 -1
  115. package/ui/dist/assets/{shield-check-D6grw3SN.js → shield-check-MPpFSxkm.js} +1 -1
  116. package/ui/dist/assets/{target-DBf96i46.js → target-DIGO1TYM.js} +1 -1
  117. package/ui/dist/assets/{terminal-zMgZPhJl.js → terminal-CSrgsUb8.js} +1 -1
  118. package/ui/dist/assets/{toggle-right-BmvgI5zG.js → toggle-right-DCiMleDU.js} +1 -1
  119. package/ui/dist/assets/{trash-2-t6zH5HEI.js → trash-2-DTaZu1r6.js} +1 -1
  120. package/ui/dist/assets/{trending-up-D3SLYrNk.js → trending-up-90cVQ7kp.js} +1 -1
  121. package/ui/dist/assets/{trophy-TA1dWZTZ.js → trophy-BXHxkiRs.js} +1 -1
  122. package/ui/dist/assets/{users-CUNoFrKL.js → users-BcXrss_m.js} +1 -1
  123. package/ui/dist/assets/{wrench-CLSuUa12.js → wrench-DLQUHLqE.js} +1 -1
  124. package/ui/dist/index.html +1 -1
  125. package/ui/dist/sw.js +1 -1
  126. package/ui/dist/assets/InfraView-BYB9Fnf4.js +0 -2
  127. package/ui/dist/assets/IntelligenceView-DowcYNie.js +0 -2
  128. package/ui/dist/assets/SettingsView-BR5yH0Ud.js +0 -2
@@ -1351,6 +1351,219 @@ async function startGateway(options) {
1351
1351
  res.status(500).json({ error: "time_travel_restore_failed", message: err.message });
1352
1352
  }
1353
1353
  });
1354
+ app.post("/api/mission/run", async (req, res) => {
1355
+ try {
1356
+ const description = String(req.body?.description ?? req.body?.prompt ?? "").trim();
1357
+ if (!description || description.length < 10) {
1358
+ res.status(400).json({ error: "description_required", message: "Provide a description (>=10 chars) of what TITAN should accomplish autonomously." });
1359
+ return;
1360
+ }
1361
+ const title = String(req.body?.title ?? "").trim() || description.split(/[.!?\n]/)[0].slice(0, 80);
1362
+ const priority = Math.max(1, Math.min(5, Number(req.body?.priority) || 3));
1363
+ const budgetLimit = Math.max(0.01, Math.min(50, Number(req.body?.budgetUsd) || 2));
1364
+ const tags = Array.isArray(req.body?.tags) ? req.body.tags.map(String) : ["mission", "autonomous"];
1365
+ let subtasks = void 0;
1366
+ try {
1367
+ const { spawnSubAgent } = await import("../agent/subAgent.js");
1368
+ const decomposePrompt = [
1369
+ `Decompose this autonomous mission into 3-6 concrete, independently-runnable subtasks.`,
1370
+ ``,
1371
+ `Mission: ${description}`,
1372
+ ``,
1373
+ `Return STRICT JSON: { "subtasks": [ { "title": "...", "description": "..." }, ... ] }.`,
1374
+ `Each title <= 60 chars, each description <= 200 chars.`,
1375
+ `Order subtasks dependency-aware (later ones may reference earlier output).`,
1376
+ `Do NOT add a verification subtask \u2014 the driver verifies automatically.`,
1377
+ `Do NOT wrap in markdown \u2014 pure JSON.`
1378
+ ].join("\n");
1379
+ const result = await spawnSubAgent({
1380
+ name: "mission-decomposer",
1381
+ task: decomposePrompt,
1382
+ tier: "fast",
1383
+ maxRounds: 1
1384
+ });
1385
+ const raw = (result.content || "").trim();
1386
+ const jsonStart = raw.indexOf("{");
1387
+ const jsonEnd = raw.lastIndexOf("}");
1388
+ if (jsonStart >= 0 && jsonEnd > jsonStart) {
1389
+ const parsed = JSON.parse(raw.slice(jsonStart, jsonEnd + 1));
1390
+ if (Array.isArray(parsed.subtasks) && parsed.subtasks.length >= 1) {
1391
+ subtasks = parsed.subtasks.filter((s) => typeof s?.title === "string" && typeof s?.description === "string").slice(0, 6).map((s) => ({ title: String(s.title).slice(0, 80), description: String(s.description).slice(0, 280) }));
1392
+ }
1393
+ }
1394
+ } catch (err) {
1395
+ logger.warn(COMPONENT, `Mission decomposition failed, creating goal with no subtasks: ${err.message}`);
1396
+ }
1397
+ const { createGoal } = await import("../agent/goals.js");
1398
+ const goal = createGoal({
1399
+ title,
1400
+ description,
1401
+ priority,
1402
+ budgetLimit,
1403
+ tags,
1404
+ subtasks,
1405
+ force: req.body?.force === true
1406
+ });
1407
+ logger.info(COMPONENT, `[Mission] Created mission goal "${title}" (id=${goal.id}, subtasks=${subtasks?.length ?? 0})`);
1408
+ res.json({
1409
+ ok: true,
1410
+ goal: { id: goal.id, title: goal.title, status: goal.status, priority: goal.priority, subtaskCount: goal.subtasks?.length ?? 0 },
1411
+ message: subtasks?.length ? `Mission "${title}" created with ${subtasks.length} subtasks. Driver will pick it up on the next tick (within 10s).` : `Mission "${title}" created. Decomposition skipped \u2014 add subtasks via the Goals API or chat.`
1412
+ });
1413
+ } catch (err) {
1414
+ res.status(500).json({ error: "mission_create_failed", message: err.message });
1415
+ }
1416
+ });
1417
+ app.get("/api/missions/active", async (_req, res) => {
1418
+ try {
1419
+ const { listActiveDrivers } = await import("../agent/goalDriver.js");
1420
+ const { listGoals } = await import("../agent/goals.js");
1421
+ const drivers = listActiveDrivers();
1422
+ const goals = listGoals("active");
1423
+ const goalsById = new Map(goals.map((g) => [g.id, g]));
1424
+ const missions = drivers.map((d) => {
1425
+ const goal = goalsById.get(d.goalId);
1426
+ const completed = Object.values(d.subtaskStates).filter((s) => s.verificationResult?.passed === true).length;
1427
+ const total = Object.keys(d.subtaskStates).length || (goal?.subtasks?.length ?? 0);
1428
+ return {
1429
+ goalId: d.goalId,
1430
+ title: goal?.title || "(unknown goal)",
1431
+ phase: d.phase,
1432
+ startedAt: d.startedAt,
1433
+ currentSubtaskId: d.currentSubtaskId ?? null,
1434
+ subtasks: { total, completed },
1435
+ budget: d.budget,
1436
+ blockedReason: d.blockedReason ?? null,
1437
+ lastHistory: (d.history || []).slice(-3)
1438
+ };
1439
+ });
1440
+ res.json({ count: missions.length, missions });
1441
+ } catch (err) {
1442
+ res.status(500).json({ error: "missions_active_failed", message: err.message });
1443
+ }
1444
+ });
1445
+ app.get("/api/missions/recent", async (req, res) => {
1446
+ try {
1447
+ const hours = Math.max(1, Math.min(168, parseInt(String(req.query.hours ?? "24"), 10) || 24));
1448
+ const { listAllDrivers } = await import("../agent/goalDriver.js");
1449
+ const { listGoals } = await import("../agent/goals.js");
1450
+ const all = listAllDrivers();
1451
+ const cutoff = Date.now() - hours * 60 * 60 * 1e3;
1452
+ const goalsById = new Map(listGoals().map((g) => [g.id, g]));
1453
+ const recent = all.filter((d) => ["done", "failed", "cancelled"].includes(d.phase)).filter((d) => {
1454
+ const completedAt = d.history?.[d.history.length - 1]?.at ?? d.startedAt;
1455
+ return new Date(completedAt).getTime() >= cutoff;
1456
+ }).map((d) => {
1457
+ const goal = goalsById.get(d.goalId);
1458
+ const completedAt = d.history?.[d.history.length - 1]?.at ?? d.startedAt;
1459
+ return {
1460
+ goalId: d.goalId,
1461
+ title: goal?.title || "(unknown goal)",
1462
+ phase: d.phase,
1463
+ startedAt: d.startedAt,
1464
+ completedAt,
1465
+ durationMs: new Date(completedAt).getTime() - new Date(d.startedAt).getTime(),
1466
+ subtaskCount: Object.keys(d.subtaskStates).length || (goal?.subtasks?.length ?? 0),
1467
+ budget: d.budget,
1468
+ lessonsLearned: d.retrospective?.lessonsLearned ?? []
1469
+ };
1470
+ }).sort((a, b) => a.completedAt < b.completedAt ? 1 : -1);
1471
+ res.json({ hours, count: recent.length, missions: recent });
1472
+ } catch (err) {
1473
+ res.status(500).json({ error: "missions_recent_failed", message: err.message });
1474
+ }
1475
+ });
1476
+ app.get("/api/missions/digest", async (req, res) => {
1477
+ try {
1478
+ const hours = Math.max(1, Math.min(168, parseInt(String(req.query.hours ?? "24"), 10) || 24));
1479
+ const { listAllDrivers, listActiveDrivers } = await import("../agent/goalDriver.js");
1480
+ const { listGoals } = await import("../agent/goals.js");
1481
+ const cutoff = Date.now() - hours * 60 * 60 * 1e3;
1482
+ const all = listAllDrivers();
1483
+ const active = listActiveDrivers();
1484
+ const goalsById = new Map(listGoals().map((g) => [g.id, g]));
1485
+ const completed = all.filter((d) => d.phase === "done").filter((d) => {
1486
+ const at = d.history?.[d.history.length - 1]?.at ?? d.startedAt;
1487
+ return new Date(at).getTime() >= cutoff;
1488
+ });
1489
+ const failed = all.filter((d) => d.phase === "failed").filter((d) => {
1490
+ const at = d.history?.[d.history.length - 1]?.at ?? d.startedAt;
1491
+ return new Date(at).getTime() >= cutoff;
1492
+ });
1493
+ const blocked = active.filter((d) => d.phase === "blocked");
1494
+ const totalCostUsd = [...completed, ...failed].reduce((sum, d) => sum + (d.budget?.costUsd ?? 0), 0);
1495
+ const totalTokens = [...completed, ...failed].reduce((sum, d) => sum + (d.budget?.tokensUsed ?? 0), 0);
1496
+ const lessons = completed.flatMap((d) => d.retrospective?.lessonsLearned ?? []).slice(0, 10);
1497
+ const summary = [];
1498
+ summary.push(`Last ${hours}h: ${completed.length} mission(s) completed, ${failed.length} failed, ${active.length} active, ${blocked.length} blocked for approval.`);
1499
+ if (completed.length > 0) {
1500
+ summary.push(``);
1501
+ summary.push(`Completed:`);
1502
+ for (const d of completed.slice(0, 10)) {
1503
+ const g = goalsById.get(d.goalId);
1504
+ summary.push(` \u2022 ${g?.title ?? d.goalId} (${Math.round((d.budget?.costUsd ?? 0) * 100) / 100} USD, ${Math.round(((d.history?.[d.history.length - 1]?.at ? new Date(d.history[d.history.length - 1].at).getTime() : Date.now()) - new Date(d.startedAt).getTime()) / 1e3)}s)`);
1505
+ }
1506
+ }
1507
+ if (failed.length > 0) {
1508
+ summary.push(``);
1509
+ summary.push(`Failed (need review):`);
1510
+ for (const d of failed.slice(0, 5)) {
1511
+ const g = goalsById.get(d.goalId);
1512
+ summary.push(` \u2022 ${g?.title ?? d.goalId}`);
1513
+ }
1514
+ }
1515
+ if (blocked.length > 0) {
1516
+ summary.push(``);
1517
+ summary.push(`Blocked for approval:`);
1518
+ for (const d of blocked.slice(0, 5)) {
1519
+ const g = goalsById.get(d.goalId);
1520
+ summary.push(` \u2022 ${g?.title ?? d.goalId} \u2014 ${d.blockedReason?.question ?? "(no reason given)"}`);
1521
+ }
1522
+ }
1523
+ if (active.length > 0 && blocked.length < active.length) {
1524
+ summary.push(``);
1525
+ summary.push(`In progress:`);
1526
+ for (const d of active.filter((a) => a.phase !== "blocked").slice(0, 10)) {
1527
+ const g = goalsById.get(d.goalId);
1528
+ summary.push(` \u2022 ${g?.title ?? d.goalId} [${d.phase}]`);
1529
+ }
1530
+ }
1531
+ res.json({
1532
+ hours,
1533
+ stats: {
1534
+ completed: completed.length,
1535
+ failed: failed.length,
1536
+ active: active.length,
1537
+ blocked: blocked.length,
1538
+ totalCostUsd: Math.round(totalCostUsd * 1e3) / 1e3,
1539
+ totalTokens
1540
+ },
1541
+ summaryText: summary.join("\n"),
1542
+ recentLessons: lessons
1543
+ });
1544
+ } catch (err) {
1545
+ res.status(500).json({ error: "mission_digest_failed", message: err.message });
1546
+ }
1547
+ });
1548
+ app.post("/api/mission/:id/cancel", async (req, res) => {
1549
+ try {
1550
+ const goalId = String(req.params.id || "").trim();
1551
+ if (!goalId) {
1552
+ res.status(400).json({ error: "missing_goal_id" });
1553
+ return;
1554
+ }
1555
+ const { getDriverState } = await import("../agent/goalDriver.js");
1556
+ const { updateGoal } = await import("../agent/goals.js");
1557
+ const state = getDriverState(goalId);
1558
+ if (state) {
1559
+ state.userControls.cancelRequested = true;
1560
+ }
1561
+ updateGoal(goalId, { status: "paused" });
1562
+ res.json({ ok: true, message: `Mission ${goalId} marked for cancellation. Driver will stop on the next tick.` });
1563
+ } catch (err) {
1564
+ res.status(500).json({ error: "mission_cancel_failed", message: err.message });
1565
+ }
1566
+ });
1354
1567
  app.post("/api/soma/gift", async (req, res) => {
1355
1568
  try {
1356
1569
  const userId = getUserIdFromReq(req);