titan-agent 5.4.2 → 5.5.6

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 (302) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/agent.js +9 -5
  3. package/dist/agent/agent.js.map +1 -1
  4. package/dist/agent/agentLoop.js +7 -3
  5. package/dist/agent/agentLoop.js.map +1 -1
  6. package/dist/agent/checkpoint.js +2 -2
  7. package/dist/agent/checkpoint.js.map +1 -1
  8. package/dist/agent/commandPost.js +3 -3
  9. package/dist/agent/commandPost.js.map +1 -1
  10. package/dist/agent/goalProposer.js +2 -2
  11. package/dist/agent/goalProposer.js.map +1 -1
  12. package/dist/agent/goals.js +3 -3
  13. package/dist/agent/goals.js.map +1 -1
  14. package/dist/agent/peerAdvise.js +1 -1
  15. package/dist/agent/peerAdvise.js.map +1 -1
  16. package/dist/agent/planner.js +4 -4
  17. package/dist/agent/planner.js.map +1 -1
  18. package/dist/agent/userProfile.js +2 -2
  19. package/dist/agent/userProfile.js.map +1 -1
  20. package/dist/cli/doctor.js +33 -0
  21. package/dist/cli/doctor.js.map +1 -1
  22. package/dist/cli/onboard.js +4 -4
  23. package/dist/cli/onboard.js.map +1 -1
  24. package/dist/config/config.js +3 -3
  25. package/dist/config/config.js.map +1 -1
  26. package/dist/config/schema.js +8 -1
  27. package/dist/config/schema.js.map +1 -1
  28. package/dist/gateway/routes/adminRouter.js +500 -0
  29. package/dist/gateway/routes/adminRouter.js.map +1 -0
  30. package/dist/gateway/routes/agents.js +231 -0
  31. package/dist/gateway/routes/agents.js.map +1 -0
  32. package/dist/gateway/routes/agentsRouter.js +32 -0
  33. package/dist/gateway/routes/agentsRouter.js.map +1 -0
  34. package/dist/gateway/routes/checkpoints.js +41 -0
  35. package/dist/gateway/routes/checkpoints.js.map +1 -0
  36. package/dist/gateway/routes/commandPost.js +755 -0
  37. package/dist/gateway/routes/commandPost.js.map +1 -0
  38. package/dist/gateway/routes/companies.js +166 -0
  39. package/dist/gateway/routes/companies.js.map +1 -0
  40. package/dist/gateway/routes/files.js +295 -0
  41. package/dist/gateway/routes/files.js.map +1 -0
  42. package/dist/gateway/routes/hardwareRouter.js +151 -0
  43. package/dist/gateway/routes/hardwareRouter.js.map +1 -0
  44. package/dist/gateway/routes/mcpRouter.js +88 -0
  45. package/dist/gateway/routes/mcpRouter.js.map +1 -0
  46. package/dist/gateway/routes/mesh.js +464 -0
  47. package/dist/gateway/routes/mesh.js.map +1 -0
  48. package/dist/gateway/routes/metricsRouter.js +131 -0
  49. package/dist/gateway/routes/metricsRouter.js.map +1 -0
  50. package/dist/gateway/routes/organism.js +82 -0
  51. package/dist/gateway/routes/organism.js.map +1 -0
  52. package/dist/gateway/routes/paperclip.js +101 -0
  53. package/dist/gateway/routes/paperclip.js.map +1 -0
  54. package/dist/gateway/routes/sessions.js +227 -0
  55. package/dist/gateway/routes/sessions.js.map +1 -0
  56. package/dist/gateway/routes/skills.js +295 -0
  57. package/dist/gateway/routes/skills.js.map +1 -0
  58. package/dist/gateway/routes/socialRouter.js +145 -0
  59. package/dist/gateway/routes/socialRouter.js.map +1 -0
  60. package/dist/gateway/routes/systemRouter.js +220 -0
  61. package/dist/gateway/routes/systemRouter.js.map +1 -0
  62. package/dist/gateway/routes/teamsRecipes.js +297 -0
  63. package/dist/gateway/routes/teamsRecipes.js.map +1 -0
  64. package/dist/gateway/routes/tests.js +401 -0
  65. package/dist/gateway/routes/tests.js.map +1 -0
  66. package/dist/gateway/routes/traces.js +33 -0
  67. package/dist/gateway/routes/traces.js.map +1 -0
  68. package/dist/gateway/routes/voiceRouter.js +770 -0
  69. package/dist/gateway/routes/voiceRouter.js.map +1 -0
  70. package/dist/gateway/routes/watchRouter.js +131 -0
  71. package/dist/gateway/routes/watchRouter.js.map +1 -0
  72. package/dist/gateway/server.js +1179 -7379
  73. package/dist/gateway/server.js.map +1 -1
  74. package/dist/mcp/registry.js +2 -2
  75. package/dist/mcp/registry.js.map +1 -1
  76. package/dist/memory/episodic.js +2 -2
  77. package/dist/memory/episodic.js.map +1 -1
  78. package/dist/memory/learning.js +3 -3
  79. package/dist/memory/learning.js.map +1 -1
  80. package/dist/memory/memory.js +3 -3
  81. package/dist/memory/memory.js.map +1 -1
  82. package/dist/organism/drives.js +2 -2
  83. package/dist/organism/drives.js.map +1 -1
  84. package/dist/providers/errorTaxonomy.js +13 -0
  85. package/dist/providers/errorTaxonomy.js.map +1 -1
  86. package/dist/providers/ollama.js +3 -1
  87. package/dist/providers/ollama.js.map +1 -1
  88. package/dist/providers/openai_compat.js +4 -3
  89. package/dist/providers/openai_compat.js.map +1 -1
  90. package/dist/providers/router.js +13 -0
  91. package/dist/providers/router.js.map +1 -1
  92. package/dist/safety/fixOscillation.js +15 -0
  93. package/dist/safety/fixOscillation.js.map +1 -1
  94. package/dist/safety/killSwitch.js +2 -2
  95. package/dist/safety/killSwitch.js.map +1 -1
  96. package/dist/safety/selfRepair.js +7 -3
  97. package/dist/safety/selfRepair.js.map +1 -1
  98. package/dist/skills/builtin/agent_debate.js +2 -2
  99. package/dist/skills/builtin/agent_debate.js.map +1 -1
  100. package/dist/skills/builtin/apply_patch.js +3 -3
  101. package/dist/skills/builtin/apply_patch.js.map +1 -1
  102. package/dist/skills/builtin/shell.js +2 -2
  103. package/dist/skills/builtin/shell.js.map +1 -1
  104. package/dist/skills/builtin/voice_control.js +49 -0
  105. package/dist/skills/builtin/voice_control.js.map +1 -0
  106. package/dist/skills/builtin/widget_gallery.js +6 -1
  107. package/dist/skills/builtin/widget_gallery.js.map +1 -1
  108. package/dist/skills/registry.js +15 -4
  109. package/dist/skills/registry.js.map +1 -1
  110. package/dist/storage/JsonStorage.js +4 -4
  111. package/dist/storage/JsonStorage.js.map +1 -1
  112. package/dist/utils/constants.js +1 -1
  113. package/dist/utils/constants.js.map +1 -1
  114. package/dist/utils/helpers.js +3 -1
  115. package/dist/utils/helpers.js.map +1 -1
  116. package/dist/utils/lifecycle.js +86 -0
  117. package/dist/utils/lifecycle.js.map +1 -0
  118. package/dist/voice/bridge.js +136 -0
  119. package/dist/voice/bridge.js.map +1 -0
  120. package/docs/COO-MASTER-PLAN-2026-05-02.md +474 -0
  121. package/docs/HANDOFF/2026-04-29.md +141 -0
  122. package/docs/HANDOFF-2026-04-30.md +144 -0
  123. package/docs/HANDOFF-2026-05-03.md +114 -0
  124. package/docs/adr/2026-04-29-widget-pipeline-traceability.md +49 -0
  125. package/docs/agent-memory/README.md +45 -0
  126. package/docs/agent-memory/commands.md +100 -0
  127. package/docs/agent-memory/context-tree.md +101 -0
  128. package/docs/agent-memory/current-state.md +54 -0
  129. package/docs/agent-memory/decisions.md +78 -0
  130. package/docs/agent-memory/known-issues.md +76 -0
  131. package/docs/agent-memory/reflections.md +52 -0
  132. package/docs/agent-memory/skills-candidates.md +80 -0
  133. package/docs/superpowers/plans/2026-04-29-comprehensive-audit.md +256 -0
  134. package/docs/superpowers/plans/2026-04-29-comprehensive-test-plan.md +396 -0
  135. package/docs/superpowers/plans/2026-04-29-fix-all-prs.md +251 -0
  136. package/docs/superpowers/plans/2026-04-29-gitnexus-gap-remediation.md +969 -0
  137. package/package.json +5 -2
  138. package/ui/dist/assets/{AuditPanel-CM6Wg9hO.js → AuditPanel-VzSndmDN.js} +2 -2
  139. package/ui/dist/assets/{AutonomyPanel-CESx3ANg.js → AutonomyPanel-BiFouzAV.js} +2 -2
  140. package/ui/dist/assets/AutopilotPanel-fjOfM668.js +1 -0
  141. package/ui/dist/assets/{AutoresearchPanel-DR47NqT5.js → AutoresearchPanel-CVCxzAH3.js} +2 -2
  142. package/ui/dist/assets/BackupPanel-CHVTG--q.js +1 -0
  143. package/ui/dist/assets/{BrowserPanel-C15x9bLn.js → BrowserPanel-D5mvMKFU.js} +2 -2
  144. package/ui/dist/assets/CPActivity-B12mt35m.js +1 -0
  145. package/ui/dist/assets/CPAgentDetail-DsdShc-1.js +1 -0
  146. package/ui/dist/assets/CPAgents-j_7C-oQV.js +1 -0
  147. package/ui/dist/assets/CPApprovals-BShKSX9X.js +1 -0
  148. package/ui/dist/assets/CPCosts-CKPlhBDs.js +1 -0
  149. package/ui/dist/assets/CPDashboard-11c0nkxK.js +1 -0
  150. package/ui/dist/assets/CPFiles-BhLEOnXy.js +1 -0
  151. package/ui/dist/assets/CPGoals-Bi3t1b2P.js +1 -0
  152. package/ui/dist/assets/CPInbox-Bbr7khp6.js +11 -0
  153. package/ui/dist/assets/CPIssueDetail-DSdgNK8r.js +1 -0
  154. package/ui/dist/assets/CPIssues-DDEVKhX6.js +1 -0
  155. package/ui/dist/assets/CPLayout-DgPOfyGv.js +17 -0
  156. package/ui/dist/assets/CPOrg-Df73RrRJ.js +8 -0
  157. package/ui/dist/assets/CPRuns-ByioAz8w.js +1 -0
  158. package/ui/dist/assets/{CPSocial-nb-j7sOE.js → CPSocial-Dlnr_w1X.js} +2 -2
  159. package/ui/dist/assets/ChannelsPanel-DQjQCTK5.js +1 -0
  160. package/ui/dist/assets/CheckpointsPanel-C4vKjlAJ.js +1 -0
  161. package/ui/dist/assets/CommandPostHub-C9pp5Giq.js +24 -0
  162. package/ui/dist/assets/CronPanel-C6bzUfrD.js +1 -0
  163. package/ui/dist/assets/DaemonPanel-BA5Tb_UO.js +1 -0
  164. package/ui/dist/assets/{DataTable-B2Ma8hfi.js → DataTable-CH7IYJJh.js} +1 -1
  165. package/ui/dist/assets/{EmptyState-CcKyk5Yn.js → EmptyState-jU6yNDnF.js} +1 -1
  166. package/ui/dist/assets/{EvalHarnessPanel-BqtMc1ZM.js → EvalHarnessPanel-DnYqredY.js} +2 -2
  167. package/ui/dist/assets/EvalPanel-ChO7CD1r.js +1 -0
  168. package/ui/dist/assets/{FilesPanel-3QKvrWPo.js → FilesPanel-CaUkv2is.js} +2 -2
  169. package/ui/dist/assets/FleetPanel-DC_5uj0N.js +1 -0
  170. package/ui/dist/assets/{HomelabPanel-DhrjTX9m.js → HomelabPanel-CE5PGRpL.js} +2 -2
  171. package/ui/dist/assets/InfraView-C-uSlvb9.js +2 -0
  172. package/ui/dist/assets/InlineEditableField-BMQjiE6-.js +1 -0
  173. package/ui/dist/assets/Input-Bu_b3qmY.js +1 -0
  174. package/ui/dist/assets/IntegrationsPanel-DsYpAq43.js +1 -0
  175. package/ui/dist/assets/IntelligenceView-DUdIO1K7.js +2 -0
  176. package/ui/dist/assets/LearningPanel-UpQZC-mA.js +1 -0
  177. package/ui/dist/assets/LogsPanel-ClXJ4fcr.js +1 -0
  178. package/ui/dist/assets/McpPanel-JKgtIERQ.js +1 -0
  179. package/ui/dist/assets/{MemoryGraphPanel-Bzvjmzvk.js → MemoryGraphPanel-Bo2OrvA6.js} +2 -2
  180. package/ui/dist/assets/MemoryWikiPanel-BqJ1AmYm.js +11 -0
  181. package/ui/dist/assets/{MeshPanel-C3LJSlht.js → MeshPanel-BJVGYvwk.js} +2 -2
  182. package/ui/dist/assets/Modal-CAAooiZU.js +1 -0
  183. package/ui/dist/assets/NvidiaPanel-BtCg3G4w.js +1 -0
  184. package/ui/dist/assets/OrganismPanel-DgrTTzcF.js +1 -0
  185. package/ui/dist/assets/OverviewPanel-rVav1Hox.js +1 -0
  186. package/ui/dist/assets/{PageHeader-BimceqQo.js → PageHeader-CnZtP8ek.js} +1 -1
  187. package/ui/dist/assets/PaperclipPanel-C-FKdhiF.js +1 -0
  188. package/ui/dist/assets/{PersonasPanel-L1j78p6H.js → PersonasPanel-BmlxokfB.js} +1 -1
  189. package/ui/dist/assets/RecipesPanel-BNKKChis.js +1 -0
  190. package/ui/dist/assets/SecurityPanel-I7JRHiNy.js +1 -0
  191. package/ui/dist/assets/SelfImprovePanel-u9h0Lt3p.js +1 -0
  192. package/ui/dist/assets/{SelfProposalsPanel-lNmiDThB.js → SelfProposalsPanel-DKl9iBjM.js} +2 -2
  193. package/ui/dist/assets/SessionsPanel-BhRiWI_g.js +1 -0
  194. package/ui/dist/assets/{SessionsTab-JQbltWww.js → SessionsTab-Bk08wyeY.js} +1 -1
  195. package/ui/dist/assets/SettingsPanel-haLfmG2k.js +1 -0
  196. package/ui/dist/assets/SettingsView--gi3fxI8.js +2 -0
  197. package/ui/dist/assets/{SkeletonLoader-atQtpcF5.js → SkeletonLoader-B5v09EF_.js} +1 -1
  198. package/ui/dist/assets/{SkillsPanel-DlFs2ih7.js → SkillsPanel-BlAHFLcQ.js} +1 -1
  199. package/ui/dist/assets/SomaView-CExtS3zw.js +5 -0
  200. package/ui/dist/assets/{StatCard-DciE_Iqc.js → StatCard-BIsyMybM.js} +1 -1
  201. package/ui/dist/assets/{StatusBadge-BtfSPoW2.js → StatusBadge-D5nU7El8.js} +1 -1
  202. package/ui/dist/assets/Tabs-BBYZrBI8.js +1 -0
  203. package/ui/dist/assets/TeamsPanel-LPXJg823.js +1 -0
  204. package/ui/dist/assets/TelemetryPanel-EqpRBmOI.js +1 -0
  205. package/ui/dist/assets/TitanCanvas-BCbWnLMd.js +985 -0
  206. package/ui/dist/assets/ToolsView-CeP0Zz-N.js +2 -0
  207. package/ui/dist/assets/{Tooltip-70UK0E2I.js → Tooltip-BSO2XVpF.js} +1 -1
  208. package/ui/dist/assets/TraceViewer-BKI7o5B0.js +1 -0
  209. package/ui/dist/assets/TrainingPanel-c-RhjdE1.js +1 -0
  210. package/ui/dist/assets/VoiceOverlay-D-gc58b0.js +27 -0
  211. package/ui/dist/assets/VramPanel-C6xc7zgd.js +1 -0
  212. package/ui/dist/assets/{WatchView-C-sGFpVy.js → WatchView-dqBVCVH0.js} +1 -1
  213. package/ui/dist/assets/WorkTab-CBoLNrTM.js +1 -0
  214. package/ui/dist/assets/{WorkflowsPanel-CvgQU1xI.js → WorkflowsPanel-BAnSTOYe.js} +2 -2
  215. package/ui/dist/assets/approvalHeadline-DB9SgR-9.js +1 -0
  216. package/ui/dist/assets/{arrow-left-DwqHtJiU.js → arrow-left-5chqas7J.js} +1 -1
  217. package/ui/dist/assets/briefcase-D4vLzudp.js +6 -0
  218. package/ui/dist/assets/{chart-column-BtNO6sRy.js → chart-column-CdFlBpoP.js} +1 -1
  219. package/ui/dist/assets/check-Bpm1IONe.js +6 -0
  220. package/ui/dist/assets/chevron-down-D7OLjvuD.js +6 -0
  221. package/ui/dist/assets/chevron-right-aQEw2mUW.js +6 -0
  222. package/ui/dist/assets/chevron-up-C5g6pEj8.js +6 -0
  223. package/ui/dist/assets/{circle-check-big-DZRE_MbN.js → circle-check-big-fPhEdP88.js} +1 -1
  224. package/ui/dist/assets/clock-CTsgP_Sn.js +6 -0
  225. package/ui/dist/assets/{dollar-sign-aVG3a5eL.js → dollar-sign-CudFVYFc.js} +1 -1
  226. package/ui/dist/assets/{download-BxiWJU4G.js → download-DZRxDn67.js} +1 -1
  227. package/ui/dist/assets/external-link-BZ0y_Ahx.js +6 -0
  228. package/ui/dist/assets/{eye-off-CkgfFYhm.js → eye-off-BmJF0YYx.js} +1 -1
  229. package/ui/dist/assets/folder-DA43TRCm.js +11 -0
  230. package/ui/dist/assets/{funnel-PkLdxKyC.js → funnel-J3mULzrz.js} +1 -1
  231. package/ui/dist/assets/{git-branch-BM-Gw95X.js → git-branch-oHibJqDq.js} +1 -1
  232. package/ui/dist/assets/{index-D0RJ8701.css → index-BR0vfkIi.css} +1 -1
  233. package/ui/dist/assets/{index-CahJbWSR.js → index-DzwowwSI.js} +20 -20
  234. package/ui/dist/assets/{layers-BuGf4FIJ.js → layers-DsyEyu7z.js} +1 -1
  235. package/ui/dist/assets/{legacy-CR6o4t-y.js → legacy-8ITl64sV.js} +1 -1
  236. package/ui/dist/assets/{lightbulb-n8gc_XAL.js → lightbulb-C54Ske-p.js} +1 -1
  237. package/ui/dist/assets/list-todo-Cnd4rdoK.js +6 -0
  238. package/ui/dist/assets/loader-circle-1YOBsoQp.js +6 -0
  239. package/ui/dist/assets/network-DbGDAdrn.js +6 -0
  240. package/ui/dist/assets/{pause-DCV52koX.js → pause-CYhO_uQo.js} +1 -1
  241. package/ui/dist/assets/{play-CcJ9BnCh.js → play-DVY9c5Ck.js} +1 -1
  242. package/ui/dist/assets/{plug-CfWBXfCl.js → plug-BcXjlPUL.js} +1 -1
  243. package/ui/dist/assets/plus-Csu2v9GN.js +6 -0
  244. package/ui/dist/assets/{proxy-CzZDfLmm.js → proxy-DxS2_9D7.js} +1 -1
  245. package/ui/dist/assets/rotate-ccw-Co-_W04j.js +6 -0
  246. package/ui/dist/assets/save-Btx-kpoW.js +6 -0
  247. package/ui/dist/assets/search-0hXTwEZR.js +6 -0
  248. package/ui/dist/assets/send-TEpapzQR.js +6 -0
  249. package/ui/dist/assets/shield-check-DjBJXZUr.js +6 -0
  250. package/ui/dist/assets/{square-DJpUhlxi.js → square-OweUvjP-.js} +1 -1
  251. package/ui/dist/assets/{target-DWcmM_9m.js → target-BRW80Xer.js} +1 -1
  252. package/ui/dist/assets/terminal-BtiqJ628.js +16 -0
  253. package/ui/dist/assets/{toggle-right-YusFQ69L.js → toggle-right-CKtSrl28.js} +1 -1
  254. package/ui/dist/assets/{trash-2-CK7yQ55V.js → trash-2-DgWrHVax.js} +1 -1
  255. package/ui/dist/assets/{trending-up-DGjFyubC.js → trending-up-MpIrE4j6.js} +1 -1
  256. package/ui/dist/assets/{trophy-uQv_NgDB.js → trophy-CECuZNhX.js} +1 -1
  257. package/ui/dist/assets/users-dZgv4ePG.js +16 -0
  258. package/ui/dist/assets/wrench-CDz3xYve.js +11 -0
  259. package/ui/dist/index.html +2 -2
  260. package/ui/dist/assets/AutopilotPanel-DtEet1hJ.js +0 -1
  261. package/ui/dist/assets/BackupPanel-BGP8p3l3.js +0 -1
  262. package/ui/dist/assets/CPAgents-DYUtPzSq.js +0 -1
  263. package/ui/dist/assets/CPDashboard-Bf0-SyCh.js +0 -6
  264. package/ui/dist/assets/CPFiles-CxgxjQcO.js +0 -1
  265. package/ui/dist/assets/CPGoals-BsmCMTvT.js +0 -1
  266. package/ui/dist/assets/CPInbox-tMSbmQ9H.js +0 -11
  267. package/ui/dist/assets/ChannelsPanel-DP5C2OKd.js +0 -1
  268. package/ui/dist/assets/CheckpointsPanel-DlranVLZ.js +0 -1
  269. package/ui/dist/assets/CommandPostHub-BgxIa4Ev.js +0 -29
  270. package/ui/dist/assets/CronPanel-LoT5yKwJ.js +0 -1
  271. package/ui/dist/assets/DaemonPanel-DBGMqaE_.js +0 -1
  272. package/ui/dist/assets/EvalPanel-Bc33j0pN.js +0 -1
  273. package/ui/dist/assets/FleetPanel-CSsXuQYj.js +0 -1
  274. package/ui/dist/assets/InfraView-CR6HyrL6.js +0 -2
  275. package/ui/dist/assets/InlineEditableField-CnvF-yFR.js +0 -1
  276. package/ui/dist/assets/Input-GTHp2Rkr.js +0 -1
  277. package/ui/dist/assets/IntegrationsPanel-CymCRE3T.js +0 -1
  278. package/ui/dist/assets/IntelligenceView-C1IHxJRC.js +0 -2
  279. package/ui/dist/assets/LearningPanel-DOCES3lH.js +0 -1
  280. package/ui/dist/assets/LogsPanel-BLnAqEaZ.js +0 -1
  281. package/ui/dist/assets/McpPanel-ChUzmr3z.js +0 -1
  282. package/ui/dist/assets/MemoryWikiPanel-Dwk3Aqwd.js +0 -11
  283. package/ui/dist/assets/NvidiaPanel-CeZK_-CV.js +0 -1
  284. package/ui/dist/assets/OrganismPanel-BB6YOiQV.js +0 -1
  285. package/ui/dist/assets/OverviewPanel-BmtBhQnv.js +0 -1
  286. package/ui/dist/assets/PaperclipPanel-C-brgwA3.js +0 -1
  287. package/ui/dist/assets/RecipesPanel-34lCfynJ.js +0 -1
  288. package/ui/dist/assets/SecurityPanel-CBTPWLj6.js +0 -1
  289. package/ui/dist/assets/SelfImprovePanel-BrPbFHhG.js +0 -1
  290. package/ui/dist/assets/SessionsPanel-DAEYIn83.js +0 -1
  291. package/ui/dist/assets/SettingsPanel-CzRROAYQ.js +0 -1
  292. package/ui/dist/assets/SettingsView-CN7ii2uw.js +0 -2
  293. package/ui/dist/assets/SomaView-Ba642Oqb.js +0 -5
  294. package/ui/dist/assets/TeamsPanel-DKQ5z2Qe.js +0 -1
  295. package/ui/dist/assets/TelemetryPanel-B6KAc55Q.js +0 -1
  296. package/ui/dist/assets/TitanCanvas-C-s0A-lv.js +0 -1092
  297. package/ui/dist/assets/ToolsView-Dei0KMP0.js +0 -2
  298. package/ui/dist/assets/TraceViewer-BniolyBx.js +0 -1
  299. package/ui/dist/assets/TrainingPanel-Bz4CTPGW.js +0 -1
  300. package/ui/dist/assets/VoiceOverlay-CmNCrLcd.js +0 -37
  301. package/ui/dist/assets/VramPanel-Xh_OtRDR.js +0 -1
  302. package/ui/dist/assets/WorkTab-BjLNmgIK.js +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/gateway/routes/voiceRouter.ts"],"sourcesContent":["/**\n * Voice Router\n *\n * Extracted from gateway/server.ts v5.5.0.\n * Accepts rate-limit / concurrency guards via factory to avoid tight coupling.\n */\n\nimport { Router, type Request, type Response } from 'express';\nimport { homedir } from 'os';\nimport { join, dirname } from 'path';\nimport fs from 'fs';\nimport { execSync, spawn } from 'child_process';\nimport { loadConfig } from '../../config/config.js';\nimport logger from '../../utils/logger.js';\nimport { processMessage } from '../../agent/agent.js';\nimport { routeMessage } from '../../agent/multiAgent.js';\n\nconst COMPONENT = 'VoiceRouter';\n\nconst VOICE_POISON_PATTERNS = [\n /i completed the tool operations/i,\n /i wasn't able to execute tools/i,\n /i completed the operations/i,\n /let me know if you need anything else\\.?\\s*$/i,\n];\nconst F5_TTS_DEFAULT_VOICES = ['andrew'];\nconst F5_TTS_PORT = 5006;\nconst F5_TTS_MODEL = 'f5-tts-mlx';\n\ninterface Guard {\n (windowMs: number, maxRequests: number): (req: Request, res: Response, next: import('express').NextFunction) => void;\n}\n\nexport function createVoiceRouter(\n sessionAborts: Map<string, AbortController>,\n sessionAbortTimes: Map<string, number>,\n rateLimit?: Guard,\n concurrencyGuard?: (maxConcurrent: number) => (req: Request, res: Response, next: import('express').NextFunction) => void,\n // Optional metrics for voice stream\n activeLlmRequestsRef?: { value: number },\n titanActiveSessions?: { inc: () => void; dec: () => void },\n titanRequestDuration?: { observe: (v: number, labels: Record<string, string>) => void },\n): Router {\n const router = Router();\n\n let f5ttsPid: number | null = null;\n\n router.get('/voice/status', async (_req, res) => {\n const cfg = loadConfig();\n const voice = cfg.voice;\n if (!voice.enabled) { res.json({ available: false, reason: 'Voice not enabled in config' }); return; }\n try {\n const livekitHttp = voice.livekitUrl.replace('ws://', 'http://').replace('wss://', 'https://');\n const resp = await fetch(livekitHttp, { signal: AbortSignal.timeout(3000) });\n res.json({ available: resp.ok, livekitUrl: voice.livekitUrl, ttsVoice: voice.ttsVoice });\n } catch {\n res.json({ available: false, livekitUrl: voice.livekitUrl, reason: 'LiveKit server unreachable' });\n }\n });\n\n router.get('/voice/config', (_req, res) => {\n res.json(loadConfig().voice);\n });\n\n router.post('/livekit/token', async (req, res) => {\n const cfg = loadConfig();\n if (!cfg.voice?.enabled) { res.status(404).json({ error: 'Voice not enabled' }); return; }\n if (!cfg.voice.livekitApiKey || !cfg.voice.livekitApiSecret) {\n res.status(503).json({ error: 'LiveKit not configured' }); return;\n }\n try {\n const livekitSdk: any = await import('livekit-server-sdk').catch(() => null);\n if (!livekitSdk?.AccessToken) { res.status(503).json({ error: 'livekit-server-sdk not installed' }); return; }\n const { AccessToken } = livekitSdk;\n const participantIdentity = `voice_user_${Math.floor(Math.random() * 10_000)}`;\n const roomName = `voice_room_${Math.floor(Math.random() * 10_000)}`;\n const at = new AccessToken(cfg.voice.livekitApiKey, cfg.voice.livekitApiSecret, { identity: participantIdentity, name: 'user', ttl: '15m' });\n at.addGrant({ room: roomName, roomJoin: true, canPublish: true, canPublishData: true, canSubscribe: true });\n let serverUrl = cfg.voice.livekitUrl;\n try {\n const reqHost = req.hostname || req.headers.host?.split(':')[0];\n if (reqHost) { const parsed = new URL(serverUrl); parsed.hostname = reqHost; serverUrl = parsed.toString().replace(/\\/$/, ''); }\n } catch { /* keep original */ }\n res.json({ serverUrl, roomName, participantName: 'user', participantToken: await at.toJwt() });\n } catch (err) {\n logger.error(COMPONENT, `LiveKit token error: ${(err as Error).message}`);\n res.status(500).json({ error: 'Failed to generate LiveKit token' });\n }\n });\n\n router.get('/voice/health', async (_req, res) => {\n const cfg = loadConfig();\n if (!cfg.voice?.enabled) { res.json({ livekit: false, stt: false, tts: false, agent: false, overall: false, ttsEngine: cfg.voice?.ttsEngine || 'f5-tts' }); return; }\n const engine = cfg.voice.ttsEngine || 'f5-tts';\n const results = { livekit: false, stt: false, tts: false, agent: false, overall: false, ttsEngine: engine };\n const sttUrl = cfg.voice.sttUrl || 'http://localhost:48421';\n const ttsUrl = cfg.voice.ttsUrl || 'http://localhost:5006';\n const nvidia = (cfg as Record<string, unknown>).nvidia as Record<string, unknown> | undefined;\n const asrCfg = nvidia?.asr as Record<string, unknown> | undefined;\n const sttHealthUrl = (cfg.voice.sttEngine || 'faster-whisper') === 'nemotron-asr'\n ? `${(asrCfg?.healthUrl as string) || 'http://localhost:9000'}/v1/health/ready`\n : `${sttUrl}/health`;\n await Promise.allSettled([\n { key: 'livekit' as const, url: cfg.voice.livekitUrl.replace('ws://', 'http://').replace('wss://', 'https://') },\n { key: 'agent' as const, url: cfg.voice.agentUrl },\n { key: 'stt' as const, url: sttHealthUrl },\n ].map(async ({ key, url }) => {\n try { const resp = await fetch(url, { signal: AbortSignal.timeout(3000) }); results[key] = resp.ok || resp.status < 500; }\n catch { results[key] = false; }\n }));\n try {\n let resp = await fetch(`${ttsUrl}/health`, { signal: AbortSignal.timeout(3000) }).catch(() => null);\n if (!resp || resp.status >= 400) {\n const voice = cfg.voice.ttsVoice || 'andrew';\n resp = await fetch(`${ttsUrl}/v1/audio/speech`, {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ model: 'f5-tts', input: '.', voice, response_format: 'pcm' }),\n signal: AbortSignal.timeout(10000),\n });\n }\n results.tts = resp ? resp.status < 500 : false;\n } catch { results.tts = false; }\n results.overall = results.tts;\n res.json(results);\n });\n\n // NVIDIA Health Checks\n router.get('/nvidia/health/cuopt', async (_req, res) => {\n const cfg = loadConfig();\n const nvidia = (cfg as Record<string, unknown>).nvidia as Record<string, unknown> | undefined;\n const cuoptUrl = ((nvidia?.cuopt as Record<string, unknown>)?.url as string) || 'http://localhost:5000';\n try { const resp = await fetch(`${cuoptUrl}/cuopt/health`, { signal: AbortSignal.timeout(5000) }); res.json({ healthy: resp.ok, status: resp.status, url: cuoptUrl }); }\n catch { res.json({ healthy: false, url: cuoptUrl }); }\n });\n\n router.get('/nvidia/health/asr', async (_req, res) => {\n const cfg = loadConfig();\n const nvidia = (cfg as Record<string, unknown>).nvidia as Record<string, unknown> | undefined;\n const healthUrl = ((nvidia?.asr as Record<string, unknown>)?.healthUrl as string) || 'http://localhost:9000';\n try { const resp = await fetch(`${healthUrl}/v1/health/ready`, { signal: AbortSignal.timeout(5000) }); res.json({ healthy: resp.ok, status: resp.status, url: healthUrl }); }\n catch { res.json({ healthy: false, url: healthUrl }); }\n });\n\n router.get('/nvidia/health/nim', async (_req, res) => {\n const cfg = loadConfig();\n const nvidia = (cfg as Record<string, unknown>).nvidia as Record<string, unknown> | undefined;\n const apiKey = (nvidia?.apiKey as string) || process.env.NVIDIA_API_KEY || '';\n if (!apiKey) { res.json({ healthy: false, reason: 'No NVIDIA API key configured' }); return; }\n try { const resp = await fetch('https://integrate.api.nvidia.com/v1/models', { headers: { Authorization: `Bearer ${apiKey}` }, signal: AbortSignal.timeout(8000) }); res.json({ healthy: resp.ok, status: resp.status }); }\n catch { res.json({ healthy: false, reason: 'NIM API unreachable' }); }\n });\n\n // Voice preview\n router.post('/voice/preview', async (req, res) => {\n const cfg = loadConfig();\n const engine = cfg.voice?.ttsEngine || 'f5-tts';\n const voiceId = req.body?.voice || cfg.voice?.ttsVoice || 'andrew';\n const rawText = req.body?.text || 'Hey! I\\'m TITAN, your AI assistant.';\n const text = rawText.length > 500 ? rawText.slice(0, 497) + '...' : rawText;\n const ttsUrl = cfg.voice?.ttsUrl || 'http://localhost:5006';\n try {\n const ttsRes = await fetch(`${ttsUrl}/v1/audio/speech`, {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ model: 'f5-tts-mlx', input: text, voice: voiceId, response_format: 'wav' }),\n signal: AbortSignal.timeout(60000),\n });\n if (!ttsRes.ok) { res.status(502).json({ error: `TTS service unavailable`, status: ttsRes.status }); return; }\n res.setHeader('Content-Type', 'audio/wav');\n res.send(Buffer.from(await ttsRes.arrayBuffer()));\n } catch { res.status(502).json({ error: `TTS service unavailable` }); }\n });\n\n // Voice stream — complex SSE endpoint with sentence chunking\n router.post('/voice/stream',\n rateLimit ? rateLimit(60000, 30) : (_req, _res, next) => next(),\n concurrencyGuard ? concurrencyGuard(10) : (_req, _res, next) => next(),\n async (req, res) => {\n const { content, sessionId: requestedSessionId, voice: reqVoice } = req.body || {};\n if (!content) { res.status(400).json({ error: 'content is required' }); return; }\n\n const cfg = loadConfig();\n const ttsUrl = cfg.voice?.ttsUrl || 'http://localhost:5006';\n const ttsEngine = cfg.voice?.ttsEngine || 'f5-tts';\n const voiceId = reqVoice || cfg.voice?.ttsVoice || 'andrew';\n const channel = 'voice';\n const userId = 'voice-user';\n\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders();\n\n let clientDisconnected = false;\n res.on('close', () => { clientDisconnected = true; });\n const safeWrite = (data: string) => { if (!clientDisconnected) { try { res.write(data); } catch { clientDisconnected = true; } } };\n\n const heartbeat = setInterval(() => { if (clientDisconnected) { clearInterval(heartbeat); return; } safeWrite(': heartbeat\\n\\n'); }, 2000);\n\n const abortController = new AbortController();\n if (requestedSessionId) { sessionAborts.set(requestedSessionId, abortController); sessionAbortTimes.set(requestedSessionId, Date.now()); }\n\n let effectiveTtsEngine: string = ttsEngine;\n const effectiveTtsUrl = ttsUrl;\n const effectiveTtsModel = 'f5-tts-mlx';\n\n try {\n const probe = await fetch(`${effectiveTtsUrl}/health`, { signal: AbortSignal.timeout(5000) });\n if (!probe.ok) effectiveTtsEngine = 'unavailable';\n } catch {\n effectiveTtsEngine = 'unavailable';\n logger.warn(COMPONENT, `F5-TTS unreachable at ${effectiveTtsUrl}`);\n }\n safeWrite(`event: tts_mode\\ndata: ${JSON.stringify({ engine: effectiveTtsEngine })}\\n\\n`);\n\n let tokenBuffer = '';\n let sentenceIndex = 0;\n let firstChunkSent = false;\n let totalTtsChars = 0;\n const FIRST_CHUNK_MIN = 60;\n const MAX_TTS_SENTENCES = 50;\n const MAX_TTS_CHARS = 10000;\n const ttsQueue: Array<{ sentence: string; index: number }> = [];\n let ttsRunning = false;\n let ttsResolve: (() => void) = () => {};\n const ttsAllDone = new Promise<void>(resolve => { ttsResolve = resolve; });\n let ttsFinished = false;\n\n const processTtsQueue = async () => { if (ttsRunning) return; ttsRunning = true; while (ttsQueue.length > 0) { if (clientDisconnected) break; const item = ttsQueue.shift()!; await fireTTSInternal(item.sentence, item.index); } ttsRunning = false; if (ttsFinished && ttsQueue.length === 0) ttsResolve(); };\n\n const cleanForVoice = (text: string): string => text\n .replace(/<TOOLCALL>[\\s\\S]*?(?:<\\/TOOLCALL>|$)/g, '')\n .replace(/<TOOLCALL>\\[[\\s\\S]*?\\]/g, '')\n .replace(/```[\\s\\S]*?```/g, '')\n .replace(/`[^`]+`/g, (m) => m.slice(1, -1))\n .replace(/\\*\\*(.*?)\\*\\*/g, '$1')\n .replace(/\\*(.*?)\\*/g, '$1')\n .replace(/^#+\\s+/gm, '')\n .replace(/^\\d+\\.\\s+/gm, '')\n .replace(/^[-*]\\s+/gm, '')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/https?:\\/\\/\\S+/g, '')\n .replace(/\\n{2,}/g, '. ')\n .replace(/\\n/g, ' ')\n .replace(/<(?:laugh|chuckle|sigh|cough|sniffle|groan|yawn|gasp|smile)>/gi, '')\n .replace(/(?:Let me |I'll |I will |I'm going to )(?:use|call|check|run|invoke|execute|try)(?: the)? \\w[\\w_]*(?: tool)?(?:\\s+(?:to|for|and)\\b[^.!?]*)?[.!?]?\\s*/gi, '')\n .replace(/\\b(?:Using|Calling|Running|Checking|Invoking|Executing) (?:the )?\\w[\\w_]*(?: tool)?(?:\\s+(?:to|for)\\b[^.!?]*)?[.!?]?\\s*/gi, '')\n .replace(/(\\w)\\s*—\\s*(\\w)/g, '$1, $2')\n .replace(/(\\w)\\s*–\\s*(\\w)/g, '$1, $2')\n .replace(/;\\s*/g, '. ')\n .replace(/\\(([^)]+)\\)/g, ', $1,')\n .replace(/([a-z]{4,}),\\s*(but|yet|so|however|although)\\s+/gi, '$1. $2 ')\n .replace(/\\.\\s*\\./g, '.')\n .replace(/,\\s*\\./g, '.')\n .replace(/\\s{2,}/g, ' ')\n .trim();\n\n const isF5TTS = effectiveTtsEngine === 'f5-tts';\n const f5Sentences: string[] = [];\n\n const fireTTSInternal = async (sentence: string, index: number) => {\n const clean = cleanForVoice(sentence);\n if (!clean || clean.length < 3) return;\n if (!isF5TTS) safeWrite(`event: sentence\\ndata: ${JSON.stringify({ text: clean, index })}\\n\\n`);\n if (index >= MAX_TTS_SENTENCES || totalTtsChars >= MAX_TTS_CHARS) return;\n totalTtsChars += clean.length;\n try {\n const ttsRes = await fetch(`${effectiveTtsUrl}/v1/audio/speech`, {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ model: effectiveTtsModel, input: clean, voice: voiceId, response_format: 'wav' }),\n signal: AbortSignal.timeout(60000),\n });\n if (ttsRes.ok && !clientDisconnected) {\n const audioBuffer = Buffer.from(await ttsRes.arrayBuffer());\n safeWrite(`event: audio\\ndata: ${JSON.stringify({ index, audio: audioBuffer.toString('base64'), format: 'wav' })}\\n\\n`);\n }\n } catch (e) { logger.debug('Gateway', `Voice stream TTS failed for sentence ${index}: ${(e as Error).message}`); }\n };\n\n const flushSentence = (text: string) => {\n const trimmed = text.trim();\n if (trimmed.length < 3) return;\n if (isF5TTS) {\n const clean = cleanForVoice(trimmed);\n if (clean && clean.length >= 3) {\n safeWrite(`event: sentence\\ndata: ${JSON.stringify({ text: clean, index: sentenceIndex++ })}\\n\\n`);\n f5Sentences.push(clean);\n }\n return;\n }\n const idx = sentenceIndex++;\n ttsQueue.push({ sentence: trimmed, index: idx });\n processTtsQueue();\n };\n\n const activeRef = activeLlmRequestsRef || { value: 0 };\n activeRef.value++;\n if (titanActiveSessions) titanActiveSessions.inc();\n const startTime = process.hrtime.bigint();\n\n try {\n const response = await routeMessage(content, channel, userId, {\n streamCallbacks: {\n onToken: (token: string) => {\n if (clientDisconnected) return;\n tokenBuffer += token;\n if (!firstChunkSent && tokenBuffer.length >= FIRST_CHUNK_MIN) {\n const lastSpace = tokenBuffer.lastIndexOf(' ');\n if (lastSpace > 30) { flushSentence(tokenBuffer.slice(0, lastSpace)); tokenBuffer = tokenBuffer.slice(lastSpace + 1); firstChunkSent = true; return; }\n }\n if (tokenBuffer.includes('\\n')) {\n const lines = tokenBuffer.split('\\n');\n tokenBuffer = lines.pop() || '';\n for (const line of lines) { if (line.trim().length >= 3) { flushSentence(line); firstChunkSent = true; } }\n return;\n }\n let match: RegExpMatchArray | null;\n while ((match = tokenBuffer.match(/^(.*?(?<![\\d])\\b(?:Dr|Mr|Mrs|Ms|vs|etc|e\\.g|i\\.e))?([.!?])(\\s+|$)/s)) !== null) {\n flushSentence(match[1]);\n tokenBuffer = tokenBuffer.slice(match[0].length);\n firstChunkSent = true;\n }\n if (tokenBuffer.length > 80) {\n const colonMatch = tokenBuffer.match(/^(.*?[:;])\\s+/s);\n if (colonMatch && colonMatch[1].length > 20) { flushSentence(colonMatch[1]); tokenBuffer = tokenBuffer.slice(colonMatch[0].length); firstChunkSent = true; return; }\n }\n if (tokenBuffer.length > 200) {\n const commaPos = tokenBuffer.lastIndexOf(', ', 180);\n if (commaPos > 40) { flushSentence(tokenBuffer.slice(0, commaPos + 1)); tokenBuffer = tokenBuffer.slice(commaPos + 2); firstChunkSent = true; }\n else { const lastSpace = tokenBuffer.lastIndexOf(' ', 180); if (lastSpace > 50) { flushSentence(tokenBuffer.slice(0, lastSpace)); tokenBuffer = tokenBuffer.slice(lastSpace + 1); firstChunkSent = true; } }\n }\n },\n onToolCall: (name: string) => { safeWrite(`event: tool\\ndata: ${JSON.stringify({ name })}\\n\\n`); },\n },\n signal: abortController.signal,\n });\n\n if (tokenBuffer.trim()) { flushSentence(tokenBuffer); tokenBuffer = ''; }\n\n if (isF5TTS && f5Sentences.length > 0) {\n const F5_MAX_CHUNK_CHARS = 600;\n const chunks: string[] = [];\n let current = '';\n for (const s of f5Sentences) {\n if (current && (current.length + s.length + 1) > F5_MAX_CHUNK_CHARS) { chunks.push(current); current = s; } else { current += (current ? ' ' : '') + s; }\n }\n if (current) chunks.push(current);\n let audioIdx = 0;\n for (const chunk of chunks) {\n if (clientDisconnected || totalTtsChars >= MAX_TTS_CHARS) break;\n totalTtsChars += chunk.length;\n try {\n const ttsRes = await fetch(`${effectiveTtsUrl}/v1/audio/speech`, {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ model: effectiveTtsModel, input: chunk, voice: voiceId, response_format: 'wav' }),\n signal: AbortSignal.timeout(120000),\n });\n if (ttsRes.ok && !clientDisconnected) {\n const audioBuffer = Buffer.from(await ttsRes.arrayBuffer());\n safeWrite(`event: audio\\ndata: ${JSON.stringify({ index: audioIdx++, audio: audioBuffer.toString('base64'), format: 'wav' })}\\n\\n`);\n }\n } catch (e) { logger.debug('Gateway', `F5-TTS chunk ${audioIdx} failed: ${(e as Error).message}`); }\n }\n }\n\n ttsFinished = true;\n if (!ttsRunning && ttsQueue.length === 0) ttsResolve();\n if (!isF5TTS) await ttsAllDone;\n\n const responseText = response.content || '';\n if (VOICE_POISON_PATTERNS.some(p => p.test(responseText)) || (response.durationMs > 60000 && responseText.length < 50)) {\n logger.warn(COMPONENT, `[VoicePoisonGuard] Detected canned/stale response — resetting voice session ${response.sessionId}`);\n try { const { closeSession } = await import('../../agent/session.js'); closeSession(response.sessionId); } catch { /* ok */ }\n }\n\n if (!clientDisconnected) {\n safeWrite(`event: done\\ndata: ${JSON.stringify({ sessionId: response.sessionId, model: response.model, durationMs: response.durationMs, toolsUsed: response.toolsUsed, fullText: response.content })}\\n\\n`);\n try { res.end(); } catch { /* client gone */ }\n }\n } catch (error) {\n if (!clientDisconnected) {\n safeWrite(`event: done\\ndata: ${JSON.stringify({ error: (error as Error).message })}\\n\\n`);\n try { res.end(); } catch { /* client gone */ }\n }\n } finally {\n clearInterval(heartbeat);\n activeRef.value--;\n if (titanActiveSessions) titanActiveSessions.dec();\n if (titanRequestDuration) titanRequestDuration.observe(Number(process.hrtime.bigint() - startTime) / 1e9, { channel });\n if (requestedSessionId) sessionAborts.delete(requestedSessionId);\n }\n });\n\n router.get('/voice/voices', async (_req, res) => {\n const cfg = loadConfig();\n const engine = cfg.voice?.ttsEngine || 'f5-tts';\n const ttsUrl = cfg.voice?.ttsUrl || 'http://localhost:5006';\n if (engine === 'f5-tts') {\n const voicesDir = join(homedir(), '.titan', 'voices');\n try { const files = fs.existsSync(voicesDir) ? fs.readdirSync(voicesDir).filter(f => f.endsWith('.wav')) : []; res.json({ voices: files.length ? files.map(f => f.replace('.wav', '')) : ['default'], engine: 'f5-tts' }); } catch { res.json({ voices: ['default'], engine: 'f5-tts' }); }\n return;\n }\n try { const ttsRes = await fetch(`${ttsUrl}/v1/audio/voices`, { signal: AbortSignal.timeout(3000) }); if (!ttsRes.ok) throw new Error(); const data = await ttsRes.json() as { voices?: string[] }; res.json({ ...data, engine: 'f5-tts' }); } catch { res.json({ voices: F5_TTS_DEFAULT_VOICES, engine: 'f5-tts' }); }\n });\n\n router.get('/voice/tts', async (req, res) => {\n try {\n const text = (req.query.text as string || '').slice(0, 2000);\n const voice = (req.query.voice as string) || 'andrew';\n const format = ((req.query.format as string) || 'mp3').toLowerCase();\n if (!text.trim()) { res.status(400).json({ error: 'text required' }); return; }\n const cfg = loadConfig();\n const ttsUrl = cfg.voice?.ttsUrl || 'http://localhost:5006';\n const ttsRes = await fetch(`${ttsUrl}/v1/audio/speech`, {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ input: text, voice, response_format: format }),\n signal: AbortSignal.timeout(180_000),\n });\n if (!ttsRes || !ttsRes.ok) { res.status(502).json({ error: 'tts backends unavailable' }); return; }\n const contentType = ttsRes.headers.get('content-type') || (format === 'wav' ? 'audio/wav' : 'audio/mpeg');\n const buf = Buffer.from(await ttsRes.arrayBuffer());\n res.setHeader('Content-Type', contentType);\n res.setHeader('Content-Length', String(buf.length));\n res.setHeader('Cache-Control', 'no-store');\n res.send(buf);\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n router.get('/voice/f5tts/status', async (_req, res) => {\n let running = false;\n try { const probe = await fetch(`http://localhost:${F5_TTS_PORT}/health`, { signal: AbortSignal.timeout(3000) }); running = probe.ok; } catch { /* not running */ }\n const voicesDir = join(homedir(), '.titan', 'voices');\n let voices: string[] = [];\n try { if (fs.existsSync(voicesDir)) voices = fs.readdirSync(voicesDir).filter(f => f.endsWith('.wav')).map(f => f.replace('.wav', '')); } catch { /* ignore */ }\n res.json({ installed: true, running, voices, port: F5_TTS_PORT, model: F5_TTS_MODEL });\n });\n\n router.post('/voice/f5tts/install', async (_req, res) => {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.flushHeaders();\n const send = (step: string, status: 'running' | 'done' | 'error', detail?: string) => { res.write(`data: ${JSON.stringify({ step, status, detail })}\\n\\n`); };\n const venvPath = join(homedir(), '.titan', 'qwen3tts-venv');\n const voicesDir = join(homedir(), '.titan', 'voices');\n try {\n send('venv', 'running', 'Creating Python virtual environment...');\n if (!fs.existsSync(join(venvPath, 'bin', 'python'))) { execSync(`python3 -m venv \"${venvPath}\"`, { timeout: 60000 }); }\n send('venv', 'done');\n const pip = join(venvPath, 'bin', 'pip');\n send('install', 'running', 'Installing F5-TTS + MLX dependencies (this may take 2-3 minutes)...');\n execSync(`\"${pip}\" install f5-tts-mlx \"mlx-audio[server]\" \"setuptools<81\" numpy`, { timeout: 600000 });\n send('install', 'done');\n if (!fs.existsSync(voicesDir)) fs.mkdirSync(voicesDir, { recursive: true });\n send('start', 'running', 'Starting voice cloning server on port 5006...');\n const python = join(venvPath, 'bin', 'python');\n const serverScript = join(__dirname, '..', '..', 'scripts', 'f5-tts-server.py');\n const scriptPath = fs.existsSync(serverScript) ? serverScript : join(__dirname, '..', '..', '..', 'scripts', 'f5-tts-server.py');\n const child = spawn(python, [scriptPath, '--host', '127.0.0.1', '--port', String(5006)], {\n detached: true, stdio: ['ignore', 'pipe', 'pipe'],\n env: { ...process.env, PATH: `${join(venvPath, 'bin')}:${process.env.PATH}` },\n });\n child.unref();\n f5ttsPid = child.pid ?? null;\n const pidFile = join(homedir(), '.titan', 'f5tts.pid');\n if (child.pid) fs.writeFileSync(pidFile, String(child.pid));\n send('model', 'running', 'Downloading F5-TTS model (~500MB, first time only)...');\n let ready = false;\n for (let i = 0; i < 120; i++) {\n await new Promise(r => setTimeout(r, 2000));\n try { const probe = await fetch(`http://localhost:${5006}/health`, { signal: AbortSignal.timeout(5000) }); if (probe.ok) { ready = true; break; } } catch { /* still loading */ }\n }\n if (ready) { send('model', 'done'); send('complete', 'done', 'Voice cloning server is ready! (F5-TTS)'); }\n else { send('model', 'error', 'Server started but model loading timed out. It may still be downloading — try again in a few minutes.'); }\n } catch (e) { send('error', 'error', (e as Error).message); }\n res.end();\n });\n\n const stopF5TTSHandler = (_req: Request, res: Response) => {\n const candidates = [join(homedir(), '.titan', 'f5tts.pid'), join(homedir(), '.titan', 'qwen3tts.pid')];\n try {\n for (const pidFile of candidates) {\n if (!fs.existsSync(pidFile)) continue;\n const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim());\n try { process.kill(pid, 'SIGTERM'); } catch { /* already dead */ }\n try { fs.unlinkSync(pidFile); } catch { /* already gone */ }\n }\n f5ttsPid = null;\n res.json({ ok: true });\n } catch (e) { res.json({ ok: false, error: (e as Error).message }); }\n };\n\n const startF5TTSSidecar = async (): Promise<void> => {\n const venvPath = join(homedir(), '.titan', 'qwen3tts-venv');\n const python = join(venvPath, 'bin', 'python');\n const pidFile = join(homedir(), '.titan', 'f5tts.pid');\n if (!fs.existsSync(python)) {\n logger.warn(COMPONENT, 'F5-TTS not installed — skipping auto-start');\n return;\n }\n try {\n const probe = await fetch(`http://localhost:${F5_TTS_PORT}/health`, { signal: AbortSignal.timeout(3000) });\n if (probe.ok) {\n logger.info(COMPONENT, 'F5-TTS sidecar already running');\n return;\n }\n } catch { /* not running */ }\n try {\n const serverScript = join(__dirname, '..', '..', 'scripts', 'f5-tts-server.py');\n const scriptPath = fs.existsSync(serverScript) ? serverScript : join(__dirname, '..', '..', '..', 'scripts', 'f5-tts-server.py');\n const child = spawn(python, [scriptPath, '--host', '127.0.0.1', '--port', String(F5_TTS_PORT)], {\n detached: true, stdio: ['ignore', 'pipe', 'pipe'],\n env: { ...process.env, PATH: `${join(venvPath, 'bin')}:${process.env.PATH}` },\n });\n child.unref();\n f5ttsPid = child.pid ?? null;\n if (child.pid) fs.writeFileSync(pidFile, String(child.pid));\n logger.info(COMPONENT, 'F5-TTS sidecar auto-starting...');\n } catch (e) {\n logger.error(COMPONENT, `F5-TTS auto-start failed: ${(e as Error).message}`);\n }\n };\n\n const startF5TTSHandler = async (_req: Request, res: Response) => {\n const venvPath = join(homedir(), '.titan', 'qwen3tts-venv');\n const python = join(venvPath, 'bin', 'python');\n const pidFile = join(homedir(), '.titan', 'f5tts.pid');\n if (!fs.existsSync(python)) { res.status(400).json({ ok: false, error: 'F5-TTS not installed. Use POST /api/voice/f5tts/install first.' }); return; }\n try { const probe = await fetch(`http://localhost:${5006}/health`, { signal: AbortSignal.timeout(3000) }); if (probe.ok) { res.json({ ok: true, message: 'F5-TTS is already running' }); return; } } catch { /* not running */ }\n try {\n const serverScript = join(__dirname, '..', '..', 'scripts', 'f5-tts-server.py');\n const scriptPath = fs.existsSync(serverScript) ? serverScript : join(__dirname, '..', '..', '..', 'scripts', 'f5-tts-server.py');\n const child = spawn(python, [scriptPath, '--host', '127.0.0.1', '--port', String(5006)], {\n detached: true, stdio: ['ignore', 'pipe', 'pipe'],\n env: { ...process.env, PATH: `${join(venvPath, 'bin')}:${process.env.PATH}` },\n });\n child.unref();\n f5ttsPid = child.pid ?? null;\n if (child.pid) fs.writeFileSync(pidFile, String(child.pid));\n res.json({ ok: true, message: 'F5-TTS server starting — model loading may take a minute.' });\n } catch (e) { res.status(500).json({ ok: false, error: (e as Error).message }); }\n };\n\n router.post('/voice/f5tts/stop', stopF5TTSHandler);\n router.post('/voice/f5tts/start', startF5TTSHandler);\n\n const deprecationWarn = (alias: string, canonical: string) => {\n logger.warn(COMPONENT, `Deprecated route ${alias} called; please switch to ${canonical}.`);\n };\n router.post('/voice/qwen3tts/stop', (req, res) => { deprecationWarn('/api/voice/qwen3tts/stop', '/api/voice/f5tts/stop'); return stopF5TTSHandler(req, res); });\n router.post('/voice/qwen3tts/start', (req, res) => { deprecationWarn('/api/voice/qwen3tts/start', '/api/voice/f5tts/start'); return startF5TTSHandler(req, res); });\n\n router.post('/voice/clone/upload', async (req, res) => {\n try {\n const voicesDir = join(homedir(), '.titan', 'voices');\n if (!fs.existsSync(voicesDir)) fs.mkdirSync(voicesDir, { recursive: true });\n const voiceName = (req.query.name as string) || req.headers['x-voice-name'] as string || 'custom';\n const safeName = voiceName.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 50) || 'custom';\n const transcript = (req.query.transcript as string) || req.headers['x-voice-transcript'] as string || '';\n const contentType = req.headers['content-type'] || '';\n if (contentType.includes('application/json')) {\n const body = req.body as { audio?: string; name?: string; transcript?: string };\n if (!body.audio) { res.status(400).json({ error: 'audio (base64) is required' }); return; }\n const audioBuffer = Buffer.from(body.audio, 'base64');\n const name = body.name?.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 50) || safeName;\n fs.writeFileSync(join(voicesDir, `${name}.wav`), audioBuffer);\n if (body.transcript || transcript) fs.writeFileSync(join(voicesDir, `${name}.txt`), body.transcript || transcript);\n res.json({ ok: true, voice: name, path: join(voicesDir, `${name}.wav`) });\n } else {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => {\n const audioBuffer = Buffer.concat(chunks);\n fs.writeFileSync(join(voicesDir, `${safeName}.wav`), audioBuffer);\n if (transcript) fs.writeFileSync(join(voicesDir, `${safeName}.txt`), transcript);\n res.json({ ok: true, voice: safeName, path: join(voicesDir, `${safeName}.wav`) });\n });\n }\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n router.get('/voice/clone/voices', (_req, res) => {\n const voicesDir = join(homedir(), '.titan', 'voices');\n try {\n if (!fs.existsSync(voicesDir)) { res.json({ voices: [] }); return; }\n const voices = fs.readdirSync(voicesDir).filter(f => f.endsWith('.wav')).map(f => {\n const name = f.replace('.wav', '');\n const hasTranscript = fs.existsSync(join(voicesDir, `${name}.txt`));\n const stat = fs.statSync(join(voicesDir, f));\n return { name, hasTranscript, sizeBytes: stat.size };\n });\n res.json({ voices });\n } catch (e) { res.json({ voices: [], error: (e as Error).message }); }\n });\n\n router.delete('/voice/clone/:name', (req, res) => {\n const voicesDir = join(homedir(), '.titan', 'voices');\n const name = req.params.name.replace(/[^a-zA-Z0-9_-]/g, '');\n try {\n const wavPath = join(voicesDir, `${name}.wav`);\n const txtPath = join(voicesDir, `${name}.txt`);\n if (fs.existsSync(wavPath)) fs.unlinkSync(wavPath);\n if (fs.existsSync(txtPath)) fs.unlinkSync(txtPath);\n res.json({ ok: true });\n } catch (e) { res.json({ ok: false, error: (e as Error).message }); }\n });\n\n // Auto-start F5-TTS sidecar if voice is enabled\n const cfg = loadConfig();\n if (cfg.voice?.enabled) {\n startF5TTSSidecar().catch(() => {});\n }\n\n return router;\n}\n"],"mappings":"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","names":["cfg"]}
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+ import { Router } from "express";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import fs from "fs";
6
+ import logger from "../../utils/logger.js";
7
+ import { titanEvents } from "../../agent/daemon.js";
8
+ const COMPONENT = "WatchRouter";
9
+ const PANE_SSE_TOPICS = [
10
+ // Soma / drives
11
+ "drive:tick",
12
+ "hormone:update",
13
+ "pressure:threshold",
14
+ "soma:proposal",
15
+ // Turns / tools
16
+ "turn:pre",
17
+ "turn:post",
18
+ "tool:call",
19
+ "tool:result",
20
+ // Goals
21
+ "goal:create",
22
+ "goal:complete",
23
+ "goal:fail",
24
+ "goal:cancel",
25
+ "goal:update",
26
+ // Command Post
27
+ "cp:activity",
28
+ "cp:proposal",
29
+ "cp:approval",
30
+ "cp:rejection",
31
+ // Health
32
+ "health:up",
33
+ "health:down",
34
+ "health:degraded",
35
+ // Multi-agent
36
+ "agent:spawn",
37
+ "agent:kill",
38
+ "agent:message",
39
+ // Alerts
40
+ "alert:warning",
41
+ "alert:critical"
42
+ ];
43
+ function createWatchRouter() {
44
+ const router = Router();
45
+ router.get("/watch/stream", async (req, res) => {
46
+ const { humanize } = await import("../../watch/humanize.js");
47
+ res.writeHead(200, {
48
+ "Content-Type": "text/event-stream",
49
+ "Cache-Control": "no-cache",
50
+ Connection: "keep-alive",
51
+ "X-Accel-Buffering": "no"
52
+ });
53
+ const topics = PANE_SSE_TOPICS;
54
+ const send = (data) => {
55
+ try {
56
+ res.write(`data: ${JSON.stringify(data)}
57
+
58
+ `);
59
+ } catch {
60
+ }
61
+ };
62
+ try {
63
+ const driveStatePath = join(homedir(), ".titan", "drive-state.json");
64
+ if (fs.existsSync(driveStatePath)) {
65
+ const raw = JSON.parse(fs.readFileSync(driveStatePath, "utf-8"));
66
+ const latest = raw.latest;
67
+ if (latest) {
68
+ send({
69
+ type: "snapshot",
70
+ drives: latest.drives || [],
71
+ totalPressure: latest.totalPressure || 0,
72
+ dominantDrives: latest.dominantDrives || [],
73
+ timestamp: latest.timestamp ? new Date(latest.timestamp).getTime() : Date.now()
74
+ });
75
+ }
76
+ }
77
+ } catch {
78
+ }
79
+ const listeners = /* @__PURE__ */ new Map();
80
+ for (const topic of topics) {
81
+ const handler = (payload) => {
82
+ const event = humanize(topic, payload || {});
83
+ if (event) send({ type: "event", ...event });
84
+ };
85
+ listeners.set(topic, handler);
86
+ titanEvents.on(topic, handler);
87
+ }
88
+ const keepalive = setInterval(() => {
89
+ try {
90
+ res.write(": keepalive\n\n");
91
+ } catch {
92
+ }
93
+ }, 15e3);
94
+ req.on("close", () => {
95
+ clearInterval(keepalive);
96
+ for (const [topic, handler] of listeners) {
97
+ titanEvents.removeListener(topic, handler);
98
+ }
99
+ });
100
+ });
101
+ router.get("/watch/snapshot", (_req, res) => {
102
+ try {
103
+ const driveStatePath = join(homedir(), ".titan", "drive-state.json");
104
+ const goalsPath = join(homedir(), ".titan", "goals.json");
105
+ const driveState = fs.existsSync(driveStatePath) ? JSON.parse(fs.readFileSync(driveStatePath, "utf-8"))?.latest : null;
106
+ const goalsRaw = fs.existsSync(goalsPath) ? JSON.parse(fs.readFileSync(goalsPath, "utf-8")) : {};
107
+ const allGoals = Array.isArray(goalsRaw) ? goalsRaw : Object.values(goalsRaw);
108
+ const activeGoals = allGoals.filter((g) => g.status === "active");
109
+ res.json({
110
+ drives: driveState?.drives || [],
111
+ totalPressure: driveState?.totalPressure || 0,
112
+ dominantDrives: driveState?.dominantDrives || [],
113
+ activeGoals: activeGoals.slice(0, 5).map((g) => ({
114
+ id: g.id,
115
+ title: g.title,
116
+ progress: g.progress || 0,
117
+ createdAt: g.createdAt
118
+ })),
119
+ timestamp: Date.now()
120
+ });
121
+ } catch (e) {
122
+ logger.error(COMPONENT, `Endpoint error: ${e.message}`);
123
+ res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
124
+ }
125
+ });
126
+ return router;
127
+ }
128
+ export {
129
+ createWatchRouter
130
+ };
131
+ //# sourceMappingURL=watchRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/gateway/routes/watchRouter.ts"],"sourcesContent":["/**\n * Watch Router\n *\n * Extracted from gateway/server.ts.\n * Consolidates all /api/watch/* routes.\n */\n\nimport { Router, type Request, type Response } from 'express';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport fs from 'fs';\nimport logger from '../../utils/logger.js';\n\n// Daemon events (for SSE)\nimport { titanEvents } from '../../agent/daemon.js';\n\nconst COMPONENT = 'WatchRouter';\n\n// ── Module-level constants (avoid per-request allocation) ──────────\nconst PANE_SSE_TOPICS = [\n // Soma / drives\n 'drive:tick', 'hormone:update', 'pressure:threshold', 'soma:proposal',\n // Turns / tools\n 'turn:pre', 'turn:post', 'tool:call', 'tool:result',\n // Goals\n 'goal:create', 'goal:complete', 'goal:fail', 'goal:cancel', 'goal:update',\n // Command Post\n 'cp:activity', 'cp:proposal', 'cp:approval', 'cp:rejection',\n // Health\n 'health:up', 'health:down', 'health:degraded',\n // Multi-agent\n 'agent:spawn', 'agent:kill', 'agent:message',\n // Alerts\n 'alert:warning', 'alert:critical',\n];\n\nexport function createWatchRouter(): Router {\n const router = Router();\n\n // ── Watch stream — unified human-readable event firehose (v4.5.0)\n // Fuses every meaningful event across TITAN into a single SSE feed\n // with plain-English captions. Used by the /watch Pane UI.\n router.get('/watch/stream', async (req: Request, res: Response) => {\n const { humanize } = await import('../../watch/humanize.js');\n\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n });\n\n // Every event topic the Pane cares about — union of drive ticks,\n // soma proposals, tool calls, goals, initiative, command-post, health,\n // multi-agent, alerts. Matches src/watch/humanize.ts dictionary.\n const topics = PANE_SSE_TOPICS;\n\n const send = (data: unknown) => {\n try { res.write(`data: ${JSON.stringify(data)}\\n\\n`); } catch { /* client gone */ }\n };\n\n // Initial snapshot — read drive state + recent goals so the UI has\n // something to render before the first live event arrives.\n try {\n const driveStatePath = join(homedir(), '.titan', 'drive-state.json');\n if (fs.existsSync(driveStatePath)) {\n const raw = JSON.parse(fs.readFileSync(driveStatePath, 'utf-8'));\n const latest = raw.latest as { timestamp?: string; drives?: unknown[]; totalPressure?: number; dominantDrives?: string[] } | undefined;\n if (latest) {\n send({\n type: 'snapshot',\n drives: latest.drives || [],\n totalPressure: latest.totalPressure || 0,\n dominantDrives: latest.dominantDrives || [],\n timestamp: latest.timestamp ? new Date(latest.timestamp).getTime() : Date.now(),\n });\n }\n }\n } catch { /* snapshot best-effort */ }\n\n // Wire live event listeners\n const listeners = new Map<string, (data: unknown) => void>();\n for (const topic of topics) {\n const handler = (payload: unknown) => {\n const event = humanize(topic, (payload as Record<string, unknown>) || {});\n if (event) send({ type: \"event\", ...event });\n };\n listeners.set(topic, handler);\n titanEvents.on(topic, handler);\n }\n\n const keepalive = setInterval(() => {\n try { res.write(': keepalive\\n\\n'); } catch { /* gone */ }\n }, 15_000);\n\n req.on('close', () => {\n clearInterval(keepalive);\n for (const [topic, handler] of listeners) {\n titanEvents.removeListener(topic, handler);\n }\n });\n });\n\n // Snapshot endpoint — returns current drive state + active goal +\n // last N events from a small ring buffer we maintain in-process.\n // Used by the Pane on first load to populate zones without waiting\n // for the next tick.\n router.get('/watch/snapshot', (_req: Request, res: Response) => {\n try {\n const driveStatePath = join(homedir(), '.titan', 'drive-state.json');\n const goalsPath = join(homedir(), '.titan', 'goals.json');\n const driveState = fs.existsSync(driveStatePath)\n ? JSON.parse(fs.readFileSync(driveStatePath, 'utf-8'))?.latest\n : null;\n const goalsRaw = fs.existsSync(goalsPath)\n ? JSON.parse(fs.readFileSync(goalsPath, 'utf-8'))\n : {};\n const allGoals = Array.isArray(goalsRaw) ? goalsRaw : Object.values(goalsRaw);\n const activeGoals = (allGoals as Array<Record<string, unknown>>).filter(g => g.status === 'active');\n res.json({\n drives: driveState?.drives || [],\n totalPressure: driveState?.totalPressure || 0,\n dominantDrives: driveState?.dominantDrives || [],\n activeGoals: activeGoals.slice(0, 5).map(g => ({\n id: g.id,\n title: g.title,\n progress: g.progress || 0,\n createdAt: g.createdAt,\n })),\n timestamp: Date.now(),\n });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n return router;\n}\n"],"mappings":";AAOA,SAAS,cAA2C;AACpD,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,OAAO,QAAQ;AACf,OAAO,YAAY;AAGnB,SAAS,mBAAmB;AAE5B,MAAM,YAAY;AAGlB,MAAM,kBAAkB;AAAA;AAAA,EAEpB;AAAA,EAAc;AAAA,EAAkB;AAAA,EAAsB;AAAA;AAAA,EAEtD;AAAA,EAAY;AAAA,EAAa;AAAA,EAAa;AAAA;AAAA,EAEtC;AAAA,EAAe;AAAA,EAAiB;AAAA,EAAa;AAAA,EAAe;AAAA;AAAA,EAE5D;AAAA,EAAe;AAAA,EAAe;AAAA,EAAe;AAAA;AAAA,EAE7C;AAAA,EAAa;AAAA,EAAe;AAAA;AAAA,EAE5B;AAAA,EAAe;AAAA,EAAc;AAAA;AAAA,EAE7B;AAAA,EAAiB;AACrB;AAEO,SAAS,oBAA4B;AAC1C,QAAM,SAAS,OAAO;AAKtB,SAAO,IAAI,iBAAiB,OAAO,KAAc,QAAkB;AACjE,UAAM,EAAE,SAAS,IAAI,MAAM,OAAO,yBAAyB;AAE3D,QAAI,UAAU,KAAK;AAAA,MACjB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,YAAY;AAAA,MACZ,qBAAqB;AAAA,IACvB,CAAC;AAKD,UAAM,SAAS;AAEf,UAAM,OAAO,CAAC,SAAkB;AAC9B,UAAI;AAAE,YAAI,MAAM,SAAS,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA,CAAM;AAAA,MAAG,QAAQ;AAAA,MAAoB;AAAA,IACpF;AAIA,QAAI;AACF,YAAM,iBAAiB,KAAK,QAAQ,GAAG,UAAU,kBAAkB;AACnE,UAAI,GAAG,WAAW,cAAc,GAAG;AACjC,cAAM,MAAM,KAAK,MAAM,GAAG,aAAa,gBAAgB,OAAO,CAAC;AAC/D,cAAM,SAAS,IAAI;AACnB,YAAI,QAAQ;AACV,eAAK;AAAA,YACH,MAAM;AAAA,YACN,QAAQ,OAAO,UAAU,CAAC;AAAA,YAC1B,eAAe,OAAO,iBAAiB;AAAA,YACvC,gBAAgB,OAAO,kBAAkB,CAAC;AAAA,YAC1C,WAAW,OAAO,YAAY,IAAI,KAAK,OAAO,SAAS,EAAE,QAAQ,IAAI,KAAK,IAAI;AAAA,UAChF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAA6B;AAGrC,UAAM,YAAY,oBAAI,IAAqC;AAC3D,eAAW,SAAS,QAAQ;AAC1B,YAAM,UAAU,CAAC,YAAqB;AACpC,cAAM,QAAQ,SAAS,OAAQ,WAAuC,CAAC,CAAC;AACxE,YAAI,MAAO,MAAK,EAAE,MAAM,SAAS,GAAG,MAAM,CAAC;AAAA,MAC7C;AACA,gBAAU,IAAI,OAAO,OAAO;AAC5B,kBAAY,GAAG,OAAO,OAAO;AAAA,IAC/B;AAEA,UAAM,YAAY,YAAY,MAAM;AAClC,UAAI;AAAE,YAAI,MAAM,iBAAiB;AAAA,MAAG,QAAQ;AAAA,MAAa;AAAA,IAC3D,GAAG,IAAM;AAET,QAAI,GAAG,SAAS,MAAM;AACpB,oBAAc,SAAS;AACvB,iBAAW,CAAC,OAAO,OAAO,KAAK,WAAW;AACxC,oBAAY,eAAe,OAAO,OAAO;AAAA,MAC3C;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAMD,SAAO,IAAI,mBAAmB,CAAC,MAAe,QAAkB;AAC9D,QAAI;AACF,YAAM,iBAAiB,KAAK,QAAQ,GAAG,UAAU,kBAAkB;AACnE,YAAM,YAAY,KAAK,QAAQ,GAAG,UAAU,YAAY;AACxD,YAAM,aAAa,GAAG,WAAW,cAAc,IAC3C,KAAK,MAAM,GAAG,aAAa,gBAAgB,OAAO,CAAC,GAAG,SACtD;AACJ,YAAM,WAAW,GAAG,WAAW,SAAS,IACpC,KAAK,MAAM,GAAG,aAAa,WAAW,OAAO,CAAC,IAC9C,CAAC;AACL,YAAM,WAAW,MAAM,QAAQ,QAAQ,IAAI,WAAW,OAAO,OAAO,QAAQ;AAC5E,YAAM,cAAe,SAA4C,OAAO,OAAK,EAAE,WAAW,QAAQ;AAClG,UAAI,KAAK;AAAA,QACP,QAAQ,YAAY,UAAU,CAAC;AAAA,QAC/B,eAAe,YAAY,iBAAiB;AAAA,QAC5C,gBAAgB,YAAY,kBAAkB,CAAC;AAAA,QAC/C,aAAa,YAAY,MAAM,GAAG,CAAC,EAAE,IAAI,QAAM;AAAA,UAC7C,IAAI,EAAE;AAAA,UACN,OAAO,EAAE;AAAA,UACT,UAAU,EAAE,YAAY;AAAA,UACxB,WAAW,EAAE;AAAA,QACf,EAAE;AAAA,QACF,WAAW,KAAK,IAAI;AAAA,MACtB,CAAC;AAAA,IACH,SAAS,GAAG;AACV,aAAO,MAAM,WAAW,mBAAoB,EAAY,OAAO,EAAE;AAAG,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iEAAiE,CAAC;AAAA,IACtK;AAAA,EACF,CAAC;AAED,SAAO;AACT;","names":[]}