titan-agent 6.4.1 → 6.4.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 (151) hide show
  1. package/dist/agent/agent.js +61 -46
  2. package/dist/agent/agent.js.map +1 -1
  3. package/dist/agent/pipeline.js +11 -6
  4. package/dist/agent/pipeline.js.map +1 -1
  5. package/dist/skills/builtin/cron.js +7 -6
  6. package/dist/skills/builtin/cron.js.map +1 -1
  7. package/dist/utils/constants.js +1 -1
  8. package/dist/utils/constants.js.map +1 -1
  9. package/package.json +10 -2
  10. package/ui/dist/assets/{AuditPanel-B55jmQpR.js → AuditPanel-BOIeotPE.js} +1 -1
  11. package/ui/dist/assets/{AutonomyPanel-MP5tOLxX.js → AutonomyPanel-Dv8ClpL9.js} +1 -1
  12. package/ui/dist/assets/{AutopilotPanel-DB38vjfF.js → AutopilotPanel-Dv3-_6fZ.js} +1 -1
  13. package/ui/dist/assets/{AutoresearchPanel-CFvfx18N.js → AutoresearchPanel-DdLmfGpC.js} +1 -1
  14. package/ui/dist/assets/{BackupPanel-0a5SbfaS.js → BackupPanel-DYr6APwV.js} +1 -1
  15. package/ui/dist/assets/{BrowserPanel-CvOVGtTp.js → BrowserPanel-DMZf6mNn.js} +1 -1
  16. package/ui/dist/assets/{CPActivity-FlC0Ac-0.js → CPActivity-B2kmaYR9.js} +1 -1
  17. package/ui/dist/assets/{CPAgentDetail-CNiDw2g2.js → CPAgentDetail--ploJZFv.js} +1 -1
  18. package/ui/dist/assets/{CPAgents-2qeSr80C.js → CPAgents-ctC99jCR.js} +1 -1
  19. package/ui/dist/assets/{CPApprovals-D2m2dYBD.js → CPApprovals-BZHaJ6d_.js} +1 -1
  20. package/ui/dist/assets/{CPCosts-CbRtk5BJ.js → CPCosts--xJmtfEi.js} +1 -1
  21. package/ui/dist/assets/{CPDashboard-sGImoKeh.js → CPDashboard-rThYnRLi.js} +1 -1
  22. package/ui/dist/assets/{CPFiles-OVqWt7zC.js → CPFiles-IRzbOaxo.js} +1 -1
  23. package/ui/dist/assets/{CPGoals-wgG_oJF6.js → CPGoals-BQ4Kasli.js} +1 -1
  24. package/ui/dist/assets/{CPInbox-C4toHLIj.js → CPInbox-DYvoAJdg.js} +1 -1
  25. package/ui/dist/assets/{CPIssueDetail-CYVivz76.js → CPIssueDetail-D4JUVm7B.js} +1 -1
  26. package/ui/dist/assets/{CPIssues-CrYg3hHZ.js → CPIssues-C_K0zuiB.js} +1 -1
  27. package/ui/dist/assets/CPLayout-B3Lr8puc.js +2 -0
  28. package/ui/dist/assets/{CPOrg-BvU8zgIR.js → CPOrg-CAKwV8Kr.js} +1 -1
  29. package/ui/dist/assets/{CPRuns-tXHGoKnO.js → CPRuns-COUYpSBB.js} +1 -1
  30. package/ui/dist/assets/{CPSocial-Dxt58jcE.js → CPSocial-BCTgcO3V.js} +1 -1
  31. package/ui/dist/assets/{CPVoice-DBWsPDYo.js → CPVoice-D-wxXUGf.js} +1 -1
  32. package/ui/dist/assets/{ChannelsPanel-Cc1rpsDr.js → ChannelsPanel-Cx3dxaP9.js} +1 -1
  33. package/ui/dist/assets/{CheckpointsPanel-BMxuQIKg.js → CheckpointsPanel-D2ATiPXs.js} +1 -1
  34. package/ui/dist/assets/{CommandPostHub--U1dHmDw.js → CommandPostHub-DTs1EGm8.js} +3 -3
  35. package/ui/dist/assets/{CronPanel-C7uIoKAb.js → CronPanel-D0j9zDVW.js} +1 -1
  36. package/ui/dist/assets/{DataTable-DtjGbNj5.js → DataTable-CgeOprjv.js} +1 -1
  37. package/ui/dist/assets/{DreamPanel-DhjOkThA.js → DreamPanel-CJAI6Aka.js} +2 -2
  38. package/ui/dist/assets/{EmptyState-cLzelkoZ.js → EmptyState-NMpt-ODJ.js} +1 -1
  39. package/ui/dist/assets/{EvalHarnessPanel-Dm6DyPTs.js → EvalHarnessPanel-ZPXlKHaK.js} +1 -1
  40. package/ui/dist/assets/{EvalPanel-BDiX_uNI.js → EvalPanel-DpmEQQXh.js} +1 -1
  41. package/ui/dist/assets/{FileViewer-CHrDPOA5.js → FileViewer-B7Mm4cJL.js} +1 -1
  42. package/ui/dist/assets/{FilesPanel-CNCqxSjS.js → FilesPanel-CvrGQwh3.js} +1 -1
  43. package/ui/dist/assets/{FleetPanel-UOTqdrh3.js → FleetPanel-BDf9E7dQ.js} +1 -1
  44. package/ui/dist/assets/{HomelabPanel-8GyBOfWC.js → HomelabPanel-CKGR1kGN.js} +1 -1
  45. package/ui/dist/assets/InfraView-CreCQSNl.js +2 -0
  46. package/ui/dist/assets/{InlineEditableField-CFx4EOF1.js → InlineEditableField-JxqorUFO.js} +1 -1
  47. package/ui/dist/assets/{Input-CT_e2ZXD.js → Input-rfmX8eLw.js} +1 -1
  48. package/ui/dist/assets/{IntegrationsPanel-_MC_FcOZ.js → IntegrationsPanel-Ds5KCGUi.js} +1 -1
  49. package/ui/dist/assets/IntelligenceView-YcrYQTVO.js +2 -0
  50. package/ui/dist/assets/{LearningPanel-DIsuxMlC.js → LearningPanel-CZUd4UsW.js} +1 -1
  51. package/ui/dist/assets/{LogsPanel-hUcRe-Pt.js → LogsPanel-CmUBA-jG.js} +1 -1
  52. package/ui/dist/assets/{McpPanel-B2L9oapp.js → McpPanel-CVyjQ6H6.js} +1 -1
  53. package/ui/dist/assets/{MemoryGraphPanel-eVPWv4Fz.js → MemoryGraphPanel-BK-AbsbF.js} +1 -1
  54. package/ui/dist/assets/{MemoryWikiPanel-CDuJkatJ.js → MemoryWikiPanel-DkfHLaje.js} +1 -1
  55. package/ui/dist/assets/{MeshPanel-C42VpfJ5.js → MeshPanel-BCDzhBRO.js} +1 -1
  56. package/ui/dist/assets/{MissionCanvas-BMw2FCD5.js → MissionCanvas-GvSYEkjz.js} +1 -1
  57. package/ui/dist/assets/{MissionChat-C9HHYet2.js → MissionChat-DpyGbBhk.js} +1 -1
  58. package/ui/dist/assets/{MissionLibrary-D2yhbAJg.js → MissionLibrary-Dwn2vP2g.js} +1 -1
  59. package/ui/dist/assets/{MissionStart-BKeop9F5.js → MissionStart-DIYEotqX.js} +1 -1
  60. package/ui/dist/assets/{Modal-Bdo-V7t6.js → Modal-BFii_RIq.js} +1 -1
  61. package/ui/dist/assets/{NvidiaPanel-CEss--Dl.js → NvidiaPanel-BCmLv4bY.js} +1 -1
  62. package/ui/dist/assets/{OrganismPanel-CToPtDaz.js → OrganismPanel-CDMC8-yf.js} +1 -1
  63. package/ui/dist/assets/{OverviewPanel-D7IolCYo.js → OverviewPanel-C9ErRTB1.js} +1 -1
  64. package/ui/dist/assets/{PageHeader-CjDtUo8m.js → PageHeader-CawSrb9E.js} +1 -1
  65. package/ui/dist/assets/{PersonaProfilesPanel-Df5og1Sg.js → PersonaProfilesPanel-B_hx3sLQ.js} +1 -1
  66. package/ui/dist/assets/{PersonasPanel-DKr-x2AB.js → PersonasPanel-BhfAKQWd.js} +1 -1
  67. package/ui/dist/assets/{PhoneDeskPanel-CkEnpVrz.js → PhoneDeskPanel-Cew4jv5F.js} +1 -1
  68. package/ui/dist/assets/{RecipesPanel-CiQiYqpA.js → RecipesPanel-KSNQil0R.js} +1 -1
  69. package/ui/dist/assets/{SecurityPanel-nTFIdiqf.js → SecurityPanel-ojbdvuZR.js} +1 -1
  70. package/ui/dist/assets/{SelfImprovePanel-mPChEVjC.js → SelfImprovePanel-D743Dh0z.js} +1 -1
  71. package/ui/dist/assets/{SelfProposalsPanel-CXIGD8P3.js → SelfProposalsPanel-DHPrgGsJ.js} +1 -1
  72. package/ui/dist/assets/{SessionsPanel-C8nt7rxV.js → SessionsPanel-Bd8dAJQa.js} +1 -1
  73. package/ui/dist/assets/{SessionsTab-Bp5WaW_Q.js → SessionsTab-BjyOfaQ9.js} +1 -1
  74. package/ui/dist/assets/{SettingsPanel-Cg5tgqDO.js → SettingsPanel-CWvFc8MJ.js} +1 -1
  75. package/ui/dist/assets/SettingsView-Chh-hdRF.js +2 -0
  76. package/ui/dist/assets/{SkeletonLoader-DwBcLrhl.js → SkeletonLoader-sjDa7Y96.js} +1 -1
  77. package/ui/dist/assets/{SkillsPanel-CHnd8BmR.js → SkillsPanel-B0UwrZGA.js} +1 -1
  78. package/ui/dist/assets/{SomaView-1zShMywA.js → SomaView-CXfqpdNP.js} +1 -1
  79. package/ui/dist/assets/{StatCard-BUO6lNlX.js → StatCard-B4qf3WOB.js} +1 -1
  80. package/ui/dist/assets/{StatusBadge-CmSemYNT.js → StatusBadge-BtDtyL5L.js} +1 -1
  81. package/ui/dist/assets/{Tabs-Zo1JoN_G.js → Tabs-CeY7i2tk.js} +1 -1
  82. package/ui/dist/assets/{TeamsPanel-C96TIEna.js → TeamsPanel-BxFpHD58.js} +1 -1
  83. package/ui/dist/assets/{TelemetryPanel-DlI9vEZU.js → TelemetryPanel-D2L98GwC.js} +1 -1
  84. package/ui/dist/assets/{TitanCanvas-BewmO4FZ.js → TitanCanvas-D7TL368k.js} +4 -4
  85. package/ui/dist/assets/{ToolsView-BY73iyvj.js → ToolsView-BiW0mds2.js} +2 -2
  86. package/ui/dist/assets/{Tooltip-DHrJsv5K.js → Tooltip-C3exOcZa.js} +1 -1
  87. package/ui/dist/assets/{TraceViewer-BVH6WsWN.js → TraceViewer-z42wYVdE.js} +1 -1
  88. package/ui/dist/assets/{TrainingPanel-Beam9vXX.js → TrainingPanel-BPVftaSt.js} +1 -1
  89. package/ui/dist/assets/{VoiceOverlay-B03bLRa6.js → VoiceOverlay-DNzDqitY.js} +1 -1
  90. package/ui/dist/assets/{VramPanel-Cv48drRb.js → VramPanel-CF4l5fe_.js} +1 -1
  91. package/ui/dist/assets/{WatchView-VtZRF3Ah.js → WatchView-CewHUoxK.js} +1 -1
  92. package/ui/dist/assets/{WorkTab-gIgsVURN.js → WorkTab-ByC4niLi.js} +1 -1
  93. package/ui/dist/assets/{WorkflowsPanel-xGphoCgy.js → WorkflowsPanel-Bsyhjv87.js} +1 -1
  94. package/ui/dist/assets/{archive-B27XcT_v.js → archive-Cz-kvWW5.js} +1 -1
  95. package/ui/dist/assets/{arrow-left-DSU0t1ZF.js → arrow-left-CGVxTClf.js} +1 -1
  96. package/ui/dist/assets/{book-open-CCt4Gq9I.js → book-open-CrOs3smB.js} +1 -1
  97. package/ui/dist/assets/{bot-DK7dbBfX.js → bot-C0wiWyCT.js} +1 -1
  98. package/ui/dist/assets/{chart-column-CixcF9Pi.js → chart-column-Dcc3a8m5.js} +1 -1
  99. package/ui/dist/assets/{check-oX--Xdud.js → check-BhmlTEFk.js} +1 -1
  100. package/ui/dist/assets/{chevron-right-D1DyaeTm.js → chevron-right-DJCsUSwz.js} +1 -1
  101. package/ui/dist/assets/{chevron-up-uKeW050h.js → chevron-up-cs3cBR4O.js} +1 -1
  102. package/ui/dist/assets/{circle-check-big-C7KjulF6.js → circle-check-big-De2k5qVr.js} +1 -1
  103. package/ui/dist/assets/{circle-stop-DMMdkBs2.js → circle-stop-u0L9iQ69.js} +1 -1
  104. package/ui/dist/assets/{clipboard-list-C3GIPAT3.js → clipboard-list-DF1F3Vjf.js} +1 -1
  105. package/ui/dist/assets/{clock-BgvC0T_E.js → clock-Bc6IrFv8.js} +1 -1
  106. package/ui/dist/assets/{dollar-sign-DuP1ZChI.js → dollar-sign-BStL62ws.js} +1 -1
  107. package/ui/dist/assets/{download-CnPu926z.js → download-VXqww0Iy.js} +1 -1
  108. package/ui/dist/assets/{external-link-fIw1hJS5.js → external-link-BjN0VrNb.js} +1 -1
  109. package/ui/dist/assets/{file-text-kYsk6v9z.js → file-text-SIULFbzy.js} +1 -1
  110. package/ui/dist/assets/{flask-conical-CAMJ0Plo.js → flask-conical-CsShOfpS.js} +1 -1
  111. package/ui/dist/assets/{folder-Dq6luLP7.js → folder-DwOkgBGj.js} +1 -1
  112. package/ui/dist/assets/{funnel-hj2rC4MH.js → funnel-CDnA2Enn.js} +1 -1
  113. package/ui/dist/assets/{git-branch-DX_X3HtU.js → git-branch-BAFSlxgi.js} +1 -1
  114. package/ui/dist/assets/{index-BISxhHXP.js → index-BlFxPvdg.js} +2 -2
  115. package/ui/dist/assets/{layers-BseADaVO.js → layers-DKKOtsYx.js} +1 -1
  116. package/ui/dist/assets/{legacy-B7aW_gUV.js → legacy-YQ03_WIq.js} +1 -1
  117. package/ui/dist/assets/{lightbulb-DdDY0MbY.js → lightbulb-D4Syh7fA.js} +1 -1
  118. package/ui/dist/assets/{link-BZZB4JyS.js → link-C_6ueq0o.js} +1 -1
  119. package/ui/dist/assets/{list-todo-bSv3SQAa.js → list-todo-3Me4V-34.js} +1 -1
  120. package/ui/dist/assets/{message-square-D9n28WBl.js → message-square-Dv6Q3VRO.js} +1 -1
  121. package/ui/dist/assets/{mic-DsWOcLkn.js → mic-BInf3BDE.js} +1 -1
  122. package/ui/dist/assets/{missions-CmBxbp0k.js → missions-BUTRB5Lq.js} +1 -1
  123. package/ui/dist/assets/{network-CkgG0zu0.js → network-BsSfc5IE.js} +1 -1
  124. package/ui/dist/assets/{pause-CvgG1y4I.js → pause-CXCmPcHk.js} +1 -1
  125. package/ui/dist/assets/{pencil-c0GVcCA9.js → pencil-DAvceqn3.js} +1 -1
  126. package/ui/dist/assets/{play-CuwPKVSv.js → play-MuD1S1C5.js} +1 -1
  127. package/ui/dist/assets/{plug-D-b321IK.js → plug-Chv5y4No.js} +1 -1
  128. package/ui/dist/assets/{plus-IFwPqtUW.js → plus-SBD6A3Qb.js} +1 -1
  129. package/ui/dist/assets/{proxy-Dm6tNAsg.js → proxy-C5UuKDtU.js} +1 -1
  130. package/ui/dist/assets/{radio-BNdFCb41.js → radio-BySzBvYH.js} +1 -1
  131. package/ui/dist/assets/{refresh-cw-CFv1BDy9.js → refresh-cw-BdcqinTm.js} +1 -1
  132. package/ui/dist/assets/{rotate-ccw-iCpZfipQ.js → rotate-ccw-CRA_s5tz.js} +1 -1
  133. package/ui/dist/assets/{save-o5ciSOaB.js → save-BWhXC8Tv.js} +1 -1
  134. package/ui/dist/assets/{search-DmeBed-X.js → search-DF7UnWEk.js} +1 -1
  135. package/ui/dist/assets/{send-BoLKNmgS.js → send-CmvPtbvy.js} +1 -1
  136. package/ui/dist/assets/{server-Ov6rxCAc.js → server-1PubNLgw.js} +1 -1
  137. package/ui/dist/assets/{shield-check-CKPsc0rI.js → shield-check-BjuAv28a.js} +1 -1
  138. package/ui/dist/assets/{terminal-CcPGcWeJ.js → terminal-BvDfNOXl.js} +1 -1
  139. package/ui/dist/assets/{test-tube-564jDt0t.js → test-tube-CrzRsKdm.js} +1 -1
  140. package/ui/dist/assets/{toggle-right-B7gXrucx.js → toggle-right-BRJ_dULZ.js} +1 -1
  141. package/ui/dist/assets/{trash-2-B7yIJ8B5.js → trash-2-Dtxmn6GE.js} +1 -1
  142. package/ui/dist/assets/{trending-up-DdxHXTsD.js → trending-up-C7pfwW1S.js} +1 -1
  143. package/ui/dist/assets/{trophy-mttLLqgk.js → trophy-Dknrgn8P.js} +1 -1
  144. package/ui/dist/assets/{volume-2-DcpX5fnm.js → volume-2-ahkfEwxT.js} +1 -1
  145. package/ui/dist/assets/{wifi-off-B4cf3i9g.js → wifi-off-wACQ7wec.js} +1 -1
  146. package/ui/dist/index.html +1 -1
  147. package/ui/dist/sw.js +1 -1
  148. package/ui/dist/assets/CPLayout-BXcOZUo7.js +0 -2
  149. package/ui/dist/assets/InfraView-Ds53Fjni.js +0 -2
  150. package/ui/dist/assets/IntelligenceView-DHKwRkc_.js +0 -2
  151. package/ui/dist/assets/SettingsView-DosHEIoU.js +0 -2
@@ -177,16 +177,21 @@ const CLASSIFICATION_RULES = [
177
177
  type: "voice",
178
178
  test: (_msg, channel) => channel === "voice"
179
179
  },
180
- // Content creationmulti-step research + write + publish (checked BEFORE social to prevent overlap)
181
- {
182
- type: "content",
183
- test: (msg) => /\b(research.*and.*(write|post|publish|create)|write.*article|blog.*post|create.*content|draft.*report|write.*comparison)\b/i.test(msg) && msg.split(/\s+/).length > 10
184
- },
185
- // Social media Facebook posting, replies, feed management
180
+ // Social mediaFacebook posting, replies, feed management.
181
+ // v6.4.4: MOVED ABOVE content because the content rule's
182
+ // `\bwrite.*comparison\b` was matching the fb-afternoon cron prompt
183
+ // ("Write an afternoon COMPARISON / HOT TAKE for the TITAN AI Facebook
184
+ // page") and routing it to the wrong pipeline. Social is more specific
185
+ // for any prompt that mentions Facebook explicitly, so it wins now.
186
186
  {
187
187
  type: "social",
188
188
  test: (msg) => /\b(fb_|facebook|post to .*(page|facebook|fb)|share on|comment.*repl|reply.*comment|read.*feed|check.*feed|autopilot.*status|post.*about|hype|comparison.*post)\b/i.test(msg) && msg.split(/\s+/).length >= 3
189
189
  },
190
+ // Content creation — multi-step research + write + publish
191
+ {
192
+ type: "content",
193
+ test: (msg) => /\b(research.*and.*(write|post|publish|create)|write.*article|blog.*post|create.*content|draft.*report|write.*comparison)\b/i.test(msg) && msg.split(/\s+/).length > 10
194
+ },
190
195
  // Home automation — smart home, IoT
191
196
  {
192
197
  type: "automation",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agent/pipeline.ts"],"sourcesContent":["/**\n * TITAN — Task Pipeline System\n *\n * Routes tasks to specialized pipelines instead of running everything\n * through the same generic agent loop configuration. Each pipeline\n * controls: round budget, SmartExit rules, tool priorities, task enforcement,\n * and completion detection.\n *\n * Safety: if classification fails, falls back to 'general' which is\n * byte-for-byte equivalent to the pre-pipeline behavior.\n */\n\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'Pipeline';\n\n// ── Pipeline Types ──────────────────────────────────────────────────\n\nexport type PipelineType =\n | 'chat'\n | 'research'\n | 'code'\n | 'social'\n | 'content'\n | 'automation'\n | 'browser'\n | 'sysadmin'\n | 'analysis'\n | 'voice'\n | 'general';\n\nexport type CompletionStrategy =\n | 'smart-exit' // Use pipeline-specific terminal tools\n | 'no-tools' // Done when model stops calling tools\n | 'terminal-tool' // Done when a specific tool succeeds\n | 'single-round'; // One tool round, then respond\n\n// ── Pipeline Profile ────────────────────────────────────────────────\n\nexport interface PipelineProfile {\n name: string;\n type: PipelineType;\n description: string;\n\n // Round budget\n minRounds: number;\n maxRounds: number;\n\n // SmartExit configuration\n smartExitEnabled: boolean;\n terminalTools: string[];\n completionStrategy: CompletionStrategy;\n\n // Tool configuration — tools to ensure are in the active set\n ensureTools: string[];\n toolSearchEnabled: boolean;\n\n // Behavioral controls\n reflectionEnabled: boolean;\n reflectionInterval: number;\n taskEnforcement: string | null;\n}\n\n// ── Pipeline Profiles ───────────────────────────────────────────────\n\nexport const PIPELINE_PROFILES: Record<PipelineType, PipelineProfile> = {\n\n chat: {\n name: 'Chat',\n type: 'chat',\n description: 'Simple Q&A, greetings, casual conversation',\n minRounds: 0,\n maxRounds: 3,\n smartExitEnabled: true,\n terminalTools: ['weather', 'system_info', 'memory'],\n completionStrategy: 'single-round',\n ensureTools: ['memory', 'weather', 'system_info', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n taskEnforcement: null,\n },\n\n research: {\n name: 'Research',\n type: 'research',\n description: 'Web research, fact-finding, information gathering',\n minRounds: 3,\n maxRounds: 15,\n smartExitEnabled: false,\n terminalTools: [],\n completionStrategy: 'no-tools',\n ensureTools: ['web_search', 'web_fetch', 'web_act', 'memory', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 4,\n taskEnforcement: '[RESEARCH PIPELINE] You MUST call web_search at least 2 times with different queries to gather comprehensive information. After searching, use web_fetch to read the full content of the most relevant results. Do NOT answer from training data alone — the user wants current, real information.',\n },\n\n code: {\n name: 'Code',\n type: 'code',\n description: 'Code editing, debugging, implementation, refactoring',\n minRounds: 3,\n maxRounds: 12,\n smartExitEnabled: true,\n terminalTools: ['write_file', 'edit_file', 'append_file'],\n completionStrategy: 'terminal-tool',\n ensureTools: ['read_file', 'write_file', 'edit_file', 'append_file', 'list_dir', 'shell', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 3,\n taskEnforcement: '[CODE PIPELINE] Follow this sequence:\\n1. read_file to understand current code\\n2. Plan your changes\\n3. edit_file or write_file to make changes\\n4. shell to verify (run tests, build, etc.)\\nCRITICAL: Do NOT stop after reading files. You MUST call edit_file or write_file to save changes.',\n },\n\n social: {\n name: 'Social',\n type: 'social',\n description: 'Facebook posting, social media, comment replies',\n minRounds: 2,\n maxRounds: 10,\n smartExitEnabled: true,\n terminalTools: ['fb_post', 'fb_reply', 'content_publish', 'x_post'],\n completionStrategy: 'terminal-tool',\n ensureTools: ['web_search', 'web_fetch', 'fb_post', 'fb_read_feed', 'fb_reply', 'fb_review_queue', 'fb_autopilot_status', 'memory', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n taskEnforcement: '[SOCIAL PIPELINE] For posting tasks:\\n1. Research the topic first (web_search if needed)\\n2. Compose the post content\\n3. Use fb_post to publish it\\nCRITICAL: You MUST call fb_post before finishing. Do NOT just describe what you would post — actually post it.',\n },\n\n content: {\n name: 'Content Creation',\n type: 'content',\n description: 'Multi-step content: research → write → publish',\n minRounds: 5,\n maxRounds: 20,\n smartExitEnabled: true,\n terminalTools: ['write_file', 'fb_post', 'content_publish', 'x_post'],\n completionStrategy: 'terminal-tool',\n ensureTools: ['web_search', 'web_fetch', 'read_file', 'write_file', 'edit_file', 'fb_post', 'memory', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 5,\n taskEnforcement: '[CONTENT PIPELINE] This is a multi-step content creation task.\\nPhase 1 — RESEARCH: Call web_search 2-3 times with different queries. Use web_fetch on top results.\\nPhase 2 — SYNTHESIZE: Analyze what you found and draft content.\\nPhase 3 — PUBLISH: Use the appropriate tool (write_file, fb_post, content_publish) to deliver the final result.\\nDo NOT skip Phase 1. Do NOT stop after research without publishing.',\n },\n\n automation: {\n name: 'Automation',\n type: 'automation',\n description: 'Home automation, smart home, IoT control',\n minRounds: 1,\n maxRounds: 5,\n smartExitEnabled: true,\n terminalTools: ['ha_control', 'ha_setup'],\n completionStrategy: 'terminal-tool',\n ensureTools: ['ha_control', 'ha_status', 'ha_devices', 'ha_setup', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n taskEnforcement: null,\n },\n\n browser: {\n name: 'Browser',\n type: 'browser',\n description: 'Browser automation, form filling, web interaction',\n minRounds: 3,\n maxRounds: 15,\n smartExitEnabled: false,\n terminalTools: [],\n completionStrategy: 'no-tools',\n ensureTools: ['web_act', 'smart_form_fill', 'web_search', 'web_fetch', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 5,\n taskEnforcement: '[BROWSER PIPELINE] Navigate to the target page first, then interact with elements. Use web_act for clicking/typing, smart_form_fill for forms. Take screenshots to verify your actions.',\n },\n\n sysadmin: {\n name: 'System Admin',\n type: 'sysadmin',\n description: 'Shell commands, process management, deployments',\n minRounds: 2,\n maxRounds: 8,\n smartExitEnabled: true,\n terminalTools: ['shell'],\n completionStrategy: 'terminal-tool',\n ensureTools: ['shell', 'read_file', 'write_file', 'list_dir', 'system_info', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n taskEnforcement: '[SYSADMIN PIPELINE] You MUST call the shell tool to execute commands. Do NOT describe what the command would do — run it and report the actual output.',\n },\n\n analysis: {\n name: 'Analysis',\n type: 'analysis',\n description: 'Data analysis, comparison, reporting',\n minRounds: 2,\n maxRounds: 10,\n smartExitEnabled: false,\n terminalTools: [],\n completionStrategy: 'no-tools',\n ensureTools: ['web_search', 'web_fetch', 'read_file', 'shell', 'memory', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 3,\n taskEnforcement: '[ANALYSIS PIPELINE] Gather data from multiple sources before synthesizing. Use web_search and web_fetch for external data. Use read_file and shell for local data. Provide specific numbers and facts, not vague summaries.',\n },\n\n voice: {\n name: 'Voice',\n type: 'voice',\n description: 'Voice interactions (handled by existing voiceFastPath)',\n minRounds: 1,\n maxRounds: 3,\n smartExitEnabled: true,\n terminalTools: ['weather', 'system_info', 'ha_control'],\n completionStrategy: 'single-round',\n ensureTools: ['shell', 'web_search', 'weather', 'memory', 'ha_control', 'ha_devices', 'ha_status', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n taskEnforcement: null,\n },\n\n general: {\n name: 'General',\n type: 'general',\n description: 'Fallback — equivalent to pre-pipeline behavior',\n minRounds: 1,\n maxRounds: 0, // 0 = use existing dynamic budget logic\n smartExitEnabled: true,\n terminalTools: ['write_file', 'append_file', 'weather', 'system_info', 'fb_post', 'fb_reply', 'content_publish'],\n completionStrategy: 'smart-exit',\n ensureTools: [], // empty = use existing DEFAULT_CORE_TOOLS\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 3,\n taskEnforcement: null, // null = use existing scattered heuristics\n },\n};\n\n// ── Classifier ──────────────────────────────────────────────────────\n\n/**\n * Classification rules — ordered by specificity (most specific first).\n * First match wins. If nothing matches, returns 'general'.\n */\nconst CLASSIFICATION_RULES: Array<{\n type: PipelineType;\n test: (msg: string, channel: string) => boolean;\n}> = [\n // Voice is detected by channel, not message content\n {\n type: 'voice',\n test: (_msg, channel) => channel === 'voice',\n },\n\n // Content creation — multi-step research + write + publish (checked BEFORE social to prevent overlap)\n {\n type: 'content',\n test: (msg) => /\\b(research.*and.*(write|post|publish|create)|write.*article|blog.*post|create.*content|draft.*report|write.*comparison)\\b/i.test(msg)\n && msg.split(/\\s+/).length > 10,\n },\n\n // Social media — Facebook posting, replies, feed management\n {\n type: 'social',\n test: (msg) => /\\b(fb_|facebook|post to .*(page|facebook|fb)|share on|comment.*repl|reply.*comment|read.*feed|check.*feed|autopilot.*status|post.*about|hype|comparison.*post)\\b/i.test(msg)\n && msg.split(/\\s+/).length >= 3,\n },\n\n // Home automation — smart home, IoT\n {\n type: 'automation',\n test: (msg) => /\\b(ha_|home assistant|turn (on|off)|lights?|thermostat|temperature|smart home|sensor|switch|door|garage|lock|unlock|dim|brighten)\\b/i.test(msg),\n },\n\n // Browser automation — form filling, web interaction\n {\n type: 'browser',\n test: (msg) => /\\b(browse|navigate to|fill.*form|click.*button|log ?in to|sign ?in to|captcha|screenshot.*page|form[- ]fill|web.?act)\\b/i.test(msg),\n },\n\n // System administration — shell, deploy, restart, install\n {\n type: 'sysadmin',\n test: (msg) => /\\b(run|execute|install|deploy|restart|reboot|start|stop|kill|ssh|systemctl|docker|npm run|git|build|compile|rsync|sudo|chmod|chown|upgrade|shutdown|daemon)\\b/i.test(msg)\n && /\\b(command|server|service|package|app|container|cluster|node|pm2)\\b/i.test(msg),\n },\n\n // Code editing — fix, edit, implement, debug\n {\n type: 'code',\n test: (msg) => /\\b(fix|edit|change|modify|update|refactor|implement|add|remove|replace|rewrite|patch|upgrade|debug|wire|hook|create.*function|create.*class|create.*component)\\b/i.test(msg)\n && /\\b(file|code|function|class|method|module|component|\\.ts|\\.js|\\.py|\\.tsx|\\.jsx|bug|feature|error|test|endpoint|route|API|skill|tool)\\b/i.test(msg),\n },\n\n // Research — web search, fact-finding\n {\n type: 'research',\n test: (msg) => /\\b(research|investigate|find out|look up|what.*(latest|new|current|trending|happening)|compare.*framework|landscape|competitive|benchmark|how does.*work|what is|who is)\\b/i.test(msg)\n && msg.split(/\\s+/).length > 6,\n },\n\n // Analysis — data analysis, reporting, comparison\n {\n type: 'analysis',\n test: (msg) => /\\b(analyze|analysis|compare|report|breakdown|summary|summarize|overview|stats|statistics|metrics|dashboard|performance|trend|insight)\\b/i.test(msg)\n && msg.split(/\\s+/).length > 6,\n },\n\n // Chat — simple Q&A, greetings (must be SHORT messages)\n {\n type: 'chat',\n test: (msg) => {\n const words = msg.trim().split(/\\s+/).length;\n if (words > 15) return false;\n return /^(hi|hello|hey|yo|sup|what'?s up|good (morning|afternoon|evening)|thanks|thank you|ok|okay|sure|yes|no|help|how are you|who are you|what can you do)/i.test(msg.trim())\n || (words <= 8 && /^(what|who|how|why|where|when|is |are |do |does |can |will |tell me|explain)\\b/i.test(msg.trim()));\n },\n },\n];\n\n/**\n * Classify a message into a pipeline type.\n * Fast regex-based classification — zero LLM cost.\n * Falls back to 'general' if nothing matches.\n */\nexport function classifyPipeline(message: string, channel: string): PipelineType {\n try {\n for (const rule of CLASSIFICATION_RULES) {\n if (rule.test(message, channel)) {\n logger.info(COMPONENT, `Classified as \"${rule.type}\": ${message.slice(0, 80)}...`);\n return rule.type;\n }\n }\n logger.info(COMPONENT, `Classified as \"general\" (no rule matched): ${message.slice(0, 80)}...`);\n return 'general';\n } catch (err) {\n logger.warn(COMPONENT, `Classification error, falling back to general: ${(err as Error).message}`);\n return 'general';\n }\n}\n\n/**\n * Resolve pipeline configuration for the agent loop.\n * Returns overrides that processMessage should apply.\n * For 'general' pipeline, returns null — use existing logic unchanged.\n */\nexport function resolvePipelineConfig(\n pipelineType: PipelineType,\n currentMaxRounds: number,\n hardCap: number,\n): {\n minRounds: number;\n maxRounds: number;\n smartExitEnabled: boolean;\n terminalTools: string[];\n reflectionEnabled: boolean;\n reflectionInterval: number;\n taskEnforcement: string | null;\n ensureTools: string[];\n completionStrategy: CompletionStrategy;\n} | null {\n // General pipeline = no overrides, use existing behavior\n if (pipelineType === 'general') return null;\n\n const profile = PIPELINE_PROFILES[pipelineType];\n\n // Pipeline maxRounds: use profile's maxRounds, capped at hardCap\n // If profile maxRounds is 0, use the existing dynamic budget\n const maxRounds = profile.maxRounds > 0\n ? Math.min(profile.maxRounds, hardCap)\n : currentMaxRounds;\n\n return {\n minRounds: profile.minRounds,\n maxRounds,\n smartExitEnabled: profile.smartExitEnabled,\n terminalTools: profile.terminalTools,\n reflectionEnabled: profile.reflectionEnabled,\n reflectionInterval: profile.reflectionInterval,\n taskEnforcement: profile.taskEnforcement,\n ensureTools: profile.ensureTools,\n completionStrategy: profile.completionStrategy,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/agent/pipeline.ts"],"sourcesContent":["/**\n * TITAN — Task Pipeline System\n *\n * Routes tasks to specialized pipelines instead of running everything\n * through the same generic agent loop configuration. Each pipeline\n * controls: round budget, SmartExit rules, tool priorities, task enforcement,\n * and completion detection.\n *\n * Safety: if classification fails, falls back to 'general' which is\n * byte-for-byte equivalent to the pre-pipeline behavior.\n */\n\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'Pipeline';\n\n// ── Pipeline Types ──────────────────────────────────────────────────\n\nexport type PipelineType =\n | 'chat'\n | 'research'\n | 'code'\n | 'social'\n | 'content'\n | 'automation'\n | 'browser'\n | 'sysadmin'\n | 'analysis'\n | 'voice'\n | 'general';\n\nexport type CompletionStrategy =\n | 'smart-exit' // Use pipeline-specific terminal tools\n | 'no-tools' // Done when model stops calling tools\n | 'terminal-tool' // Done when a specific tool succeeds\n | 'single-round'; // One tool round, then respond\n\n// ── Pipeline Profile ────────────────────────────────────────────────\n\nexport interface PipelineProfile {\n name: string;\n type: PipelineType;\n description: string;\n\n // Round budget\n minRounds: number;\n maxRounds: number;\n\n // SmartExit configuration\n smartExitEnabled: boolean;\n terminalTools: string[];\n completionStrategy: CompletionStrategy;\n\n // Tool configuration — tools to ensure are in the active set\n ensureTools: string[];\n toolSearchEnabled: boolean;\n\n // Behavioral controls\n reflectionEnabled: boolean;\n reflectionInterval: number;\n taskEnforcement: string | null;\n}\n\n// ── Pipeline Profiles ───────────────────────────────────────────────\n\nexport const PIPELINE_PROFILES: Record<PipelineType, PipelineProfile> = {\n\n chat: {\n name: 'Chat',\n type: 'chat',\n description: 'Simple Q&A, greetings, casual conversation',\n minRounds: 0,\n maxRounds: 3,\n smartExitEnabled: true,\n terminalTools: ['weather', 'system_info', 'memory'],\n completionStrategy: 'single-round',\n ensureTools: ['memory', 'weather', 'system_info', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n taskEnforcement: null,\n },\n\n research: {\n name: 'Research',\n type: 'research',\n description: 'Web research, fact-finding, information gathering',\n minRounds: 3,\n maxRounds: 15,\n smartExitEnabled: false,\n terminalTools: [],\n completionStrategy: 'no-tools',\n ensureTools: ['web_search', 'web_fetch', 'web_act', 'memory', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 4,\n taskEnforcement: '[RESEARCH PIPELINE] You MUST call web_search at least 2 times with different queries to gather comprehensive information. After searching, use web_fetch to read the full content of the most relevant results. Do NOT answer from training data alone — the user wants current, real information.',\n },\n\n code: {\n name: 'Code',\n type: 'code',\n description: 'Code editing, debugging, implementation, refactoring',\n minRounds: 3,\n maxRounds: 12,\n smartExitEnabled: true,\n terminalTools: ['write_file', 'edit_file', 'append_file'],\n completionStrategy: 'terminal-tool',\n ensureTools: ['read_file', 'write_file', 'edit_file', 'append_file', 'list_dir', 'shell', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 3,\n taskEnforcement: '[CODE PIPELINE] Follow this sequence:\\n1. read_file to understand current code\\n2. Plan your changes\\n3. edit_file or write_file to make changes\\n4. shell to verify (run tests, build, etc.)\\nCRITICAL: Do NOT stop after reading files. You MUST call edit_file or write_file to save changes.',\n },\n\n social: {\n name: 'Social',\n type: 'social',\n description: 'Facebook posting, social media, comment replies',\n minRounds: 2,\n maxRounds: 10,\n smartExitEnabled: true,\n terminalTools: ['fb_post', 'fb_reply', 'content_publish', 'x_post'],\n completionStrategy: 'terminal-tool',\n ensureTools: ['web_search', 'web_fetch', 'fb_post', 'fb_read_feed', 'fb_reply', 'fb_review_queue', 'fb_autopilot_status', 'memory', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n taskEnforcement: '[SOCIAL PIPELINE] For posting tasks:\\n1. Research the topic first (web_search if needed)\\n2. Compose the post content\\n3. Use fb_post to publish it\\nCRITICAL: You MUST call fb_post before finishing. Do NOT just describe what you would post — actually post it.',\n },\n\n content: {\n name: 'Content Creation',\n type: 'content',\n description: 'Multi-step content: research → write → publish',\n minRounds: 5,\n maxRounds: 20,\n smartExitEnabled: true,\n terminalTools: ['write_file', 'fb_post', 'content_publish', 'x_post'],\n completionStrategy: 'terminal-tool',\n ensureTools: ['web_search', 'web_fetch', 'read_file', 'write_file', 'edit_file', 'fb_post', 'memory', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 5,\n taskEnforcement: '[CONTENT PIPELINE] This is a multi-step content creation task.\\nPhase 1 — RESEARCH: Call web_search 2-3 times with different queries. Use web_fetch on top results.\\nPhase 2 — SYNTHESIZE: Analyze what you found and draft content.\\nPhase 3 — PUBLISH: Use the appropriate tool (write_file, fb_post, content_publish) to deliver the final result.\\nDo NOT skip Phase 1. Do NOT stop after research without publishing.',\n },\n\n automation: {\n name: 'Automation',\n type: 'automation',\n description: 'Home automation, smart home, IoT control',\n minRounds: 1,\n maxRounds: 5,\n smartExitEnabled: true,\n terminalTools: ['ha_control', 'ha_setup'],\n completionStrategy: 'terminal-tool',\n ensureTools: ['ha_control', 'ha_status', 'ha_devices', 'ha_setup', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n taskEnforcement: null,\n },\n\n browser: {\n name: 'Browser',\n type: 'browser',\n description: 'Browser automation, form filling, web interaction',\n minRounds: 3,\n maxRounds: 15,\n smartExitEnabled: false,\n terminalTools: [],\n completionStrategy: 'no-tools',\n ensureTools: ['web_act', 'smart_form_fill', 'web_search', 'web_fetch', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 5,\n taskEnforcement: '[BROWSER PIPELINE] Navigate to the target page first, then interact with elements. Use web_act for clicking/typing, smart_form_fill for forms. Take screenshots to verify your actions.',\n },\n\n sysadmin: {\n name: 'System Admin',\n type: 'sysadmin',\n description: 'Shell commands, process management, deployments',\n minRounds: 2,\n maxRounds: 8,\n smartExitEnabled: true,\n terminalTools: ['shell'],\n completionStrategy: 'terminal-tool',\n ensureTools: ['shell', 'read_file', 'write_file', 'list_dir', 'system_info', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n taskEnforcement: '[SYSADMIN PIPELINE] You MUST call the shell tool to execute commands. Do NOT describe what the command would do — run it and report the actual output.',\n },\n\n analysis: {\n name: 'Analysis',\n type: 'analysis',\n description: 'Data analysis, comparison, reporting',\n minRounds: 2,\n maxRounds: 10,\n smartExitEnabled: false,\n terminalTools: [],\n completionStrategy: 'no-tools',\n ensureTools: ['web_search', 'web_fetch', 'read_file', 'shell', 'memory', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 3,\n taskEnforcement: '[ANALYSIS PIPELINE] Gather data from multiple sources before synthesizing. Use web_search and web_fetch for external data. Use read_file and shell for local data. Provide specific numbers and facts, not vague summaries.',\n },\n\n voice: {\n name: 'Voice',\n type: 'voice',\n description: 'Voice interactions (handled by existing voiceFastPath)',\n minRounds: 1,\n maxRounds: 3,\n smartExitEnabled: true,\n terminalTools: ['weather', 'system_info', 'ha_control'],\n completionStrategy: 'single-round',\n ensureTools: ['shell', 'web_search', 'weather', 'memory', 'ha_control', 'ha_devices', 'ha_status', 'tool_search'],\n toolSearchEnabled: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n taskEnforcement: null,\n },\n\n general: {\n name: 'General',\n type: 'general',\n description: 'Fallback — equivalent to pre-pipeline behavior',\n minRounds: 1,\n maxRounds: 0, // 0 = use existing dynamic budget logic\n smartExitEnabled: true,\n terminalTools: ['write_file', 'append_file', 'weather', 'system_info', 'fb_post', 'fb_reply', 'content_publish'],\n completionStrategy: 'smart-exit',\n ensureTools: [], // empty = use existing DEFAULT_CORE_TOOLS\n toolSearchEnabled: true,\n reflectionEnabled: true,\n reflectionInterval: 3,\n taskEnforcement: null, // null = use existing scattered heuristics\n },\n};\n\n// ── Classifier ──────────────────────────────────────────────────────\n\n/**\n * Classification rules — ordered by specificity (most specific first).\n * First match wins. If nothing matches, returns 'general'.\n */\nconst CLASSIFICATION_RULES: Array<{\n type: PipelineType;\n test: (msg: string, channel: string) => boolean;\n}> = [\n // Voice is detected by channel, not message content\n {\n type: 'voice',\n test: (_msg, channel) => channel === 'voice',\n },\n\n // Social media — Facebook posting, replies, feed management.\n // v6.4.4: MOVED ABOVE content because the content rule's\n // `\\bwrite.*comparison\\b` was matching the fb-afternoon cron prompt\n // (\"Write an afternoon COMPARISON / HOT TAKE for the TITAN AI Facebook\n // page\") and routing it to the wrong pipeline. Social is more specific\n // for any prompt that mentions Facebook explicitly, so it wins now.\n {\n type: 'social',\n test: (msg) => /\\b(fb_|facebook|post to .*(page|facebook|fb)|share on|comment.*repl|reply.*comment|read.*feed|check.*feed|autopilot.*status|post.*about|hype|comparison.*post)\\b/i.test(msg)\n && msg.split(/\\s+/).length >= 3,\n },\n\n // Content creation — multi-step research + write + publish\n {\n type: 'content',\n test: (msg) => /\\b(research.*and.*(write|post|publish|create)|write.*article|blog.*post|create.*content|draft.*report|write.*comparison)\\b/i.test(msg)\n && msg.split(/\\s+/).length > 10,\n },\n\n // Home automation — smart home, IoT\n {\n type: 'automation',\n test: (msg) => /\\b(ha_|home assistant|turn (on|off)|lights?|thermostat|temperature|smart home|sensor|switch|door|garage|lock|unlock|dim|brighten)\\b/i.test(msg),\n },\n\n // Browser automation — form filling, web interaction\n {\n type: 'browser',\n test: (msg) => /\\b(browse|navigate to|fill.*form|click.*button|log ?in to|sign ?in to|captcha|screenshot.*page|form[- ]fill|web.?act)\\b/i.test(msg),\n },\n\n // System administration — shell, deploy, restart, install\n {\n type: 'sysadmin',\n test: (msg) => /\\b(run|execute|install|deploy|restart|reboot|start|stop|kill|ssh|systemctl|docker|npm run|git|build|compile|rsync|sudo|chmod|chown|upgrade|shutdown|daemon)\\b/i.test(msg)\n && /\\b(command|server|service|package|app|container|cluster|node|pm2)\\b/i.test(msg),\n },\n\n // Code editing — fix, edit, implement, debug\n {\n type: 'code',\n test: (msg) => /\\b(fix|edit|change|modify|update|refactor|implement|add|remove|replace|rewrite|patch|upgrade|debug|wire|hook|create.*function|create.*class|create.*component)\\b/i.test(msg)\n && /\\b(file|code|function|class|method|module|component|\\.ts|\\.js|\\.py|\\.tsx|\\.jsx|bug|feature|error|test|endpoint|route|API|skill|tool)\\b/i.test(msg),\n },\n\n // Research — web search, fact-finding\n {\n type: 'research',\n test: (msg) => /\\b(research|investigate|find out|look up|what.*(latest|new|current|trending|happening)|compare.*framework|landscape|competitive|benchmark|how does.*work|what is|who is)\\b/i.test(msg)\n && msg.split(/\\s+/).length > 6,\n },\n\n // Analysis — data analysis, reporting, comparison\n {\n type: 'analysis',\n test: (msg) => /\\b(analyze|analysis|compare|report|breakdown|summary|summarize|overview|stats|statistics|metrics|dashboard|performance|trend|insight)\\b/i.test(msg)\n && msg.split(/\\s+/).length > 6,\n },\n\n // Chat — simple Q&A, greetings (must be SHORT messages)\n {\n type: 'chat',\n test: (msg) => {\n const words = msg.trim().split(/\\s+/).length;\n if (words > 15) return false;\n return /^(hi|hello|hey|yo|sup|what'?s up|good (morning|afternoon|evening)|thanks|thank you|ok|okay|sure|yes|no|help|how are you|who are you|what can you do)/i.test(msg.trim())\n || (words <= 8 && /^(what|who|how|why|where|when|is |are |do |does |can |will |tell me|explain)\\b/i.test(msg.trim()));\n },\n },\n];\n\n/**\n * Classify a message into a pipeline type.\n * Fast regex-based classification — zero LLM cost.\n * Falls back to 'general' if nothing matches.\n */\nexport function classifyPipeline(message: string, channel: string): PipelineType {\n try {\n for (const rule of CLASSIFICATION_RULES) {\n if (rule.test(message, channel)) {\n logger.info(COMPONENT, `Classified as \"${rule.type}\": ${message.slice(0, 80)}...`);\n return rule.type;\n }\n }\n logger.info(COMPONENT, `Classified as \"general\" (no rule matched): ${message.slice(0, 80)}...`);\n return 'general';\n } catch (err) {\n logger.warn(COMPONENT, `Classification error, falling back to general: ${(err as Error).message}`);\n return 'general';\n }\n}\n\n/**\n * Resolve pipeline configuration for the agent loop.\n * Returns overrides that processMessage should apply.\n * For 'general' pipeline, returns null — use existing logic unchanged.\n */\nexport function resolvePipelineConfig(\n pipelineType: PipelineType,\n currentMaxRounds: number,\n hardCap: number,\n): {\n minRounds: number;\n maxRounds: number;\n smartExitEnabled: boolean;\n terminalTools: string[];\n reflectionEnabled: boolean;\n reflectionInterval: number;\n taskEnforcement: string | null;\n ensureTools: string[];\n completionStrategy: CompletionStrategy;\n} | null {\n // General pipeline = no overrides, use existing behavior\n if (pipelineType === 'general') return null;\n\n const profile = PIPELINE_PROFILES[pipelineType];\n\n // Pipeline maxRounds: use profile's maxRounds, capped at hardCap\n // If profile maxRounds is 0, use the existing dynamic budget\n const maxRounds = profile.maxRounds > 0\n ? Math.min(profile.maxRounds, hardCap)\n : currentMaxRounds;\n\n return {\n minRounds: profile.minRounds,\n maxRounds,\n smartExitEnabled: profile.smartExitEnabled,\n terminalTools: profile.terminalTools,\n reflectionEnabled: profile.reflectionEnabled,\n reflectionInterval: profile.reflectionInterval,\n taskEnforcement: profile.taskEnforcement,\n ensureTools: profile.ensureTools,\n completionStrategy: profile.completionStrategy,\n };\n}\n"],"mappings":"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","names":[]}
@@ -41,12 +41,13 @@ async function executePrompt(prompt, allowedTools) {
41
41
  try {
42
42
  const { processMessage } = await import("../../agent/agent.js");
43
43
  const sessionId = `cron-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
44
- const result = await processMessage(prompt, sessionId, "cron", {
45
- channel: "cron",
46
- // allowedTools is plumbed but not yet honored end-to-end —
47
- // logged here so a future hardening pass can enforce it.
48
- ...allowedTools && allowedTools.length > 0 ? { allowedTools } : {}
49
- });
44
+ const normalized = Array.isArray(allowedTools) ? allowedTools : typeof allowedTools === "string" && allowedTools.length > 0 ? allowedTools.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
45
+ const result = await processMessage(
46
+ prompt,
47
+ sessionId,
48
+ "cron",
49
+ normalized && normalized.length > 0 ? { allowedTools: normalized } : void 0
50
+ );
50
51
  const summary = (result.content || "").slice(0, 500);
51
52
  const tools = (result.toolsUsed || []).join(", ");
52
53
  return `[tool-mode] used tools: ${tools || "(none)"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/skills/builtin/cron.ts"],"sourcesContent":["/**\n * TITAN — Cron Skill (Built-in)\n * Schedule and manage automated tasks using node-cron.\n * Jobs are persisted to the DB and re-scheduled on gateway boot via initCronScheduler().\n */\nimport * as cron from 'node-cron';\nimport { exec } from 'child_process';\nimport { registerSkill } from '../registry.js';\nimport { getDb } from '../../memory/memory.js';\nimport { v4 as uuid } from 'uuid';\nimport logger from '../../utils/logger.js';\n\nconst COMPONENT = 'Cron';\n\n// ─── Active task handles ────────────────────────────────────────\n// Maps job ID → node-cron ScheduledTask so we can stop/restart them.\nconst activeTasks = new Map<string, ReturnType<typeof cron.schedule>>();\n\n// ─── Shell execution helper ─────────────────────────────────────\n\n/** Execute a shell command and return its output (stdout + stderr). */\nfunction executeCommand(command: string, timeout: number = 60000): Promise<string> {\n return new Promise((resolve) => {\n const proc = exec(\n command,\n {\n timeout,\n maxBuffer: 1024 * 1024 * 5, // 5 MB\n shell: '/bin/bash',\n },\n (error, stdout, stderr) => {\n let output = '';\n if (stdout) output += stdout;\n if (stderr) output += (output ? '\\n' : '') + `[stderr] ${stderr}`;\n if (error) {\n if (error.killed) {\n output += (output ? '\\n' : '') + `[timed out after ${timeout}ms]`;\n } else {\n output += (output ? '\\n' : '') + `[exit code: ${error.code}]`;\n }\n }\n // Truncate very long output so we don't bloat logs\n if (output.length > 10000) {\n output = output.slice(0, 5000) + '\\n... [truncated] ...\\n' + output.slice(-5000);\n }\n resolve(output || '(no output)');\n },\n );\n void proc; // satisfy TS — proc is managed by exec callback\n });\n}\n\n/**\n * v6.0.5 — Send the command as an LLM prompt to the agent loop. This is\n * the `mode: \"tool\"` path. Previously the scheduler ignored the mode flag\n * and ALL jobs (including tool-mode ones with English prompts) were\n * shoved through bash, which mangled FB posts among other things.\n */\nasync function executePrompt(prompt: string, allowedTools?: string[]): Promise<string> {\n try {\n const { processMessage } = await import('../../agent/agent.js');\n // sessionId 'cron-<rand>' so the agent loop doesn't try to recall\n // chat history. userId 'cron' identifies the surface in logs.\n const sessionId = `cron-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n const result = await processMessage(prompt, sessionId, 'cron', {\n channel: 'cron',\n // allowedTools is plumbed but not yet honored end-to-end —\n // logged here so a future hardening pass can enforce it.\n ...(allowedTools && allowedTools.length > 0 ? { allowedTools } : {}),\n } as Parameters<typeof processMessage>[3]);\n const summary = (result.content || '').slice(0, 500);\n const tools = (result.toolsUsed || []).join(', ');\n return `[tool-mode] used tools: ${tools || '(none)'}\\n${summary}`;\n } catch (err) {\n return `[tool-mode error] ${(err as Error).message}`;\n }\n}\n\n/**\n * v6.0.5 — Defensive guard for shell-mode jobs. Refuses commands that\n * clearly look like English prose / LLM prompts rather than shell.\n *\n * The trigger for this guard: FB autopilot cron jobs were created with\n * the agent prompt as the `command` field but with `mode` left at the\n * default (\"shell\"). Because the scheduler also ignored `mode`, those\n * prompts went straight into `/bin/bash -c \"You are TITAN...\"` every day,\n * resulting in mangled / broken output. We catch the \"looks like a\n * prompt\" case BEFORE bash sees it so the only fix needed is the\n * scheduler honoring mode.\n *\n * Heuristic: starts with words like \"you are\", \"post a\", \"write a\",\n * \"build a\", or contains \"Use the X tool\" — strong signals of an LLM\n * prompt accidentally routed here. Conservative; real shell commands\n * (cd, ls, git, npm, python, node, /bin/, ./, ~, $(...) , etc.) never\n * match.\n */\nfunction looksLikeLlmPrompt(cmd: string): boolean {\n const head = cmd.trim().slice(0, 200).toLowerCase();\n const promptStarts = [\n 'you are ', 'you are titan',\n 'post a ', 'post an ',\n 'write a ', 'write an ',\n 'build a ', 'build me ', 'build the ',\n 'create a ', 'create me ',\n 'generate a ', 'generate an ',\n 'compose a ', 'compose an ',\n 'draft a ', 'draft an ',\n ];\n if (promptStarts.some(p => head.startsWith(p))) return true;\n if (/\\buse the (?:[a-z_]+) tool\\b/i.test(cmd)) return true;\n if (/\\bunder \\d+ words\\b/i.test(cmd)) return true;\n return false;\n}\n\n// ─── Scheduler helpers ──────────────────────────────────────────\n\n/**\n * Start a node-cron task for a persisted job. Returns the task handle.\n *\n * v6.0.5 — Now honors `mode` (shell vs tool). Previously this signature\n * was `(jobId, schedule, command)` and ALL jobs were bash-executed\n * regardless of `record.mode`. That bug mangled the FB autopilot cron\n * jobs (English prompts routed to /bin/bash). The signature now takes\n * a record-shaped arg so callers don't have to remember to pass mode +\n * allowedTools separately, and forgetting the mode flag silently\n * defaults to shell at most, never bypasses it.\n */\ninterface CronJobLike {\n id: string;\n name?: string;\n schedule: string;\n command: string;\n mode?: 'shell' | 'tool';\n allowedTools?: string[];\n}\n\nfunction scheduleJob(job: CronJobLike): ReturnType<typeof cron.schedule> | null {\n if (!cron.validate(job.schedule)) {\n logger.warn(COMPONENT, `Invalid cron expression for job ${job.id}: \"${job.schedule}\"`);\n return null;\n }\n\n const mode: 'shell' | 'tool' = job.mode === 'tool' ? 'tool' : 'shell';\n const task = cron.schedule(job.schedule, async () => {\n logger.info(COMPONENT, `Running cron job ${job.id} (mode=${mode}, name=${job.name ?? '(unnamed)'}): ${job.command.slice(0, 120)}`);\n\n const store = getDb();\n const record = store.cronJobs.find((j) => j.id === job.id);\n if (record) record.last_run = new Date().toISOString();\n\n try {\n let output: string;\n if (mode === 'tool') {\n output = await executePrompt(job.command, job.allowedTools);\n } else if (looksLikeLlmPrompt(job.command)) {\n // Defensive guard for \"shell\" mode that's actually an LLM\n // prompt. Refuse instead of letting bash mangle it.\n output = `[refused] This shell-mode cron job looks like an LLM prompt, not a shell command. Re-create the job with mode:\"tool\" so it routes to the agent loop. Command head: ${job.command.slice(0, 80)}…`;\n logger.warn(COMPONENT, `Cron job ${job.id}: REFUSED to bash-execute LLM-prompt-shaped command`);\n } else {\n output = await executeCommand(job.command);\n }\n logger.info(COMPONENT, `Cron job ${job.id} completed:\\n${output.slice(0, 500)}`);\n } catch (err) {\n logger.error(COMPONENT, `Cron job ${job.id} failed: ${(err as Error).message}`);\n }\n });\n\n return task;\n}\n\n/** Stop and remove a task from the active map. */\nfunction stopAndRemoveTask(jobId: string): void {\n const task = activeTasks.get(jobId);\n if (task) {\n task.stop();\n activeTasks.delete(jobId);\n logger.debug(COMPONENT, `Stopped cron task: ${jobId}`);\n }\n}\n\n// ─── Public: init scheduler on gateway boot ─────────────────────\n\n/**\n * Called once from server.ts on startup.\n * Reads all enabled cron jobs from the DB and schedules them.\n */\nexport function initCronScheduler(): void {\n const store = getDb();\n let scheduled = 0;\n let skipped = 0;\n\n for (const job of store.cronJobs) {\n if (!job.enabled) { skipped++; continue; }\n\n const task = scheduleJob(job);\n if (task) {\n activeTasks.set(job.id, task);\n scheduled++;\n logger.debug(COMPONENT, `Scheduled: \"${job.name}\" (${job.schedule}, mode=${job.mode ?? 'shell'})`);\n } else {\n skipped++;\n }\n }\n\n logger.info(COMPONENT, `Cron scheduler initialised — ${scheduled} active, ${skipped} skipped`);\n}\n\n// ─── Skill registration ─────────────────────────────────────────\n\nexport function registerCronSkill(): void {\n registerSkill(\n { name: 'cron', description: 'Schedule and manage recurring tasks using cron expressions. USE THIS WHEN Tony says: \"schedule this to run every X\", \"set up a recurring task\", \"run this daily at 9am\", \"run this every 5 minutes\", \"add a cron job\", \"automate X on a schedule\". WORKFLOW: action=create with name, schedule (cron expression), and shell command — action=list to see all jobs — action=enable/disable to toggle — action=delete to remove.', version: '1.1.0', source: 'bundled', enabled: true },\n {\n name: 'cron',\n description: `Schedule, list, toggle, or delete recurring jobs. Jobs persist across restarts and run via node-cron in-process. Each job has a name, a cron expression, a command, and an execution mode (shell or tool).\n\nUSE WHEN: \"schedule this to run every X\" / \"set up a recurring task\" / \"run this daily at 9am\" / \"every 5 minutes do X\" / \"add a cron job\" / \"automate X on a schedule\" / \"stop that cron job\".\n\nDO NOT USE FOR:\n- One-off tasks → just run them now via shell or the appropriate tool.\n- Tasks triggered by events (file change, webhook) → use event_triggers / webhook respectively.\n- Tasks that need TITAN to \"wake up\" and decide what to do → set mode:\"tool\" so the cron sends a message to the agent loop instead of running a bare shell.\n\nParameters:\n- action (string, required) — one of: \"create\", \"list\", \"delete\", \"enable\", \"disable\".\n- name (string, required for create) — human-readable label.\n- schedule (string, required for create) — cron expression. \"0 9 * * *\" = daily at 9am, \"*/5 * * * *\" = every 5 minutes, \"0 0 * * 0\" = Sunday midnight. Five fields, no seconds.\n- command (string, required for create) — shell command OR message to TITAN (depending on mode).\n- mode (string, optional) — \"shell\" (default; runs as raw shell) or \"tool\" (sent as a message to the agent loop, gets all tools).\n- allowedTools (string, optional) — comma-separated tool allowlist for mode:\"tool\" jobs.\n- jobId (string, required for delete/enable/disable) — id from list.\n\nReturns:\n- create → \"Created job: <id>\"\n- list → table of all jobs with status, next-run, last-run.\n- delete/enable/disable → confirmation with the affected jobId.\n\nErrors:\n- \"Invalid cron expression\" — five fields required, no seconds. Test at crontab.guru.\n- \"Job not found: <id>\" — call list to discover the right id.\n- \"Tool not in allowlist\" (mode:\"tool\") — extend allowedTools or remove the constraint.`,\n parameters: {\n type: 'object',\n properties: {\n action: {\n type: 'string',\n enum: ['create', 'list', 'delete', 'enable', 'disable'],\n description: 'Action to perform',\n },\n name: { type: 'string', description: 'Human-readable name for the cron job' },\n schedule: {\n type: 'string',\n description: 'Cron schedule expression (e.g., \"0 9 * * *\" for daily at 9 am, \"*/5 * * * *\" every 5 min)',\n },\n command: { type: 'string', description: 'Shell command or task description to execute when the job runs' },\n jobId: { type: 'string', description: 'Job ID (required for delete/enable/disable)' },\n mode: { type: 'string', enum: ['shell', 'tool'], description: 'Execution mode: \"shell\" (default) runs a shell command, \"tool\" sends the command as a message to TITAN for tool-based execution' },\n allowedTools: { type: 'string', description: 'Comma-separated list of allowed tool names for tool-mode jobs (e.g., \"web_search,write_file,shell\")' },\n },\n required: ['action'],\n },\n execute: async (args) => {\n const action = args.action as string;\n const store = getDb();\n\n switch (action) {\n // ── Create ─────────────────────────────────\n case 'create': {\n const name = args.name as string;\n const schedule = args.schedule as string;\n const command = args.command as string;\n\n if (!name || !schedule || !command) {\n return 'Error: name, schedule, and command are required for create';\n }\n\n if (!cron.validate(schedule)) {\n return `Error: \"${schedule}\" is not a valid cron expression. Example: \"0 9 * * *\" (daily at 9 am)`;\n }\n\n const mode = (args.mode as string) === 'tool' ? 'tool' : 'shell';\n const allowedToolsStr = args.allowedTools as string | undefined;\n const allowedTools = allowedToolsStr ? allowedToolsStr.split(',').map(t => t.trim()).filter(Boolean) : undefined;\n\n const id = uuid();\n const record = {\n id,\n name,\n schedule,\n command,\n mode: mode as 'shell' | 'tool',\n allowedTools,\n enabled: true,\n created_at: new Date().toISOString(),\n };\n store.cronJobs.push(record);\n\n // Schedule immediately so it runs without a restart.\n // v6.0.5 — Pass the WHOLE record so scheduleJob can\n // dispatch on mode + honor allowedTools.\n const task = scheduleJob(record);\n if (task) {\n activeTasks.set(id, task);\n }\n\n logger.info(COMPONENT, `Created and scheduled cron job: ${name} (${schedule}, mode=${mode})`);\n return `Created cron job \"${name}\" (ID: ${id})\\nSchedule: ${schedule}\\nMode: ${mode}\\nCommand: ${command}\\nStatus: Active and running`;\n }\n\n // ── List ──────────────────────────────────\n case 'list': {\n if (store.cronJobs.length === 0) return 'No cron jobs configured.';\n\n return store.cronJobs.map((j) => {\n const running = activeTasks.has(j.id) ? '(running)' : '(not scheduled)';\n const lastRun = j.last_run ? `\\n Last run: ${j.last_run}` : '';\n return [\n `• ${j.name} [${j.enabled ? '✅ enabled' : '❌ disabled'}] ${running}`,\n ` ID: ${j.id}`,\n ` Schedule: ${j.schedule}`,\n ` Command: ${j.command}`,\n lastRun,\n ].filter(Boolean).join('\\n');\n }).join('\\n\\n');\n }\n\n // ── Delete ────────────────────────────────\n case 'delete': {\n const jobId = args.jobId as string;\n if (!jobId) return 'Error: jobId is required';\n\n const job = store.cronJobs.find((j) => j.id === jobId);\n if (!job) return `Error: no cron job found with ID: ${jobId}`;\n\n // Stop the running task first\n stopAndRemoveTask(jobId);\n\n store.cronJobs = store.cronJobs.filter((j) => j.id !== jobId);\n logger.info(COMPONENT, `Deleted cron job: ${job.name} (${jobId})`);\n return `Deleted cron job \"${job.name}\" (ID: ${jobId})`;\n }\n\n // ── Enable ────────────────────────────────\n case 'enable': {\n const eId = args.jobId as string;\n if (!eId) return 'Error: jobId is required';\n\n const job = store.cronJobs.find((j) => j.id === eId);\n if (!job) return `Error: no cron job found with ID: ${eId}`;\n\n job.enabled = true;\n\n // Start scheduling if not already active.\n // v6.0.5 — Pass the whole record so mode is honored.\n if (!activeTasks.has(eId)) {\n const task = scheduleJob(job);\n if (task) {\n activeTasks.set(eId, task);\n }\n }\n\n logger.info(COMPONENT, `Enabled cron job: ${job.name} (${eId})`);\n return `Enabled cron job \"${job.name}\" — it will now run on schedule (${job.schedule})`;\n }\n\n // ── Disable ───────────────────────────────\n case 'disable': {\n const dId = args.jobId as string;\n if (!dId) return 'Error: jobId is required';\n\n const job = store.cronJobs.find((j) => j.id === dId);\n if (!job) return `Error: no cron job found with ID: ${dId}`;\n\n job.enabled = false;\n\n // Stop the running task\n stopAndRemoveTask(dId);\n\n logger.info(COMPONENT, `Disabled cron job: ${job.name} (${dId})`);\n return `Disabled cron job \"${job.name}\" — it will no longer run until re-enabled`;\n }\n\n default:\n return `Unknown action: ${action}. Valid actions: create, list, delete, enable, disable`;\n }\n },\n },\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/skills/builtin/cron.ts"],"sourcesContent":["/**\n * TITAN — Cron Skill (Built-in)\n * Schedule and manage automated tasks using node-cron.\n * Jobs are persisted to the DB and re-scheduled on gateway boot via initCronScheduler().\n */\nimport * as cron from 'node-cron';\nimport { exec } from 'child_process';\nimport { registerSkill } from '../registry.js';\nimport { getDb } from '../../memory/memory.js';\nimport { v4 as uuid } from 'uuid';\nimport logger from '../../utils/logger.js';\n\nconst COMPONENT = 'Cron';\n\n// ─── Active task handles ────────────────────────────────────────\n// Maps job ID → node-cron ScheduledTask so we can stop/restart them.\nconst activeTasks = new Map<string, ReturnType<typeof cron.schedule>>();\n\n// ─── Shell execution helper ─────────────────────────────────────\n\n/** Execute a shell command and return its output (stdout + stderr). */\nfunction executeCommand(command: string, timeout: number = 60000): Promise<string> {\n return new Promise((resolve) => {\n const proc = exec(\n command,\n {\n timeout,\n maxBuffer: 1024 * 1024 * 5, // 5 MB\n shell: '/bin/bash',\n },\n (error, stdout, stderr) => {\n let output = '';\n if (stdout) output += stdout;\n if (stderr) output += (output ? '\\n' : '') + `[stderr] ${stderr}`;\n if (error) {\n if (error.killed) {\n output += (output ? '\\n' : '') + `[timed out after ${timeout}ms]`;\n } else {\n output += (output ? '\\n' : '') + `[exit code: ${error.code}]`;\n }\n }\n // Truncate very long output so we don't bloat logs\n if (output.length > 10000) {\n output = output.slice(0, 5000) + '\\n... [truncated] ...\\n' + output.slice(-5000);\n }\n resolve(output || '(no output)');\n },\n );\n void proc; // satisfy TS — proc is managed by exec callback\n });\n}\n\n/**\n * v6.0.5 — Send the command as an LLM prompt to the agent loop. This is\n * the `mode: \"tool\"` path. Previously the scheduler ignored the mode flag\n * and ALL jobs (including tool-mode ones with English prompts) were\n * shoved through bash, which mangled FB posts among other things.\n */\nasync function executePrompt(prompt: string, allowedTools?: string[] | string): Promise<string> {\n try {\n const { processMessage } = await import('../../agent/agent.js');\n // sessionId 'cron-<rand>' so the agent loop doesn't try to recall\n // chat history. userId 'cron' identifies the surface in logs.\n const sessionId = `cron-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n // v6.4.4: normalize allowedTools. Legacy DB entries (anything created\n // before this ship) store this as a comma-separated string like\n // \"fb_post,fb_read_feed\" instead of an array. Accept both shapes so we\n // don't need a DB migration.\n const normalized: string[] | undefined = Array.isArray(allowedTools)\n ? allowedTools\n : (typeof allowedTools === 'string' && allowedTools.length > 0\n ? allowedTools.split(',').map(s => s.trim()).filter(Boolean)\n : undefined);\n // v6.4.4: allowedTools is now honored end-to-end. processMessage's\n // overrides type accepts it; when present, the agent loop short-\n // circuits the persona / brain / toolSearch / dangerous filter\n // chain and binds EXACTLY these tools. This is what cron jobs\n // like fb-* need to make the model actually call fb_post instead\n // of hallucinating <tool_call name=\"fb_post\"> in the response text.\n // (channel='cron' is already passed as the 3rd positional arg above.)\n const result = await processMessage(prompt, sessionId, 'cron',\n normalized && normalized.length > 0 ? { allowedTools: normalized } : undefined);\n const summary = (result.content || '').slice(0, 500);\n const tools = (result.toolsUsed || []).join(', ');\n return `[tool-mode] used tools: ${tools || '(none)'}\\n${summary}`;\n } catch (err) {\n return `[tool-mode error] ${(err as Error).message}`;\n }\n}\n\n/**\n * v6.0.5 — Defensive guard for shell-mode jobs. Refuses commands that\n * clearly look like English prose / LLM prompts rather than shell.\n *\n * The trigger for this guard: FB autopilot cron jobs were created with\n * the agent prompt as the `command` field but with `mode` left at the\n * default (\"shell\"). Because the scheduler also ignored `mode`, those\n * prompts went straight into `/bin/bash -c \"You are TITAN...\"` every day,\n * resulting in mangled / broken output. We catch the \"looks like a\n * prompt\" case BEFORE bash sees it so the only fix needed is the\n * scheduler honoring mode.\n *\n * Heuristic: starts with words like \"you are\", \"post a\", \"write a\",\n * \"build a\", or contains \"Use the X tool\" — strong signals of an LLM\n * prompt accidentally routed here. Conservative; real shell commands\n * (cd, ls, git, npm, python, node, /bin/, ./, ~, $(...) , etc.) never\n * match.\n */\nfunction looksLikeLlmPrompt(cmd: string): boolean {\n const head = cmd.trim().slice(0, 200).toLowerCase();\n const promptStarts = [\n 'you are ', 'you are titan',\n 'post a ', 'post an ',\n 'write a ', 'write an ',\n 'build a ', 'build me ', 'build the ',\n 'create a ', 'create me ',\n 'generate a ', 'generate an ',\n 'compose a ', 'compose an ',\n 'draft a ', 'draft an ',\n ];\n if (promptStarts.some(p => head.startsWith(p))) return true;\n if (/\\buse the (?:[a-z_]+) tool\\b/i.test(cmd)) return true;\n if (/\\bunder \\d+ words\\b/i.test(cmd)) return true;\n return false;\n}\n\n// ─── Scheduler helpers ──────────────────────────────────────────\n\n/**\n * Start a node-cron task for a persisted job. Returns the task handle.\n *\n * v6.0.5 — Now honors `mode` (shell vs tool). Previously this signature\n * was `(jobId, schedule, command)` and ALL jobs were bash-executed\n * regardless of `record.mode`. That bug mangled the FB autopilot cron\n * jobs (English prompts routed to /bin/bash). The signature now takes\n * a record-shaped arg so callers don't have to remember to pass mode +\n * allowedTools separately, and forgetting the mode flag silently\n * defaults to shell at most, never bypasses it.\n */\ninterface CronJobLike {\n id: string;\n name?: string;\n schedule: string;\n command: string;\n mode?: 'shell' | 'tool';\n allowedTools?: string[];\n}\n\nfunction scheduleJob(job: CronJobLike): ReturnType<typeof cron.schedule> | null {\n if (!cron.validate(job.schedule)) {\n logger.warn(COMPONENT, `Invalid cron expression for job ${job.id}: \"${job.schedule}\"`);\n return null;\n }\n\n const mode: 'shell' | 'tool' = job.mode === 'tool' ? 'tool' : 'shell';\n const task = cron.schedule(job.schedule, async () => {\n logger.info(COMPONENT, `Running cron job ${job.id} (mode=${mode}, name=${job.name ?? '(unnamed)'}): ${job.command.slice(0, 120)}`);\n\n const store = getDb();\n const record = store.cronJobs.find((j) => j.id === job.id);\n if (record) record.last_run = new Date().toISOString();\n\n try {\n let output: string;\n if (mode === 'tool') {\n output = await executePrompt(job.command, job.allowedTools);\n } else if (looksLikeLlmPrompt(job.command)) {\n // Defensive guard for \"shell\" mode that's actually an LLM\n // prompt. Refuse instead of letting bash mangle it.\n output = `[refused] This shell-mode cron job looks like an LLM prompt, not a shell command. Re-create the job with mode:\"tool\" so it routes to the agent loop. Command head: ${job.command.slice(0, 80)}…`;\n logger.warn(COMPONENT, `Cron job ${job.id}: REFUSED to bash-execute LLM-prompt-shaped command`);\n } else {\n output = await executeCommand(job.command);\n }\n logger.info(COMPONENT, `Cron job ${job.id} completed:\\n${output.slice(0, 500)}`);\n } catch (err) {\n logger.error(COMPONENT, `Cron job ${job.id} failed: ${(err as Error).message}`);\n }\n });\n\n return task;\n}\n\n/** Stop and remove a task from the active map. */\nfunction stopAndRemoveTask(jobId: string): void {\n const task = activeTasks.get(jobId);\n if (task) {\n task.stop();\n activeTasks.delete(jobId);\n logger.debug(COMPONENT, `Stopped cron task: ${jobId}`);\n }\n}\n\n// ─── Public: init scheduler on gateway boot ─────────────────────\n\n/**\n * Called once from server.ts on startup.\n * Reads all enabled cron jobs from the DB and schedules them.\n */\nexport function initCronScheduler(): void {\n const store = getDb();\n let scheduled = 0;\n let skipped = 0;\n\n for (const job of store.cronJobs) {\n if (!job.enabled) { skipped++; continue; }\n\n const task = scheduleJob(job);\n if (task) {\n activeTasks.set(job.id, task);\n scheduled++;\n logger.debug(COMPONENT, `Scheduled: \"${job.name}\" (${job.schedule}, mode=${job.mode ?? 'shell'})`);\n } else {\n skipped++;\n }\n }\n\n logger.info(COMPONENT, `Cron scheduler initialised — ${scheduled} active, ${skipped} skipped`);\n}\n\n// ─── Skill registration ─────────────────────────────────────────\n\nexport function registerCronSkill(): void {\n registerSkill(\n { name: 'cron', description: 'Schedule and manage recurring tasks using cron expressions. USE THIS WHEN Tony says: \"schedule this to run every X\", \"set up a recurring task\", \"run this daily at 9am\", \"run this every 5 minutes\", \"add a cron job\", \"automate X on a schedule\". WORKFLOW: action=create with name, schedule (cron expression), and shell command — action=list to see all jobs — action=enable/disable to toggle — action=delete to remove.', version: '1.1.0', source: 'bundled', enabled: true },\n {\n name: 'cron',\n description: `Schedule, list, toggle, or delete recurring jobs. Jobs persist across restarts and run via node-cron in-process. Each job has a name, a cron expression, a command, and an execution mode (shell or tool).\n\nUSE WHEN: \"schedule this to run every X\" / \"set up a recurring task\" / \"run this daily at 9am\" / \"every 5 minutes do X\" / \"add a cron job\" / \"automate X on a schedule\" / \"stop that cron job\".\n\nDO NOT USE FOR:\n- One-off tasks → just run them now via shell or the appropriate tool.\n- Tasks triggered by events (file change, webhook) → use event_triggers / webhook respectively.\n- Tasks that need TITAN to \"wake up\" and decide what to do → set mode:\"tool\" so the cron sends a message to the agent loop instead of running a bare shell.\n\nParameters:\n- action (string, required) — one of: \"create\", \"list\", \"delete\", \"enable\", \"disable\".\n- name (string, required for create) — human-readable label.\n- schedule (string, required for create) — cron expression. \"0 9 * * *\" = daily at 9am, \"*/5 * * * *\" = every 5 minutes, \"0 0 * * 0\" = Sunday midnight. Five fields, no seconds.\n- command (string, required for create) — shell command OR message to TITAN (depending on mode).\n- mode (string, optional) — \"shell\" (default; runs as raw shell) or \"tool\" (sent as a message to the agent loop, gets all tools).\n- allowedTools (string, optional) — comma-separated tool allowlist for mode:\"tool\" jobs.\n- jobId (string, required for delete/enable/disable) — id from list.\n\nReturns:\n- create → \"Created job: <id>\"\n- list → table of all jobs with status, next-run, last-run.\n- delete/enable/disable → confirmation with the affected jobId.\n\nErrors:\n- \"Invalid cron expression\" — five fields required, no seconds. Test at crontab.guru.\n- \"Job not found: <id>\" — call list to discover the right id.\n- \"Tool not in allowlist\" (mode:\"tool\") — extend allowedTools or remove the constraint.`,\n parameters: {\n type: 'object',\n properties: {\n action: {\n type: 'string',\n enum: ['create', 'list', 'delete', 'enable', 'disable'],\n description: 'Action to perform',\n },\n name: { type: 'string', description: 'Human-readable name for the cron job' },\n schedule: {\n type: 'string',\n description: 'Cron schedule expression (e.g., \"0 9 * * *\" for daily at 9 am, \"*/5 * * * *\" every 5 min)',\n },\n command: { type: 'string', description: 'Shell command or task description to execute when the job runs' },\n jobId: { type: 'string', description: 'Job ID (required for delete/enable/disable)' },\n mode: { type: 'string', enum: ['shell', 'tool'], description: 'Execution mode: \"shell\" (default) runs a shell command, \"tool\" sends the command as a message to TITAN for tool-based execution' },\n allowedTools: { type: 'string', description: 'Comma-separated list of allowed tool names for tool-mode jobs (e.g., \"web_search,write_file,shell\")' },\n },\n required: ['action'],\n },\n execute: async (args) => {\n const action = args.action as string;\n const store = getDb();\n\n switch (action) {\n // ── Create ─────────────────────────────────\n case 'create': {\n const name = args.name as string;\n const schedule = args.schedule as string;\n const command = args.command as string;\n\n if (!name || !schedule || !command) {\n return 'Error: name, schedule, and command are required for create';\n }\n\n if (!cron.validate(schedule)) {\n return `Error: \"${schedule}\" is not a valid cron expression. Example: \"0 9 * * *\" (daily at 9 am)`;\n }\n\n const mode = (args.mode as string) === 'tool' ? 'tool' : 'shell';\n const allowedToolsStr = args.allowedTools as string | undefined;\n const allowedTools = allowedToolsStr ? allowedToolsStr.split(',').map(t => t.trim()).filter(Boolean) : undefined;\n\n const id = uuid();\n const record = {\n id,\n name,\n schedule,\n command,\n mode: mode as 'shell' | 'tool',\n allowedTools,\n enabled: true,\n created_at: new Date().toISOString(),\n };\n store.cronJobs.push(record);\n\n // Schedule immediately so it runs without a restart.\n // v6.0.5 — Pass the WHOLE record so scheduleJob can\n // dispatch on mode + honor allowedTools.\n const task = scheduleJob(record);\n if (task) {\n activeTasks.set(id, task);\n }\n\n logger.info(COMPONENT, `Created and scheduled cron job: ${name} (${schedule}, mode=${mode})`);\n return `Created cron job \"${name}\" (ID: ${id})\\nSchedule: ${schedule}\\nMode: ${mode}\\nCommand: ${command}\\nStatus: Active and running`;\n }\n\n // ── List ──────────────────────────────────\n case 'list': {\n if (store.cronJobs.length === 0) return 'No cron jobs configured.';\n\n return store.cronJobs.map((j) => {\n const running = activeTasks.has(j.id) ? '(running)' : '(not scheduled)';\n const lastRun = j.last_run ? `\\n Last run: ${j.last_run}` : '';\n return [\n `• ${j.name} [${j.enabled ? '✅ enabled' : '❌ disabled'}] ${running}`,\n ` ID: ${j.id}`,\n ` Schedule: ${j.schedule}`,\n ` Command: ${j.command}`,\n lastRun,\n ].filter(Boolean).join('\\n');\n }).join('\\n\\n');\n }\n\n // ── Delete ────────────────────────────────\n case 'delete': {\n const jobId = args.jobId as string;\n if (!jobId) return 'Error: jobId is required';\n\n const job = store.cronJobs.find((j) => j.id === jobId);\n if (!job) return `Error: no cron job found with ID: ${jobId}`;\n\n // Stop the running task first\n stopAndRemoveTask(jobId);\n\n store.cronJobs = store.cronJobs.filter((j) => j.id !== jobId);\n logger.info(COMPONENT, `Deleted cron job: ${job.name} (${jobId})`);\n return `Deleted cron job \"${job.name}\" (ID: ${jobId})`;\n }\n\n // ── Enable ────────────────────────────────\n case 'enable': {\n const eId = args.jobId as string;\n if (!eId) return 'Error: jobId is required';\n\n const job = store.cronJobs.find((j) => j.id === eId);\n if (!job) return `Error: no cron job found with ID: ${eId}`;\n\n job.enabled = true;\n\n // Start scheduling if not already active.\n // v6.0.5 — Pass the whole record so mode is honored.\n if (!activeTasks.has(eId)) {\n const task = scheduleJob(job);\n if (task) {\n activeTasks.set(eId, task);\n }\n }\n\n logger.info(COMPONENT, `Enabled cron job: ${job.name} (${eId})`);\n return `Enabled cron job \"${job.name}\" — it will now run on schedule (${job.schedule})`;\n }\n\n // ── Disable ───────────────────────────────\n case 'disable': {\n const dId = args.jobId as string;\n if (!dId) return 'Error: jobId is required';\n\n const job = store.cronJobs.find((j) => j.id === dId);\n if (!job) return `Error: no cron job found with ID: ${dId}`;\n\n job.enabled = false;\n\n // Stop the running task\n stopAndRemoveTask(dId);\n\n logger.info(COMPONENT, `Disabled cron job: ${job.name} (${dId})`);\n return `Disabled cron job \"${job.name}\" — it will no longer run until re-enabled`;\n }\n\n default:\n return `Unknown action: ${action}. Valid actions: create, list, delete, enable, disable`;\n }\n },\n },\n );\n}\n"],"mappings":"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","names":[]}
@@ -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.4.1";
4
+ const TITAN_VERSION = "6.4.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.4.1';\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":[]}
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.4.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.4.1",
3
+ "version": "6.4.4",
4
4
  "description": "TITAN — TypeScript AI agent framework with multi-agent orchestration, provider routing, approval gates, receipts, evals, gateway APIs, and a React Mission Control dashboard. Open-source, MIT licensed.",
5
5
  "author": "Tony Elliott (https://github.com/Djtony707)",
6
6
  "repository": {
@@ -139,7 +139,15 @@
139
139
  "axios": "^1.15.0",
140
140
  "vite": "^6.4.2",
141
141
  "follow-redirects": "^1.16.0",
142
- "protobufjs": "^7.5.8"
142
+ "protobufjs": "^7.5.8",
143
+ "qs": "^6.15.2"
144
+ },
145
+ "pnpm": {
146
+ "overrides": {
147
+ "protobufjs": "^7.5.8",
148
+ "qs": "^6.15.2",
149
+ "uuid": "^11.1.1"
150
+ }
143
151
  },
144
152
  "dependencies": {
145
153
  "@inquirer/prompts": "^8.4.3",
@@ -1 +1 @@
1
- import{r as d,j as e,aK as w,aL as S}from"./index-BISxhHXP.js";import{P as A}from"./PageHeader-CjDtUo8m.js";import{R as C}from"./refresh-cw-CFv1BDy9.js";import{F as T}from"./funnel-hj2rC4MH.js";import{C as R}from"./clipboard-list-C3GIPAT3.js";import"./chevron-right-D1DyaeTm.js";function P(){var b;const[l,v]=d.useState([]),[r,p]=d.useState(null),[h,i]=d.useState(!0),[a,c]=d.useState(""),[n,x]=d.useState(""),m=async()=>{i(!0);try{const[t,s]=await Promise.all([w({source:a||void 0,action:n||void 0,limit:100}),S(24)]);v(t),p(s)}catch{}i(!1)};d.useEffect(()=>{m()},[a,n]);const g=t=>t==="success"?"text-[var(--success)]":t==="failure"?"text-[var(--error)]":t==="escalated"?"text-[var(--warning)]":"text-[var(--text-muted)]",j=t=>t==="success"?"bg-[var(--success)]/10 border-[var(--success)]/20":t==="failure"?"bg-[var(--error)]/10 border-[var(--error)]/20":t==="escalated"?"bg-[var(--warning)]/10 border-[var(--warning)]/20":"bg-[var(--bg-tertiary)] border-[var(--border)]",u=t=>t.startsWith("daemon")?"bg-purple-500/10 text-purple-400 border-purple-500/20":t==="autopilot"?"bg-blue-500/10 text-blue-400 border-blue-500/20":t==="user"?"bg-emerald-500/10 text-emerald-400 border-emerald-500/20":t==="initiative"?"bg-amber-500/10 text-amber-400 border-amber-500/20":"bg-[var(--bg-tertiary)] text-[var(--text-muted)] border-[var(--border)]",f=t=>{try{const s=new Date(t),o=new Date().getTime()-s.getTime();return o<6e4?`${Math.round(o/1e3)}s ago`:o<36e5?`${Math.round(o/6e4)}m ago`:o<864e5?`${Math.round(o/36e5)}h ago`:s.toLocaleDateString()}catch{return t}},N=[...new Set(l.map(t=>t.source))].sort(),y=[...new Set(l.map(t=>t.action))].sort();return e.jsxs("div",{className:"space-y-6",children:[e.jsx(A,{title:"Audit Log",subtitle:"Every autonomous action logged for accountability",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"System"},{label:"Audit"}],actions:e.jsx("button",{onClick:m,className:"p-2 rounded-md border border-[var(--border)] text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] transition-colors",children:e.jsx(C,{size:14})})}),r&&e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4",children:[e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Actions (24h)"}),e.jsx("p",{className:"text-2xl font-bold text-[var(--text)]",children:r.totalActions})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Success Rate"}),e.jsxs("p",{className:`text-2xl font-bold ${r.successRate>=80?"text-[var(--success)]":r.successRate>=50?"text-[var(--warning)]":"text-[var(--error)]"}`,children:[r.successRate,"%"]})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Sources"}),e.jsx("p",{className:"text-2xl font-bold text-[var(--text)]",children:Object.keys(r.bySource).length})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Top Tool"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--accent)] truncate",children:((b=r.topTools[0])==null?void 0:b.tool)||"none"})]})]}),r&&Object.keys(r.bySource).length>0&&e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--text)] mb-3",children:"Actions by Source (24h)"}),e.jsx("div",{className:"flex flex-wrap gap-2",children:Object.entries(r.bySource).sort((t,s)=>s[1]-t[1]).map(([t,s])=>e.jsxs("button",{onClick:()=>c(a===t?"":t),className:`px-3 py-1.5 text-xs rounded-md border transition-colors ${a===t?"bg-[var(--accent)] text-white border-[var(--accent)]":u(t)}`,children:[t,": ",s]},t))})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(T,{size:14,className:"text-[var(--text-muted)]"}),e.jsxs("select",{value:a,onChange:t=>c(t.target.value),className:"px-3 py-1.5 text-xs bg-[var(--bg-secondary)] border border-[var(--border)] rounded-md text-[var(--text)]",children:[e.jsx("option",{value:"",children:"All Sources"}),N.map(t=>e.jsx("option",{value:t,children:t},t))]}),e.jsxs("select",{value:n,onChange:t=>x(t.target.value),className:"px-3 py-1.5 text-xs bg-[var(--bg-secondary)] border border-[var(--border)] rounded-md text-[var(--text)]",children:[e.jsx("option",{value:"",children:"All Actions"}),y.map(t=>e.jsx("option",{value:t,children:t},t))]}),(a||n)&&e.jsx("button",{onClick:()=>{c(""),x("")},className:"text-xs text-[var(--accent)] hover:underline",children:"Clear"})]}),h?e.jsx("div",{className:"text-[var(--text-muted)] text-sm",children:"Loading audit log..."}):l.length===0?e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-8 text-center",children:[e.jsx(R,{className:"w-10 h-10 text-[var(--text-muted)] mx-auto mb-3"}),e.jsx("p",{className:"text-[var(--text-secondary)]",children:"No audit entries found"}),e.jsx("p",{className:"text-xs text-[var(--text-muted)]",children:"Autonomous actions will appear here as TITAN operates."})]}):e.jsx("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg overflow-hidden",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-[var(--border)] text-[var(--text-muted)]",children:[e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Time"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Action"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Source"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Tool"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Result"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Detail"})]})}),e.jsx("tbody",{children:l.map((t,s)=>e.jsxs("tr",{className:"border-b border-[var(--border)]/50 last:border-0 hover:bg-[var(--bg-tertiary)] transition-colors",children:[e.jsx("td",{className:"px-4 py-2 text-[var(--text-muted)] whitespace-nowrap",children:f(t.timestamp)}),e.jsx("td",{className:"px-4 py-2 text-[var(--text)] font-medium",children:t.action}),e.jsx("td",{className:"px-4 py-2",children:e.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] border ${u(t.source)}`,children:t.source})}),e.jsx("td",{className:"px-4 py-2 text-[var(--text-secondary)] font-mono",children:t.tool||"-"}),e.jsx("td",{className:"px-4 py-2",children:t.result?e.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] border ${j(t.result)} ${g(t.result)}`,children:t.result}):"-"}),e.jsx("td",{className:"px-4 py-2 text-[var(--text-muted)] max-w-[200px] truncate",children:t.detail?JSON.stringify(t.detail):"-"})]},s))})]})})})]})}export{P as default};
1
+ import{r as d,j as e,aK as w,aL as S}from"./index-BlFxPvdg.js";import{P as A}from"./PageHeader-CawSrb9E.js";import{R as C}from"./refresh-cw-BdcqinTm.js";import{F as T}from"./funnel-CDnA2Enn.js";import{C as R}from"./clipboard-list-DF1F3Vjf.js";import"./chevron-right-DJCsUSwz.js";function P(){var b;const[l,v]=d.useState([]),[r,p]=d.useState(null),[h,i]=d.useState(!0),[a,c]=d.useState(""),[n,x]=d.useState(""),m=async()=>{i(!0);try{const[t,s]=await Promise.all([w({source:a||void 0,action:n||void 0,limit:100}),S(24)]);v(t),p(s)}catch{}i(!1)};d.useEffect(()=>{m()},[a,n]);const g=t=>t==="success"?"text-[var(--success)]":t==="failure"?"text-[var(--error)]":t==="escalated"?"text-[var(--warning)]":"text-[var(--text-muted)]",j=t=>t==="success"?"bg-[var(--success)]/10 border-[var(--success)]/20":t==="failure"?"bg-[var(--error)]/10 border-[var(--error)]/20":t==="escalated"?"bg-[var(--warning)]/10 border-[var(--warning)]/20":"bg-[var(--bg-tertiary)] border-[var(--border)]",u=t=>t.startsWith("daemon")?"bg-purple-500/10 text-purple-400 border-purple-500/20":t==="autopilot"?"bg-blue-500/10 text-blue-400 border-blue-500/20":t==="user"?"bg-emerald-500/10 text-emerald-400 border-emerald-500/20":t==="initiative"?"bg-amber-500/10 text-amber-400 border-amber-500/20":"bg-[var(--bg-tertiary)] text-[var(--text-muted)] border-[var(--border)]",f=t=>{try{const s=new Date(t),o=new Date().getTime()-s.getTime();return o<6e4?`${Math.round(o/1e3)}s ago`:o<36e5?`${Math.round(o/6e4)}m ago`:o<864e5?`${Math.round(o/36e5)}h ago`:s.toLocaleDateString()}catch{return t}},N=[...new Set(l.map(t=>t.source))].sort(),y=[...new Set(l.map(t=>t.action))].sort();return e.jsxs("div",{className:"space-y-6",children:[e.jsx(A,{title:"Audit Log",subtitle:"Every autonomous action logged for accountability",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"System"},{label:"Audit"}],actions:e.jsx("button",{onClick:m,className:"p-2 rounded-md border border-[var(--border)] text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] transition-colors",children:e.jsx(C,{size:14})})}),r&&e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4",children:[e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Actions (24h)"}),e.jsx("p",{className:"text-2xl font-bold text-[var(--text)]",children:r.totalActions})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Success Rate"}),e.jsxs("p",{className:`text-2xl font-bold ${r.successRate>=80?"text-[var(--success)]":r.successRate>=50?"text-[var(--warning)]":"text-[var(--error)]"}`,children:[r.successRate,"%"]})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Sources"}),e.jsx("p",{className:"text-2xl font-bold text-[var(--text)]",children:Object.keys(r.bySource).length})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Top Tool"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--accent)] truncate",children:((b=r.topTools[0])==null?void 0:b.tool)||"none"})]})]}),r&&Object.keys(r.bySource).length>0&&e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("h2",{className:"text-sm font-semibold text-[var(--text)] mb-3",children:"Actions by Source (24h)"}),e.jsx("div",{className:"flex flex-wrap gap-2",children:Object.entries(r.bySource).sort((t,s)=>s[1]-t[1]).map(([t,s])=>e.jsxs("button",{onClick:()=>c(a===t?"":t),className:`px-3 py-1.5 text-xs rounded-md border transition-colors ${a===t?"bg-[var(--accent)] text-white border-[var(--accent)]":u(t)}`,children:[t,": ",s]},t))})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(T,{size:14,className:"text-[var(--text-muted)]"}),e.jsxs("select",{value:a,onChange:t=>c(t.target.value),className:"px-3 py-1.5 text-xs bg-[var(--bg-secondary)] border border-[var(--border)] rounded-md text-[var(--text)]",children:[e.jsx("option",{value:"",children:"All Sources"}),N.map(t=>e.jsx("option",{value:t,children:t},t))]}),e.jsxs("select",{value:n,onChange:t=>x(t.target.value),className:"px-3 py-1.5 text-xs bg-[var(--bg-secondary)] border border-[var(--border)] rounded-md text-[var(--text)]",children:[e.jsx("option",{value:"",children:"All Actions"}),y.map(t=>e.jsx("option",{value:t,children:t},t))]}),(a||n)&&e.jsx("button",{onClick:()=>{c(""),x("")},className:"text-xs text-[var(--accent)] hover:underline",children:"Clear"})]}),h?e.jsx("div",{className:"text-[var(--text-muted)] text-sm",children:"Loading audit log..."}):l.length===0?e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-8 text-center",children:[e.jsx(R,{className:"w-10 h-10 text-[var(--text-muted)] mx-auto mb-3"}),e.jsx("p",{className:"text-[var(--text-secondary)]",children:"No audit entries found"}),e.jsx("p",{className:"text-xs text-[var(--text-muted)]",children:"Autonomous actions will appear here as TITAN operates."})]}):e.jsx("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg overflow-hidden",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-[var(--border)] text-[var(--text-muted)]",children:[e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Time"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Action"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Source"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Tool"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Result"}),e.jsx("th",{className:"text-left px-4 py-2 font-medium",children:"Detail"})]})}),e.jsx("tbody",{children:l.map((t,s)=>e.jsxs("tr",{className:"border-b border-[var(--border)]/50 last:border-0 hover:bg-[var(--bg-tertiary)] transition-colors",children:[e.jsx("td",{className:"px-4 py-2 text-[var(--text-muted)] whitespace-nowrap",children:f(t.timestamp)}),e.jsx("td",{className:"px-4 py-2 text-[var(--text)] font-medium",children:t.action}),e.jsx("td",{className:"px-4 py-2",children:e.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] border ${u(t.source)}`,children:t.source})}),e.jsx("td",{className:"px-4 py-2 text-[var(--text-secondary)] font-mono",children:t.tool||"-"}),e.jsx("td",{className:"px-4 py-2",children:t.result?e.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] border ${j(t.result)} ${g(t.result)}`,children:t.result}):"-"}),e.jsx("td",{className:"px-4 py-2 text-[var(--text-muted)] max-w-[200px] truncate",children:t.detail?JSON.stringify(t.detail):"-"})]},s))})]})})})]})}export{P as default};
@@ -1,4 +1,4 @@
1
- import{c as M,r as c,a8 as B,j as e,L as C,i as F,Z as E,a9 as O,t as T,b,f as g,B as q,U as G,G as V,h as I}from"./index-BISxhHXP.js";import{S as D}from"./save-o5ciSOaB.js";import{C as H}from"./circle-check-big-C7KjulF6.js";import{R as U}from"./refresh-cw-CFv1BDy9.js";import{R}from"./rotate-ccw-iCpZfipQ.js";import{R as J}from"./radio-BNdFCb41.js";import{B as Z}from"./bot-DK7dbBfX.js";import{S as W}from"./server-Ov6rxCAc.js";import{N as K}from"./network-CkgG0zu0.js";import{P as Q}from"./play-CuwPKVSv.js";import{M as X}from"./message-square-D9n28WBl.js";import{D as Y}from"./TitanCanvas-BewmO4FZ.js";import"./chevron-up-uKeW050h.js";import"./plus-IFwPqtUW.js";import"./file-text-kYsk6v9z.js";import"./terminal-CcPGcWeJ.js";import"./book-open-CCt4Gq9I.js";import"./pencil-c0GVcCA9.js";import"./search-DmeBed-X.js";import"./send-BoLKNmgS.js";import"./mic-DsWOcLkn.js";import"./flask-conical-CAMJ0Plo.js";import"./clock-BgvC0T_E.js";import"./archive-B27XcT_v.js";import"./test-tube-564jDt0t.js";import"./VoiceOverlay-B03bLRa6.js";import"./chevron-right-D1DyaeTm.js";import"./volume-2-DcpX5fnm.js";import"./folder-Dq6luLP7.js";import"./check-oX--Xdud.js";import"./shield-check-CKPsc0rI.js";import"./external-link-fIw1hJS5.js";import"./wifi-off-B4cf3i9g.js";import"./chart-column-CixcF9Pi.js";import"./link-BZZB4JyS.js";import"./trash-2-B7yIJ8B5.js";/**
1
+ import{c as M,r as c,a8 as B,j as e,L as C,i as F,Z as E,a9 as O,t as T,b,f as g,B as q,U as G,G as V,h as I}from"./index-BlFxPvdg.js";import{S as D}from"./save-BWhXC8Tv.js";import{C as H}from"./circle-check-big-De2k5qVr.js";import{R as U}from"./refresh-cw-BdcqinTm.js";import{R}from"./rotate-ccw-CRA_s5tz.js";import{R as J}from"./radio-BySzBvYH.js";import{B as Z}from"./bot-C0wiWyCT.js";import{S as W}from"./server-1PubNLgw.js";import{N as K}from"./network-BsSfc5IE.js";import{P as Q}from"./play-MuD1S1C5.js";import{M as X}from"./message-square-Dv6Q3VRO.js";import{D as Y}from"./TitanCanvas-D7TL368k.js";import"./chevron-up-cs3cBR4O.js";import"./plus-SBD6A3Qb.js";import"./file-text-SIULFbzy.js";import"./terminal-BvDfNOXl.js";import"./book-open-CrOs3smB.js";import"./pencil-DAvceqn3.js";import"./search-DF7UnWEk.js";import"./send-CmvPtbvy.js";import"./mic-BInf3BDE.js";import"./flask-conical-CsShOfpS.js";import"./clock-Bc6IrFv8.js";import"./archive-Cz-kvWW5.js";import"./test-tube-CrzRsKdm.js";import"./VoiceOverlay-DNzDqitY.js";import"./chevron-right-DJCsUSwz.js";import"./volume-2-ahkfEwxT.js";import"./folder-DwOkgBGj.js";import"./check-BhmlTEFk.js";import"./shield-check-BjuAv28a.js";import"./external-link-BjN0VrNb.js";import"./wifi-off-wACQ7wec.js";import"./chart-column-Dcc3a8m5.js";import"./link-C_6ueq0o.js";import"./trash-2-Dtxmn6GE.js";/**
2
2
  * @license lucide-react v0.513.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r,b as n,j as e}from"./index-BISxhHXP.js";import{P as c}from"./PageHeader-CjDtUo8m.js";import"./chevron-right-D1DyaeTm.js";function p(){const[t,s]=r.useState(null),[l,i]=r.useState(!0);return r.useEffect(()=>{n("/api/config",{headers:{"Content-Type":"application/json"}}).then(a=>a.json()).then(a=>{var o,d;return s({mode:((o=a.autonomy)==null?void 0:o.mode)||"supervised",interval:(d=a.autonomy)==null?void 0:d.autopilotIntervalMs})}).catch(()=>s({mode:"supervised"})).finally(()=>i(!1))},[]),l?e.jsx("div",{className:"text-[var(--text-muted)]",children:"Loading autopilot config..."}):e.jsxs("div",{className:"space-y-6",children:[e.jsx(c,{title:"Autopilot",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"Agent"},{label:"Autopilot"}]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Mode"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--text)] capitalize",children:t==null?void 0:t.mode})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Interval"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--text)]",children:t!=null&&t.interval?`${Math.round(t.interval/6e4)} min`:"Not set"})]})]}),e.jsx("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Autopilot mode enables TITAN to run scheduled tasks automatically. Configure via titan.json."})})]})}export{p as default};
1
+ import{r,b as n,j as e}from"./index-BlFxPvdg.js";import{P as c}from"./PageHeader-CawSrb9E.js";import"./chevron-right-DJCsUSwz.js";function p(){const[t,s]=r.useState(null),[l,i]=r.useState(!0);return r.useEffect(()=>{n("/api/config",{headers:{"Content-Type":"application/json"}}).then(a=>a.json()).then(a=>{var o,d;return s({mode:((o=a.autonomy)==null?void 0:o.mode)||"supervised",interval:(d=a.autonomy)==null?void 0:d.autopilotIntervalMs})}).catch(()=>s({mode:"supervised"})).finally(()=>i(!1))},[]),l?e.jsx("div",{className:"text-[var(--text-muted)]",children:"Loading autopilot config..."}):e.jsxs("div",{className:"space-y-6",children:[e.jsx(c,{title:"Autopilot",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"Agent"},{label:"Autopilot"}]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Mode"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--text)] capitalize",children:t==null?void 0:t.mode})]}),e.jsxs("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Interval"}),e.jsx("p",{className:"text-lg font-semibold text-[var(--text)]",children:t!=null&&t.interval?`${Math.round(t.interval/6e4)} min`:"Not set"})]})]}),e.jsx("div",{className:"bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg p-4",children:e.jsx("p",{className:"text-sm text-[var(--text-muted)]",children:"Autopilot mode enables TITAN to run scheduled tasks automatically. Configure via titan.json."})})]})}export{p as default};
@@ -1,4 +1,4 @@
1
- import{c as q,r as p,b as A,j as e,a7 as O,V as W}from"./index-BISxhHXP.js";import{P as B}from"./PageHeader-CjDtUo8m.js";import{P as M}from"./play-CuwPKVSv.js";import{P as H}from"./pause-CvgG1y4I.js";import{R as V}from"./refresh-cw-CFv1BDy9.js";import{F as P}from"./flask-conical-CAMJ0Plo.js";import{T as G}from"./trophy-mttLLqgk.js";import{T as U}from"./trending-up-DdxHXTsD.js";import{C as J}from"./clock-BgvC0T_E.js";import{C as K}from"./chevron-right-D1DyaeTm.js";/**
1
+ import{c as q,r as p,b as A,j as e,a7 as O,V as W}from"./index-BlFxPvdg.js";import{P as B}from"./PageHeader-CawSrb9E.js";import{P as M}from"./play-MuD1S1C5.js";import{P as H}from"./pause-CXCmPcHk.js";import{R as V}from"./refresh-cw-BdcqinTm.js";import{F as P}from"./flask-conical-CsShOfpS.js";import{T as G}from"./trophy-Dknrgn8P.js";import{T as U}from"./trending-up-C7pfwW1S.js";import{C as J}from"./clock-Bc6IrFv8.js";import{C as K}from"./chevron-right-DJCsUSwz.js";/**
2
2
  * @license lucide-react v0.513.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r as s,aO as p,j as e,aP as u,aQ as h}from"./index-BISxhHXP.js";import{P as g}from"./PageHeader-CjDtUo8m.js";import{P as b}from"./plus-IFwPqtUW.js";import{R as y}from"./refresh-cw-CFv1BDy9.js";import{A as j}from"./archive-B27XcT_v.js";import{C as v}from"./circle-check-big-C7KjulF6.js";import"./chevron-right-D1DyaeTm.js";function A(){const[c,m]=s.useState([]),[i,r]=s.useState(!0),[l,n]=s.useState(!1),[d,o]=s.useState(null),t=s.useCallback(async()=>{r(!0);try{const a=await p();m(a.backups||[])}catch{}r(!1)},[]);s.useEffect(()=>{t()},[t]);const x=async()=>{n(!0);try{await u(),await t()}catch{}n(!1)},f=async a=>{o(a);try{await h(a),alert("Backup verified successfully")}catch{alert("Backup verification failed")}o(null)};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(g,{title:"Backup Manager",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"System"},{label:"Backups"}]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{onClick:x,disabled:l,className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-[#6366f1] text-white text-sm font-medium hover:bg-[#4f46e5] disabled:opacity-50",children:[e.jsx(b,{className:"w-4 h-4"})," ",l?"Creating...":"Create Backup"]}),e.jsxs("button",{onClick:t,disabled:i,className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-[#27272a] text-[#a1a1aa] text-sm font-medium hover:bg-[#3f3f46] disabled:opacity-50",children:[e.jsx(y,{className:`w-4 h-4 ${i?"animate-spin":""}`})," Refresh"]})]}),c.length===0&&!i&&e.jsx("div",{className:"text-sm text-[#52525b]",children:"No backups found."}),e.jsx("div",{className:"space-y-2",children:c.map(a=>e.jsxs("div",{className:"flex items-center justify-between p-3 rounded-lg bg-[#0a0a0f] border border-[#27272a]",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(j,{className:"w-4 h-4 text-[#6366f1]"}),e.jsxs("div",{children:[e.jsx("div",{className:"text-sm text-[#e4e4e7]",children:new Date(a.createdAt).toLocaleString()}),e.jsxs("div",{className:"text-xs text-[#52525b]",children:[(a.sizeBytes/1024/1024).toFixed(1)," MB"]})]})]}),e.jsxs("button",{onClick:()=>f(a.path),disabled:d===a.path,className:"flex items-center gap-1.5 px-2.5 py-1.5 rounded-md bg-[#27272a] text-[#a1a1aa] text-xs hover:bg-[#3f3f46] disabled:opacity-50",children:[e.jsx(v,{className:"w-3.5 h-3.5"})," ",d===a.path?"Verifying...":"Verify"]})]},a.path))})]})}export{A as default};
1
+ import{r as s,aO as p,j as e,aP as u,aQ as h}from"./index-BlFxPvdg.js";import{P as g}from"./PageHeader-CawSrb9E.js";import{P as b}from"./plus-SBD6A3Qb.js";import{R as y}from"./refresh-cw-BdcqinTm.js";import{A as j}from"./archive-Cz-kvWW5.js";import{C as v}from"./circle-check-big-De2k5qVr.js";import"./chevron-right-DJCsUSwz.js";function A(){const[c,m]=s.useState([]),[i,r]=s.useState(!0),[l,n]=s.useState(!1),[d,o]=s.useState(null),t=s.useCallback(async()=>{r(!0);try{const a=await p();m(a.backups||[])}catch{}r(!1)},[]);s.useEffect(()=>{t()},[t]);const x=async()=>{n(!0);try{await u(),await t()}catch{}n(!1)},f=async a=>{o(a);try{await h(a),alert("Backup verified successfully")}catch{alert("Backup verification failed")}o(null)};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(g,{title:"Backup Manager",breadcrumbs:[{label:"Admin",href:"/overview"},{label:"System"},{label:"Backups"}]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{onClick:x,disabled:l,className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-[#6366f1] text-white text-sm font-medium hover:bg-[#4f46e5] disabled:opacity-50",children:[e.jsx(b,{className:"w-4 h-4"})," ",l?"Creating...":"Create Backup"]}),e.jsxs("button",{onClick:t,disabled:i,className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-[#27272a] text-[#a1a1aa] text-sm font-medium hover:bg-[#3f3f46] disabled:opacity-50",children:[e.jsx(y,{className:`w-4 h-4 ${i?"animate-spin":""}`})," Refresh"]})]}),c.length===0&&!i&&e.jsx("div",{className:"text-sm text-[#52525b]",children:"No backups found."}),e.jsx("div",{className:"space-y-2",children:c.map(a=>e.jsxs("div",{className:"flex items-center justify-between p-3 rounded-lg bg-[#0a0a0f] border border-[#27272a]",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(j,{className:"w-4 h-4 text-[#6366f1]"}),e.jsxs("div",{children:[e.jsx("div",{className:"text-sm text-[#e4e4e7]",children:new Date(a.createdAt).toLocaleString()}),e.jsxs("div",{className:"text-xs text-[#52525b]",children:[(a.sizeBytes/1024/1024).toFixed(1)," MB"]})]})]}),e.jsxs("button",{onClick:()=>f(a.path),disabled:d===a.path,className:"flex items-center gap-1.5 px-2.5 py-1.5 rounded-md bg-[#27272a] text-[#a1a1aa] text-xs hover:bg-[#3f3f46] disabled:opacity-50",children:[e.jsx(v,{className:"w-3.5 h-3.5"})," ",d===a.path?"Verifying...":"Verify"]})]},a.path))})]})}export{A as default};
@@ -1,4 +1,4 @@
1
- import{c as i,r as s,j as e,G as m,ax as p}from"./index-BISxhHXP.js";import{P as x}from"./PageHeader-CjDtUo8m.js";import"./chevron-right-D1DyaeTm.js";/**
1
+ import{c as i,r as s,j as e,G as m,ax as p}from"./index-BlFxPvdg.js";import{P as x}from"./PageHeader-CawSrb9E.js";import"./chevron-right-DJCsUSwz.js";/**
2
2
  * @license lucide-react v0.513.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r as a,x as b,j as e,A as u,Z as j,W as S,K as A,T as E}from"./index-BISxhHXP.js";import"./Input-CT_e2ZXD.js";import{P as N}from"./PageHeader-CjDtUo8m.js";import{E as T}from"./EmptyState-cLzelkoZ.js";import{T as C}from"./Tabs-Zo1JoN_G.js";import{S as w}from"./SkeletonLoader-DwBcLrhl.js";import{S as P}from"./shield-check-CKPsc0rI.js";import{D as $}from"./dollar-sign-DuP1ZChI.js";import{B as k}from"./bot-DK7dbBfX.js";import"./clsx-B-dksMZM.js";import"./chevron-right-D1DyaeTm.js";function I(s){const r=Math.floor((Date.now()-new Date(s).getTime())/1e3);return r<60?`${r}s`:r<3600?`${Math.floor(r/60)}m`:r<86400?`${Math.floor(r/3600)}h`:`${Math.floor(r/86400)}d`}const D={agent:k,goal:E,budget:$,approval:P,error:A,tool:S,system:j},M={agent:"text-accent",goal:"text-success",budget:"text-warning",approval:"text-info",error:"text-error",tool:"text-text-secondary",system:"text-text-muted"};function K(){const[s,r]=a.useState([]),[i,f]=a.useState(!0),[o,x]=a.useState(null),[n,h]=a.useState("all"),l=a.useRef(null),c=a.useCallback(async()=>{try{const t=await b(100);r(t),x(null)}catch(t){x(t instanceof Error?t.message:"Failed to load activity")}f(!1)},[]);a.useEffect(()=>(c(),l.current=setInterval(c,1e4),()=>{l.current&&clearInterval(l.current)}),[c]);const p=Array.from(new Set(s.map(t=>t.type))),g=[{id:"all",label:"All",count:s.length},...p.map(t=>({id:t,label:t.charAt(0).toUpperCase()+t.slice(1),count:s.filter(d=>d.type===t).length}))],m=n==="all"?s:s.filter(t=>t.type===n);return e.jsxs("div",{className:"space-y-4",children:[e.jsx(N,{title:"Activity",subtitle:"Auto-refreshes every 10s",breadcrumbs:[{label:"Command Post"},{label:"Activity"}]}),p.length>0&&e.jsx(C,{tabs:g,activeTab:n,onChange:h}),i&&e.jsx(w,{variant:"row",count:8}),o&&e.jsx("div",{className:"text-center py-8 text-error text-sm",children:o}),!i&&!o&&m.length===0&&e.jsx(T,{icon:e.jsx(u,{size:32}),title:"No activity",description:"Agent activity will appear here as events occur."}),!i&&!o&&m.length>0&&e.jsx("div",{className:"divide-y divide-border rounded-xl border border-border bg-bg-secondary overflow-hidden",children:m.map((t,d)=>{const v=D[t.type]??u,y=M[t.type]??"text-text-muted";return e.jsxs("div",{className:"flex items-start gap-3 px-4 py-2.5",children:[e.jsx(v,{size:14,className:`${y} mt-0.5 flex-shrink-0`}),e.jsx("span",{className:"flex-1 text-sm text-text-secondary",children:t.message}),t.agentId&&e.jsx("span",{className:"text-[10px] text-text-muted flex-shrink-0",children:t.agentId}),e.jsx("span",{className:"text-xs text-text-muted flex-shrink-0",children:I(t.timestamp)})]},`${t.id}-${d}`)})})]})}export{K as default};
1
+ import{r as a,x as b,j as e,A as u,Z as j,W as S,K as A,T as E}from"./index-BlFxPvdg.js";import"./Input-rfmX8eLw.js";import{P as N}from"./PageHeader-CawSrb9E.js";import{E as T}from"./EmptyState-NMpt-ODJ.js";import{T as C}from"./Tabs-CeY7i2tk.js";import{S as w}from"./SkeletonLoader-sjDa7Y96.js";import{S as P}from"./shield-check-BjuAv28a.js";import{D as $}from"./dollar-sign-BStL62ws.js";import{B as k}from"./bot-C0wiWyCT.js";import"./clsx-B-dksMZM.js";import"./chevron-right-DJCsUSwz.js";function I(s){const r=Math.floor((Date.now()-new Date(s).getTime())/1e3);return r<60?`${r}s`:r<3600?`${Math.floor(r/60)}m`:r<86400?`${Math.floor(r/3600)}h`:`${Math.floor(r/86400)}d`}const D={agent:k,goal:E,budget:$,approval:P,error:A,tool:S,system:j},M={agent:"text-accent",goal:"text-success",budget:"text-warning",approval:"text-info",error:"text-error",tool:"text-text-secondary",system:"text-text-muted"};function K(){const[s,r]=a.useState([]),[i,f]=a.useState(!0),[o,x]=a.useState(null),[n,h]=a.useState("all"),l=a.useRef(null),c=a.useCallback(async()=>{try{const t=await b(100);r(t),x(null)}catch(t){x(t instanceof Error?t.message:"Failed to load activity")}f(!1)},[]);a.useEffect(()=>(c(),l.current=setInterval(c,1e4),()=>{l.current&&clearInterval(l.current)}),[c]);const p=Array.from(new Set(s.map(t=>t.type))),g=[{id:"all",label:"All",count:s.length},...p.map(t=>({id:t,label:t.charAt(0).toUpperCase()+t.slice(1),count:s.filter(d=>d.type===t).length}))],m=n==="all"?s:s.filter(t=>t.type===n);return e.jsxs("div",{className:"space-y-4",children:[e.jsx(N,{title:"Activity",subtitle:"Auto-refreshes every 10s",breadcrumbs:[{label:"Command Post"},{label:"Activity"}]}),p.length>0&&e.jsx(C,{tabs:g,activeTab:n,onChange:h}),i&&e.jsx(w,{variant:"row",count:8}),o&&e.jsx("div",{className:"text-center py-8 text-error text-sm",children:o}),!i&&!o&&m.length===0&&e.jsx(T,{icon:e.jsx(u,{size:32}),title:"No activity",description:"Agent activity will appear here as events occur."}),!i&&!o&&m.length>0&&e.jsx("div",{className:"divide-y divide-border rounded-xl border border-border bg-bg-secondary overflow-hidden",children:m.map((t,d)=>{const v=D[t.type]??u,y=M[t.type]??"text-text-muted";return e.jsxs("div",{className:"flex items-start gap-3 px-4 py-2.5",children:[e.jsx(v,{size:14,className:`${y} mt-0.5 flex-shrink-0`}),e.jsx("span",{className:"flex-1 text-sm text-text-secondary",children:t.message}),t.agentId&&e.jsx("span",{className:"text-[10px] text-text-muted flex-shrink-0",children:t.agentId}),e.jsx("span",{className:"text-xs text-text-muted flex-shrink-0",children:I(t.timestamp)})]},`${t.id}-${d}`)})})]})}export{K as default};
@@ -1 +1 @@
1
- import{p as O,u as Y,r as s,$ as G,Y as J,y as K,aC as Q,j as e,D as A,bf as V}from"./index-BISxhHXP.js";import{B as T}from"./Input-CT_e2ZXD.js";import{S as c}from"./StatusBadge-CmSemYNT.js";import{P as x}from"./PageHeader-CjDtUo8m.js";import{E as W}from"./EmptyState-cLzelkoZ.js";import{T as X}from"./Tabs-Zo1JoN_G.js";import{S as Z}from"./SkeletonLoader-DwBcLrhl.js";import{A as _}from"./arrow-left-DSU0t1ZF.js";import{C as ee}from"./circle-check-big-C7KjulF6.js";import{D as te}from"./dollar-sign-DuP1ZChI.js";import{C as se}from"./clock-BgvC0T_E.js";import{S as ae}from"./save-o5ciSOaB.js";import"./clsx-B-dksMZM.js";import"./chevron-right-D1DyaeTm.js";function k(a){const o=Math.floor((Date.now()-new Date(a).getTime())/1e3);return o<60?`${o}s`:o<3600?`${Math.floor(o/60)}m`:o<86400?`${Math.floor(o/3600)}h`:`${Math.floor(o/86400)}d`}function re(a){return a?a<1e3?`${a}ms`:`${(a/1e3).toFixed(1)}s`:"-"}function je(){const{id:a}=O(),o=Y(),[r,m]=s.useState(null),[n,E]=s.useState([]),[u,M]=s.useState([]),[P,b]=s.useState(!0),[p,i]=s.useState(null),[d,D]=s.useState("overview"),[h,g]=s.useState(""),[f,j]=s.useState(""),[v,N]=s.useState(""),[y,S]=s.useState(""),[z,C]=s.useState(!1),[R,$]=s.useState([]),w=s.useCallback(async()=>{if(a){try{const l=(await G()).find(q=>q.id===a);if(!l){i("Agent not found"),b(!1);return}m(l),g(l.role),j(l.reportsTo??""),N(l.title??""),S(l.model??"");const[H,U]=await Promise.all([J(a),K({assignee:a})]);E(H),M(U),i(null)}catch(t){i(t instanceof Error?t.message:"Failed to load agent")}b(!1)}},[a]);s.useEffect(()=>{w()},[w]),s.useEffect(()=>{Q().then(t=>$(t)).catch(()=>{})},[]);const B=async()=>{if(a){C(!0);try{const t=await V(a,{role:h,reportsTo:f||void 0,title:v||void 0,model:y||void 0});m(t)}catch{}C(!1)}},L=[{id:"overview",label:"Overview"},{id:"runs",label:"Runs",count:n.length},{id:"config",label:"Config"}];if(P)return e.jsxs("div",{className:"space-y-6",children:[e.jsx(x,{title:"Agent",breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:"..."}]}),e.jsx(Z,{variant:"card",count:1})]});if(p||!r)return e.jsxs("div",{className:"space-y-6",children:[e.jsx(x,{title:"Agent",breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:"Error"}]}),e.jsxs("div",{className:"text-center py-12",children:[e.jsx("p",{className:"text-error text-sm mb-4",children:p??"Agent not found"}),e.jsx(T,{variant:"secondary",size:"sm",onClick:()=>o("/command-post/agents"),children:"Back to Agents"})]})]});const I=Date.now()-new Date(r.createdAt).getTime(),F=Math.floor(I/36e5);return e.jsxs("div",{className:"space-y-4",children:[e.jsx(x,{title:r.name,subtitle:r.title??r.role,breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:r.name}],actions:e.jsxs(A,{to:"/command-post/agents",className:"flex items-center gap-1 text-xs text-text-muted hover:text-text",children:[e.jsx(_,{size:14})," Back"]})}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(c,{status:r.status}),e.jsxs("span",{className:"text-xs text-text-muted",children:["Last heartbeat: ",k(r.lastHeartbeat)," ago"]})]}),e.jsx(X,{tabs:L,activeTab:d,onChange:D}),d==="overview"&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(ee,{size:16,className:"text-success mx-auto mb-1"}),e.jsx("div",{className:"text-xl font-bold text-text",children:r.totalTasksCompleted}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Tasks Completed"})]}),e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(te,{size:16,className:"text-warning mx-auto mb-1"}),e.jsxs("div",{className:"text-xl font-bold text-text",children:["$",r.totalCostUsd.toFixed(4)]}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Total Cost"})]}),e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(se,{size:16,className:"text-accent mx-auto mb-1"}),e.jsxs("div",{className:"text-xl font-bold text-text",children:[F,"h"]}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Uptime"})]})]}),e.jsxs("div",{className:"bg-bg-secondary border border-border rounded-xl overflow-hidden",children:[e.jsx("div",{className:"px-4 py-3 border-b border-border",children:e.jsx("h3",{className:"text-sm font-semibold text-text",children:"Assigned Issues"})}),u.length===0?e.jsx("div",{className:"py-8 text-center text-xs text-text-muted",children:"No assigned issues"}):e.jsx("div",{className:"divide-y divide-border",children:u.map(t=>e.jsxs(A,{to:`/command-post/issues/${t.id}`,className:"flex items-center gap-2 px-4 py-2 hover:bg-bg-tertiary transition-colors",children:[e.jsx(c,{status:t.status,variant:"dot",size:"sm"}),e.jsx("span",{className:"text-xs text-text flex-1 truncate",children:t.title}),e.jsx("span",{className:"text-[10px] text-text-muted font-mono",children:t.identifier})]},t.id))})]})]}),d==="runs"&&e.jsx("div",{className:"bg-bg-secondary border border-border rounded-xl overflow-hidden",children:n.length===0?e.jsx(W,{title:"No runs",description:"This agent has not executed any runs yet."}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-border text-left",children:[e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Source"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Status"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Duration"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Tools"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Started"})]})}),e.jsx("tbody",{className:"divide-y divide-border",children:n.map(t=>e.jsxs("tr",{className:"hover:bg-bg-tertiary/50 transition-colors",children:[e.jsx("td",{className:"px-4 py-2 text-text-secondary capitalize",children:t.source}),e.jsx("td",{className:"px-4 py-2",children:e.jsx(c,{status:t.status,size:"sm"})}),e.jsx("td",{className:"px-4 py-2 text-text-secondary",children:re(t.durationMs)}),e.jsx("td",{className:"px-4 py-2 text-text-muted",children:t.toolsUsed.length}),e.jsxs("td",{className:"px-4 py-2 text-text-muted",children:[k(t.startedAt)," ago"]})]},t.id))})]})})}),d==="config"&&e.jsxs("div",{className:"bg-bg-secondary border border-border rounded-xl p-4 space-y-4 max-w-md",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Role"}),e.jsx("select",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:h,onChange:t=>g(t.target.value),children:["ceo","manager","engineer","researcher","general"].map(t=>e.jsx("option",{value:t,children:t},t))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Reports To"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"Agent ID...",value:f,onChange:t=>j(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Title"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"e.g. Senior Engineer",value:v,onChange:t=>N(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Model"}),e.jsx("input",{list:"cp-agent-models",className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"e.g. ollama/qwen3.5:cloud",value:y,onChange:t=>S(t.target.value)}),e.jsx("datalist",{id:"cp-agent-models",children:R.map(t=>e.jsx("option",{value:t.id,children:t.name??t.id},t.id))})]}),e.jsx(T,{size:"sm",icon:e.jsx(ae,{size:14}),onClick:B,loading:z,children:"Save"})]})]})}export{je as default};
1
+ import{p as O,u as Y,r as s,$ as G,Y as J,y as K,aC as Q,j as e,D as A,bf as V}from"./index-BlFxPvdg.js";import{B as T}from"./Input-rfmX8eLw.js";import{S as c}from"./StatusBadge-BtDtyL5L.js";import{P as x}from"./PageHeader-CawSrb9E.js";import{E as W}from"./EmptyState-NMpt-ODJ.js";import{T as X}from"./Tabs-CeY7i2tk.js";import{S as Z}from"./SkeletonLoader-sjDa7Y96.js";import{A as _}from"./arrow-left-CGVxTClf.js";import{C as ee}from"./circle-check-big-De2k5qVr.js";import{D as te}from"./dollar-sign-BStL62ws.js";import{C as se}from"./clock-Bc6IrFv8.js";import{S as ae}from"./save-BWhXC8Tv.js";import"./clsx-B-dksMZM.js";import"./chevron-right-DJCsUSwz.js";function k(a){const o=Math.floor((Date.now()-new Date(a).getTime())/1e3);return o<60?`${o}s`:o<3600?`${Math.floor(o/60)}m`:o<86400?`${Math.floor(o/3600)}h`:`${Math.floor(o/86400)}d`}function re(a){return a?a<1e3?`${a}ms`:`${(a/1e3).toFixed(1)}s`:"-"}function je(){const{id:a}=O(),o=Y(),[r,m]=s.useState(null),[n,E]=s.useState([]),[u,M]=s.useState([]),[P,b]=s.useState(!0),[p,i]=s.useState(null),[d,D]=s.useState("overview"),[h,g]=s.useState(""),[f,j]=s.useState(""),[v,N]=s.useState(""),[y,S]=s.useState(""),[z,C]=s.useState(!1),[R,$]=s.useState([]),w=s.useCallback(async()=>{if(a){try{const l=(await G()).find(q=>q.id===a);if(!l){i("Agent not found"),b(!1);return}m(l),g(l.role),j(l.reportsTo??""),N(l.title??""),S(l.model??"");const[H,U]=await Promise.all([J(a),K({assignee:a})]);E(H),M(U),i(null)}catch(t){i(t instanceof Error?t.message:"Failed to load agent")}b(!1)}},[a]);s.useEffect(()=>{w()},[w]),s.useEffect(()=>{Q().then(t=>$(t)).catch(()=>{})},[]);const B=async()=>{if(a){C(!0);try{const t=await V(a,{role:h,reportsTo:f||void 0,title:v||void 0,model:y||void 0});m(t)}catch{}C(!1)}},L=[{id:"overview",label:"Overview"},{id:"runs",label:"Runs",count:n.length},{id:"config",label:"Config"}];if(P)return e.jsxs("div",{className:"space-y-6",children:[e.jsx(x,{title:"Agent",breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:"..."}]}),e.jsx(Z,{variant:"card",count:1})]});if(p||!r)return e.jsxs("div",{className:"space-y-6",children:[e.jsx(x,{title:"Agent",breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:"Error"}]}),e.jsxs("div",{className:"text-center py-12",children:[e.jsx("p",{className:"text-error text-sm mb-4",children:p??"Agent not found"}),e.jsx(T,{variant:"secondary",size:"sm",onClick:()=>o("/command-post/agents"),children:"Back to Agents"})]})]});const I=Date.now()-new Date(r.createdAt).getTime(),F=Math.floor(I/36e5);return e.jsxs("div",{className:"space-y-4",children:[e.jsx(x,{title:r.name,subtitle:r.title??r.role,breadcrumbs:[{label:"Command Post"},{label:"Agents",href:"/command-post/agents"},{label:r.name}],actions:e.jsxs(A,{to:"/command-post/agents",className:"flex items-center gap-1 text-xs text-text-muted hover:text-text",children:[e.jsx(_,{size:14})," Back"]})}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(c,{status:r.status}),e.jsxs("span",{className:"text-xs text-text-muted",children:["Last heartbeat: ",k(r.lastHeartbeat)," ago"]})]}),e.jsx(X,{tabs:L,activeTab:d,onChange:D}),d==="overview"&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(ee,{size:16,className:"text-success mx-auto mb-1"}),e.jsx("div",{className:"text-xl font-bold text-text",children:r.totalTasksCompleted}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Tasks Completed"})]}),e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(te,{size:16,className:"text-warning mx-auto mb-1"}),e.jsxs("div",{className:"text-xl font-bold text-text",children:["$",r.totalCostUsd.toFixed(4)]}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Total Cost"})]}),e.jsxs("div",{className:"bg-bg-tertiary/50 border border-border rounded-xl p-4 text-center",children:[e.jsx(se,{size:16,className:"text-accent mx-auto mb-1"}),e.jsxs("div",{className:"text-xl font-bold text-text",children:[F,"h"]}),e.jsx("div",{className:"text-[10px] text-text-muted",children:"Uptime"})]})]}),e.jsxs("div",{className:"bg-bg-secondary border border-border rounded-xl overflow-hidden",children:[e.jsx("div",{className:"px-4 py-3 border-b border-border",children:e.jsx("h3",{className:"text-sm font-semibold text-text",children:"Assigned Issues"})}),u.length===0?e.jsx("div",{className:"py-8 text-center text-xs text-text-muted",children:"No assigned issues"}):e.jsx("div",{className:"divide-y divide-border",children:u.map(t=>e.jsxs(A,{to:`/command-post/issues/${t.id}`,className:"flex items-center gap-2 px-4 py-2 hover:bg-bg-tertiary transition-colors",children:[e.jsx(c,{status:t.status,variant:"dot",size:"sm"}),e.jsx("span",{className:"text-xs text-text flex-1 truncate",children:t.title}),e.jsx("span",{className:"text-[10px] text-text-muted font-mono",children:t.identifier})]},t.id))})]})]}),d==="runs"&&e.jsx("div",{className:"bg-bg-secondary border border-border rounded-xl overflow-hidden",children:n.length===0?e.jsx(W,{title:"No runs",description:"This agent has not executed any runs yet."}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-border text-left",children:[e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Source"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Status"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Duration"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Tools"}),e.jsx("th",{className:"px-4 py-2 text-text-muted font-medium",children:"Started"})]})}),e.jsx("tbody",{className:"divide-y divide-border",children:n.map(t=>e.jsxs("tr",{className:"hover:bg-bg-tertiary/50 transition-colors",children:[e.jsx("td",{className:"px-4 py-2 text-text-secondary capitalize",children:t.source}),e.jsx("td",{className:"px-4 py-2",children:e.jsx(c,{status:t.status,size:"sm"})}),e.jsx("td",{className:"px-4 py-2 text-text-secondary",children:re(t.durationMs)}),e.jsx("td",{className:"px-4 py-2 text-text-muted",children:t.toolsUsed.length}),e.jsxs("td",{className:"px-4 py-2 text-text-muted",children:[k(t.startedAt)," ago"]})]},t.id))})]})})}),d==="config"&&e.jsxs("div",{className:"bg-bg-secondary border border-border rounded-xl p-4 space-y-4 max-w-md",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Role"}),e.jsx("select",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text focus:outline-none focus:border-accent",value:h,onChange:t=>g(t.target.value),children:["ceo","manager","engineer","researcher","general"].map(t=>e.jsx("option",{value:t,children:t},t))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Reports To"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"Agent ID...",value:f,onChange:t=>j(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Title"}),e.jsx("input",{className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"e.g. Senior Engineer",value:v,onChange:t=>N(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-text-muted mb-1",children:"Model"}),e.jsx("input",{list:"cp-agent-models",className:"w-full rounded-lg border border-border bg-bg-tertiary px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:border-accent",placeholder:"e.g. ollama/qwen3.5:cloud",value:y,onChange:t=>S(t.target.value)}),e.jsx("datalist",{id:"cp-agent-models",children:R.map(t=>e.jsx("option",{value:t.id,children:t.name??t.id},t.id))})]}),e.jsx(T,{size:"sm",icon:e.jsx(ae,{size:14}),onClick:B,loading:z,children:"Save"})]})]})}export{je as default};
@@ -1 +1 @@
1
- import{r,$ as m,j as e,U as p,D as u,X as h,b as j}from"./index-BISxhHXP.js";import"./Input-CT_e2ZXD.js";import{S as g}from"./StatusBadge-CmSemYNT.js";import{P as f}from"./PageHeader-CjDtUo8m.js";import{E as b}from"./EmptyState-cLzelkoZ.js";import{S as N}from"./SkeletonLoader-DwBcLrhl.js";import"./clsx-B-dksMZM.js";import"./chevron-right-D1DyaeTm.js";function y(a){const s=Math.floor((Date.now()-new Date(a).getTime())/1e3);return s<60?`${s}s`:s<3600?`${Math.floor(s/60)}m`:s<86400?`${Math.floor(s/3600)}h`:`${Math.floor(s/86400)}d`}function v(a){const s=a.split("/");return s[s.length-1]}function D(){const[a,s]=r.useState([]),[c,d]=r.useState(!0),[o,l]=r.useState(null),i=r.useCallback(async()=>{try{const t=await m();s(t),l(null)}catch(t){l(t instanceof Error?t.message:"Failed to load agents")}d(!1)},[]);r.useEffect(()=>{i()},[i]);const x=async(t,n)=>{n.preventDefault(),n.stopPropagation();try{await j(`/api/command-post/agents/${t}`,{method:"DELETE"}),await i()}catch{}};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(f,{title:"Agents",breadcrumbs:[{label:"Command Post"},{label:"Agents"}]}),c&&e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",children:e.jsx(N,{variant:"card",count:6})}),o&&e.jsx("div",{className:"text-center py-8 text-error text-sm",children:o}),!c&&!o&&a.length===0&&e.jsx(b,{icon:e.jsx(p,{size:32}),title:"No agents registered",description:"Agents will appear here once they connect to Command Post."}),!c&&!o&&a.length>0&&e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",children:a.map(t=>e.jsxs(u,{to:`/command-post/agents/${t.id}`,className:"relative bg-bg-secondary border border-border rounded-xl p-4 hover:border-border-light transition-colors group",children:[t.role!=="ceo"&&e.jsx("button",{onClick:n=>x(t.id,n),className:"absolute top-3 right-3 p-1 rounded-md text-text-muted hover:text-error hover:bg-error/10 opacity-0 group-hover:opacity-100 transition-all",title:"Remove agent",children:e.jsx(h,{size:14})}),e.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[e.jsx("span",{className:"text-sm font-semibold text-text",children:t.name}),e.jsx(g,{status:t.status,size:"sm"})]}),e.jsxs("div",{className:"space-y-1.5",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Role"}),e.jsx("span",{className:"text-xs text-text-secondary capitalize",children:t.role})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Model"}),e.jsx("span",{className:"text-xs text-text-secondary truncate max-w-[140px]",children:v(t.model)})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Last Heartbeat"}),e.jsxs("span",{className:"text-xs text-text-secondary",children:[y(t.lastHeartbeat)," ago"]})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Tasks Done"}),e.jsx("span",{className:"text-xs text-text-secondary",children:t.totalTasksCompleted})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Cost"}),e.jsxs("span",{className:"text-xs text-text-secondary",children:["$",t.totalCostUsd.toFixed(4)]})]})]})]},t.id))})]})}export{D as default};
1
+ import{r,$ as m,j as e,U as p,D as u,X as h,b as j}from"./index-BlFxPvdg.js";import"./Input-rfmX8eLw.js";import{S as g}from"./StatusBadge-BtDtyL5L.js";import{P as f}from"./PageHeader-CawSrb9E.js";import{E as b}from"./EmptyState-NMpt-ODJ.js";import{S as N}from"./SkeletonLoader-sjDa7Y96.js";import"./clsx-B-dksMZM.js";import"./chevron-right-DJCsUSwz.js";function y(a){const s=Math.floor((Date.now()-new Date(a).getTime())/1e3);return s<60?`${s}s`:s<3600?`${Math.floor(s/60)}m`:s<86400?`${Math.floor(s/3600)}h`:`${Math.floor(s/86400)}d`}function v(a){const s=a.split("/");return s[s.length-1]}function D(){const[a,s]=r.useState([]),[c,d]=r.useState(!0),[o,l]=r.useState(null),i=r.useCallback(async()=>{try{const t=await m();s(t),l(null)}catch(t){l(t instanceof Error?t.message:"Failed to load agents")}d(!1)},[]);r.useEffect(()=>{i()},[i]);const x=async(t,n)=>{n.preventDefault(),n.stopPropagation();try{await j(`/api/command-post/agents/${t}`,{method:"DELETE"}),await i()}catch{}};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(f,{title:"Agents",breadcrumbs:[{label:"Command Post"},{label:"Agents"}]}),c&&e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",children:e.jsx(N,{variant:"card",count:6})}),o&&e.jsx("div",{className:"text-center py-8 text-error text-sm",children:o}),!c&&!o&&a.length===0&&e.jsx(b,{icon:e.jsx(p,{size:32}),title:"No agents registered",description:"Agents will appear here once they connect to Command Post."}),!c&&!o&&a.length>0&&e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",children:a.map(t=>e.jsxs(u,{to:`/command-post/agents/${t.id}`,className:"relative bg-bg-secondary border border-border rounded-xl p-4 hover:border-border-light transition-colors group",children:[t.role!=="ceo"&&e.jsx("button",{onClick:n=>x(t.id,n),className:"absolute top-3 right-3 p-1 rounded-md text-text-muted hover:text-error hover:bg-error/10 opacity-0 group-hover:opacity-100 transition-all",title:"Remove agent",children:e.jsx(h,{size:14})}),e.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[e.jsx("span",{className:"text-sm font-semibold text-text",children:t.name}),e.jsx(g,{status:t.status,size:"sm"})]}),e.jsxs("div",{className:"space-y-1.5",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Role"}),e.jsx("span",{className:"text-xs text-text-secondary capitalize",children:t.role})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Model"}),e.jsx("span",{className:"text-xs text-text-secondary truncate max-w-[140px]",children:v(t.model)})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Last Heartbeat"}),e.jsxs("span",{className:"text-xs text-text-secondary",children:[y(t.lastHeartbeat)," ago"]})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Tasks Done"}),e.jsx("span",{className:"text-xs text-text-secondary",children:t.totalTasksCompleted})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-muted uppercase",children:"Cost"}),e.jsxs("span",{className:"text-xs text-text-secondary",children:["$",t.totalCostUsd.toFixed(4)]})]})]})]},t.id))})]})}export{D as default};