titan-agent 7.0.0 → 7.2.0

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 (343) hide show
  1. package/README.md +54 -2
  2. package/dist/agent/agent.js +55 -10
  3. package/dist/agent/agent.js.map +1 -1
  4. package/dist/agent/contextManager.js +1 -1
  5. package/dist/agent/contextManager.js.map +1 -1
  6. package/dist/agent/honestyGuard.js +66 -0
  7. package/dist/agent/honestyGuard.js.map +1 -0
  8. package/dist/agent/selfCritique.js +101 -0
  9. package/dist/agent/selfCritique.js.map +1 -0
  10. package/dist/agent/toolSearch.js +9 -2
  11. package/dist/agent/toolSearch.js.map +1 -1
  12. package/dist/agent/trajectoryLogger.js +19 -2
  13. package/dist/agent/trajectoryLogger.js.map +1 -1
  14. package/dist/analytics/collector.js +1 -1
  15. package/dist/analytics/collector.js.map +1 -1
  16. package/dist/config/schema.js +32 -0
  17. package/dist/config/schema.js.map +1 -1
  18. package/dist/eval/harness.js +4 -1
  19. package/dist/eval/harness.js.map +1 -1
  20. package/dist/gateway/server.js +24 -8
  21. package/dist/gateway/server.js.map +1 -1
  22. package/dist/gateway/slashCommands.js +31 -0
  23. package/dist/gateway/slashCommands.js.map +1 -1
  24. package/dist/mcp/agentTools.js +135 -0
  25. package/dist/mcp/agentTools.js.map +1 -0
  26. package/dist/mcp/server.js +19 -2
  27. package/dist/mcp/server.js.map +1 -1
  28. package/dist/memory/graph.js +2 -0
  29. package/dist/memory/graph.js.map +1 -1
  30. package/dist/providers/errorTaxonomy.js +4 -0
  31. package/dist/providers/errorTaxonomy.js.map +1 -1
  32. package/dist/providers/moa.js +151 -0
  33. package/dist/providers/moa.js.map +1 -0
  34. package/dist/providers/modelCapabilities.js +65 -2
  35. package/dist/providers/modelCapabilities.js.map +1 -1
  36. package/dist/providers/ollama.js +1 -0
  37. package/dist/providers/ollama.js.map +1 -1
  38. package/dist/providers/openai_compat.js +14 -2
  39. package/dist/providers/openai_compat.js.map +1 -1
  40. package/dist/providers/router.js +3 -1
  41. package/dist/providers/router.js.map +1 -1
  42. package/dist/utils/benchMode.js +8 -0
  43. package/dist/utils/benchMode.js.map +1 -0
  44. package/dist/utils/constants.js +1 -1
  45. package/dist/utils/constants.js.map +1 -1
  46. package/package.json +26 -26
  47. package/ui/dist/assets/AboutYouMemory-57X5SqHQ.js +1 -0
  48. package/ui/dist/assets/AuditPanel-AyB8ciLZ.js +1 -0
  49. package/ui/dist/assets/AutonomyPanel-wmVPr-n3.js +1 -0
  50. package/ui/dist/assets/AutopilotPanel-CbLy4lJP.js +1 -0
  51. package/ui/dist/assets/AutoresearchPanel-DxRO1rBF.js +1 -0
  52. package/ui/dist/assets/BackupPanel-BUh9rQ-3.js +1 -0
  53. package/ui/dist/assets/BrowserPanel-OqKwGrIL.js +1 -0
  54. package/ui/dist/assets/CPActivity-DGfS7EY3.js +1 -0
  55. package/ui/dist/assets/CPAgentDetail-p0Kvzhhb.js +1 -0
  56. package/ui/dist/assets/CPAgents-B6wSSQZM.js +1 -0
  57. package/ui/dist/assets/CPApprovals-CYv9Do0i.js +1 -0
  58. package/ui/dist/assets/CPCosts-Dx6Q71rN.js +1 -0
  59. package/ui/dist/assets/CPDashboard-DA8-tE-l.js +1 -0
  60. package/ui/dist/assets/CPFiles-BJ49tzai.js +1 -0
  61. package/ui/dist/assets/CPGoals-h0dKL5V_.js +1 -0
  62. package/ui/dist/assets/CPInbox-CDvefgNH.js +1 -0
  63. package/ui/dist/assets/CPIssueDetail-CnmLzzDD.js +1 -0
  64. package/ui/dist/assets/CPIssues-DknAIMyl.js +1 -0
  65. package/ui/dist/assets/CPLayout-C2nqgkmY.js +2 -0
  66. package/ui/dist/assets/CPOrg-tDbHFphX.js +1 -0
  67. package/ui/dist/assets/CPRuns-UlrIURqG.js +1 -0
  68. package/ui/dist/assets/CPSocial-B-gHgvEw.js +1 -0
  69. package/ui/dist/assets/CPVoice-CJy938ZF.js +1 -0
  70. package/ui/dist/assets/ChannelsPanel-DLbxg1zO.js +1 -0
  71. package/ui/dist/assets/CheckpointsPanel-BB6TT0aE.js +1 -0
  72. package/ui/dist/assets/CommandPostHub-fIslwU_t.js +2 -0
  73. package/ui/dist/assets/ConfirmDialog-6hcNjmAh.js +9 -0
  74. package/ui/dist/assets/CronPanel-DLpw3Y25.js +1 -0
  75. package/ui/dist/assets/DataTable-KVqtDzvP.js +1 -0
  76. package/ui/dist/assets/DreamPanel-lcMXpbEb.js +5 -0
  77. package/ui/dist/assets/EmptyState-D-Dg8B8r.js +1 -0
  78. package/ui/dist/assets/EvalHarnessPanel-BKzRiiQw.js +2 -0
  79. package/ui/dist/assets/EvalPanel-Dtodacmj.js +1 -0
  80. package/ui/dist/assets/FileViewer-DjcVKkO2.js +60 -0
  81. package/ui/dist/assets/FilesPanel-BI90Pplh.js +1 -0
  82. package/ui/dist/assets/FleetPanel-tr0wjAwr.js +1 -0
  83. package/ui/dist/assets/HomelabPanel-m8vMm5rE.js +1 -0
  84. package/ui/dist/assets/InfraView-3WriJnyE.js +2 -0
  85. package/ui/dist/assets/IntegrationsPanel-BmL70fOw.js +1 -0
  86. package/ui/dist/assets/IntelligenceView-BGVxCKD3.js +2 -0
  87. package/ui/dist/assets/LearningPanel-CrYLQ6eV.js +1 -0
  88. package/ui/dist/assets/LiveStudio-CnW3eZYF.js +2 -0
  89. package/ui/dist/assets/LogsPanel-DbShr4Ic.js +1 -0
  90. package/ui/dist/assets/McpPanel-uMYEsPI4.js +1 -0
  91. package/ui/dist/assets/MemoryGraphPanel-C7-Z8f0v.js +1 -0
  92. package/ui/dist/assets/MemoryTaxonomyPanel-KrxN8U1G.js +1 -0
  93. package/ui/dist/assets/MemoryWikiPanel-DI3q6IHC.js +1 -0
  94. package/ui/dist/assets/MeshPanel-D4-g2tlu.js +1 -0
  95. package/ui/dist/assets/MissionCanvas-D8cjakJe.js +61 -0
  96. package/ui/dist/assets/MissionChat-BvqCUf1E.js +3 -0
  97. package/ui/dist/assets/MissionLibrary-CZoJQ9WD.js +1 -0
  98. package/ui/dist/assets/MissionStart-CXCq-_AD.js +1 -0
  99. package/ui/dist/assets/NvidiaPanel-BfI1K5Pg.js +1 -0
  100. package/ui/dist/assets/OrganismPanel-B7Gm9pP-.js +1 -0
  101. package/ui/dist/assets/OverviewPanel-B9tKWRK2.js +1 -0
  102. package/ui/dist/assets/PageHeader-CH85uoZG.js +1 -0
  103. package/ui/dist/assets/PersonaProfilesPanel-BPi8fl-U.js +3 -0
  104. package/ui/dist/assets/PersonasPanel-Dwr7sycT.js +1 -0
  105. package/ui/dist/assets/PhoneDeskPanel-D7qc6wUI.js +1 -0
  106. package/ui/dist/assets/RecipesPanel-DcqPWRSU.js +1 -0
  107. package/ui/dist/assets/SecurityPanel-Bv_B9Ahf.js +1 -0
  108. package/ui/dist/assets/SelfImprovePanel-Ca4rGwEG.js +1 -0
  109. package/ui/dist/assets/SelfProposalsPanel-DteMSASq.js +1 -0
  110. package/ui/dist/assets/SessionsPanel-BdbbKTbL.js +1 -0
  111. package/ui/dist/assets/SessionsTab-BJipiVJs.js +1 -0
  112. package/ui/dist/assets/SettingsPanel-mV7ykOCc.js +1 -0
  113. package/ui/dist/assets/SettingsView-Bu_wYTT5.js +2 -0
  114. package/ui/dist/assets/SkeletonLoader-9H7QWa9E.js +1 -0
  115. package/ui/dist/assets/SkillsPanel-CHwX2kDR.js +1 -0
  116. package/ui/dist/assets/SomaView-Bpsm4ZW7.js +5 -0
  117. package/ui/dist/assets/SomaView-Dghh9om4.css +1 -0
  118. package/ui/dist/assets/StatCard-wmSgMAM3.js +1 -0
  119. package/ui/dist/assets/TeamsPanel-XJiHxdDX.js +1 -0
  120. package/ui/dist/assets/TelemetryPanel-_pmGVxpO.js +1 -0
  121. package/ui/dist/assets/TitanCanvas-Cb7KG80W.css +1 -0
  122. package/ui/dist/assets/TitanCanvas-CmLy7p2N.js +883 -0
  123. package/ui/dist/assets/ToolsView-PHizHZBx.js +2 -0
  124. package/ui/dist/assets/TraceViewer-CYAzIcfB.js +1 -0
  125. package/ui/dist/assets/TracesPanel-CRSN_dzK.js +1 -0
  126. package/ui/dist/assets/TrainingPanel-B9rcuMsV.js +1 -0
  127. package/ui/dist/assets/VoiceOverlay-BL_ns1ZA.js +7 -0
  128. package/ui/dist/assets/VramPanel-BNPQ06_9.js +1 -0
  129. package/ui/dist/assets/WatchView-BgAkNnur.js +13 -0
  130. package/ui/dist/assets/WorkTab-RRpPpEOg.js +1 -0
  131. package/ui/dist/assets/WorkflowBuilderPanel-BPHIH8pf.js +1 -0
  132. package/ui/dist/assets/WorkflowsPanel-DqtmT0NB.js +1 -0
  133. package/ui/dist/assets/approvalHeadline-BRg3ReTi.js +1 -0
  134. package/ui/dist/assets/archive-DcK9HrbE.js +1 -0
  135. package/ui/dist/assets/arrow-left-4c2FnES1.js +1 -0
  136. package/ui/dist/assets/book-open-Ds_yciQm.js +1 -0
  137. package/ui/dist/assets/bot-CWDCc265.js +1 -0
  138. package/ui/dist/assets/chart-column-hhZMdCgB.js +1 -0
  139. package/ui/dist/assets/chevron-right-A_pvnRh_.js +1 -0
  140. package/ui/dist/assets/circle-check-big-C-JUM7wb.js +1 -0
  141. package/ui/dist/assets/circle-stop-yZuG9ZAE.js +1 -0
  142. package/ui/dist/assets/clipboard-list-C2HUEjZc.js +1 -0
  143. package/ui/dist/assets/clock-DSPgndSD.js +1 -0
  144. package/ui/dist/assets/clsx-CjueKrWZ.js +1 -0
  145. package/ui/dist/assets/createLucideIcon-qnlrxww7.js +1 -0
  146. package/ui/dist/assets/database-C1SgHa8T.js +1 -0
  147. package/ui/dist/assets/dollar-sign-D9siqK91.js +1 -0
  148. package/ui/dist/assets/download-CJZWQNfV.js +1 -0
  149. package/ui/dist/assets/external-link-CpQLWKKE.js +1 -0
  150. package/ui/dist/assets/file-text-BbGTi-Po.js +1 -0
  151. package/ui/dist/assets/flask-conical-B1YVft4E.js +1 -0
  152. package/ui/dist/assets/folder-EbR-w2Mq.js +1 -0
  153. package/ui/dist/assets/funnel-D2Dkn8Pf.js +1 -0
  154. package/ui/dist/assets/git-branch-CQ2yNSzU.js +1 -0
  155. package/ui/dist/assets/git-pull-request-BxL5wlGX.js +1 -0
  156. package/ui/dist/assets/index-Cz8190xO.css +2 -0
  157. package/ui/dist/assets/index-WI-E4I3h.js +243 -0
  158. package/ui/dist/assets/jsx-runtime-B15e6lOy.js +1 -0
  159. package/ui/dist/assets/layers-BPu5SRjI.js +1 -0
  160. package/ui/dist/assets/legacy-BIzifT-t.js +3 -0
  161. package/ui/dist/assets/lightbulb-BY7CpMte.js +1 -0
  162. package/ui/dist/assets/link-VDQkrGJw.js +1 -0
  163. package/ui/dist/assets/list-todo-DfnNUV_T.js +1 -0
  164. package/ui/dist/assets/message-square-BpzrINHz.js +1 -0
  165. package/ui/dist/assets/mic-De6oZFaV.js +1 -0
  166. package/ui/dist/assets/missions-y-qg_f9b.js +1 -0
  167. package/ui/dist/assets/network-CM7y1ats.js +1 -0
  168. package/ui/dist/assets/pause-C4TDJSfA.js +1 -0
  169. package/ui/dist/assets/pencil-Dd_vzHE-.js +1 -0
  170. package/ui/dist/assets/play-9WxZWVHA.js +1 -0
  171. package/ui/dist/assets/plug-Dm25Wbrc.js +1 -0
  172. package/ui/dist/assets/plus-CHryCIkk.js +1 -0
  173. package/ui/dist/assets/radio-D-XnoAVu.js +1 -0
  174. package/ui/dist/assets/refresh-cw-Disd5wRB.js +1 -0
  175. package/ui/dist/assets/rotate-ccw-Cw1T2W1T.js +1 -0
  176. package/ui/dist/assets/save-DP7oNar2.js +1 -0
  177. package/ui/dist/assets/search-aNVcX-nv.js +1 -0
  178. package/ui/dist/assets/send-D7iQD57b.js +1 -0
  179. package/ui/dist/assets/server-DFfeYjoM.js +1 -0
  180. package/ui/dist/assets/settings-5NWXzgyY.js +1 -0
  181. package/ui/dist/assets/shared-DLz_Jtg3.js +1 -0
  182. package/ui/dist/assets/shield-check-Cd50jCAn.js +1 -0
  183. package/ui/dist/assets/square-DlcoJ1ZM.js +1 -0
  184. package/ui/dist/assets/terminal-DgFz2Vsf.js +1 -0
  185. package/ui/dist/assets/test-tube-BimtS92_.js +1 -0
  186. package/ui/dist/assets/toggle-right-BMaR-sfT.js +1 -0
  187. package/ui/dist/assets/trash-2-CwUv7ww1.js +1 -0
  188. package/ui/dist/assets/trending-up-Ba9TwEow.js +1 -0
  189. package/ui/dist/assets/trophy-Gfv1QC45.js +1 -0
  190. package/ui/dist/assets/volume-2-BVlfVl2U.js +1 -0
  191. package/ui/dist/assets/wifi-off-CWLeusSV.js +1 -0
  192. package/ui/dist/assets/zoom-out-DeggJ7RM.js +1 -0
  193. package/ui/dist/index.html +4 -2
  194. package/ui/dist/sw.js +1 -1
  195. package/ui/dist/assets/AboutYouMemory-CZKEOd_O.js +0 -11
  196. package/ui/dist/assets/AuditPanel-Bu71N5Er.js +0 -1
  197. package/ui/dist/assets/AutonomyPanel-DB-39dX4.js +0 -11
  198. package/ui/dist/assets/AutopilotPanel-Cj8oKxWv.js +0 -1
  199. package/ui/dist/assets/AutoresearchPanel-3s8pjm6Z.js +0 -6
  200. package/ui/dist/assets/BackupPanel-EUQJMJE7.js +0 -1
  201. package/ui/dist/assets/BrowserPanel-Beo4F5Ar.js +0 -6
  202. package/ui/dist/assets/Button-MdXmb4_n.js +0 -1
  203. package/ui/dist/assets/CPActivity-B1Se9VVc.js +0 -1
  204. package/ui/dist/assets/CPAgentDetail-BYji6tT8.js +0 -1
  205. package/ui/dist/assets/CPAgents-BSUKucTe.js +0 -1
  206. package/ui/dist/assets/CPApprovals-B4v0em6e.js +0 -1
  207. package/ui/dist/assets/CPCosts-9ikh61Kv.js +0 -1
  208. package/ui/dist/assets/CPDashboard-sOok2T5v.js +0 -1
  209. package/ui/dist/assets/CPFiles-DB2nY5JE.js +0 -1
  210. package/ui/dist/assets/CPGoals-8Z1yOY8e.js +0 -1
  211. package/ui/dist/assets/CPInbox-Cq6-UY8F.js +0 -11
  212. package/ui/dist/assets/CPIssueDetail-NAGfmJg8.js +0 -1
  213. package/ui/dist/assets/CPIssues-BzX6Rckx.js +0 -1
  214. package/ui/dist/assets/CPLayout-BcRM6QSg.js +0 -2
  215. package/ui/dist/assets/CPOrg-B7DQ4UQP.js +0 -1
  216. package/ui/dist/assets/CPRuns-D-cpPw_M.js +0 -1
  217. package/ui/dist/assets/CPSocial-DshtmcK0.js +0 -11
  218. package/ui/dist/assets/CPVoice-DfXRnQVU.js +0 -1
  219. package/ui/dist/assets/ChannelsPanel-BnaTOeyf.js +0 -1
  220. package/ui/dist/assets/CheckpointsPanel-C62naE4U.js +0 -1
  221. package/ui/dist/assets/CommandPostHub-B_L1CAss.js +0 -24
  222. package/ui/dist/assets/ConfirmDialog-VErHWAbo.js +0 -1
  223. package/ui/dist/assets/CronPanel-BwMrrZ5d.js +0 -1
  224. package/ui/dist/assets/DataTable-BCqSUuN7.js +0 -1
  225. package/ui/dist/assets/DreamPanel-BNAcCLBB.js +0 -15
  226. package/ui/dist/assets/EmptyState-D9kQf1PX.js +0 -1
  227. package/ui/dist/assets/EvalHarnessPanel-BaMLdBNm.js +0 -2
  228. package/ui/dist/assets/EvalPanel-CW-SSjtg.js +0 -1
  229. package/ui/dist/assets/FileViewer-BL00nsd7.js +0 -61
  230. package/ui/dist/assets/FilesPanel-CeC6YUSd.js +0 -16
  231. package/ui/dist/assets/FleetPanel-EyWF7l3W.js +0 -1
  232. package/ui/dist/assets/HomelabPanel-BRfKRYe7.js +0 -6
  233. package/ui/dist/assets/InfraView-Jmqhg_Yc.js +0 -2
  234. package/ui/dist/assets/InlineEditableField-DgMrLDj1.js +0 -1
  235. package/ui/dist/assets/Input-CBTwa_Iw.js +0 -1
  236. package/ui/dist/assets/IntegrationsPanel-DQL-7Ec6.js +0 -1
  237. package/ui/dist/assets/IntelligenceView-DxD78qfP.js +0 -2
  238. package/ui/dist/assets/LearningPanel-C_JzSbJU.js +0 -1
  239. package/ui/dist/assets/LiveStudio-Db5yeaWo.js +0 -7
  240. package/ui/dist/assets/LogsPanel-CpinKwaZ.js +0 -1
  241. package/ui/dist/assets/McpPanel-D-MqN_f-.js +0 -1
  242. package/ui/dist/assets/MemoryGraphPanel-BnD5jRqf.js +0 -1
  243. package/ui/dist/assets/MemoryTaxonomyPanel-BBI202dd.js +0 -6
  244. package/ui/dist/assets/MemoryWikiPanel-Dn3XZHL0.js +0 -6
  245. package/ui/dist/assets/MeshPanel-C-mRJpr0.js +0 -11
  246. package/ui/dist/assets/MissionCanvas-CbZW83Ch.js +0 -72
  247. package/ui/dist/assets/MissionChat-Bs75jPAD.js +0 -7
  248. package/ui/dist/assets/MissionLibrary-BxQ6SxPe.js +0 -5
  249. package/ui/dist/assets/MissionStart-CBuBPasl.js +0 -1
  250. package/ui/dist/assets/Modal-ybPGvlQs.js +0 -9
  251. package/ui/dist/assets/NvidiaPanel-DdG13OX4.js +0 -1
  252. package/ui/dist/assets/OrganismPanel-xZWUJKyr.js +0 -1
  253. package/ui/dist/assets/OverviewPanel-B45ZYhJV.js +0 -1
  254. package/ui/dist/assets/PageHeader-DdrbS1aq.js +0 -1
  255. package/ui/dist/assets/PersonaProfilesPanel-CKER-EU5.js +0 -8
  256. package/ui/dist/assets/PersonasPanel-CMMQ3nh3.js +0 -1
  257. package/ui/dist/assets/PhoneDeskPanel-Bw_Y-cVE.js +0 -11
  258. package/ui/dist/assets/RecipesPanel-DzMcxpJJ.js +0 -1
  259. package/ui/dist/assets/SecurityPanel-BCq3Fzbr.js +0 -1
  260. package/ui/dist/assets/SelfImprovePanel-YE3hT8jK.js +0 -1
  261. package/ui/dist/assets/SelfProposalsPanel-DXuqBM6p.js +0 -1
  262. package/ui/dist/assets/SessionsPanel-DORz5goR.js +0 -1
  263. package/ui/dist/assets/SessionsTab-qYHDOuC8.js +0 -1
  264. package/ui/dist/assets/SettingsPanel-BbwPu1i7.js +0 -1
  265. package/ui/dist/assets/SettingsView-zgqkDDBJ.js +0 -2
  266. package/ui/dist/assets/SkeletonLoader-r6OGXaU9.js +0 -1
  267. package/ui/dist/assets/SkillsPanel-BjEOBMRo.js +0 -11
  268. package/ui/dist/assets/SomaView-BtCkeWfZ.css +0 -1
  269. package/ui/dist/assets/SomaView-DLzApZVW.js +0 -5
  270. package/ui/dist/assets/StatCard-DLq8aBps.js +0 -1
  271. package/ui/dist/assets/StatusBadge-BqlvIbdj.js +0 -1
  272. package/ui/dist/assets/Tabs-DdUThlp1.js +0 -1
  273. package/ui/dist/assets/TeamsPanel-uIIKoU7I.js +0 -1
  274. package/ui/dist/assets/TelemetryPanel-dVr3yQQr.js +0 -1
  275. package/ui/dist/assets/TitanCanvas-BfitWg9R.css +0 -1
  276. package/ui/dist/assets/TitanCanvas-xHAJqwG2.js +0 -945
  277. package/ui/dist/assets/ToolsView-CG1mZdiy.js +0 -2
  278. package/ui/dist/assets/Tooltip-C2tXJcrN.js +0 -6
  279. package/ui/dist/assets/TraceViewer-BIMD_Osr.js +0 -1
  280. package/ui/dist/assets/TracesPanel-nMUiyMj5.js +0 -1
  281. package/ui/dist/assets/TrainingPanel-CT5R4HWm.js +0 -1
  282. package/ui/dist/assets/VoiceOverlay-Br4QnCtw.js +0 -17
  283. package/ui/dist/assets/VramPanel-CdnJYytg.js +0 -1
  284. package/ui/dist/assets/WatchView-TNU6Ns1Q.js +0 -13
  285. package/ui/dist/assets/WorkTab-wTlX_UKP.js +0 -1
  286. package/ui/dist/assets/WorkflowBuilderPanel-CDpVt5ux.js +0 -6
  287. package/ui/dist/assets/WorkflowsPanel-zsFqsLdn.js +0 -11
  288. package/ui/dist/assets/approvalHeadline-DB9SgR-9.js +0 -1
  289. package/ui/dist/assets/archive-Bh689DNg.js +0 -6
  290. package/ui/dist/assets/arrow-left-WkVYLm80.js +0 -6
  291. package/ui/dist/assets/book-open-ohEXgUsS.js +0 -6
  292. package/ui/dist/assets/bot-DvEcyQcD.js +0 -6
  293. package/ui/dist/assets/chart-column-w3goFd5f.js +0 -6
  294. package/ui/dist/assets/check-CuRYh4Zh.js +0 -6
  295. package/ui/dist/assets/chevron-right-DbLSHmff.js +0 -6
  296. package/ui/dist/assets/circle-check-big-Bkba568b.js +0 -6
  297. package/ui/dist/assets/circle-stop-Bx2ohEg8.js +0 -6
  298. package/ui/dist/assets/clipboard-list-Dj9tORnu.js +0 -6
  299. package/ui/dist/assets/clock-JyQAYy6X.js +0 -6
  300. package/ui/dist/assets/clsx-B-dksMZM.js +0 -1
  301. package/ui/dist/assets/dollar-sign-XxwPAA2a.js +0 -6
  302. package/ui/dist/assets/download-D1izVEOL.js +0 -6
  303. package/ui/dist/assets/external-link-DZ_rZsIt.js +0 -6
  304. package/ui/dist/assets/file-text-Cfawu8Bs.js +0 -6
  305. package/ui/dist/assets/flask-conical-Ql8QxhgY.js +0 -6
  306. package/ui/dist/assets/folder-DAdIw-Pb.js +0 -6
  307. package/ui/dist/assets/funnel-FycfZcXo.js +0 -6
  308. package/ui/dist/assets/git-branch-C2qrhGGu.js +0 -6
  309. package/ui/dist/assets/index-Cml2x9pA.js +0 -507
  310. package/ui/dist/assets/index-yWybhcfI.css +0 -1
  311. package/ui/dist/assets/layers-yzjtGlHB.js +0 -6
  312. package/ui/dist/assets/legacy-D21WSMF1.js +0 -3
  313. package/ui/dist/assets/lightbulb-BZJNK69S.js +0 -6
  314. package/ui/dist/assets/link-CnFkBkaY.js +0 -6
  315. package/ui/dist/assets/list-todo-BlgfQHup.js +0 -6
  316. package/ui/dist/assets/message-square-CRsG9u6_.js +0 -6
  317. package/ui/dist/assets/mic-B32RHc7Y.js +0 -6
  318. package/ui/dist/assets/missions-1iw8Z2_0.js +0 -1
  319. package/ui/dist/assets/network-C9RXQGrV.js +0 -6
  320. package/ui/dist/assets/pause-BT1Bc2q3.js +0 -6
  321. package/ui/dist/assets/pencil-BK5IU6o0.js +0 -6
  322. package/ui/dist/assets/play-Cl1nC_gc.js +0 -6
  323. package/ui/dist/assets/plug-DvcpOalj.js +0 -6
  324. package/ui/dist/assets/plus-Dpo1qfrl.js +0 -6
  325. package/ui/dist/assets/radio-C5c9IyqX.js +0 -6
  326. package/ui/dist/assets/refresh-cw-C-iZkzuS.js +0 -6
  327. package/ui/dist/assets/rotate-ccw-qzrB3SNw.js +0 -6
  328. package/ui/dist/assets/save-DwR9wQVu.js +0 -6
  329. package/ui/dist/assets/search-D10l0m3-.js +0 -6
  330. package/ui/dist/assets/send-DtXGsaFW.js +0 -6
  331. package/ui/dist/assets/server-BzisF3LL.js +0 -6
  332. package/ui/dist/assets/settings-Dbwd1_-c.js +0 -6
  333. package/ui/dist/assets/shield-check-CXlDJyAW.js +0 -6
  334. package/ui/dist/assets/square-BBC-JKzY.js +0 -11
  335. package/ui/dist/assets/terminal-DaBIlWEM.js +0 -6
  336. package/ui/dist/assets/test-tube-CDATRD5t.js +0 -11
  337. package/ui/dist/assets/toggle-right-DTmrC5RD.js +0 -11
  338. package/ui/dist/assets/trash-2-CTeyjxD_.js +0 -6
  339. package/ui/dist/assets/trending-up-CXPRjRV4.js +0 -6
  340. package/ui/dist/assets/trophy-DbVdXRtU.js +0 -6
  341. package/ui/dist/assets/volume-2-DYDBmm3M.js +0 -6
  342. package/ui/dist/assets/wifi-off-BBuIGbiP.js +0 -6
  343. package/ui/dist/assets/zoom-out-D47_pRAO.js +0 -16
@@ -52,6 +52,37 @@ ${aliasLines || " (none configured)"}`
52
52
  setSessionModelOverride(channel, userId, args);
53
53
  return { handled: true, response: `\u2705 Model switched to \`${args}\` for this session.` };
54
54
  });
55
+ registerSlashCommand("moa", async (args, channel, userId) => {
56
+ const { getMoaPresets } = await import("../providers/moa.js");
57
+ const presets = getMoaPresets();
58
+ const names = Object.keys(presets);
59
+ const cfg = loadConfig();
60
+ const def = cfg.moa?.defaultPreset || names[0];
61
+ const trimmed = args.trim();
62
+ if (!trimmed) {
63
+ if (names.length === 0) {
64
+ return { handled: true, response: "No MoA presets configured yet. Add one under `moa.presets` in titan.json \u2014 e.g. a local council of your own models advising an aggregator. Docs: MOA section in README." };
65
+ }
66
+ return { handled: true, response: `\u{1F9E0} Mixture of Agents \u2014 presets: ${names.map((n) => n === def ? `**${n}** (default)` : n).join(", ")}
67
+ Use: \`/moa <prompt>\` (one-shot) \xB7 \`/moa use <preset>\` (session) \xB7 \`/moa off\`` };
68
+ }
69
+ if (trimmed === "off") {
70
+ setSessionModelOverride(channel, userId, "");
71
+ return { handled: true, response: "MoA off \u2014 back to your configured model." };
72
+ }
73
+ if (trimmed.startsWith("use ")) {
74
+ const name = trimmed.slice(4).trim();
75
+ if (!presets[name]) return { handled: true, response: `No MoA preset "${name}". Available: ${names.join(", ") || "(none configured)"}` };
76
+ setSessionModelOverride(channel, userId, `moa/${name}`);
77
+ return { handled: true, response: `\u{1F9E0} This session now thinks as a council: **moa/${name}** (${presets[name].references.length} advisors \u2192 ${presets[name].aggregator}). \`/moa off\` to revert.` };
78
+ }
79
+ if (!def) {
80
+ return { handled: true, response: "No MoA presets configured yet \u2014 add one under `moa.presets` in titan.json first." };
81
+ }
82
+ const { processMessage } = await import("../agent/agent.js");
83
+ const res = await processMessage(trimmed, channel, userId, { model: `moa/${def}` });
84
+ return { handled: true, response: res.content };
85
+ });
55
86
  registerSlashCommand("think", (args, channel, userId) => {
56
87
  const session = getOrCreateSession(channel, userId, "default");
57
88
  const config = loadConfig();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/gateway/slashCommands.ts"],"sourcesContent":["/**\n * TITAN — Slash Command System (OpenClaw-Inspired)\n * Provides /model, /think, /usage, /compact, /verbose, /reset, /new, /status commands\n * that users can run from any channel to control their session in real-time.\n */\nimport { loadConfig } from '../config/config.js';\nimport { getOrCreateSession, closeSession, replaceSessionContext, setSessionModelOverride, setSessionThinkingOverride, setSessionVerbose } from '../agent/session.js';\nimport { getSessionCost, getDailyTotal, formatCostSummary } from '../agent/costOptimizer.js';\nimport { getContextMessages } from '../agent/session.js';\nimport { forceCompactContext } from '../agent/contextManager.js';\nimport { resolveModel, getModelAliases } from '../providers/router.js';\nimport { isModelAllowed } from '../providers/router.js';\nimport { getDeliberation, cancelDeliberation, analyze, generatePlan } from '../agent/deliberation.js';\nimport logger from '../utils/logger.js';\nimport { TITAN_VERSION } from '../utils/constants.js';\n\nconst COMPONENT = 'SlashCmd';\n\nexport interface SlashCommandResult {\n handled: boolean;\n response: string;\n}\n\ntype SlashCommandHandler = (\n args: string,\n channel: string,\n userId: string,\n) => Promise<SlashCommandResult> | SlashCommandResult;\n\n/** Command registry */\nconst commands: Map<string, SlashCommandHandler> = new Map();\n\n/** Register a slash command */\nexport function registerSlashCommand(name: string, handler: SlashCommandHandler): void {\n commands.set(name.toLowerCase(), handler);\n}\n\n/** Parse and dispatch a slash command. Returns null if not a slash command. */\nexport async function handleSlashCommand(\n message: string,\n channel: string,\n userId: string,\n): Promise<SlashCommandResult | null> {\n const trimmed = message.trim();\n if (!trimmed.startsWith('/')) return null;\n\n const spaceIdx = trimmed.indexOf(' ');\n const name = (spaceIdx > 0 ? trimmed.slice(1, spaceIdx) : trimmed.slice(1)).toLowerCase();\n const args = spaceIdx > 0 ? trimmed.slice(spaceIdx + 1).trim() : '';\n\n const handler = commands.get(name);\n if (!handler) return null;\n\n logger.info(COMPONENT, `Executing /${name} ${args ? `(args: ${args})` : ''} [${channel}/${userId}]`);\n return handler(args, channel, userId);\n}\n\n/** Initialize all built-in slash commands */\nexport function initSlashCommands(): void {\n // ── /model [provider/model] ──\n registerSlashCommand('model', async (args, channel, userId) => {\n const config = loadConfig();\n const session = getOrCreateSession(channel, userId, 'default');\n\n if (!args) {\n // Show current model + aliases\n const current = session.modelOverride || config.agent.model;\n const aliases = getModelAliases();\n const aliasLines = Object.entries(aliases)\n .map(([name, model]) => ` \\`${name}\\` → ${model}`)\n .join('\\n');\n return {\n handled: true,\n response: `🤖 **Current Model**: \\`${current}\\`${session.modelOverride ? ' (session override)' : ''}\\n\\n**Aliases:**\\n${aliasLines || ' (none configured)'}`,\n };\n }\n\n // Validate the model\n try {\n // Check allowlist first\n if (!isModelAllowed(args)) {\n return { handled: true, response: `⛔ Model \\`${args}\\` is not in the allowed models list.` };\n }\n resolveModel(args);\n } catch (e) {\n return { handled: true, response: `❌ Invalid model: ${(e as Error).message}` };\n }\n\n setSessionModelOverride(channel, userId, args);\n return { handled: true, response: `✅ Model switched to \\`${args}\\` for this session.` };\n });\n\n // ── /think [off|low|medium|high] ──\n registerSlashCommand('think', (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n const config = loadConfig();\n\n if (!args) {\n const current = session.thinkingOverride || config.agent.thinkingMode || 'off';\n return {\n handled: true,\n response: `🧠 **Thinking Mode**: \\`${current}\\`${session.thinkingOverride ? ' (session override)' : ''}\\nOptions: \\`off\\`, \\`low\\`, \\`medium\\`, \\`high\\``,\n };\n }\n\n const level = args.toLowerCase();\n if (!['off', 'low', 'medium', 'high'].includes(level)) {\n return { handled: true, response: `❌ Invalid thinking level: \\`${args}\\`. Use: off, low, medium, high` };\n }\n\n setSessionThinkingOverride(channel, userId, level as 'off' | 'low' | 'medium' | 'high');\n return { handled: true, response: `✅ Thinking mode set to \\`${level}\\` for this session.` };\n });\n\n // ── /usage ──\n registerSlashCommand('usage', (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n const cost = getSessionCost(session.id);\n const dailyTotal = getDailyTotal();\n\n if (!cost) {\n return { handled: true, response: `📊 **Usage**: No API calls recorded in this session yet.\\n• **Daily Total**: $${dailyTotal.toFixed(5)}` };\n }\n\n return {\n handled: true,\n response: [\n `📊 **Session Usage**`,\n `• **API Calls**: ${cost.calls}`,\n `• **Tokens**: ${cost.inputTokens.toLocaleString()} input + ${cost.outputTokens.toLocaleString()} output = ${(cost.inputTokens + cost.outputTokens).toLocaleString()} total`,\n `• **Session Cost**: $${cost.estimatedUsd.toFixed(5)}`,\n `• **Daily Total**: $${dailyTotal.toFixed(5)}`,\n ].join('\\n'),\n };\n });\n\n // ── /compact ──\n registerSlashCommand('compact', (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n const messages = getContextMessages(session);\n\n if (messages.length <= 4) {\n return { handled: true, response: `📦 Context is already compact (${messages.length} messages).` };\n }\n\n const { messages: compacted, savedTokens } = forceCompactContext(messages);\n replaceSessionContext(session, compacted);\n\n return {\n handled: true,\n response: `📦 **Context Compacted**\\n• Before: ${messages.length} messages\\n• After: ${compacted.length} messages\\n• Saved: ~${savedTokens.toLocaleString()} tokens`,\n };\n });\n\n // ── /verbose [on|off] ──\n registerSlashCommand('verbose', (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n\n if (!args) {\n return {\n handled: true,\n response: `🔊 **Verbose Mode**: \\`${session.verboseMode ? 'on' : 'off'}\\``,\n };\n }\n\n const on = ['on', '1', 'true', 'yes'].includes(args.toLowerCase());\n setSessionVerbose(channel, userId, on);\n return { handled: true, response: `✅ Verbose mode \\`${on ? 'on' : 'off'}\\` for this session.` };\n });\n\n // ── /reset and /new ──\n const resetHandler: SlashCommandHandler = (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n closeSession(session.id);\n return { handled: true, response: `🔄 Session **${session.id.slice(0, 8)}** has been reset. Context and overrides cleared.` };\n };\n registerSlashCommand('reset', resetHandler);\n registerSlashCommand('new', resetHandler);\n\n // ── /status ──\n registerSlashCommand('status', (args, channel, userId) => {\n const config = loadConfig();\n const session = getOrCreateSession(channel, userId, 'default');\n const costStr = formatCostSummary(session.id);\n\n const lines = [\n `📊 **TITAN Status** (v${TITAN_VERSION})`,\n `• **Session**: \\`${session.id.slice(0, 8)}\\` (${session.messageCount} messages)`,\n `• **Model**: ${session.modelOverride || config.agent.model}${session.modelOverride ? ' *(session override)*' : ''}`,\n `• **Thinking**: ${session.thinkingOverride || config.agent.thinkingMode || 'off'}${session.thinkingOverride ? ' *(session override)*' : ''}`,\n `• **Verbose**: ${session.verboseMode ? 'on' : 'off'}`,\n `• **Usage**: ${costStr}`,\n ];\n\n return { handled: true, response: lines.join('\\n') };\n });\n\n // ── /plan [goal|status|cancel] ──\n registerSlashCommand('plan', async (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n\n if (!args || args.toLowerCase() === 'status') {\n const delib = getDeliberation(session.id);\n if (!delib) {\n return { handled: true, response: '📋 No active deliberation plan for this session.' };\n }\n const statusLines = [\n `📋 **Deliberation Status**: \\`${delib.stage}\\``,\n `**Goal**: ${delib.originalMessage.slice(0, 100)}`,\n `**Tasks completed**: ${delib.results.filter(r => r.success).length}/${delib.results.length}`,\n ];\n if (delib.planMarkdown) statusLines.push('', delib.planMarkdown);\n return { handled: true, response: statusLines.join('\\n') };\n }\n\n if (args.toLowerCase() === 'cancel') {\n const cancelled = cancelDeliberation(session.id);\n return { handled: true, response: cancelled ? '✅ Deliberation cancelled.' : '❌ No active deliberation to cancel.' };\n }\n\n // Force deliberation mode with the given goal\n const config = loadConfig();\n try {\n const state = await analyze(args, session.id, config);\n if (state.stage === 'planning') {\n const planned = await generatePlan(state, config);\n if (planned.planMarkdown) {\n return { handled: true, response: planned.planMarkdown };\n }\n }\n return { handled: true, response: `❌ Deliberation failed: ${state.error || 'Could not generate a plan.'}` };\n } catch (e) {\n return { handled: true, response: `❌ Error: ${(e as Error).message}` };\n }\n });\n\n logger.info(COMPONENT, `Registered ${commands.size} slash commands`);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/gateway/slashCommands.ts"],"sourcesContent":["/**\n * TITAN — Slash Command System (OpenClaw-Inspired)\n * Provides /model, /think, /usage, /compact, /verbose, /reset, /new, /status commands\n * that users can run from any channel to control their session in real-time.\n */\nimport { loadConfig } from '../config/config.js';\nimport { getOrCreateSession, closeSession, replaceSessionContext, setSessionModelOverride, setSessionThinkingOverride, setSessionVerbose } from '../agent/session.js';\nimport { getSessionCost, getDailyTotal, formatCostSummary } from '../agent/costOptimizer.js';\nimport { getContextMessages } from '../agent/session.js';\nimport { forceCompactContext } from '../agent/contextManager.js';\nimport { resolveModel, getModelAliases } from '../providers/router.js';\nimport { isModelAllowed } from '../providers/router.js';\nimport { getDeliberation, cancelDeliberation, analyze, generatePlan } from '../agent/deliberation.js';\nimport logger from '../utils/logger.js';\nimport { TITAN_VERSION } from '../utils/constants.js';\n\nconst COMPONENT = 'SlashCmd';\n\nexport interface SlashCommandResult {\n handled: boolean;\n response: string;\n}\n\ntype SlashCommandHandler = (\n args: string,\n channel: string,\n userId: string,\n) => Promise<SlashCommandResult> | SlashCommandResult;\n\n/** Command registry */\nconst commands: Map<string, SlashCommandHandler> = new Map();\n\n/** Register a slash command */\nexport function registerSlashCommand(name: string, handler: SlashCommandHandler): void {\n commands.set(name.toLowerCase(), handler);\n}\n\n/** Parse and dispatch a slash command. Returns null if not a slash command. */\nexport async function handleSlashCommand(\n message: string,\n channel: string,\n userId: string,\n): Promise<SlashCommandResult | null> {\n const trimmed = message.trim();\n if (!trimmed.startsWith('/')) return null;\n\n const spaceIdx = trimmed.indexOf(' ');\n const name = (spaceIdx > 0 ? trimmed.slice(1, spaceIdx) : trimmed.slice(1)).toLowerCase();\n const args = spaceIdx > 0 ? trimmed.slice(spaceIdx + 1).trim() : '';\n\n const handler = commands.get(name);\n if (!handler) return null;\n\n logger.info(COMPONENT, `Executing /${name} ${args ? `(args: ${args})` : ''} [${channel}/${userId}]`);\n return handler(args, channel, userId);\n}\n\n/** Initialize all built-in slash commands */\nexport function initSlashCommands(): void {\n // ── /model [provider/model] ──\n registerSlashCommand('model', async (args, channel, userId) => {\n const config = loadConfig();\n const session = getOrCreateSession(channel, userId, 'default');\n\n if (!args) {\n // Show current model + aliases\n const current = session.modelOverride || config.agent.model;\n const aliases = getModelAliases();\n const aliasLines = Object.entries(aliases)\n .map(([name, model]) => ` \\`${name}\\` → ${model}`)\n .join('\\n');\n return {\n handled: true,\n response: `🤖 **Current Model**: \\`${current}\\`${session.modelOverride ? ' (session override)' : ''}\\n\\n**Aliases:**\\n${aliasLines || ' (none configured)'}`,\n };\n }\n\n // Validate the model\n try {\n // Check allowlist first\n if (!isModelAllowed(args)) {\n return { handled: true, response: `⛔ Model \\`${args}\\` is not in the allowed models list.` };\n }\n resolveModel(args);\n } catch (e) {\n return { handled: true, response: `❌ Invalid model: ${(e as Error).message}` };\n }\n\n setSessionModelOverride(channel, userId, args);\n return { handled: true, response: `✅ Model switched to \\`${args}\\` for this session.` };\n });\n\n // ── /think [off|low|medium|high] ──\n // ── /moa — Mixture of Agents (v7.1) ─────────────────────────────\n // \"/moa <prompt>\" = one-shot: answer this with the default MoA preset.\n // \"/moa use <preset>\" = make the preset this session's model.\n // \"/moa off\" = back to the configured model.\n // \"/moa\" = status + available presets.\n registerSlashCommand('moa', async (args, channel, userId) => {\n const { getMoaPresets } = await import('../providers/moa.js');\n const presets = getMoaPresets();\n const names = Object.keys(presets);\n const cfg = loadConfig() as { moa?: { defaultPreset?: string } };\n const def = cfg.moa?.defaultPreset || names[0];\n const trimmed = args.trim();\n\n if (!trimmed) {\n if (names.length === 0) {\n return { handled: true, response: 'No MoA presets configured yet. Add one under `moa.presets` in titan.json — e.g. a local council of your own models advising an aggregator. Docs: MOA section in README.' };\n }\n return { handled: true, response: `🧠 Mixture of Agents — presets: ${names.map(n => n === def ? `**${n}** (default)` : n).join(', ')}\\nUse: \\`/moa <prompt>\\` (one-shot) · \\`/moa use <preset>\\` (session) · \\`/moa off\\`` };\n }\n if (trimmed === 'off') {\n setSessionModelOverride(channel, userId, '');\n return { handled: true, response: 'MoA off — back to your configured model.' };\n }\n if (trimmed.startsWith('use ')) {\n const name = trimmed.slice(4).trim();\n if (!presets[name]) return { handled: true, response: `No MoA preset \"${name}\". Available: ${names.join(', ') || '(none configured)'}` };\n setSessionModelOverride(channel, userId, `moa/${name}`);\n return { handled: true, response: `🧠 This session now thinks as a council: **moa/${name}** (${presets[name].references.length} advisors → ${presets[name].aggregator}). \\`/moa off\\` to revert.` };\n }\n if (!def) {\n return { handled: true, response: 'No MoA presets configured yet — add one under `moa.presets` in titan.json first.' };\n }\n // One-shot: run the prompt through the preset without changing the session\n const { processMessage } = await import('../agent/agent.js');\n const res = await processMessage(trimmed, channel, userId, { model: `moa/${def}` });\n return { handled: true, response: res.content };\n });\n\n registerSlashCommand('think', (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n const config = loadConfig();\n\n if (!args) {\n const current = session.thinkingOverride || config.agent.thinkingMode || 'off';\n return {\n handled: true,\n response: `🧠 **Thinking Mode**: \\`${current}\\`${session.thinkingOverride ? ' (session override)' : ''}\\nOptions: \\`off\\`, \\`low\\`, \\`medium\\`, \\`high\\``,\n };\n }\n\n const level = args.toLowerCase();\n if (!['off', 'low', 'medium', 'high'].includes(level)) {\n return { handled: true, response: `❌ Invalid thinking level: \\`${args}\\`. Use: off, low, medium, high` };\n }\n\n setSessionThinkingOverride(channel, userId, level as 'off' | 'low' | 'medium' | 'high');\n return { handled: true, response: `✅ Thinking mode set to \\`${level}\\` for this session.` };\n });\n\n // ── /usage ──\n registerSlashCommand('usage', (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n const cost = getSessionCost(session.id);\n const dailyTotal = getDailyTotal();\n\n if (!cost) {\n return { handled: true, response: `📊 **Usage**: No API calls recorded in this session yet.\\n• **Daily Total**: $${dailyTotal.toFixed(5)}` };\n }\n\n return {\n handled: true,\n response: [\n `📊 **Session Usage**`,\n `• **API Calls**: ${cost.calls}`,\n `• **Tokens**: ${cost.inputTokens.toLocaleString()} input + ${cost.outputTokens.toLocaleString()} output = ${(cost.inputTokens + cost.outputTokens).toLocaleString()} total`,\n `• **Session Cost**: $${cost.estimatedUsd.toFixed(5)}`,\n `• **Daily Total**: $${dailyTotal.toFixed(5)}`,\n ].join('\\n'),\n };\n });\n\n // ── /compact ──\n registerSlashCommand('compact', (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n const messages = getContextMessages(session);\n\n if (messages.length <= 4) {\n return { handled: true, response: `📦 Context is already compact (${messages.length} messages).` };\n }\n\n const { messages: compacted, savedTokens } = forceCompactContext(messages);\n replaceSessionContext(session, compacted);\n\n return {\n handled: true,\n response: `📦 **Context Compacted**\\n• Before: ${messages.length} messages\\n• After: ${compacted.length} messages\\n• Saved: ~${savedTokens.toLocaleString()} tokens`,\n };\n });\n\n // ── /verbose [on|off] ──\n registerSlashCommand('verbose', (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n\n if (!args) {\n return {\n handled: true,\n response: `🔊 **Verbose Mode**: \\`${session.verboseMode ? 'on' : 'off'}\\``,\n };\n }\n\n const on = ['on', '1', 'true', 'yes'].includes(args.toLowerCase());\n setSessionVerbose(channel, userId, on);\n return { handled: true, response: `✅ Verbose mode \\`${on ? 'on' : 'off'}\\` for this session.` };\n });\n\n // ── /reset and /new ──\n const resetHandler: SlashCommandHandler = (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n closeSession(session.id);\n return { handled: true, response: `🔄 Session **${session.id.slice(0, 8)}** has been reset. Context and overrides cleared.` };\n };\n registerSlashCommand('reset', resetHandler);\n registerSlashCommand('new', resetHandler);\n\n // ── /status ──\n registerSlashCommand('status', (args, channel, userId) => {\n const config = loadConfig();\n const session = getOrCreateSession(channel, userId, 'default');\n const costStr = formatCostSummary(session.id);\n\n const lines = [\n `📊 **TITAN Status** (v${TITAN_VERSION})`,\n `• **Session**: \\`${session.id.slice(0, 8)}\\` (${session.messageCount} messages)`,\n `• **Model**: ${session.modelOverride || config.agent.model}${session.modelOverride ? ' *(session override)*' : ''}`,\n `• **Thinking**: ${session.thinkingOverride || config.agent.thinkingMode || 'off'}${session.thinkingOverride ? ' *(session override)*' : ''}`,\n `• **Verbose**: ${session.verboseMode ? 'on' : 'off'}`,\n `• **Usage**: ${costStr}`,\n ];\n\n return { handled: true, response: lines.join('\\n') };\n });\n\n // ── /plan [goal|status|cancel] ──\n registerSlashCommand('plan', async (args, channel, userId) => {\n const session = getOrCreateSession(channel, userId, 'default');\n\n if (!args || args.toLowerCase() === 'status') {\n const delib = getDeliberation(session.id);\n if (!delib) {\n return { handled: true, response: '📋 No active deliberation plan for this session.' };\n }\n const statusLines = [\n `📋 **Deliberation Status**: \\`${delib.stage}\\``,\n `**Goal**: ${delib.originalMessage.slice(0, 100)}`,\n `**Tasks completed**: ${delib.results.filter(r => r.success).length}/${delib.results.length}`,\n ];\n if (delib.planMarkdown) statusLines.push('', delib.planMarkdown);\n return { handled: true, response: statusLines.join('\\n') };\n }\n\n if (args.toLowerCase() === 'cancel') {\n const cancelled = cancelDeliberation(session.id);\n return { handled: true, response: cancelled ? '✅ Deliberation cancelled.' : '❌ No active deliberation to cancel.' };\n }\n\n // Force deliberation mode with the given goal\n const config = loadConfig();\n try {\n const state = await analyze(args, session.id, config);\n if (state.stage === 'planning') {\n const planned = await generatePlan(state, config);\n if (planned.planMarkdown) {\n return { handled: true, response: planned.planMarkdown };\n }\n }\n return { handled: true, response: `❌ Deliberation failed: ${state.error || 'Could not generate a plan.'}` };\n } catch (e) {\n return { handled: true, response: `❌ Error: ${(e as Error).message}` };\n }\n });\n\n logger.info(COMPONENT, `Registered ${commands.size} slash commands`);\n}\n"],"mappings":";AAKA,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB,cAAc,uBAAuB,yBAAyB,4BAA4B,yBAAyB;AAChJ,SAAS,gBAAgB,eAAe,yBAAyB;AACjE,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,cAAc,uBAAuB;AAC9C,SAAS,sBAAsB;AAC/B,SAAS,iBAAiB,oBAAoB,SAAS,oBAAoB;AAC3E,OAAO,YAAY;AACnB,SAAS,qBAAqB;AAE9B,MAAM,YAAY;AAclB,MAAM,WAA6C,oBAAI,IAAI;AAGpD,SAAS,qBAAqB,MAAc,SAAoC;AACnF,WAAS,IAAI,KAAK,YAAY,GAAG,OAAO;AAC5C;AAGA,eAAsB,mBAClB,SACA,SACA,QACkC;AAClC,QAAM,UAAU,QAAQ,KAAK;AAC7B,MAAI,CAAC,QAAQ,WAAW,GAAG,EAAG,QAAO;AAErC,QAAM,WAAW,QAAQ,QAAQ,GAAG;AACpC,QAAM,QAAQ,WAAW,IAAI,QAAQ,MAAM,GAAG,QAAQ,IAAI,QAAQ,MAAM,CAAC,GAAG,YAAY;AACxF,QAAM,OAAO,WAAW,IAAI,QAAQ,MAAM,WAAW,CAAC,EAAE,KAAK,IAAI;AAEjE,QAAM,UAAU,SAAS,IAAI,IAAI;AACjC,MAAI,CAAC,QAAS,QAAO;AAErB,SAAO,KAAK,WAAW,cAAc,IAAI,IAAI,OAAO,UAAU,IAAI,MAAM,EAAE,KAAK,OAAO,IAAI,MAAM,GAAG;AACnG,SAAO,QAAQ,MAAM,SAAS,MAAM;AACxC;AAGO,SAAS,oBAA0B;AAEtC,uBAAqB,SAAS,OAAO,MAAM,SAAS,WAAW;AAC3D,UAAM,SAAS,WAAW;AAC1B,UAAM,UAAU,mBAAmB,SAAS,QAAQ,SAAS;AAE7D,QAAI,CAAC,MAAM;AAEP,YAAM,UAAU,QAAQ,iBAAiB,OAAO,MAAM;AACtD,YAAM,UAAU,gBAAgB;AAChC,YAAM,aAAa,OAAO,QAAQ,OAAO,EACpC,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,OAAO,IAAI,aAAQ,KAAK,EAAE,EACjD,KAAK,IAAI;AACd,aAAO;AAAA,QACH,SAAS;AAAA,QACT,UAAU,kCAA2B,OAAO,KAAK,QAAQ,gBAAgB,wBAAwB,EAAE;AAAA;AAAA;AAAA,EAAqB,cAAc,qBAAqB;AAAA,MAC/J;AAAA,IACJ;AAGA,QAAI;AAEA,UAAI,CAAC,eAAe,IAAI,GAAG;AACvB,eAAO,EAAE,SAAS,MAAM,UAAU,kBAAa,IAAI,wCAAwC;AAAA,MAC/F;AACA,mBAAa,IAAI;AAAA,IACrB,SAAS,GAAG;AACR,aAAO,EAAE,SAAS,MAAM,UAAU,yBAAqB,EAAY,OAAO,GAAG;AAAA,IACjF;AAEA,4BAAwB,SAAS,QAAQ,IAAI;AAC7C,WAAO,EAAE,SAAS,MAAM,UAAU,8BAAyB,IAAI,uBAAuB;AAAA,EAC1F,CAAC;AAQD,uBAAqB,OAAO,OAAO,MAAM,SAAS,WAAW;AACzD,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,qBAAqB;AAC5D,UAAM,UAAU,cAAc;AAC9B,UAAM,QAAQ,OAAO,KAAK,OAAO;AACjC,UAAM,MAAM,WAAW;AACvB,UAAM,MAAM,IAAI,KAAK,iBAAiB,MAAM,CAAC;AAC7C,UAAM,UAAU,KAAK,KAAK;AAE1B,QAAI,CAAC,SAAS;AACV,UAAI,MAAM,WAAW,GAAG;AACpB,eAAO,EAAE,SAAS,MAAM,UAAU,+KAA0K;AAAA,MAChN;AACA,aAAO,EAAE,SAAS,MAAM,UAAU,+CAAmC,MAAM,IAAI,OAAK,MAAM,MAAM,KAAK,CAAC,iBAAiB,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,0FAAuF;AAAA,IAC/N;AACA,QAAI,YAAY,OAAO;AACnB,8BAAwB,SAAS,QAAQ,EAAE;AAC3C,aAAO,EAAE,SAAS,MAAM,UAAU,gDAA2C;AAAA,IACjF;AACA,QAAI,QAAQ,WAAW,MAAM,GAAG;AAC5B,YAAM,OAAO,QAAQ,MAAM,CAAC,EAAE,KAAK;AACnC,UAAI,CAAC,QAAQ,IAAI,EAAG,QAAO,EAAE,SAAS,MAAM,UAAU,kBAAkB,IAAI,iBAAiB,MAAM,KAAK,IAAI,KAAK,mBAAmB,GAAG;AACvI,8BAAwB,SAAS,QAAQ,OAAO,IAAI,EAAE;AACtD,aAAO,EAAE,SAAS,MAAM,UAAU,yDAAkD,IAAI,OAAO,QAAQ,IAAI,EAAE,WAAW,MAAM,oBAAe,QAAQ,IAAI,EAAE,UAAU,6BAA6B;AAAA,IACtM;AACA,QAAI,CAAC,KAAK;AACN,aAAO,EAAE,SAAS,MAAM,UAAU,wFAAmF;AAAA,IACzH;AAEA,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,mBAAmB;AAC3D,UAAM,MAAM,MAAM,eAAe,SAAS,SAAS,QAAQ,EAAE,OAAO,OAAO,GAAG,GAAG,CAAC;AAClF,WAAO,EAAE,SAAS,MAAM,UAAU,IAAI,QAAQ;AAAA,EAClD,CAAC;AAED,uBAAqB,SAAS,CAAC,MAAM,SAAS,WAAW;AACrD,UAAM,UAAU,mBAAmB,SAAS,QAAQ,SAAS;AAC7D,UAAM,SAAS,WAAW;AAE1B,QAAI,CAAC,MAAM;AACP,YAAM,UAAU,QAAQ,oBAAoB,OAAO,MAAM,gBAAgB;AACzE,aAAO;AAAA,QACH,SAAS;AAAA,QACT,UAAU,kCAA2B,OAAO,KAAK,QAAQ,mBAAmB,wBAAwB,EAAE;AAAA;AAAA,MAC1G;AAAA,IACJ;AAEA,UAAM,QAAQ,KAAK,YAAY;AAC/B,QAAI,CAAC,CAAC,OAAO,OAAO,UAAU,MAAM,EAAE,SAAS,KAAK,GAAG;AACnD,aAAO,EAAE,SAAS,MAAM,UAAU,oCAA+B,IAAI,kCAAkC;AAAA,IAC3G;AAEA,+BAA2B,SAAS,QAAQ,KAA0C;AACtF,WAAO,EAAE,SAAS,MAAM,UAAU,iCAA4B,KAAK,uBAAuB;AAAA,EAC9F,CAAC;AAGD,uBAAqB,SAAS,CAAC,MAAM,SAAS,WAAW;AACrD,UAAM,UAAU,mBAAmB,SAAS,QAAQ,SAAS;AAC7D,UAAM,OAAO,eAAe,QAAQ,EAAE;AACtC,UAAM,aAAa,cAAc;AAEjC,QAAI,CAAC,MAAM;AACP,aAAO,EAAE,SAAS,MAAM,UAAU;AAAA,2BAAiF,WAAW,QAAQ,CAAC,CAAC,GAAG;AAAA,IAC/I;AAEA,WAAO;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,QACN;AAAA,QACA,yBAAoB,KAAK,KAAK;AAAA,QAC9B,sBAAiB,KAAK,YAAY,eAAe,CAAC,YAAY,KAAK,aAAa,eAAe,CAAC,cAAc,KAAK,cAAc,KAAK,cAAc,eAAe,CAAC;AAAA,QACpK,6BAAwB,KAAK,aAAa,QAAQ,CAAC,CAAC;AAAA,QACpD,4BAAuB,WAAW,QAAQ,CAAC,CAAC;AAAA,MAChD,EAAE,KAAK,IAAI;AAAA,IACf;AAAA,EACJ,CAAC;AAGD,uBAAqB,WAAW,CAAC,MAAM,SAAS,WAAW;AACvD,UAAM,UAAU,mBAAmB,SAAS,QAAQ,SAAS;AAC7D,UAAM,WAAW,mBAAmB,OAAO;AAE3C,QAAI,SAAS,UAAU,GAAG;AACtB,aAAO,EAAE,SAAS,MAAM,UAAU,yCAAkC,SAAS,MAAM,cAAc;AAAA,IACrG;AAEA,UAAM,EAAE,UAAU,WAAW,YAAY,IAAI,oBAAoB,QAAQ;AACzE,0BAAsB,SAAS,SAAS;AAExC,WAAO;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,iBAAuC,SAAS,MAAM;AAAA,gBAAuB,UAAU,MAAM;AAAA,iBAAwB,YAAY,eAAe,CAAC;AAAA,IAC/J;AAAA,EACJ,CAAC;AAGD,uBAAqB,WAAW,CAAC,MAAM,SAAS,WAAW;AACvD,UAAM,UAAU,mBAAmB,SAAS,QAAQ,SAAS;AAE7D,QAAI,CAAC,MAAM;AACP,aAAO;AAAA,QACH,SAAS;AAAA,QACT,UAAU,iCAA0B,QAAQ,cAAc,OAAO,KAAK;AAAA,MAC1E;AAAA,IACJ;AAEA,UAAM,KAAK,CAAC,MAAM,KAAK,QAAQ,KAAK,EAAE,SAAS,KAAK,YAAY,CAAC;AACjE,sBAAkB,SAAS,QAAQ,EAAE;AACrC,WAAO,EAAE,SAAS,MAAM,UAAU,yBAAoB,KAAK,OAAO,KAAK,uBAAuB;AAAA,EAClG,CAAC;AAGD,QAAM,eAAoC,CAAC,MAAM,SAAS,WAAW;AACjE,UAAM,UAAU,mBAAmB,SAAS,QAAQ,SAAS;AAC7D,iBAAa,QAAQ,EAAE;AACvB,WAAO,EAAE,SAAS,MAAM,UAAU,uBAAgB,QAAQ,GAAG,MAAM,GAAG,CAAC,CAAC,oDAAoD;AAAA,EAChI;AACA,uBAAqB,SAAS,YAAY;AAC1C,uBAAqB,OAAO,YAAY;AAGxC,uBAAqB,UAAU,CAAC,MAAM,SAAS,WAAW;AACtD,UAAM,SAAS,WAAW;AAC1B,UAAM,UAAU,mBAAmB,SAAS,QAAQ,SAAS;AAC7D,UAAM,UAAU,kBAAkB,QAAQ,EAAE;AAE5C,UAAM,QAAQ;AAAA,MACV,gCAAyB,aAAa;AAAA,MACtC,yBAAoB,QAAQ,GAAG,MAAM,GAAG,CAAC,CAAC,OAAO,QAAQ,YAAY;AAAA,MACrE,qBAAgB,QAAQ,iBAAiB,OAAO,MAAM,KAAK,GAAG,QAAQ,gBAAgB,0BAA0B,EAAE;AAAA,MAClH,wBAAmB,QAAQ,oBAAoB,OAAO,MAAM,gBAAgB,KAAK,GAAG,QAAQ,mBAAmB,0BAA0B,EAAE;AAAA,MAC3I,uBAAkB,QAAQ,cAAc,OAAO,KAAK;AAAA,MACpD,qBAAgB,OAAO;AAAA,IAC3B;AAEA,WAAO,EAAE,SAAS,MAAM,UAAU,MAAM,KAAK,IAAI,EAAE;AAAA,EACvD,CAAC;AAGD,uBAAqB,QAAQ,OAAO,MAAM,SAAS,WAAW;AAC1D,UAAM,UAAU,mBAAmB,SAAS,QAAQ,SAAS;AAE7D,QAAI,CAAC,QAAQ,KAAK,YAAY,MAAM,UAAU;AAC1C,YAAM,QAAQ,gBAAgB,QAAQ,EAAE;AACxC,UAAI,CAAC,OAAO;AACR,eAAO,EAAE,SAAS,MAAM,UAAU,0DAAmD;AAAA,MACzF;AACA,YAAM,cAAc;AAAA,QAChB,wCAAiC,MAAM,KAAK;AAAA,QAC5C,aAAa,MAAM,gBAAgB,MAAM,GAAG,GAAG,CAAC;AAAA,QAChD,wBAAwB,MAAM,QAAQ,OAAO,OAAK,EAAE,OAAO,EAAE,MAAM,IAAI,MAAM,QAAQ,MAAM;AAAA,MAC/F;AACA,UAAI,MAAM,aAAc,aAAY,KAAK,IAAI,MAAM,YAAY;AAC/D,aAAO,EAAE,SAAS,MAAM,UAAU,YAAY,KAAK,IAAI,EAAE;AAAA,IAC7D;AAEA,QAAI,KAAK,YAAY,MAAM,UAAU;AACjC,YAAM,YAAY,mBAAmB,QAAQ,EAAE;AAC/C,aAAO,EAAE,SAAS,MAAM,UAAU,YAAY,mCAA8B,2CAAsC;AAAA,IACtH;AAGA,UAAM,SAAS,WAAW;AAC1B,QAAI;AACA,YAAM,QAAQ,MAAM,QAAQ,MAAM,QAAQ,IAAI,MAAM;AACpD,UAAI,MAAM,UAAU,YAAY;AAC5B,cAAM,UAAU,MAAM,aAAa,OAAO,MAAM;AAChD,YAAI,QAAQ,cAAc;AACtB,iBAAO,EAAE,SAAS,MAAM,UAAU,QAAQ,aAAa;AAAA,QAC3D;AAAA,MACJ;AACA,aAAO,EAAE,SAAS,MAAM,UAAU,+BAA0B,MAAM,SAAS,4BAA4B,GAAG;AAAA,IAC9G,SAAS,GAAG;AACR,aAAO,EAAE,SAAS,MAAM,UAAU,iBAAa,EAAY,OAAO,GAAG;AAAA,IACzE;AAAA,EACJ,CAAC;AAED,SAAO,KAAK,WAAW,cAAc,SAAS,IAAI,iBAAiB;AACvE;","names":[]}
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+ import { randomBytes } from "crypto";
3
+ import logger from "../utils/logger.js";
4
+ import { TITAN_VERSION } from "../utils/constants.js";
5
+ const COMPONENT = "McpAgentTools";
6
+ const tasks = /* @__PURE__ */ new Map();
7
+ const MAX_TASKS = 200;
8
+ function pruneTasks() {
9
+ if (tasks.size <= MAX_TASKS) return;
10
+ const sorted = [...tasks.values()].sort((a, b) => a.startedAt.localeCompare(b.startedAt));
11
+ for (const t of sorted.slice(0, tasks.size - MAX_TASKS)) tasks.delete(t.id);
12
+ }
13
+ const AGENT_TOOLS = [
14
+ {
15
+ name: "titan_chat",
16
+ description: `Have one conversational turn with the TITAN agent. TITAN runs its full agent loop \u2014 it can use its own tools (files, web, widgets, memory, automations) and replies when done. Use sessionId to continue a conversation across calls. Synchronous: returns the final answer (may take up to a couple of minutes for tool-heavy asks). For long jobs use titan_delegate_task instead. Returns: the reply text, prefixed with the tools TITAN used. Errors: returns "Error: ..." text if TITAN's providers are unavailable.`,
17
+ inputSchema: {
18
+ type: "object",
19
+ properties: {
20
+ message: { type: "string", description: "What to say or ask." },
21
+ sessionId: { type: "string", description: "Optional conversation id for continuity." }
22
+ },
23
+ required: ["message"]
24
+ },
25
+ execute: async (args) => {
26
+ const { processMessage } = await import("../agent/agent.js");
27
+ const sessionId = String(args.sessionId || "peer");
28
+ const res = await processMessage(String(args.message || ""), "mcp", `mcp-${sessionId}`);
29
+ const used = res.toolsUsed.length ? `[tools: ${res.toolsUsed.join(", ")}]
30
+ ` : "";
31
+ return `${used}${res.content}`;
32
+ }
33
+ },
34
+ {
35
+ name: "titan_delegate_task",
36
+ description: 'Delegate a long-running task to TITAN and return immediately with a taskId. TITAN works autonomously (full agent loop + tools) in the background. Poll titan_task_status with the taskId; typical tasks finish in 1-10 minutes. Use for anything that would time out a synchronous call. Returns: {"taskId": "..."} as JSON text. Errors: none at submit time \u2014 failures surface in titan_task_status.',
37
+ inputSchema: {
38
+ type: "object",
39
+ properties: {
40
+ task: { type: "string", description: "The work to do, self-contained (TITAN has no other context about your session)." }
41
+ },
42
+ required: ["task"]
43
+ },
44
+ execute: async (args) => {
45
+ const id = `tt-${Date.now().toString(36)}-${randomBytes(3).toString("hex")}`;
46
+ const record = { id, task: String(args.task || ""), status: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString() };
47
+ tasks.set(id, record);
48
+ pruneTasks();
49
+ (async () => {
50
+ try {
51
+ const { processMessage } = await import("../agent/agent.js");
52
+ const res = await processMessage(record.task, "mcp", `mcp-task-${id}`);
53
+ record.status = "done";
54
+ record.result = res.content;
55
+ record.toolsUsed = res.toolsUsed;
56
+ } catch (e) {
57
+ record.status = "failed";
58
+ record.error = e.message;
59
+ } finally {
60
+ record.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
61
+ logger.info(COMPONENT, `Delegated task ${id} ${record.status} (${record.task.slice(0, 60)})`);
62
+ }
63
+ })();
64
+ return JSON.stringify({ taskId: id });
65
+ }
66
+ },
67
+ {
68
+ name: "titan_task_status",
69
+ description: `Check a task started with titan_delegate_task. Returns: JSON text \u2014 {"status":"running"} while working, or {"status":"done","result":"...","toolsUsed":[...]} / {"status":"failed","error":"..."} when finished. Errors: {"status":"unknown"} if the taskId does not exist (tasks are kept for the gateway's lifetime, max 200).`,
70
+ inputSchema: {
71
+ type: "object",
72
+ properties: {
73
+ taskId: { type: "string", description: "The id returned by titan_delegate_task." }
74
+ },
75
+ required: ["taskId"]
76
+ },
77
+ execute: async (args) => {
78
+ const t = tasks.get(String(args.taskId || ""));
79
+ if (!t) return JSON.stringify({ status: "unknown" });
80
+ if (t.status === "running") return JSON.stringify({ status: "running", startedAt: t.startedAt });
81
+ if (t.status === "failed") return JSON.stringify({ status: "failed", error: t.error, finishedAt: t.finishedAt });
82
+ return JSON.stringify({ status: "done", result: t.result, toolsUsed: t.toolsUsed, finishedAt: t.finishedAt });
83
+ }
84
+ },
85
+ {
86
+ name: "titan_moa",
87
+ description: `Ask TITAN's Mixture-of-Agents council: several models advise in parallel and an aggregator synthesizes one answer \u2014 typically sharper than any single model. Great for design decisions, reviews, and second opinions. Slower than titan_chat (advisors run first). Returns: the council's synthesized answer. Errors: "Error: ..." if no MoA preset is configured on this TITAN.`,
88
+ inputSchema: {
89
+ type: "object",
90
+ properties: {
91
+ prompt: { type: "string", description: "The question or task for the council." },
92
+ preset: { type: "string", description: "Optional preset name (defaults to the configured default)." }
93
+ },
94
+ required: ["prompt"]
95
+ },
96
+ execute: async (args) => {
97
+ const { getMoaPresets } = await import("../providers/moa.js");
98
+ const { loadConfig } = await import("../config/config.js");
99
+ const presets = getMoaPresets();
100
+ const cfg = loadConfig();
101
+ const name = String(args.preset || cfg.moa?.defaultPreset || Object.keys(presets)[0] || "");
102
+ if (!name || !presets[name]) return `Error: no MoA preset ${name ? `"${name}" ` : ""}configured on this TITAN.`;
103
+ const { processMessage } = await import("../agent/agent.js");
104
+ const res = await processMessage(String(args.prompt || ""), "mcp", "mcp-moa", { model: `moa/${name}` });
105
+ return res.content;
106
+ }
107
+ },
108
+ {
109
+ name: "titan_status",
110
+ description: "TITAN identity + health for peer discovery. Returns: JSON text \u2014 version, configured model, tool count, MoA presets, uptime seconds. Errors: none.",
111
+ inputSchema: { type: "object", properties: {} },
112
+ execute: async () => {
113
+ const { loadConfig } = await import("../config/config.js");
114
+ const { getRegisteredTools } = await import("../agent/toolRunner.js");
115
+ const { getMoaPresets } = await import("../providers/moa.js");
116
+ const cfg = loadConfig();
117
+ return JSON.stringify({
118
+ agent: "TITAN",
119
+ version: TITAN_VERSION,
120
+ model: cfg.agent.model,
121
+ tools: getRegisteredTools().length,
122
+ moaPresets: Object.keys(getMoaPresets()),
123
+ uptimeSec: Math.round(process.uptime())
124
+ });
125
+ }
126
+ }
127
+ ];
128
+ function getAgentTool(name) {
129
+ return AGENT_TOOLS.find((t) => t.name === name);
130
+ }
131
+ export {
132
+ AGENT_TOOLS,
133
+ getAgentTool
134
+ };
135
+ //# sourceMappingURL=agentTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/mcp/agentTools.ts"],"sourcesContent":["/**\n * Agent-level MCP tools (v7.1 interop) — TITAN as a tool inside other agents.\n *\n * These are exposed ONLY over the MCP server surface (tools/list, tools/call)\n * and never registered into TITAN's own agent loop — a peer agent (Hermes,\n * OpenClaw, Claude, anything MCP) can converse with TITAN, delegate work, and\n * consult TITAN's mixture-of-agents council, with zero recursion risk.\n *\n * Registration is one line on the peer:\n * Hermes → ~/.hermes/config.yaml mcp_servers: { titan: { command: \"titan\", args: [\"mcp\",\"serve\"] } }\n * OpenClaw → openclaw mcp add titan --command titan --arg mcp --arg serve\n */\nimport { randomBytes } from 'crypto';\nimport logger from '../utils/logger.js';\nimport { TITAN_VERSION } from '../utils/constants.js';\n\nconst COMPONENT = 'McpAgentTools';\n\ninterface DelegatedTask {\n id: string;\n task: string;\n status: 'running' | 'done' | 'failed';\n startedAt: string;\n finishedAt?: string;\n result?: string;\n error?: string;\n toolsUsed?: string[];\n}\n\nconst tasks = new Map<string, DelegatedTask>();\nconst MAX_TASKS = 200;\n\nfunction pruneTasks(): void {\n if (tasks.size <= MAX_TASKS) return;\n const sorted = [...tasks.values()].sort((a, b) => a.startedAt.localeCompare(b.startedAt));\n for (const t of sorted.slice(0, tasks.size - MAX_TASKS)) tasks.delete(t.id);\n}\n\nexport interface McpAgentTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n execute: (args: Record<string, unknown>) => Promise<string>;\n}\n\nexport const AGENT_TOOLS: McpAgentTool[] = [\n {\n name: 'titan_chat',\n description: 'Have one conversational turn with the TITAN agent. TITAN runs its full agent loop — it can use its own tools (files, web, widgets, memory, automations) and replies when done. Use sessionId to continue a conversation across calls. Synchronous: returns the final answer (may take up to a couple of minutes for tool-heavy asks). For long jobs use titan_delegate_task instead. Returns: the reply text, prefixed with the tools TITAN used. Errors: returns \"Error: ...\" text if TITAN\\'s providers are unavailable.',\n inputSchema: {\n type: 'object',\n properties: {\n message: { type: 'string', description: 'What to say or ask.' },\n sessionId: { type: 'string', description: 'Optional conversation id for continuity.' },\n },\n required: ['message'],\n },\n execute: async (args) => {\n const { processMessage } = await import('../agent/agent.js');\n const sessionId = String(args.sessionId || 'peer');\n const res = await processMessage(String(args.message || ''), 'mcp', `mcp-${sessionId}`);\n const used = res.toolsUsed.length ? `[tools: ${res.toolsUsed.join(', ')}]\\n` : '';\n return `${used}${res.content}`;\n },\n },\n {\n name: 'titan_delegate_task',\n description: 'Delegate a long-running task to TITAN and return immediately with a taskId. TITAN works autonomously (full agent loop + tools) in the background. Poll titan_task_status with the taskId; typical tasks finish in 1-10 minutes. Use for anything that would time out a synchronous call. Returns: {\"taskId\": \"...\"} as JSON text. Errors: none at submit time — failures surface in titan_task_status.',\n inputSchema: {\n type: 'object',\n properties: {\n task: { type: 'string', description: 'The work to do, self-contained (TITAN has no other context about your session).' },\n },\n required: ['task'],\n },\n execute: async (args) => {\n const id = `tt-${Date.now().toString(36)}-${randomBytes(3).toString('hex')}`;\n const record: DelegatedTask = { id, task: String(args.task || ''), status: 'running', startedAt: new Date().toISOString() };\n tasks.set(id, record);\n pruneTasks();\n (async () => {\n try {\n const { processMessage } = await import('../agent/agent.js');\n const res = await processMessage(record.task, 'mcp', `mcp-task-${id}`);\n record.status = 'done';\n record.result = res.content;\n record.toolsUsed = res.toolsUsed;\n } catch (e) {\n record.status = 'failed';\n record.error = (e as Error).message;\n } finally {\n record.finishedAt = new Date().toISOString();\n logger.info(COMPONENT, `Delegated task ${id} ${record.status} (${record.task.slice(0, 60)})`);\n }\n })();\n return JSON.stringify({ taskId: id });\n },\n },\n {\n name: 'titan_task_status',\n description: 'Check a task started with titan_delegate_task. Returns: JSON text — {\"status\":\"running\"} while working, or {\"status\":\"done\",\"result\":\"...\",\"toolsUsed\":[...]} / {\"status\":\"failed\",\"error\":\"...\"} when finished. Errors: {\"status\":\"unknown\"} if the taskId does not exist (tasks are kept for the gateway\\'s lifetime, max 200).',\n inputSchema: {\n type: 'object',\n properties: {\n taskId: { type: 'string', description: 'The id returned by titan_delegate_task.' },\n },\n required: ['taskId'],\n },\n execute: async (args) => {\n const t = tasks.get(String(args.taskId || ''));\n if (!t) return JSON.stringify({ status: 'unknown' });\n if (t.status === 'running') return JSON.stringify({ status: 'running', startedAt: t.startedAt });\n if (t.status === 'failed') return JSON.stringify({ status: 'failed', error: t.error, finishedAt: t.finishedAt });\n return JSON.stringify({ status: 'done', result: t.result, toolsUsed: t.toolsUsed, finishedAt: t.finishedAt });\n },\n },\n {\n name: 'titan_moa',\n description: 'Ask TITAN\\'s Mixture-of-Agents council: several models advise in parallel and an aggregator synthesizes one answer — typically sharper than any single model. Great for design decisions, reviews, and second opinions. Slower than titan_chat (advisors run first). Returns: the council\\'s synthesized answer. Errors: \"Error: ...\" if no MoA preset is configured on this TITAN.',\n inputSchema: {\n type: 'object',\n properties: {\n prompt: { type: 'string', description: 'The question or task for the council.' },\n preset: { type: 'string', description: 'Optional preset name (defaults to the configured default).' },\n },\n required: ['prompt'],\n },\n execute: async (args) => {\n const { getMoaPresets } = await import('../providers/moa.js');\n const { loadConfig } = await import('../config/config.js');\n const presets = getMoaPresets();\n const cfg = loadConfig() as { moa?: { defaultPreset?: string } };\n const name = String(args.preset || cfg.moa?.defaultPreset || Object.keys(presets)[0] || '');\n if (!name || !presets[name]) return `Error: no MoA preset ${name ? `\"${name}\" ` : ''}configured on this TITAN.`;\n const { processMessage } = await import('../agent/agent.js');\n const res = await processMessage(String(args.prompt || ''), 'mcp', 'mcp-moa', { model: `moa/${name}` });\n return res.content;\n },\n },\n {\n name: 'titan_status',\n description: 'TITAN identity + health for peer discovery. Returns: JSON text — version, configured model, tool count, MoA presets, uptime seconds. Errors: none.',\n inputSchema: { type: 'object', properties: {} },\n execute: async () => {\n const { loadConfig } = await import('../config/config.js');\n const { getRegisteredTools } = await import('../agent/toolRunner.js');\n const { getMoaPresets } = await import('../providers/moa.js');\n const cfg = loadConfig();\n return JSON.stringify({\n agent: 'TITAN',\n version: TITAN_VERSION,\n model: cfg.agent.model,\n tools: getRegisteredTools().length,\n moaPresets: Object.keys(getMoaPresets()),\n uptimeSec: Math.round(process.uptime()),\n });\n },\n },\n];\n\nexport function getAgentTool(name: string): McpAgentTool | undefined {\n return AGENT_TOOLS.find(t => t.name === name);\n}\n"],"mappings":";AAYA,SAAS,mBAAmB;AAC5B,OAAO,YAAY;AACnB,SAAS,qBAAqB;AAE9B,MAAM,YAAY;AAalB,MAAM,QAAQ,oBAAI,IAA2B;AAC7C,MAAM,YAAY;AAElB,SAAS,aAAmB;AACxB,MAAI,MAAM,QAAQ,UAAW;AAC7B,QAAM,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,SAAS,CAAC;AACxF,aAAW,KAAK,OAAO,MAAM,GAAG,MAAM,OAAO,SAAS,EAAG,OAAM,OAAO,EAAE,EAAE;AAC9E;AASO,MAAM,cAA8B;AAAA,EACvC;AAAA,IACI,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACT,MAAM;AAAA,MACN,YAAY;AAAA,QACR,SAAS,EAAE,MAAM,UAAU,aAAa,sBAAsB;AAAA,QAC9D,WAAW,EAAE,MAAM,UAAU,aAAa,2CAA2C;AAAA,MACzF;AAAA,MACA,UAAU,CAAC,SAAS;AAAA,IACxB;AAAA,IACA,SAAS,OAAO,SAAS;AACrB,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,mBAAmB;AAC3D,YAAM,YAAY,OAAO,KAAK,aAAa,MAAM;AACjD,YAAM,MAAM,MAAM,eAAe,OAAO,KAAK,WAAW,EAAE,GAAG,OAAO,OAAO,SAAS,EAAE;AACtF,YAAM,OAAO,IAAI,UAAU,SAAS,WAAW,IAAI,UAAU,KAAK,IAAI,CAAC;AAAA,IAAQ;AAC/E,aAAO,GAAG,IAAI,GAAG,IAAI,OAAO;AAAA,IAChC;AAAA,EACJ;AAAA,EACA;AAAA,IACI,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACT,MAAM;AAAA,MACN,YAAY;AAAA,QACR,MAAM,EAAE,MAAM,UAAU,aAAa,kFAAkF;AAAA,MAC3H;AAAA,MACA,UAAU,CAAC,MAAM;AAAA,IACrB;AAAA,IACA,SAAS,OAAO,SAAS;AACrB,YAAM,KAAK,MAAM,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,SAAS,KAAK,CAAC;AAC1E,YAAM,SAAwB,EAAE,IAAI,MAAM,OAAO,KAAK,QAAQ,EAAE,GAAG,QAAQ,WAAW,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE;AAC1H,YAAM,IAAI,IAAI,MAAM;AACpB,iBAAW;AACX,OAAC,YAAY;AACT,YAAI;AACA,gBAAM,EAAE,eAAe,IAAI,MAAM,OAAO,mBAAmB;AAC3D,gBAAM,MAAM,MAAM,eAAe,OAAO,MAAM,OAAO,YAAY,EAAE,EAAE;AACrE,iBAAO,SAAS;AAChB,iBAAO,SAAS,IAAI;AACpB,iBAAO,YAAY,IAAI;AAAA,QAC3B,SAAS,GAAG;AACR,iBAAO,SAAS;AAChB,iBAAO,QAAS,EAAY;AAAA,QAChC,UAAE;AACE,iBAAO,cAAa,oBAAI,KAAK,GAAE,YAAY;AAC3C,iBAAO,KAAK,WAAW,kBAAkB,EAAE,IAAI,OAAO,MAAM,KAAK,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC,GAAG;AAAA,QAChG;AAAA,MACJ,GAAG;AACH,aAAO,KAAK,UAAU,EAAE,QAAQ,GAAG,CAAC;AAAA,IACxC;AAAA,EACJ;AAAA,EACA;AAAA,IACI,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACT,MAAM;AAAA,MACN,YAAY;AAAA,QACR,QAAQ,EAAE,MAAM,UAAU,aAAa,0CAA0C;AAAA,MACrF;AAAA,MACA,UAAU,CAAC,QAAQ;AAAA,IACvB;AAAA,IACA,SAAS,OAAO,SAAS;AACrB,YAAM,IAAI,MAAM,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;AAC7C,UAAI,CAAC,EAAG,QAAO,KAAK,UAAU,EAAE,QAAQ,UAAU,CAAC;AACnD,UAAI,EAAE,WAAW,UAAW,QAAO,KAAK,UAAU,EAAE,QAAQ,WAAW,WAAW,EAAE,UAAU,CAAC;AAC/F,UAAI,EAAE,WAAW,SAAU,QAAO,KAAK,UAAU,EAAE,QAAQ,UAAU,OAAO,EAAE,OAAO,YAAY,EAAE,WAAW,CAAC;AAC/G,aAAO,KAAK,UAAU,EAAE,QAAQ,QAAQ,QAAQ,EAAE,QAAQ,WAAW,EAAE,WAAW,YAAY,EAAE,WAAW,CAAC;AAAA,IAChH;AAAA,EACJ;AAAA,EACA;AAAA,IACI,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,MACT,MAAM;AAAA,MACN,YAAY;AAAA,QACR,QAAQ,EAAE,MAAM,UAAU,aAAa,wCAAwC;AAAA,QAC/E,QAAQ,EAAE,MAAM,UAAU,aAAa,6DAA6D;AAAA,MACxG;AAAA,MACA,UAAU,CAAC,QAAQ;AAAA,IACvB;AAAA,IACA,SAAS,OAAO,SAAS;AACrB,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,qBAAqB;AAC5D,YAAM,EAAE,WAAW,IAAI,MAAM,OAAO,qBAAqB;AACzD,YAAM,UAAU,cAAc;AAC9B,YAAM,MAAM,WAAW;AACvB,YAAM,OAAO,OAAO,KAAK,UAAU,IAAI,KAAK,iBAAiB,OAAO,KAAK,OAAO,EAAE,CAAC,KAAK,EAAE;AAC1F,UAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAG,QAAO,wBAAwB,OAAO,IAAI,IAAI,OAAO,EAAE;AACpF,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,mBAAmB;AAC3D,YAAM,MAAM,MAAM,eAAe,OAAO,KAAK,UAAU,EAAE,GAAG,OAAO,WAAW,EAAE,OAAO,OAAO,IAAI,GAAG,CAAC;AACtG,aAAO,IAAI;AAAA,IACf;AAAA,EACJ;AAAA,EACA;AAAA,IACI,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE;AAAA,IAC9C,SAAS,YAAY;AACjB,YAAM,EAAE,WAAW,IAAI,MAAM,OAAO,qBAAqB;AACzD,YAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,wBAAwB;AACpE,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,qBAAqB;AAC5D,YAAM,MAAM,WAAW;AACvB,aAAO,KAAK,UAAU;AAAA,QAClB,OAAO;AAAA,QACP,SAAS;AAAA,QACT,OAAO,IAAI,MAAM;AAAA,QACjB,OAAO,mBAAmB,EAAE;AAAA,QAC5B,YAAY,OAAO,KAAK,cAAc,CAAC;AAAA,QACvC,WAAW,KAAK,MAAM,QAAQ,OAAO,CAAC;AAAA,MAC1C,CAAC;AAAA,IACL;AAAA,EACJ;AACJ;AAEO,SAAS,aAAa,MAAwC;AACjE,SAAO,YAAY,KAAK,OAAK,EAAE,SAAS,IAAI;AAChD;","names":[]}
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { getRegisteredTools } from "../agent/toolRunner.js";
3
+ import { AGENT_TOOLS, getAgentTool } from "./agentTools.js";
3
4
  import { isToolSkillEnabled } from "../skills/registry.js";
4
5
  import { loadConfig } from "../config/config.js";
5
6
  import { TITAN_VERSION, TITAN_NAME } from "../utils/constants.js";
@@ -83,8 +84,9 @@ function handleToolsList() {
83
84
  inputSchema: tool.parameters || { type: "object", properties: {} },
84
85
  _skillSource: tool?._skillSource || "core"
85
86
  }));
86
- logger.info(COMPONENT, `Listed ${tools.length} of ${allTools.length} total tools (filtered by security policy)`);
87
- return { tools };
87
+ const agentLevel = AGENT_TOOLS.filter((t) => !denied.has(t.name) && (allowed.size === 0 || allowed.has(t.name))).map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema, _skillSource: "agent" }));
88
+ logger.info(COMPONENT, `Listed ${agentLevel.length} agent-level + ${tools.length} of ${allTools.length} granular tools (filtered by security policy)`);
89
+ return { tools: [...agentLevel, ...tools] };
88
90
  }
89
91
  async function handleToolsCall(params) {
90
92
  return internalToolsCall(params);
@@ -98,6 +100,21 @@ async function internalToolsCall(params) {
98
100
  isError: true
99
101
  };
100
102
  }
103
+ const agentTool = getAgentTool(toolName);
104
+ if (agentTool) {
105
+ const config2 = loadConfig();
106
+ const denied2 = new Set(config2.security.deniedTools);
107
+ const allowed2 = new Set(config2.security.allowedTools);
108
+ if (denied2.has(toolName) || allowed2.size > 0 && !allowed2.has(toolName)) {
109
+ return { content: [{ type: "text", text: `Error: tool "${toolName}" is denied by security policy` }], isError: true };
110
+ }
111
+ try {
112
+ const text = await agentTool.execute(args);
113
+ return { content: [{ type: "text", text }] };
114
+ } catch (e) {
115
+ return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
116
+ }
117
+ }
101
118
  const tools = getRegisteredTools();
102
119
  const handler = tools.find((t) => t.name === toolName);
103
120
  if (!handler) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/server.ts"],"sourcesContent":["/**\n * TITAN — MCP Server\n * Exposes TITAN's registered tools via the Model Context Protocol (MCP).\n * Other agents (Claude Code, Cursor, Windsurf, etc.) can connect to TITAN\n * as an MCP server and use any of its ~195 tools.\n *\n * Supports two transport modes:\n * - **HTTP/SSE**: Runs on a configurable port (default 48421), compatible with\n * MCP clients that use HTTP transport (e.g. Claude Code remote MCP).\n * - **Stdio**: When launched as a subprocess, communicates via stdin/stdout\n * JSON-RPC (standard MCP stdio transport).\n *\n * Protocol: JSON-RPC 2.0 per MCP specification (2025-03-26)\n *\n * Features:\n * - Dynamic tool discovery (exposes all registered TITAN tools)\n * - Session management for multi-turn conversations\n * - Proper error responses per JSON-RPC 2.0 spec\n * - Health check endpoint\n */\nimport type { Express } from 'express';\nimport { getRegisteredTools } from '../agent/toolRunner.js';\nimport { isToolSkillEnabled } from '../skills/registry.js';\nimport { loadConfig } from '../config/config.js';\nimport { TITAN_VERSION, TITAN_NAME } from '../utils/constants.js';\nimport logger from '../utils/logger.js';\n\n// ─── Session Management ──────────────────────────────────────────\n\ninterface McpSession {\n sessionId: string;\n createdAt: number;\n lastActivityAt: number;\n messageCount: number;\n clientInfo: Record<string, unknown>;\n metadata: Record<string, unknown>;\n}\n\nconst sessions = new Map<string, McpSession>();\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n\nconst COMPONENT = 'MCPServer';\n\n// ─── JSON-RPC Types ──────────────────────────────────────────────\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0';\n id?: string | number;\n method: string;\n params?: Record<string, unknown>;\n}\n\ninterface JsonRpcResponse {\n jsonrpc: '2.0';\n id: string | number | null;\n result?: unknown;\n error?: { code: number; message: string; data?: unknown };\n}\n\n// ─── MCP Server State ────────────────────────────────────────────\n\nlet initialized = false;\nlet clientCapabilities: Record<string, unknown> = {};\n\n// ─── Session Management Helpers ───────────────────────────────────\n\nfunction generateSessionId(): string {\n return `mcp-session-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction createSession(clientInfo: Record<string, unknown>): McpSession {\n const sessionId = generateSessionId();\n const now = Date.now();\n const session: McpSession = {\n sessionId,\n createdAt: now,\n lastActivityAt: now,\n messageCount: 0,\n clientInfo,\n metadata: {},\n };\n sessions.set(sessionId, session);\n\n // Clean up expired sessions\n cleanupExpiredSessions();\n\n logger.info(COMPONENT, `Created session ${sessionId}`);\n return session;\n}\n\nfunction updateSessionActivity(sessionId: string): void {\n const session = sessions.get(sessionId);\n if (session) {\n session.lastActivityAt = Date.now();\n session.messageCount++;\n }\n}\n\nfunction cleanupExpiredSessions(): void {\n const now = Date.now();\n for (const [id, session] of sessions.entries()) {\n if (now - session.lastActivityAt > SESSION_TIMEOUT_MS) {\n sessions.delete(id);\n logger.debug(COMPONENT, `Expired session: ${id}`);\n }\n }\n}\n\nfunction getSessionCount(): number {\n return sessions.size;\n}\n\n// ─── MCP Protocol Handlers ───────────────────────────────────────\n\nfunction handleInitialize(params: Record<string, unknown>): unknown {\n const clientInfo = (params.clientInfo as Record<string, unknown>) || {};\n clientCapabilities = (params.capabilities as Record<string, unknown>) || {};\n initialized = true;\n\n // Create session for this client\n const session = createSession(clientInfo);\n\n logger.info(COMPONENT, `Client initialized: ${JSON.stringify(clientInfo)} [session: ${session.sessionId}]`);\n\n return {\n protocolVersion: '2025-03-26',\n capabilities: {\n tools: { listChanged: true },\n resources: { subscribe: false },\n prompts: { listChanged: false },\n },\n serverInfo: {\n name: TITAN_NAME,\n version: TITAN_VERSION,\n },\n sessionId: session.sessionId,\n };\n}\n\nfunction handleToolsList(): unknown {\n const config = loadConfig();\n const denied = new Set(config.security.deniedTools);\n const allowed = new Set(config.security.allowedTools);\n\n const allTools = getRegisteredTools();\n const tools = allTools\n .filter((tool) => {\n if (denied.has(tool.name)) return false;\n if (allowed.size > 0 && !allowed.has(tool.name)) return false;\n if (!isToolSkillEnabled(tool.name)) return false;\n return true;\n })\n .map((tool) => ({\n name: tool.name,\n description: tool.description || 'No description available',\n inputSchema: tool.parameters || { type: 'object', properties: {} },\n _skillSource: ((tool as unknown) as { _skillSource?: string })?._skillSource || 'core',\n }));\n\n logger.info(COMPONENT, `Listed ${tools.length} of ${allTools.length} total tools (filtered by security policy)`);\n return { tools };\n}\n\nasync function handleToolsCall(params: Record<string, unknown>): Promise<unknown> {\n return internalToolsCall(params);\n}\n\nasync function internalToolsCall(params: Record<string, unknown>): Promise<unknown> {\n const toolName = params.name as string;\n const args = (params.arguments as Record<string, unknown>) || {};\n\n // Validate tool name\n if (!toolName || typeof toolName !== 'string') {\n return {\n content: [{ type: 'text', text: 'Error: tool name is required and must be a string' }],\n isError: true,\n };\n }\n\n const tools = getRegisteredTools();\n const handler = tools.find((t) => t.name === toolName);\n\n if (!handler) {\n // Provide helpful error with available tools count\n const totalTools = tools.length;\n return {\n content: [{ type: 'text', text: `Error: unknown tool \"${toolName}\". ${totalTools} tools are available. Use tools/list to see available tools.` }],\n isError: true,\n };\n }\n\n // Check if skill is enabled\n if (!isToolSkillEnabled(handler.name)) {\n return {\n content: [{ type: 'text', text: `Error: tool \"${toolName}\" is disabled (skill not loaded)` }],\n isError: true,\n };\n }\n\n // Check security policy\n const config = loadConfig();\n if (config.security.deniedTools.includes(handler.name)) {\n return {\n content: [{ type: 'text', text: `Error: tool \"${toolName}\" is denied by security policy` }],\n isError: true,\n };\n }\n\n try {\n const timeout = config.security.commandTimeout || 30000;\n const startTime = Date.now();\n\n const result = await Promise.race([\n handler.execute(args),\n new Promise<string>((_, reject) =>\n setTimeout(() => reject(new Error(`Tool \"${toolName}\" timed out after ${timeout}ms`)), timeout)\n ),\n ]);\n\n const durationMs = Date.now() - startTime;\n logger.info(COMPONENT, `Tool ${toolName} executed in ${durationMs}ms via MCP`);\n\n // Truncate large output at 100KB for MCP safety\n const maxOutputSize = 100000;\n const content = typeof result === 'string' && result.length > maxOutputSize\n ? result.slice(0, maxOutputSize) + `\\n\\n[Output truncated at ${maxOutputSize / 1000}KB]`\n : (typeof result === 'string' ? result : JSON.stringify(result));\n\n return {\n content: [{ type: 'text', text: content }],\n isError: false,\n };\n } catch (err) {\n const errorDetails = err instanceof Error ? {\n message: err.message,\n stack: process.env.NODE_ENV === 'development' ? err.stack : undefined,\n } : { message: String(err), stack: undefined };\n\n logger.error(COMPONENT, `Tool ${toolName} failed: ${errorDetails.message}`);\n return {\n content: [{ type: 'text', text: `Error: ${errorDetails.message}` }],\n isError: true,\n };\n }\n}\n\n// ─── JSON-RPC Error Codes ────────────────────────────────────────\n\nconst JsonRpcErrorCode = {\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n NOT_INITIALIZED: -32000,\n TOOL_NOT_FOUND: -32001,\n TOOL_EXECUTION_ERROR: -32002,\n SESSION_EXPIRED: -32003,\n} as const;\n\n// ─── JSON-RPC Dispatcher ─────────────────────────────────────────\n\nexport async function handleJsonRpcRequest(request: JsonRpcRequest): Promise<JsonRpcResponse | null> {\n // Validate JSON-RPC version first\n if (!request.jsonrpc || request.jsonrpc !== '2.0') {\n return {\n jsonrpc: '2.0',\n id: request.id ?? null,\n error: { code: JsonRpcErrorCode.INVALID_REQUEST, message: 'Invalid JSON-RPC version, expected \"2.0\"' },\n };\n }\n\n const { method, params, id } = request;\n\n // Notifications (no id) — don't send a response\n if (method?.startsWith('notifications/')) {\n logger.debug(COMPONENT, `Notification: ${method}`);\n return null;\n }\n\n // Validate method exists\n if (!method || typeof method !== 'string') {\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: JsonRpcErrorCode.METHOD_NOT_FOUND, message: 'Method name is required' },\n };\n }\n\n // Methods that require a response\n try {\n switch (method) {\n case 'initialize':\n return { jsonrpc: '2.0', id: id ?? null, result: handleInitialize(params || {}) };\n\n case 'ping':\n return { jsonrpc: '2.0', id: id ?? null, result: {} };\n\n case 'tools/list':\n if (!initialized) {\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: JsonRpcErrorCode.NOT_INITIALIZED, message: 'Server not initialized. Call initialize first.' },\n };\n }\n return { jsonrpc: '2.0', id: id ?? null, result: handleToolsList() };\n\n case 'tools/call':\n if (!initialized) {\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: JsonRpcErrorCode.NOT_INITIALIZED, message: 'Server not initialized. Call initialize first.' },\n };\n }\n if (!params || !(params as Record<string, unknown>).name) {\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: JsonRpcErrorCode.INVALID_PARAMS, message: 'Tool name is required in params' },\n };\n }\n return { jsonrpc: '2.0', id: id ?? null, result: await internalToolsCall(params || {}) };\n\n case 'session/info':\n // New session info endpoint for debugging\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n result: {\n sessionId: null, // Session not tracked via JSON-RPC\n activeSessions: getSessionCount(),\n serverVersion: TITAN_VERSION,\n },\n };\n\n default:\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: JsonRpcErrorCode.METHOD_NOT_FOUND, message: `Method not found: ${method}` },\n };\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n logger.error(COMPONENT, `Handler error for ${method}: ${error.message}`);\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: {\n code: JsonRpcErrorCode.INTERNAL_ERROR,\n message: error.message,\n data: process.env.NODE_ENV === 'development' ? { stack: error.stack } : undefined,\n },\n };\n }\n}\n\n// ─── Stdio Transport ─────────────────────────────────────────────\n\nlet stdioBuf = '';\n\nfunction handleStdioData(chunk: string): void {\n stdioBuf += chunk;\n // Process complete lines (JSON-RPC messages are newline-delimited)\n const lines = stdioBuf.split('\\n');\n stdioBuf = lines.pop() || ''; // Keep incomplete last line in buffer\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n try {\n const request = JSON.parse(trimmed) as JsonRpcRequest;\n handleJsonRpcRequest(request).then((response) => {\n if (response) {\n process.stdout.write(JSON.stringify(response) + '\\n');\n }\n }).catch((err) => {\n const errorResponse: JsonRpcResponse = {\n jsonrpc: '2.0',\n id: request.id ?? null,\n error: { code: -32603, message: (err as Error).message },\n };\n process.stdout.write(JSON.stringify(errorResponse) + '\\n');\n });\n } catch {\n // Invalid JSON — ignore malformed input\n logger.warn(COMPONENT, `Invalid JSON-RPC input: ${trimmed.slice(0, 100)}`);\n }\n }\n}\n\n/**\n * Start MCP server in stdio mode.\n * Used when TITAN is launched as a subprocess by an MCP client.\n */\nexport function startStdioServer(): void {\n logger.info(COMPONENT, 'Starting MCP server (stdio transport)');\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', handleStdioData);\n process.stdin.on('end', () => {\n logger.info(COMPONENT, 'Stdio transport closed');\n process.exit(0);\n });\n}\n\n// ─── HTTP/SSE Transport ──────────────────────────────────────────\n\n/**\n * Mount MCP HTTP endpoints on an Express app.\n * Implements the MCP Streamable HTTP transport (2025-03-26 spec).\n *\n * Endpoints:\n * - GET /mcp/health — Health check endpoint\n * - POST /mcp — JSON-RPC request/response\n * - GET /mcp — SSE stream for server-initiated notifications (future)\n * - DELETE /mcp — Terminate session (future)\n */\nexport function mountMcpHttpEndpoints(app: Express): void {\n const config = loadConfig();\n if (!config.mcp?.server?.enabled) {\n logger.debug(COMPONENT, 'MCP server mode disabled in config');\n return;\n }\n\n // GET /mcp/health — Health check endpoint\n app.get('/mcp/health', (_req, res) => {\n const tools = getRegisteredTools();\n const enabledTools = tools.filter((t) => isToolSkillEnabled(t.name));\n const status = {\n status: 'ok',\n initialized,\n version: TITAN_VERSION,\n serverName: TITAN_NAME,\n toolCount: enabledTools.length,\n totalToolCount: tools.length,\n activeSessions: getSessionCount(),\n uptime: process.uptime(),\n timestamp: new Date().toISOString(),\n };\n res.json(status);\n });\n\n // POST /mcp — main JSON-RPC endpoint\n app.post('/mcp', async (req, res) => {\n try {\n const request = req.body as JsonRpcRequest;\n\n if (!request.jsonrpc || request.jsonrpc !== '2.0') {\n res.status(400).json({\n jsonrpc: '2.0',\n id: request.id ?? null,\n error: { code: -32600, message: 'Invalid JSON-RPC version, expected \"2.0\"' },\n });\n return;\n }\n\n if (!request.method) {\n res.status(400).json({\n jsonrpc: '2.0',\n id: request.id ?? null,\n error: { code: -32600, message: 'Method name is required' },\n });\n return;\n }\n\n const response = await handleJsonRpcRequest(request);\n if (response) {\n res.json(response);\n } else {\n // Notification — no response body\n res.status(204).end();\n }\n } catch (err) {\n res.status(500).json({\n jsonrpc: '2.0',\n id: null,\n error: { code: -32603, message: (err as Error).message },\n });\n }\n });\n\n // GET /mcp — SSE stream (placeholder for future server-initiated notifications)\n app.get('/mcp', (_req, res) => {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sock = (res as any).socket;\n if (sock && typeof sock.setNoDelay === 'function') sock.setNoDelay(true);\n res.write('event: endpoint\\ndata: /mcp\\n\\n');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (typeof (res as any).flush === 'function') (res as any).flush();\n // Keep connection open for future notifications\n // Client can close when done\n });\n\n // DELETE /mcp — Session termination (for future session management)\n app.delete('/mcp', (req, res) => {\n // For future: support session termination via ?sessionId= query param\n res.json({\n message: 'Session management - use session/info to get current session details',\n activeSessions: getSessionCount(),\n });\n });\n\n logger.info(COMPONENT, 'MCP HTTP endpoints mounted at /mcp and /mcp/health');\n}\n\n/**\n * Get MCP server status for the dashboard/API.\n */\nexport function getMcpServerStatus(): {\n enabled: boolean;\n initialized: boolean;\n toolCount: number;\n totalToolCount: number;\n activeSessions: number;\n clientInfo: unknown;\n} {\n const config = loadConfig();\n const allTools = getRegisteredTools();\n const enabledTools = allTools.filter((t) => isToolSkillEnabled(t.name));\n return {\n enabled: config.mcp?.server?.enabled ?? false,\n initialized,\n toolCount: enabledTools.length,\n totalToolCount: allTools.length,\n activeSessions: getSessionCount(),\n clientInfo: clientCapabilities,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/mcp/server.ts"],"sourcesContent":["/**\n * TITAN — MCP Server\n * Exposes TITAN's registered tools via the Model Context Protocol (MCP).\n * Other agents (Claude Code, Cursor, Windsurf, etc.) can connect to TITAN\n * as an MCP server and use any of its ~195 tools.\n *\n * Supports two transport modes:\n * - **HTTP/SSE**: Runs on a configurable port (default 48421), compatible with\n * MCP clients that use HTTP transport (e.g. Claude Code remote MCP).\n * - **Stdio**: When launched as a subprocess, communicates via stdin/stdout\n * JSON-RPC (standard MCP stdio transport).\n *\n * Protocol: JSON-RPC 2.0 per MCP specification (2025-03-26)\n *\n * Features:\n * - Dynamic tool discovery (exposes all registered TITAN tools)\n * - Session management for multi-turn conversations\n * - Proper error responses per JSON-RPC 2.0 spec\n * - Health check endpoint\n */\nimport type { Express } from 'express';\nimport { getRegisteredTools } from '../agent/toolRunner.js';\nimport { AGENT_TOOLS, getAgentTool } from './agentTools.js';\nimport { isToolSkillEnabled } from '../skills/registry.js';\nimport { loadConfig } from '../config/config.js';\nimport { TITAN_VERSION, TITAN_NAME } from '../utils/constants.js';\nimport logger from '../utils/logger.js';\n\n// ─── Session Management ──────────────────────────────────────────\n\ninterface McpSession {\n sessionId: string;\n createdAt: number;\n lastActivityAt: number;\n messageCount: number;\n clientInfo: Record<string, unknown>;\n metadata: Record<string, unknown>;\n}\n\nconst sessions = new Map<string, McpSession>();\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n\nconst COMPONENT = 'MCPServer';\n\n// ─── JSON-RPC Types ──────────────────────────────────────────────\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0';\n id?: string | number;\n method: string;\n params?: Record<string, unknown>;\n}\n\ninterface JsonRpcResponse {\n jsonrpc: '2.0';\n id: string | number | null;\n result?: unknown;\n error?: { code: number; message: string; data?: unknown };\n}\n\n// ─── MCP Server State ────────────────────────────────────────────\n\nlet initialized = false;\nlet clientCapabilities: Record<string, unknown> = {};\n\n// ─── Session Management Helpers ───────────────────────────────────\n\nfunction generateSessionId(): string {\n return `mcp-session-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction createSession(clientInfo: Record<string, unknown>): McpSession {\n const sessionId = generateSessionId();\n const now = Date.now();\n const session: McpSession = {\n sessionId,\n createdAt: now,\n lastActivityAt: now,\n messageCount: 0,\n clientInfo,\n metadata: {},\n };\n sessions.set(sessionId, session);\n\n // Clean up expired sessions\n cleanupExpiredSessions();\n\n logger.info(COMPONENT, `Created session ${sessionId}`);\n return session;\n}\n\nfunction updateSessionActivity(sessionId: string): void {\n const session = sessions.get(sessionId);\n if (session) {\n session.lastActivityAt = Date.now();\n session.messageCount++;\n }\n}\n\nfunction cleanupExpiredSessions(): void {\n const now = Date.now();\n for (const [id, session] of sessions.entries()) {\n if (now - session.lastActivityAt > SESSION_TIMEOUT_MS) {\n sessions.delete(id);\n logger.debug(COMPONENT, `Expired session: ${id}`);\n }\n }\n}\n\nfunction getSessionCount(): number {\n return sessions.size;\n}\n\n// ─── MCP Protocol Handlers ───────────────────────────────────────\n\nfunction handleInitialize(params: Record<string, unknown>): unknown {\n const clientInfo = (params.clientInfo as Record<string, unknown>) || {};\n clientCapabilities = (params.capabilities as Record<string, unknown>) || {};\n initialized = true;\n\n // Create session for this client\n const session = createSession(clientInfo);\n\n logger.info(COMPONENT, `Client initialized: ${JSON.stringify(clientInfo)} [session: ${session.sessionId}]`);\n\n return {\n protocolVersion: '2025-03-26',\n capabilities: {\n tools: { listChanged: true },\n resources: { subscribe: false },\n prompts: { listChanged: false },\n },\n serverInfo: {\n name: TITAN_NAME,\n version: TITAN_VERSION,\n },\n sessionId: session.sessionId,\n };\n}\n\nfunction handleToolsList(): unknown {\n const config = loadConfig();\n const denied = new Set(config.security.deniedTools);\n const allowed = new Set(config.security.allowedTools);\n\n const allTools = getRegisteredTools();\n const tools = allTools\n .filter((tool) => {\n if (denied.has(tool.name)) return false;\n if (allowed.size > 0 && !allowed.has(tool.name)) return false;\n if (!isToolSkillEnabled(tool.name)) return false;\n return true;\n })\n .map((tool) => ({\n name: tool.name,\n description: tool.description || 'No description available',\n inputSchema: tool.parameters || { type: 'object', properties: {} },\n _skillSource: ((tool as unknown) as { _skillSource?: string })?._skillSource || 'core',\n }));\n\n // v7.1 interop: agent-level tools (titan_chat / delegate / moa / status) —\n // MCP-surface only, listed first so peer agents discover the whole-agent\n // interface before the 250+ granular tools.\n const agentLevel = AGENT_TOOLS\n .filter(t => !denied.has(t.name) && (allowed.size === 0 || allowed.has(t.name)))\n .map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema, _skillSource: 'agent' }));\n\n logger.info(COMPONENT, `Listed ${agentLevel.length} agent-level + ${tools.length} of ${allTools.length} granular tools (filtered by security policy)`);\n return { tools: [...agentLevel, ...tools] };\n}\n\nasync function handleToolsCall(params: Record<string, unknown>): Promise<unknown> {\n return internalToolsCall(params);\n}\n\nasync function internalToolsCall(params: Record<string, unknown>): Promise<unknown> {\n const toolName = params.name as string;\n const args = (params.arguments as Record<string, unknown>) || {};\n\n // Validate tool name\n if (!toolName || typeof toolName !== 'string') {\n return {\n content: [{ type: 'text', text: 'Error: tool name is required and must be a string' }],\n isError: true,\n };\n }\n\n // v7.1 interop: agent-level tools take precedence (MCP-surface only)\n const agentTool = getAgentTool(toolName);\n if (agentTool) {\n const config2 = loadConfig();\n const denied2 = new Set(config2.security.deniedTools);\n const allowed2 = new Set(config2.security.allowedTools);\n if (denied2.has(toolName) || (allowed2.size > 0 && !allowed2.has(toolName))) {\n return { content: [{ type: 'text', text: `Error: tool \"${toolName}\" is denied by security policy` }], isError: true };\n }\n try {\n const text = await agentTool.execute(args);\n return { content: [{ type: 'text', text }] };\n } catch (e) {\n return { content: [{ type: 'text', text: `Error: ${(e as Error).message}` }], isError: true };\n }\n }\n\n const tools = getRegisteredTools();\n const handler = tools.find((t) => t.name === toolName);\n\n if (!handler) {\n // Provide helpful error with available tools count\n const totalTools = tools.length;\n return {\n content: [{ type: 'text', text: `Error: unknown tool \"${toolName}\". ${totalTools} tools are available. Use tools/list to see available tools.` }],\n isError: true,\n };\n }\n\n // Check if skill is enabled\n if (!isToolSkillEnabled(handler.name)) {\n return {\n content: [{ type: 'text', text: `Error: tool \"${toolName}\" is disabled (skill not loaded)` }],\n isError: true,\n };\n }\n\n // Check security policy\n const config = loadConfig();\n if (config.security.deniedTools.includes(handler.name)) {\n return {\n content: [{ type: 'text', text: `Error: tool \"${toolName}\" is denied by security policy` }],\n isError: true,\n };\n }\n\n try {\n const timeout = config.security.commandTimeout || 30000;\n const startTime = Date.now();\n\n const result = await Promise.race([\n handler.execute(args),\n new Promise<string>((_, reject) =>\n setTimeout(() => reject(new Error(`Tool \"${toolName}\" timed out after ${timeout}ms`)), timeout)\n ),\n ]);\n\n const durationMs = Date.now() - startTime;\n logger.info(COMPONENT, `Tool ${toolName} executed in ${durationMs}ms via MCP`);\n\n // Truncate large output at 100KB for MCP safety\n const maxOutputSize = 100000;\n const content = typeof result === 'string' && result.length > maxOutputSize\n ? result.slice(0, maxOutputSize) + `\\n\\n[Output truncated at ${maxOutputSize / 1000}KB]`\n : (typeof result === 'string' ? result : JSON.stringify(result));\n\n return {\n content: [{ type: 'text', text: content }],\n isError: false,\n };\n } catch (err) {\n const errorDetails = err instanceof Error ? {\n message: err.message,\n stack: process.env.NODE_ENV === 'development' ? err.stack : undefined,\n } : { message: String(err), stack: undefined };\n\n logger.error(COMPONENT, `Tool ${toolName} failed: ${errorDetails.message}`);\n return {\n content: [{ type: 'text', text: `Error: ${errorDetails.message}` }],\n isError: true,\n };\n }\n}\n\n// ─── JSON-RPC Error Codes ────────────────────────────────────────\n\nconst JsonRpcErrorCode = {\n PARSE_ERROR: -32700,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n INVALID_PARAMS: -32602,\n INTERNAL_ERROR: -32603,\n NOT_INITIALIZED: -32000,\n TOOL_NOT_FOUND: -32001,\n TOOL_EXECUTION_ERROR: -32002,\n SESSION_EXPIRED: -32003,\n} as const;\n\n// ─── JSON-RPC Dispatcher ─────────────────────────────────────────\n\nexport async function handleJsonRpcRequest(request: JsonRpcRequest): Promise<JsonRpcResponse | null> {\n // Validate JSON-RPC version first\n if (!request.jsonrpc || request.jsonrpc !== '2.0') {\n return {\n jsonrpc: '2.0',\n id: request.id ?? null,\n error: { code: JsonRpcErrorCode.INVALID_REQUEST, message: 'Invalid JSON-RPC version, expected \"2.0\"' },\n };\n }\n\n const { method, params, id } = request;\n\n // Notifications (no id) — don't send a response\n if (method?.startsWith('notifications/')) {\n logger.debug(COMPONENT, `Notification: ${method}`);\n return null;\n }\n\n // Validate method exists\n if (!method || typeof method !== 'string') {\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: JsonRpcErrorCode.METHOD_NOT_FOUND, message: 'Method name is required' },\n };\n }\n\n // Methods that require a response\n try {\n switch (method) {\n case 'initialize':\n return { jsonrpc: '2.0', id: id ?? null, result: handleInitialize(params || {}) };\n\n case 'ping':\n return { jsonrpc: '2.0', id: id ?? null, result: {} };\n\n case 'tools/list':\n if (!initialized) {\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: JsonRpcErrorCode.NOT_INITIALIZED, message: 'Server not initialized. Call initialize first.' },\n };\n }\n return { jsonrpc: '2.0', id: id ?? null, result: handleToolsList() };\n\n case 'tools/call':\n if (!initialized) {\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: JsonRpcErrorCode.NOT_INITIALIZED, message: 'Server not initialized. Call initialize first.' },\n };\n }\n if (!params || !(params as Record<string, unknown>).name) {\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: JsonRpcErrorCode.INVALID_PARAMS, message: 'Tool name is required in params' },\n };\n }\n return { jsonrpc: '2.0', id: id ?? null, result: await internalToolsCall(params || {}) };\n\n case 'session/info':\n // New session info endpoint for debugging\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n result: {\n sessionId: null, // Session not tracked via JSON-RPC\n activeSessions: getSessionCount(),\n serverVersion: TITAN_VERSION,\n },\n };\n\n default:\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: { code: JsonRpcErrorCode.METHOD_NOT_FOUND, message: `Method not found: ${method}` },\n };\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n logger.error(COMPONENT, `Handler error for ${method}: ${error.message}`);\n return {\n jsonrpc: '2.0',\n id: id ?? null,\n error: {\n code: JsonRpcErrorCode.INTERNAL_ERROR,\n message: error.message,\n data: process.env.NODE_ENV === 'development' ? { stack: error.stack } : undefined,\n },\n };\n }\n}\n\n// ─── Stdio Transport ─────────────────────────────────────────────\n\nlet stdioBuf = '';\n\nfunction handleStdioData(chunk: string): void {\n stdioBuf += chunk;\n // Process complete lines (JSON-RPC messages are newline-delimited)\n const lines = stdioBuf.split('\\n');\n stdioBuf = lines.pop() || ''; // Keep incomplete last line in buffer\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n try {\n const request = JSON.parse(trimmed) as JsonRpcRequest;\n handleJsonRpcRequest(request).then((response) => {\n if (response) {\n process.stdout.write(JSON.stringify(response) + '\\n');\n }\n }).catch((err) => {\n const errorResponse: JsonRpcResponse = {\n jsonrpc: '2.0',\n id: request.id ?? null,\n error: { code: -32603, message: (err as Error).message },\n };\n process.stdout.write(JSON.stringify(errorResponse) + '\\n');\n });\n } catch {\n // Invalid JSON — ignore malformed input\n logger.warn(COMPONENT, `Invalid JSON-RPC input: ${trimmed.slice(0, 100)}`);\n }\n }\n}\n\n/**\n * Start MCP server in stdio mode.\n * Used when TITAN is launched as a subprocess by an MCP client.\n */\nexport function startStdioServer(): void {\n logger.info(COMPONENT, 'Starting MCP server (stdio transport)');\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', handleStdioData);\n process.stdin.on('end', () => {\n logger.info(COMPONENT, 'Stdio transport closed');\n process.exit(0);\n });\n}\n\n// ─── HTTP/SSE Transport ──────────────────────────────────────────\n\n/**\n * Mount MCP HTTP endpoints on an Express app.\n * Implements the MCP Streamable HTTP transport (2025-03-26 spec).\n *\n * Endpoints:\n * - GET /mcp/health — Health check endpoint\n * - POST /mcp — JSON-RPC request/response\n * - GET /mcp — SSE stream for server-initiated notifications (future)\n * - DELETE /mcp — Terminate session (future)\n */\nexport function mountMcpHttpEndpoints(app: Express): void {\n const config = loadConfig();\n if (!config.mcp?.server?.enabled) {\n logger.debug(COMPONENT, 'MCP server mode disabled in config');\n return;\n }\n\n // GET /mcp/health — Health check endpoint\n app.get('/mcp/health', (_req, res) => {\n const tools = getRegisteredTools();\n const enabledTools = tools.filter((t) => isToolSkillEnabled(t.name));\n const status = {\n status: 'ok',\n initialized,\n version: TITAN_VERSION,\n serverName: TITAN_NAME,\n toolCount: enabledTools.length,\n totalToolCount: tools.length,\n activeSessions: getSessionCount(),\n uptime: process.uptime(),\n timestamp: new Date().toISOString(),\n };\n res.json(status);\n });\n\n // POST /mcp — main JSON-RPC endpoint\n app.post('/mcp', async (req, res) => {\n try {\n const request = req.body as JsonRpcRequest;\n\n if (!request.jsonrpc || request.jsonrpc !== '2.0') {\n res.status(400).json({\n jsonrpc: '2.0',\n id: request.id ?? null,\n error: { code: -32600, message: 'Invalid JSON-RPC version, expected \"2.0\"' },\n });\n return;\n }\n\n if (!request.method) {\n res.status(400).json({\n jsonrpc: '2.0',\n id: request.id ?? null,\n error: { code: -32600, message: 'Method name is required' },\n });\n return;\n }\n\n const response = await handleJsonRpcRequest(request);\n if (response) {\n res.json(response);\n } else {\n // Notification — no response body\n res.status(204).end();\n }\n } catch (err) {\n res.status(500).json({\n jsonrpc: '2.0',\n id: null,\n error: { code: -32603, message: (err as Error).message },\n });\n }\n });\n\n // GET /mcp — SSE stream (placeholder for future server-initiated notifications)\n app.get('/mcp', (_req, res) => {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sock = (res as any).socket;\n if (sock && typeof sock.setNoDelay === 'function') sock.setNoDelay(true);\n res.write('event: endpoint\\ndata: /mcp\\n\\n');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (typeof (res as any).flush === 'function') (res as any).flush();\n // Keep connection open for future notifications\n // Client can close when done\n });\n\n // DELETE /mcp — Session termination (for future session management)\n app.delete('/mcp', (req, res) => {\n // For future: support session termination via ?sessionId= query param\n res.json({\n message: 'Session management - use session/info to get current session details',\n activeSessions: getSessionCount(),\n });\n });\n\n logger.info(COMPONENT, 'MCP HTTP endpoints mounted at /mcp and /mcp/health');\n}\n\n/**\n * Get MCP server status for the dashboard/API.\n */\nexport function getMcpServerStatus(): {\n enabled: boolean;\n initialized: boolean;\n toolCount: number;\n totalToolCount: number;\n activeSessions: number;\n clientInfo: unknown;\n} {\n const config = loadConfig();\n const allTools = getRegisteredTools();\n const enabledTools = allTools.filter((t) => isToolSkillEnabled(t.name));\n return {\n enabled: config.mcp?.server?.enabled ?? false,\n initialized,\n toolCount: enabledTools.length,\n totalToolCount: allTools.length,\n activeSessions: getSessionCount(),\n clientInfo: clientCapabilities,\n };\n}\n"],"mappings":"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","names":[]}
@@ -195,6 +195,8 @@ function initGraph() {
195
195
  }, 24 * 60 * 60 * 1e3).unref();
196
196
  }
197
197
  async function extractEntities(content) {
198
+ const { isBenchMode } = await import("../utils/benchMode.js");
199
+ if (isBenchMode()) return { entities: [], relations: [] };
198
200
  try {
199
201
  const { chat: routerChat } = await import("../providers/router.js");
200
202
  const config = loadConfig();