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
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/skills/builtin/shell.ts"],"sourcesContent":["/**\n * TITAN — Shell Skill (Built-in)\n * Execute shell commands with sandboxing and output capture.\n */\nimport { exec } from 'child_process';\nimport { registerSkill } from '../registry.js';\nimport logger from '../../utils/logger.js';\n\nconst COMPONENT = 'Shell';\n\n/**\n * S5 / Hunt Finding #28 (2026-04-14): Dangerous command patterns that should\n * be blocked.\n *\n * Finding #28 was caught during Phase 5.6 injection testing: a prompt\n * injection attempt containing backtick-wrapped `rm -rf /tmp/` was obeyed by\n * the model, the shell tool executed it, and the command wiped user dj's\n * files in /tmp on the live Titan PC. Root cause: the original `rm -rf /`\n * regex `\\brm\\s+(-[rfRF]+\\s+)?\\/(?!\\w)` was specifically designed to allow\n * `rm -rf /tmp/foo`, but the `(?!\\w)` boundary meant `rm -rf /tmp` ALSO\n * passed — so any top-level directory (/tmp, /var, /home, /etc, /usr, /opt,\n * /root, /bin, /sbin, /lib, /boot, /dev, /mnt, /media, /run, /srv, /sys,\n * /proc) could be wiped.\n *\n * New rule: block `rm -rf` on ANY top-level directory by name. Require at\n * least TWO path components after `/` for the command to pass. This still\n * allows `rm -rf /tmp/foo`, `rm -rf /var/log/old`, etc. — legitimate scoped\n * cleanup — but blocks the whole-directory nuke attacks.\n *\n * Also added home-directory wipe patterns (`~`, `$HOME`) and extended the\n * chmod/chown patterns to catch more than just `/`.\n */\n// Common fragment: an `rm` flag set that includes -r and/or -f in any order\n// (e.g., -rf, -Rf, -fr, -rfv, -Rvf). Matches one or more flag words.\nconst RM_DESTRUCTIVE_FLAGS = /(?:-[a-zA-Z]*[rfRF][a-zA-Z]*\\s+)+/.source;\n\nconst BLOCKED_COMMANDS = [\n // rm on / itself (root directory) — either exactly /, or / followed by a\n // non-path character (whitespace, quote, terminator, or end).\n new RegExp(`\\\\brm\\\\s+${RM_DESTRUCTIVE_FLAGS}\\\\/(?![a-zA-Z0-9_])`),\n // rm on a top-level directory by name where the path does NOT continue\n // into a subdirectory. Matches: /tmp, /tmp/, /tmp \"..., /var, etc.\n // Does NOT match: /tmp/foo, /var/log/old (legitimate scoped rm).\n // The key is the lookahead `(?!\\/[a-zA-Z0-9_])` — \"not followed by\n // slash-then-wordchar\" — which means a trailing `/` is OK only if\n // nothing else comes after it.\n new RegExp(\n `\\\\brm\\\\s+${RM_DESTRUCTIVE_FLAGS}\\\\/(?:tmp|var|home|etc|usr|opt|root|bin|sbin|lib|lib32|lib64|boot|dev|mnt|media|run|srv|sys|proc)\\\\/?(?!\\\\/?[a-zA-Z0-9_])`,\n ),\n // Home-directory wipe: rm -rf ~, rm -rf $HOME, rm -rf $HOME/\n new RegExp(\n `\\\\brm\\\\s+${RM_DESTRUCTIVE_FLAGS}(?:~|\\\\$HOME|\\\\$\\\\{HOME\\\\})(?!\\\\/?[a-zA-Z0-9_])`,\n ),\n // Glob wipe: rm -rf /* or rm -rf *\n new RegExp(`\\\\brm\\\\s+${RM_DESTRUCTIVE_FLAGS}\\\\/?\\\\*(?:\\\\s|$|[\"'\\`;&|])`),\n // Also block rm with SEPARATED flags: rm -r -f /tmp\n /\\brm(?:\\s+-[rRfF])+\\s+\\/(?:tmp|var|home|etc|usr|opt|root|bin|sbin|lib|lib32|lib64|boot|dev|mnt|media|run|srv|sys|proc)\\/?(?!\\/?[a-zA-Z0-9_])/,\n // dd to raw devices\n /\\bdd\\b[^;|&\\n]*\\bof\\s*=\\s*\\/dev\\//,\n // Filesystem format\n /\\bmkfs(?:\\.\\w+)?\\b/,\n /\\bformat\\b[^;|&\\n]*\\/dev\\//,\n // System power\n /\\bshutdown\\b/,\n /\\breboot\\b/,\n /\\bhalt\\b/,\n /\\bpoweroff\\b/,\n // Overly permissive chmod/chown on sensitive paths. The allowlist\n // exempts /tmp/, /home/<user>/, /var/tmp/ which are user-writable areas\n // where 777 is legitimate for shared sockets etc.\n /\\bchmod\\s+-R?\\s*777\\s+\\/(?!tmp\\/|home\\/\\w+\\/|var\\/tmp\\/)/,\n /\\bchmod\\s+-?R?\\s*777\\s+\\/(?!tmp\\/|home\\/\\w+\\/|var\\/tmp\\/)/,\n // chown on sensitive system dirs. The (?:-R\\s+)? absorbs optional -R.\n /\\bchown\\s+(?:-R\\s+)?[^\\s]+\\s+\\/(?:etc|usr|bin|sbin|lib|boot|dev|root|sys|proc)(?:\\/|\\s|$|[\"'`])/,\n // Fork bomb\n /:\\(\\)\\s*\\{[^}]*:\\s*\\|\\s*:[^}]*\\}/,\n // Embedded rm in command substitution\n /\\$\\([^)]*\\brm\\s+-[a-zA-Z]*[rfRF][a-zA-Z]*[^)]*\\)/,\n // eval / exec of arbitrary strings\n /\\beval\\s+[\"'`]/,\n // Sourcing from device files\n /\\bsource\\s+\\/dev\\//,\n // Redirects to system config\n />\\s*\\/etc\\//,\n />\\s*\\/boot\\//,\n // Attribute changes on critical dirs\n /\\bchattr\\b/,\n // Firewall manipulation\n /\\biptables\\b/,\n /\\bufw\\s+(?:disable|enable|reset|default|delete|allow|deny)/,\n /\\bnftables\\b/,\n // Curl|pipe|bash — classic \"pipe from internet to shell\" attacks\n /\\bcurl\\s+[^|;&\\n]+\\|\\s*(?:sudo\\s+)?(?:bash|sh|zsh)\\b/,\n /\\bwget\\s+-\\w*O-\\s+[^|;&\\n]+\\|\\s*(?:sudo\\s+)?(?:bash|sh|zsh)\\b/,\n];\n\nexport function validateCommand(command: string): string | null {\n // Legacy regex-based block list (Finding #28)\n for (const pattern of BLOCKED_COMMANDS) {\n if (pattern.test(command)) {\n logger.warn(COMPONENT, `[Finding28Guard] Blocked dangerous command: ${command.slice(0, 200)}`);\n return `Command blocked: this pattern is not allowed for security reasons. The command appears to match a destructive / unsafe pattern (e.g., wiping a top-level directory, formatting a device, piping internet content to bash). If this was legitimate, scope it to a more specific path.`;\n }\n }\n\n // Scored command scanner (Hermes competitive gap fix)\n // Catches exfiltration, escalation, and resource patterns the regex list misses\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { scanCommand } = require('../../security/commandScanner.js') as typeof import('../../security/commandScanner.js');\n const scan = scanCommand(command);\n if (scan.level === 'block') {\n return `Command blocked (risk score ${scan.score}/100): ${scan.reasons.join(', ')}. Rephrase the command to be more specific and scoped.`;\n }\n if (scan.level === 'warn') {\n logger.warn(COMPONENT, `[CommandScanner] WARN (${scan.score}/100): ${command.slice(0, 120)} — ${scan.reasons.join(', ')}`);\n }\n } catch {\n // Scanner module not available at startup — fall through to legacy behavior\n }\n\n return null;\n}\n\n/** Execute a shell command and return output */\nfunction executeCommand(command: string, cwd?: string, timeout: number = 30000): Promise<string> {\n // S5: Validate command before execution\n const cmdErr = validateCommand(command);\n if (cmdErr) return Promise.resolve(cmdErr);\n\n logger.info(COMPONENT, `Executing: ${command.slice(0, 200)}`);\n\n return new Promise((resolve, reject) => {\n exec(command, {\n cwd: cwd || process.cwd(),\n timeout,\n maxBuffer: 1024 * 1024 * 10, // 10MB\n shell: '/bin/bash',\n }, (error, stdout, stderr) => {\n if (error && error.killed) {\n reject(new Error(`Command timed out after ${timeout}ms`));\n return;\n }\n\n let output = '';\n if (stdout) output += stdout;\n if (stderr) output += (output ? '\\n' : '') + `[stderr] ${stderr}`;\n if (error) output += (output ? '\\n' : '') + `[exit code: ${error.code}]`;\n\n // Truncate very long output\n if (output.length > 50000) {\n output = output.slice(0, 25000) + '\\n\\n... [output truncated] ...\\n\\n' + output.slice(-25000);\n }\n\n resolve(output || '(no output)');\n });\n });\n}\n\n/**\n * Start a background process and verify it's running.\n * Solves the gap where TITAN can't start dev servers because shell timeout kills them.\n */\nfunction startBackgroundProcess(command: string, cwd?: string, verifyPort?: number): Promise<string> {\n const cmdErr = validateCommand(command);\n if (cmdErr) return Promise.resolve(cmdErr);\n\n logger.info(COMPONENT, `[Background] Starting: ${command.slice(0, 200)}`);\n\n return new Promise((resolve) => {\n // Start process detached so it survives shell timeout\n const bgCmd = `cd ${cwd || '.'} && nohup ${command} > /tmp/titan-bg-process.log 2>&1 &`;\n exec(bgCmd, { shell: '/bin/bash', timeout: 5000 }, () => {\n // Wait for process to start, then verify\n if (verifyPort) {\n const safePort = String(Math.abs(Math.floor(Number(verifyPort)))); // Sanitize port\n let attempts = 0;\n const check = () => {\n exec(`ss -tlnp | grep :${safePort}`, { timeout: 3000 }, (err, stdout) => {\n if (stdout && stdout.includes(String(verifyPort))) {\n resolve(`Process started on port ${verifyPort}. Log: /tmp/titan-bg-process.log`);\n } else if (attempts < 10) {\n attempts++;\n setTimeout(check, 2000);\n } else {\n // Read log for errors\n exec('tail -20 /tmp/titan-bg-process.log', { timeout: 3000 }, (_e, log) => {\n resolve(`Process may not have started. Port ${verifyPort} not listening after 20s.\\nLog output:\\n${log || '(no log)'}`);\n });\n }\n });\n };\n setTimeout(check, 3000); // Initial wait before first check\n } else {\n setTimeout(() => resolve('Background process started. Log: /tmp/titan-bg-process.log'), 2000);\n }\n });\n });\n}\n\nexport function registerShellSkill(): void {\n registerSkill(\n {\n name: 'shell',\n description: 'Execute shell commands on the system',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'shell',\n description: 'Execute any shell command on the system and return the real output.\\n\\nUSE THIS WHEN Tony says: \"run X\" / \"execute X\" / \"install X\" / \"check if X is installed\" / \"what\\'s running on port X\" / \"build the project\" / \"git X\" / \"npm X\" / \"start X\" / \"restart X\" / \"check X status\"\\n\\nRULES:\\n- ALWAYS actually run the command — never describe what you would do\\n- ALWAYS show the real output to Tony, not a summary\\n- For long-running tasks, use exec with background:true instead\\n- Use cwd parameter when the command must run in a specific directory',\n parameters: {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description: 'The shell command to execute',\n },\n cwd: {\n type: 'string',\n description: 'Working directory for the command (optional)',\n },\n timeout: {\n type: 'number',\n description: 'Timeout in milliseconds (default: 30000)',\n },\n background: {\n type: 'boolean',\n description: 'Run the process in the background (for dev servers, long-running tasks). Process persists after shell returns.',\n },\n verify_port: {\n type: 'number',\n description: 'When background=true, wait up to 20s for this port to start listening. Use for dev servers (e.g., 3000, 48421).',\n },\n },\n required: ['command'],\n },\n execute: async (args) => {\n const command = args.command as string;\n const cwd = args.cwd as string | undefined;\n const timeout = (args.timeout as number) ?? 30000;\n const background = args.background as boolean | undefined;\n const verifyPort = args.verify_port as number | undefined;\n\n if (background) {\n return await startBackgroundProcess(command, cwd, verifyPort);\n }\n\n logger.info(COMPONENT, `Executing: ${command}`);\n return await executeCommand(command, cwd, timeout);\n },\n },\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/skills/builtin/shell.ts"],"sourcesContent":["/**\n * TITAN — Shell Skill (Built-in)\n * Execute shell commands with sandboxing and output capture.\n */\nimport { exec } from 'child_process';\nimport { registerSkill } from '../registry.js';\nimport logger from '../../utils/logger.js';\n\nconst COMPONENT = 'Shell';\n\n/**\n * S5 / Hunt Finding #28 (2026-04-14): Dangerous command patterns that should\n * be blocked.\n *\n * Finding #28 was caught during Phase 5.6 injection testing: a prompt\n * injection attempt containing backtick-wrapped `rm -rf /tmp/` was obeyed by\n * the model, the shell tool executed it, and the command wiped user dj's\n * files in /tmp on the live Titan PC. Root cause: the original `rm -rf /`\n * regex `\\brm\\s+(-[rfRF]+\\s+)?\\/(?!\\w)` was specifically designed to allow\n * `rm -rf /tmp/foo`, but the `(?!\\w)` boundary meant `rm -rf /tmp` ALSO\n * passed — so any top-level directory (/tmp, /var, /home, /etc, /usr, /opt,\n * /root, /bin, /sbin, /lib, /boot, /dev, /mnt, /media, /run, /srv, /sys,\n * /proc) could be wiped.\n *\n * New rule: block `rm -rf` on ANY top-level directory by name. Require at\n * least TWO path components after `/` for the command to pass. This still\n * allows `rm -rf /tmp/foo`, `rm -rf /var/log/old`, etc. — legitimate scoped\n * cleanup — but blocks the whole-directory nuke attacks.\n *\n * Also added home-directory wipe patterns (`~`, `$HOME`) and extended the\n * chmod/chown patterns to catch more than just `/`.\n */\n// Common fragment: an `rm` flag set that includes -r and/or -f in any order\n// (e.g., -rf, -Rf, -fr, -rfv, -Rvf). Matches one or more flag words.\nconst RM_DESTRUCTIVE_FLAGS = /(?:-[a-zA-Z]*[rfRF][a-zA-Z]*\\s+)+/.source;\n\nconst BLOCKED_COMMANDS = [\n // rm on / itself (root directory) — either exactly /, or / followed by a\n // non-path character (whitespace, quote, terminator, or end).\n new RegExp(`\\\\brm\\\\s+${RM_DESTRUCTIVE_FLAGS}\\\\/(?![a-zA-Z0-9_])`),\n // rm on a top-level directory by name where the path does NOT continue\n // into a subdirectory. Matches: /tmp, /tmp/, /tmp \"..., /var, etc.\n // Does NOT match: /tmp/foo, /var/log/old (legitimate scoped rm).\n // The key is the lookahead `(?!\\/[a-zA-Z0-9_])` — \"not followed by\n // slash-then-wordchar\" — which means a trailing `/` is OK only if\n // nothing else comes after it.\n new RegExp(\n `\\\\brm\\\\s+${RM_DESTRUCTIVE_FLAGS}\\\\/(?:tmp|var|home|etc|usr|opt|root|bin|sbin|lib|lib32|lib64|boot|dev|mnt|media|run|srv|sys|proc)\\\\/?(?!\\\\/?[a-zA-Z0-9_])`,\n ),\n // Home-directory wipe: rm -rf ~, rm -rf $HOME, rm -rf $HOME/\n new RegExp(\n `\\\\brm\\\\s+${RM_DESTRUCTIVE_FLAGS}(?:~|\\\\$HOME|\\\\$\\\\{HOME\\\\})(?!\\\\/?[a-zA-Z0-9_])`,\n ),\n // Glob wipe: rm -rf /* or rm -rf *\n new RegExp(`\\\\brm\\\\s+${RM_DESTRUCTIVE_FLAGS}\\\\/?\\\\*(?:\\\\s|$|[\"'\\`;&|])`),\n // Also block rm with SEPARATED flags: rm -r -f /tmp\n /\\brm(?:\\s+-[rRfF])+\\s+\\/(?:tmp|var|home|etc|usr|opt|root|bin|sbin|lib|lib32|lib64|boot|dev|mnt|media|run|srv|sys|proc)\\/?(?!\\/?[a-zA-Z0-9_])/,\n // dd to raw devices\n /\\bdd\\b[^;|&\\n]*\\bof\\s*=\\s*\\/dev\\//,\n // Filesystem format\n /\\bmkfs(?:\\.\\w+)?\\b/,\n /\\bformat\\b[^;|&\\n]*\\/dev\\//,\n // System power\n /\\bshutdown\\b/,\n /\\breboot\\b/,\n /\\bhalt\\b/,\n /\\bpoweroff\\b/,\n // Overly permissive chmod/chown on sensitive paths. The allowlist\n // exempts /tmp/, /home/<user>/, /var/tmp/ which are user-writable areas\n // where 777 is legitimate for shared sockets etc.\n /\\bchmod\\s+-R?\\s*777\\s+\\/(?!tmp\\/|home\\/\\w+\\/|var\\/tmp\\/)/,\n /\\bchmod\\s+-?R?\\s*777\\s+\\/(?!tmp\\/|home\\/\\w+\\/|var\\/tmp\\/)/,\n // chown on sensitive system dirs. The (?:-R\\s+)? absorbs optional -R.\n /\\bchown\\s+(?:-R\\s+)?[^\\s]+\\s+\\/(?:etc|usr|bin|sbin|lib|boot|dev|root|sys|proc)(?:\\/|\\s|$|[\"'`])/,\n // Fork bomb\n /:\\(\\)\\s*\\{[^}]*:\\s*\\|\\s*:[^}]*\\}/,\n // Embedded rm in command substitution\n /\\$\\([^)]*\\brm\\s+-[a-zA-Z]*[rfRF][a-zA-Z]*[^)]*\\)/,\n // eval / exec of arbitrary strings\n /\\beval\\s+[\"'`]/,\n // Sourcing from device files\n /\\bsource\\s+\\/dev\\//,\n // Redirects to system config\n />\\s*\\/etc\\//,\n />\\s*\\/boot\\//,\n // Attribute changes on critical dirs\n /\\bchattr\\b/,\n // Firewall manipulation\n /\\biptables\\b/,\n /\\bufw\\s+(?:disable|enable|reset|default|delete|allow|deny)/,\n /\\bnftables\\b/,\n // Curl|pipe|bash — classic \"pipe from internet to shell\" attacks\n /\\bcurl\\s+[^|;&\\n]+\\|\\s*(?:sudo\\s+)?(?:bash|sh|zsh)\\b/,\n /\\bwget\\s+-\\w*O-\\s+[^|;&\\n]+\\|\\s*(?:sudo\\s+)?(?:bash|sh|zsh)\\b/,\n];\n\nexport function validateCommand(command: string): string | null {\n // Legacy regex-based block list (Finding #28)\n for (const pattern of BLOCKED_COMMANDS) {\n if (pattern.test(command)) {\n logger.warn(COMPONENT, `[Finding28Guard] Blocked dangerous command: ${command.slice(0, 200)}`);\n return `Command blocked: this pattern is not allowed for security reasons. The command appears to match a destructive / unsafe pattern (e.g., wiping a top-level directory, formatting a device, piping internet content to bash). If this was legitimate, scope it to a more specific path.`;\n }\n }\n\n // Scored command scanner (Hermes competitive gap fix)\n // Catches exfiltration, escalation, and resource patterns the regex list misses\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { scanCommand } = require('../../security/commandScanner.js') as typeof import('../../security/commandScanner.js');\n const scan = scanCommand(command);\n if (scan.level === 'block') {\n return `Command blocked (risk score ${scan.score}/100): ${scan.reasons.join(', ')}. Rephrase the command to be more specific and scoped.`;\n }\n if (scan.level === 'warn') {\n logger.warn(COMPONENT, `[CommandScanner] WARN (${scan.score}/100): ${command.slice(0, 120)} — ${scan.reasons.join(', ')}`);\n }\n } catch {\n // Scanner module not available at startup — fall through to legacy behavior\n }\n\n return null;\n}\n\n/** Execute a shell command and return output */\nfunction executeCommand(command: string, cwd?: string, timeout: number = 60000): Promise<string> {\n // S5: Validate command before execution\n const cmdErr = validateCommand(command);\n if (cmdErr) return Promise.resolve(cmdErr);\n\n logger.info(COMPONENT, `Executing: ${command.slice(0, 200)}`);\n\n return new Promise((resolve, reject) => {\n exec(command, {\n cwd: cwd || process.cwd(),\n timeout,\n maxBuffer: 1024 * 1024 * 10, // 10MB\n shell: '/bin/bash',\n }, (error, stdout, stderr) => {\n if (error && error.killed) {\n reject(new Error(`Command timed out after ${timeout}ms`));\n return;\n }\n\n let output = '';\n if (stdout) output += stdout;\n if (stderr) output += (output ? '\\n' : '') + `[stderr] ${stderr}`;\n if (error) output += (output ? '\\n' : '') + `[exit code: ${error.code}]`;\n\n // Truncate very long output\n if (output.length > 50000) {\n output = output.slice(0, 25000) + '\\n\\n... [output truncated] ...\\n\\n' + output.slice(-25000);\n }\n\n resolve(output || '(no output)');\n });\n });\n}\n\n/**\n * Start a background process and verify it's running.\n * Solves the gap where TITAN can't start dev servers because shell timeout kills them.\n */\nfunction startBackgroundProcess(command: string, cwd?: string, verifyPort?: number): Promise<string> {\n const cmdErr = validateCommand(command);\n if (cmdErr) return Promise.resolve(cmdErr);\n\n logger.info(COMPONENT, `[Background] Starting: ${command.slice(0, 200)}`);\n\n return new Promise((resolve) => {\n // Start process detached so it survives shell timeout\n const bgCmd = `cd ${cwd || '.'} && nohup ${command} > /tmp/titan-bg-process.log 2>&1 &`;\n exec(bgCmd, { shell: '/bin/bash', timeout: 5000 }, () => {\n // Wait for process to start, then verify\n if (verifyPort) {\n const safePort = String(Math.abs(Math.floor(Number(verifyPort)))); // Sanitize port\n let attempts = 0;\n const check = () => {\n exec(`ss -tlnp | grep :${safePort}`, { timeout: 3000 }, (err, stdout) => {\n if (stdout && stdout.includes(String(verifyPort))) {\n resolve(`Process started on port ${verifyPort}. Log: /tmp/titan-bg-process.log`);\n } else if (attempts < 10) {\n attempts++;\n setTimeout(check, 2000);\n } else {\n // Read log for errors\n exec('tail -20 /tmp/titan-bg-process.log', { timeout: 3000 }, (_e, log) => {\n resolve(`Process may not have started. Port ${verifyPort} not listening after 20s.\\nLog output:\\n${log || '(no log)'}`);\n });\n }\n });\n };\n setTimeout(check, 3000); // Initial wait before first check\n } else {\n setTimeout(() => resolve('Background process started. Log: /tmp/titan-bg-process.log'), 2000);\n }\n });\n });\n}\n\nexport function registerShellSkill(): void {\n registerSkill(\n {\n name: 'shell',\n description: 'Execute shell commands on the system',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'shell',\n description: 'Execute any shell command on the system and return the real output.\\n\\nUSE THIS WHEN Tony says: \"run X\" / \"execute X\" / \"install X\" / \"check if X is installed\" / \"what\\'s running on port X\" / \"build the project\" / \"git X\" / \"npm X\" / \"start X\" / \"restart X\" / \"check X status\"\\n\\nRULES:\\n- ALWAYS actually run the command — never describe what you would do\\n- ALWAYS show the real output to Tony, not a summary\\n- For long-running tasks, use exec with background:true instead\\n- Use cwd parameter when the command must run in a specific directory',\n parameters: {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description: 'The shell command to execute',\n },\n cwd: {\n type: 'string',\n description: 'Working directory for the command (optional)',\n },\n timeout: {\n type: 'number',\n description: 'Timeout in milliseconds (default: 60000)',\n },\n background: {\n type: 'boolean',\n description: 'Run the process in the background (for dev servers, long-running tasks). Process persists after shell returns.',\n },\n verify_port: {\n type: 'number',\n description: 'When background=true, wait up to 20s for this port to start listening. Use for dev servers (e.g., 3000, 48421).',\n },\n },\n required: ['command'],\n },\n execute: async (args) => {\n const command = args.command as string;\n const cwd = args.cwd as string | undefined;\n const timeout = (args.timeout as number) ?? 30000;\n const background = args.background as boolean | undefined;\n const verifyPort = args.verify_port as number | undefined;\n\n if (background) {\n return await startBackgroundProcess(command, cwd, verifyPort);\n }\n\n logger.info(COMPONENT, `Executing: ${command}`);\n return await executeCommand(command, cwd, timeout);\n },\n },\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import { TitanAgentBridge } from "../../voice/bridge.js";
3
+ let bridge = null;
4
+ async function startVoiceAgent(model) {
5
+ if (bridge?.getStatus().running) {
6
+ return "Voice agent already running";
7
+ }
8
+ bridge = new TitanAgentBridge({ model });
9
+ await bridge.start();
10
+ try {
11
+ const { getLifecycleManager } = await import("../../utils/lifecycle.js");
12
+ getLifecycleManager().register(
13
+ "voice-bridge",
14
+ () => Promise.resolve(),
15
+ () => stopVoiceAgent().then(() => {
16
+ })
17
+ );
18
+ } catch {
19
+ }
20
+ return `Voice agent started${model ? ` (model: ${model})` : ""}`;
21
+ }
22
+ async function stopVoiceAgent() {
23
+ if (!bridge) {
24
+ return "Voice agent not running";
25
+ }
26
+ await bridge.stop();
27
+ bridge = null;
28
+ return "Voice agent stopped";
29
+ }
30
+ async function getVoiceStatus() {
31
+ if (!bridge) {
32
+ return { running: false, uptime: 0 };
33
+ }
34
+ return bridge.getStatus();
35
+ }
36
+ async function processVoiceAudio(base64Audio) {
37
+ if (!bridge) {
38
+ throw new Error("Voice agent not started. Call start_voice_agent first.");
39
+ }
40
+ const buffer = Buffer.from(base64Audio, "base64");
41
+ return bridge.processAudio(buffer);
42
+ }
43
+ export {
44
+ getVoiceStatus,
45
+ processVoiceAudio,
46
+ startVoiceAgent,
47
+ stopVoiceAgent
48
+ };
49
+ //# sourceMappingURL=voice_control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/skills/builtin/voice_control.ts"],"sourcesContent":["/**\n * Voice control skill for TITAN agent.\n *\n * Bridges to TitanAgentBridge so the agent can start/stop/process\n * voice via the standard tool interface.\n */\n\nimport { TitanAgentBridge, type AgentStatus } from '../../voice/bridge.js';\n\nlet bridge: TitanAgentBridge | null = null;\n\nexport async function startVoiceAgent(model?: string): Promise<string> {\n if (bridge?.getStatus().running) {\n return 'Voice agent already running';\n }\n bridge = new TitanAgentBridge({ model });\n await bridge.start();\n // Register with LifecycleManager for graceful shutdown\n try {\n const { getLifecycleManager } = await import('../../utils/lifecycle.js');\n getLifecycleManager().register(\n 'voice-bridge',\n () => Promise.resolve(),\n () => stopVoiceAgent().then(() => {})\n );\n } catch { /* lifecycle not available yet */ }\n return `Voice agent started${model ? ` (model: ${model})` : ''}`;\n}\n\nexport async function stopVoiceAgent(): Promise<string> {\n if (!bridge) {\n return 'Voice agent not running';\n }\n await bridge.stop();\n bridge = null;\n return 'Voice agent stopped';\n}\n\nexport async function getVoiceStatus(): Promise<AgentStatus> {\n if (!bridge) {\n return { running: false, uptime: 0 };\n }\n return bridge.getStatus();\n}\n\nexport async function processVoiceAudio(base64Audio: string): Promise<string> {\n if (!bridge) {\n throw new Error('Voice agent not started. Call start_voice_agent first.');\n }\n const buffer = Buffer.from(base64Audio, 'base64');\n return bridge.processAudio(buffer);\n}\n"],"mappings":";AAOA,SAAS,wBAA0C;AAEnD,IAAI,SAAkC;AAEtC,eAAsB,gBAAgB,OAAiC;AACrE,MAAI,QAAQ,UAAU,EAAE,SAAS;AAC/B,WAAO;AAAA,EACT;AACA,WAAS,IAAI,iBAAiB,EAAE,MAAM,CAAC;AACvC,QAAM,OAAO,MAAM;AAEnB,MAAI;AACF,UAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,0BAA0B;AACvE,wBAAoB,EAAE;AAAA,MACpB;AAAA,MACA,MAAM,QAAQ,QAAQ;AAAA,MACtB,MAAM,eAAe,EAAE,KAAK,MAAM;AAAA,MAAC,CAAC;AAAA,IACtC;AAAA,EACF,QAAQ;AAAA,EAAoC;AAC5C,SAAO,sBAAsB,QAAQ,YAAY,KAAK,MAAM,EAAE;AAChE;AAEA,eAAsB,iBAAkC;AACtD,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AACA,QAAM,OAAO,KAAK;AAClB,WAAS;AACT,SAAO;AACT;AAEA,eAAsB,iBAAuC;AAC3D,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,SAAS,OAAO,QAAQ,EAAE;AAAA,EACrC;AACA,SAAO,OAAO,UAAU;AAC1B;AAEA,eAAsB,kBAAkB,aAAsC;AAC5E,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AACA,QAAM,SAAS,OAAO,KAAK,aAAa,QAAQ;AAChD,SAAO,OAAO,aAAa,MAAM;AACnC;","names":[]}
@@ -259,12 +259,17 @@ function registerWidgetGallerySkill() {
259
259
  const fill = args.fill && typeof args.fill === "object" ? args.fill : void 0;
260
260
  const t = getTemplate(id, fill);
261
261
  if (!t) return JSON.stringify({ error: `Template not found: ${id}` });
262
+ const w = t.defaultSize?.w ?? 4;
263
+ const h = t.defaultSize?.h ?? 4;
264
+ const metaComment = `// __WIDGET_META__ w=${w} h=${h}
265
+ `;
266
+ const sourceWithMeta = metaComment + (t.source || "");
262
267
  return JSON.stringify({
263
268
  id: t.id,
264
269
  name: t.name,
265
270
  category: t.category,
266
271
  defaultSize: t.defaultSize,
267
- source: t.source,
272
+ source: sourceWithMeta,
268
273
  placeholders: t.placeholders ?? []
269
274
  });
270
275
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/skills/builtin/widget_gallery.ts"],"sourcesContent":["/**\n * TITAN — Widget Gallery Skill\n *\n * Loads the bundled widget template library at startup and exposes:\n * - gallery_search(query): fuzzy match against triggers, tags, name, description\n * - gallery_get(id): fetch a single template by id (with source code + placeholders)\n * - gallery_list(category?): list templates (optionally filtered)\n * - gallery_categories(): list of all category names with counts\n *\n * The canvas chat agent should ALWAYS call gallery_search FIRST when the user\n * asks for a widget. Only generate from scratch when nothing matches well.\n *\n * Templates live in assets/widget-templates/<category>/<id>.json with schema:\n * {\n * id, name, category, tags[], description, triggers[],\n * defaultSize: { w, h },\n * source: \"function MyWidget() {...} render(<MyWidget/>);\",\n * placeholders: [{ name, description, default }]\n * }\n *\n * `source` is the React component body for <WidgetSandbox> srcdoc — it's\n * wrapped in the iframe's React 18 UMD + Babel standalone runtime by the\n * canvas, so just author it as a self-contained component ending in\n * `render(<X/>);`. Inline tokens like REPLACE_WITH_SYMBOL are swapped at\n * runtime via gallery_get's `fill` argument.\n */\n\nimport { readdirSync, readFileSync, existsSync, statSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { registerSkill } from '../registry.js';\nimport logger from '../../utils/logger.js';\n\nconst COMPONENT = 'WidgetGallery';\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// dist/skills/builtin → ../../../assets = project root / assets\n// Fallback to CWD/assets/widget-templates when bundled or invoked from a\n// non-standard layout (kimi review: defensive for single-file bundles).\nconst PRIMARY_TEMPLATES_DIR = join(__dirname, '../../../assets/widget-templates');\nconst TEMPLATES_DIR = existsSync(PRIMARY_TEMPLATES_DIR)\n ? PRIMARY_TEMPLATES_DIR\n : join(process.cwd(), 'assets/widget-templates');\n\nexport interface WidgetPlaceholder {\n name: string;\n description: string;\n default?: string;\n}\n\nexport interface WidgetTemplate {\n id: string;\n name: string;\n category: string;\n tags: string[];\n description: string;\n triggers: string[];\n defaultSize?: { w: number; h: number };\n source: string;\n placeholders?: WidgetPlaceholder[];\n}\n\n// System widgets are hardcoded React components in the UI.\n// They are included in the gallery so the agent can discover and emit them.\nconst SYSTEM_WIDGET_TEMPLATES: WidgetTemplate[] = [\n { id: 'system-backup', name: 'Backup Manager', category: 'system', tags: ['backup', 'storage', 'archive'], description: 'Create, list, and verify TITAN data backups', triggers: ['backup', 'snapshot', 'archive'], defaultSize: { w: 6, h: 6 }, source: 'system:backup' },\n { id: 'system-training', name: 'Training Dashboard', category: 'system', tags: ['training', 'model', 'specialist'], description: 'View training stats, progress, and export data', triggers: ['training', 'train', 'specialist', 'model'], defaultSize: { w: 6, h: 6 }, source: 'system:training' },\n { id: 'system-recipes', name: 'Recipe Kitchen', category: 'system', tags: ['recipe', 'playbook', 'workflow'], description: 'Run and manage AI playbook recipes', triggers: ['recipe', 'playbook', 'workflow', 'jarvis'], defaultSize: { w: 6, h: 6 }, source: 'system:recipes' },\n { id: 'system-vram', name: 'VRAM Monitor', category: 'system', tags: ['vram', 'gpu', 'memory', 'nvidia'], description: 'Monitor GPU memory usage and manage leases', triggers: ['vram', 'gpu', 'memory', 'nvidia'], defaultSize: { w: 6, h: 6 }, source: 'system:vram' },\n { id: 'system-teams', name: 'Team Hub', category: 'system', tags: ['team', 'member', 'role', 'rbac'], description: 'Manage teams, members, and role permissions', triggers: ['team', 'member', 'role', 'permission', 'rbac'], defaultSize: { w: 6, h: 6 }, source: 'system:teams' },\n { id: 'system-cron', name: 'Cron Scheduler', category: 'system', tags: ['cron', 'schedule', 'job', 'timer'], description: 'View and manage scheduled cron jobs', triggers: ['cron', 'schedule', 'job', 'timer'], defaultSize: { w: 6, h: 6 }, source: 'system:cron' },\n { id: 'system-checkpoints', name: 'Checkpoints', category: 'system', tags: ['checkpoint', 'restore', 'save'], description: 'Browse and restore session checkpoints', triggers: ['checkpoint', 'restore', 'save state'], defaultSize: { w: 6, h: 5 }, source: 'system:checkpoints' },\n { id: 'system-organism', name: 'Organism Monitor', category: 'system', tags: ['organism', 'drive', 'safety', 'alert'], description: 'View organism drives, safety alerts, and metrics', triggers: ['organism', 'drive', 'safety', 'alert', 'guardrail'], defaultSize: { w: 6, h: 6 }, source: 'system:organism' },\n { id: 'system-fleet', name: 'Fleet Router', category: 'system', tags: ['fleet', 'node', 'route', 'mesh'], description: 'View mesh fleet nodes and route requests', triggers: ['fleet', 'node', 'route', 'mesh'], defaultSize: { w: 6, h: 5 }, source: 'system:fleet' },\n { id: 'system-browser', name: 'Browser Tools', category: 'system', tags: ['browser', 'captcha', 'automation'], description: 'Solve captchas and automate browser tasks', triggers: ['captcha', 'browser', 'form fill', 'web automation'], defaultSize: { w: 6, h: 5 }, source: 'system:browser' },\n { id: 'system-paperclip', name: 'Paperclip', category: 'system', tags: ['paperclip', 'sidecar', 'helper'], description: 'Control the Paperclip sidecar assistant', triggers: ['paperclip', 'sidecar', 'helper'], defaultSize: { w: 6, h: 5 }, source: 'system:paperclip' },\n { id: 'system-eval', name: 'Test Lab', category: 'system', tags: ['test', 'eval', 'flaky', 'coverage'], description: 'View test health, failing tests, and run evaluations', triggers: ['test', 'flaky', 'failing', 'coverage', 'eval'], defaultSize: { w: 6, h: 6 }, source: 'system:eval' },\n // Previously wired orphaned panels\n { id: 'system-daemon', name: 'Daemon', category: 'system', tags: ['daemon', 'process', 'status'], description: 'Monitor and control the TITAN daemon process', triggers: ['daemon', 'process', 'background'], defaultSize: { w: 6, h: 6 }, source: 'system:daemon' },\n { id: 'system-memory-wiki', name: 'Memory Wiki', category: 'system', tags: ['wiki', 'memory', 'knowledge', 'entity'], description: 'Browse the memory wiki and knowledge graph entities', triggers: ['wiki', 'memory', 'knowledge', 'entity'], defaultSize: { w: 6, h: 6 }, source: 'system:memory-wiki' },\n { id: 'system-autoresearch', name: 'Autoresearch', category: 'system', tags: ['research', 'benchmark', 'deploy'], description: 'Run autoresearch benchmarks and deploy pipelines', triggers: ['research', 'benchmark', 'deploy'], defaultSize: { w: 6, h: 6 }, source: 'system:autoresearch' },\n { id: 'system-self-proposals', name: 'Self-Proposals', category: 'system', tags: ['proposal', 'self-improve', 'pr'], description: 'Review and manage self-improvement proposals', triggers: ['proposal', 'self-improve', 'pr'], defaultSize: { w: 6, h: 6 }, source: 'system:self-proposals' },\n { id: 'system-overview', name: 'Overview', category: 'system', tags: ['overview', 'stats', 'dashboard'], description: 'System overview and activity dashboard', triggers: ['overview', 'stats', 'dashboard'], defaultSize: { w: 6, h: 5 }, source: 'system:overview' },\n { id: 'system-sessions', name: 'Sessions', category: 'system', tags: ['session', 'chat', 'history'], description: 'Browse and manage chat sessions', triggers: ['session', 'chat', 'history'], defaultSize: { w: 6, h: 5 }, source: 'system:sessions' },\n { id: 'system-watch', name: 'Watch', category: 'system', tags: ['watch', 'monitor', 'live'], description: 'Live organism and drive activity monitor', triggers: ['watch', 'monitor', 'live', 'activity'], defaultSize: { w: 8, h: 7 }, source: 'system:watch' },\n];\n\nlet templateCache: Map<string, WidgetTemplate> | null = null;\n\nfunction loadTemplates(): Map<string, WidgetTemplate> {\n if (templateCache) return templateCache;\n const map = new Map<string, WidgetTemplate>();\n if (!existsSync(TEMPLATES_DIR)) {\n logger.warn(COMPONENT, `Templates dir missing: ${TEMPLATES_DIR}`);\n templateCache = map;\n return map;\n }\n const walk = (dir: string): void => {\n let entries: string[] = [];\n try { entries = readdirSync(dir); } catch { return; }\n for (const name of entries) {\n const full = join(dir, name);\n let stat;\n try { stat = statSync(full); } catch { continue; }\n if (stat.isDirectory()) { walk(full); continue; }\n if (!name.endsWith('.json')) continue;\n try {\n const raw = readFileSync(full, 'utf-8');\n const t = JSON.parse(raw) as WidgetTemplate;\n if (!t.id || !t.source) {\n logger.warn(COMPONENT, `Skipped malformed template: ${full}`);\n continue;\n }\n if (map.has(t.id)) {\n logger.warn(COMPONENT, `Duplicate template id \"${t.id}\" — keeping first.`);\n continue;\n }\n // Fill defaults so search/list don't have to null-check\n t.tags = Array.isArray(t.tags) ? t.tags : [];\n t.triggers = Array.isArray(t.triggers) ? t.triggers : [];\n t.description = t.description || '';\n t.category = t.category || 'misc';\n map.set(t.id, t);\n } catch (e) {\n logger.warn(COMPONENT, `Failed to parse ${full}: ${(e as Error).message}`);\n }\n }\n };\n walk(TEMPLATES_DIR);\n // Merge system widgets into the gallery\n for (const sw of SYSTEM_WIDGET_TEMPLATES) {\n if (!map.has(sw.id)) {\n map.set(sw.id, sw);\n }\n }\n templateCache = map;\n logger.info(COMPONENT, `Loaded ${map.size} widget templates (${map.size - SYSTEM_WIDGET_TEMPLATES.length} JSON + ${SYSTEM_WIDGET_TEMPLATES.length} system) from ${TEMPLATES_DIR}`);\n return map;\n}\n\n/** Drop the cache so the next call reloads from disk (useful for dev). */\nexport function reloadTemplates(): number {\n templateCache = null;\n return loadTemplates().size;\n}\n\nfunction tokenize(s: string): string[] {\n return s.toLowerCase().replace(/[^a-z0-9\\s]+/g, ' ').split(/\\s+/).filter(Boolean);\n}\n\ninterface ScoredTemplate {\n template: WidgetTemplate;\n score: number;\n matched: string[];\n}\n\nfunction scoreTemplate(t: WidgetTemplate, queryTokens: Set<string>): ScoredTemplate {\n let score = 0;\n const matched = new Set<string>();\n\n // Trigger phrases get the heaviest weight (they're hand-curated intents)\n for (const trigger of t.triggers) {\n const triggerLower = trigger.toLowerCase();\n // Whole-phrase match in original query\n const queryString = Array.from(queryTokens).join(' ');\n if (queryString.includes(triggerLower)) {\n score += 10;\n matched.add(trigger);\n } else {\n const triggerTokens = tokenize(trigger);\n for (const tk of triggerTokens) {\n if (queryTokens.has(tk)) { score += 3; matched.add(trigger); }\n }\n }\n }\n\n // Tags: medium weight\n for (const tag of t.tags) {\n if (queryTokens.has(tag.toLowerCase())) { score += 2; matched.add(tag); }\n }\n\n // Name tokens: weight 4 each (a name match is strong signal)\n for (const tk of tokenize(t.name)) {\n if (queryTokens.has(tk)) { score += 4; matched.add(t.name); }\n }\n\n // Description: weight 1 (last-resort match)\n for (const tk of tokenize(t.description)) {\n if (queryTokens.has(tk)) { score += 1; }\n }\n\n // Category: weight 1\n if (queryTokens.has(t.category.toLowerCase())) { score += 1; matched.add(`category:${t.category}`); }\n\n return { template: t, score, matched: Array.from(matched) };\n}\n\nexport function searchGallery(query: string, limit = 5): ScoredTemplate[] {\n const map = loadTemplates();\n const queryTokens = new Set(tokenize(query));\n if (queryTokens.size === 0) return [];\n const scored: ScoredTemplate[] = [];\n for (const t of map.values()) {\n const s = scoreTemplate(t, queryTokens);\n if (s.score > 0) scored.push(s);\n }\n scored.sort((a, b) => b.score - a.score);\n return scored.slice(0, limit);\n}\n\nexport function getTemplate(id: string, fill?: Record<string, string>): WidgetTemplate | null {\n const t = loadTemplates().get(id);\n if (!t) return null;\n if (!fill || Object.keys(fill).length === 0) return t;\n let source = t.source;\n for (const [key, value] of Object.entries(fill)) {\n // Replace both REPLACE_WITH_X and {{X}} forms; agent may use either.\n // Escape backslash, single-quote, AND backtick (kimi review: backticks\n // could break out of template literals if a value contained one).\n const safe = String(value)\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"\\\\'\")\n .replace(/`/g, '\\\\`');\n source = source.split(`REPLACE_WITH_${key}`).join(safe);\n source = source.split(`{{${key}}}`).join(safe);\n }\n return { ...t, source };\n}\n\nexport function listTemplates(category?: string): Array<Omit<WidgetTemplate, 'source'>> {\n const map = loadTemplates();\n const out: Array<Omit<WidgetTemplate, 'source'>> = [];\n for (const t of map.values()) {\n if (category && t.category !== category) continue;\n // Strip `source` so list calls stay token-light. Agent calls gallery_get to fetch source.\n const { source: _omit, ...rest } = t;\n out.push(rest);\n }\n out.sort((a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name));\n return out;\n}\n\nexport function listCategories(): Array<{ category: string; count: number }> {\n const map = loadTemplates();\n const counts = new Map<string, number>();\n for (const t of map.values()) {\n counts.set(t.category, (counts.get(t.category) ?? 0) + 1);\n }\n return Array.from(counts.entries())\n .map(([category, count]) => ({ category, count }))\n .sort((a, b) => a.category.localeCompare(b.category));\n}\n\nexport function registerWidgetGallerySkill(): void {\n // Eager-load so the count appears in startup logs.\n loadTemplates();\n\n registerSkill(\n {\n name: 'widget_gallery',\n description: 'Curated library of pre-built canvas widget templates (timers, trackers, dashboards, automations, smart-home controls, agent-employee panels, software-builder skeletons). The canvas chat agent should ALWAYS call gallery_search FIRST when the user asks for a widget. Only generate from scratch when no template matches.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'gallery_search',\n description: 'Search the widget template gallery for matches. Returns top scored templates with id, name, category, description, and matched signals. ALWAYS call this FIRST when the user wants a new widget — only generate from scratch when no result scores well.',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'User intent — phrase, keyword, or full request. Example: \"stock tracker for AAPL\", \"pomodoro\", \"control my smart lights\".' },\n limit: { type: 'number', description: 'Max results (default 5).' },\n },\n required: ['query'],\n },\n execute: async (args: Record<string, unknown>) => {\n const query = String(args.query ?? '');\n const limit = typeof args.limit === 'number' ? args.limit : undefined;\n const results = searchGallery(query, limit ?? 5);\n if (results.length === 0) {\n return JSON.stringify({\n query,\n results: [],\n hint: 'No matches. Generate a custom widget from scratch.',\n });\n }\n return JSON.stringify({\n query,\n results: results.map(r => ({\n id: r.template.id,\n name: r.template.name,\n category: r.template.category,\n description: r.template.description,\n defaultSize: r.template.defaultSize,\n placeholders: r.template.placeholders ?? [],\n score: r.score,\n matched: r.matched,\n })),\n hint: 'Pick the best match, call gallery_get with its id and a `fill` map of placeholder values from the user request.',\n });\n },\n },\n );\n\n registerSkill(\n {\n name: 'widget_gallery_get',\n description: 'Fetch the full source of a gallery template, with placeholders replaced.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'gallery_get',\n description: 'Fetch a widget template by id with placeholder values filled in. Returns the full React component source ready to drop into the canvas.',\n parameters: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Template id from gallery_search results.' },\n fill: {\n type: 'object',\n description: 'Placeholder values (key = placeholder name without REPLACE_WITH_ prefix, value = string to insert). Example: {\"SYMBOL\": \"AAPL\"} → REPLACE_WITH_SYMBOL becomes \"AAPL\".',\n additionalProperties: { type: 'string' },\n },\n },\n required: ['id'],\n },\n execute: async (args: Record<string, unknown>) => {\n const id = String(args.id ?? '');\n const fill = (args.fill && typeof args.fill === 'object') ? args.fill as Record<string, string> : undefined;\n const t = getTemplate(id, fill);\n if (!t) return JSON.stringify({ error: `Template not found: ${id}` });\n return JSON.stringify({\n id: t.id,\n name: t.name,\n category: t.category,\n defaultSize: t.defaultSize,\n source: t.source,\n placeholders: t.placeholders ?? [],\n });\n },\n },\n );\n\n registerSkill(\n {\n name: 'widget_gallery_list',\n description: 'List all gallery templates (optionally filtered by category).',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'gallery_list',\n description: 'List all widget gallery templates. Filter by category. Returns metadata (id, name, category, description, tags, triggers) without source — call gallery_get to fetch source.',\n parameters: {\n type: 'object',\n properties: {\n category: { type: 'string', description: 'Optional category filter (e.g. \"finance\", \"productivity\", \"automation\", \"smart-home\", \"agents\").' },\n },\n },\n execute: async (args: Record<string, unknown>) => {\n const category = typeof args.category === 'string' ? args.category : undefined;\n const items = listTemplates(category);\n return JSON.stringify({\n count: items.length,\n categories: listCategories(),\n templates: items,\n });\n },\n },\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/skills/builtin/widget_gallery.ts"],"sourcesContent":["/**\n * TITAN — Widget Gallery Skill\n *\n * Loads the bundled widget template library at startup and exposes:\n * - gallery_search(query): fuzzy match against triggers, tags, name, description\n * - gallery_get(id): fetch a single template by id (with source code + placeholders)\n * - gallery_list(category?): list templates (optionally filtered)\n * - gallery_categories(): list of all category names with counts\n *\n * The canvas chat agent should ALWAYS call gallery_search FIRST when the user\n * asks for a widget. Only generate from scratch when nothing matches well.\n *\n * Templates live in assets/widget-templates/<category>/<id>.json with schema:\n * {\n * id, name, category, tags[], description, triggers[],\n * defaultSize: { w, h },\n * source: \"function MyWidget() {...} render(<MyWidget/>);\",\n * placeholders: [{ name, description, default }]\n * }\n *\n * `source` is the React component body for <WidgetSandbox> srcdoc — it's\n * wrapped in the iframe's React 18 UMD + Babel standalone runtime by the\n * canvas, so just author it as a self-contained component ending in\n * `render(<X/>);`. Inline tokens like REPLACE_WITH_SYMBOL are swapped at\n * runtime via gallery_get's `fill` argument.\n */\n\nimport { readdirSync, readFileSync, existsSync, statSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { registerSkill } from '../registry.js';\nimport logger from '../../utils/logger.js';\n\nconst COMPONENT = 'WidgetGallery';\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// dist/skills/builtin → ../../../assets = project root / assets\n// Fallback to CWD/assets/widget-templates when bundled or invoked from a\n// non-standard layout (kimi review: defensive for single-file bundles).\nconst PRIMARY_TEMPLATES_DIR = join(__dirname, '../../../assets/widget-templates');\nconst TEMPLATES_DIR = existsSync(PRIMARY_TEMPLATES_DIR)\n ? PRIMARY_TEMPLATES_DIR\n : join(process.cwd(), 'assets/widget-templates');\n\nexport interface WidgetPlaceholder {\n name: string;\n description: string;\n default?: string;\n}\n\nexport interface WidgetTemplate {\n id: string;\n name: string;\n category: string;\n tags: string[];\n description: string;\n triggers: string[];\n defaultSize?: { w: number; h: number };\n source: string;\n placeholders?: WidgetPlaceholder[];\n}\n\n// System widgets are hardcoded React components in the UI.\n// They are included in the gallery so the agent can discover and emit them.\nconst SYSTEM_WIDGET_TEMPLATES: WidgetTemplate[] = [\n { id: 'system-backup', name: 'Backup Manager', category: 'system', tags: ['backup', 'storage', 'archive'], description: 'Create, list, and verify TITAN data backups', triggers: ['backup', 'snapshot', 'archive'], defaultSize: { w: 6, h: 6 }, source: 'system:backup' },\n { id: 'system-training', name: 'Training Dashboard', category: 'system', tags: ['training', 'model', 'specialist'], description: 'View training stats, progress, and export data', triggers: ['training', 'train', 'specialist', 'model'], defaultSize: { w: 6, h: 6 }, source: 'system:training' },\n { id: 'system-recipes', name: 'Recipe Kitchen', category: 'system', tags: ['recipe', 'playbook', 'workflow'], description: 'Run and manage AI playbook recipes', triggers: ['recipe', 'playbook', 'workflow', 'jarvis'], defaultSize: { w: 6, h: 6 }, source: 'system:recipes' },\n { id: 'system-vram', name: 'VRAM Monitor', category: 'system', tags: ['vram', 'gpu', 'memory', 'nvidia'], description: 'Monitor GPU memory usage and manage leases', triggers: ['vram', 'gpu', 'memory', 'nvidia'], defaultSize: { w: 6, h: 6 }, source: 'system:vram' },\n { id: 'system-teams', name: 'Team Hub', category: 'system', tags: ['team', 'member', 'role', 'rbac'], description: 'Manage teams, members, and role permissions', triggers: ['team', 'member', 'role', 'permission', 'rbac'], defaultSize: { w: 6, h: 6 }, source: 'system:teams' },\n { id: 'system-cron', name: 'Cron Scheduler', category: 'system', tags: ['cron', 'schedule', 'job', 'timer'], description: 'View and manage scheduled cron jobs', triggers: ['cron', 'schedule', 'job', 'timer'], defaultSize: { w: 6, h: 6 }, source: 'system:cron' },\n { id: 'system-checkpoints', name: 'Checkpoints', category: 'system', tags: ['checkpoint', 'restore', 'save'], description: 'Browse and restore session checkpoints', triggers: ['checkpoint', 'restore', 'save state'], defaultSize: { w: 6, h: 5 }, source: 'system:checkpoints' },\n { id: 'system-organism', name: 'Organism Monitor', category: 'system', tags: ['organism', 'drive', 'safety', 'alert'], description: 'View organism drives, safety alerts, and metrics', triggers: ['organism', 'drive', 'safety', 'alert', 'guardrail'], defaultSize: { w: 6, h: 6 }, source: 'system:organism' },\n { id: 'system-fleet', name: 'Fleet Router', category: 'system', tags: ['fleet', 'node', 'route', 'mesh'], description: 'View mesh fleet nodes and route requests', triggers: ['fleet', 'node', 'route', 'mesh'], defaultSize: { w: 6, h: 5 }, source: 'system:fleet' },\n { id: 'system-browser', name: 'Browser Tools', category: 'system', tags: ['browser', 'captcha', 'automation'], description: 'Solve captchas and automate browser tasks', triggers: ['captcha', 'browser', 'form fill', 'web automation'], defaultSize: { w: 6, h: 5 }, source: 'system:browser' },\n { id: 'system-paperclip', name: 'Paperclip', category: 'system', tags: ['paperclip', 'sidecar', 'helper'], description: 'Control the Paperclip sidecar assistant', triggers: ['paperclip', 'sidecar', 'helper'], defaultSize: { w: 6, h: 5 }, source: 'system:paperclip' },\n { id: 'system-eval', name: 'Test Lab', category: 'system', tags: ['test', 'eval', 'flaky', 'coverage'], description: 'View test health, failing tests, and run evaluations', triggers: ['test', 'flaky', 'failing', 'coverage', 'eval'], defaultSize: { w: 6, h: 6 }, source: 'system:eval' },\n // Previously wired orphaned panels\n { id: 'system-daemon', name: 'Daemon', category: 'system', tags: ['daemon', 'process', 'status'], description: 'Monitor and control the TITAN daemon process', triggers: ['daemon', 'process', 'background'], defaultSize: { w: 6, h: 6 }, source: 'system:daemon' },\n { id: 'system-memory-wiki', name: 'Memory Wiki', category: 'system', tags: ['wiki', 'memory', 'knowledge', 'entity'], description: 'Browse the memory wiki and knowledge graph entities', triggers: ['wiki', 'memory', 'knowledge', 'entity'], defaultSize: { w: 6, h: 6 }, source: 'system:memory-wiki' },\n { id: 'system-autoresearch', name: 'Autoresearch', category: 'system', tags: ['research', 'benchmark', 'deploy'], description: 'Run autoresearch benchmarks and deploy pipelines', triggers: ['research', 'benchmark', 'deploy'], defaultSize: { w: 6, h: 6 }, source: 'system:autoresearch' },\n { id: 'system-self-proposals', name: 'Self-Proposals', category: 'system', tags: ['proposal', 'self-improve', 'pr'], description: 'Review and manage self-improvement proposals', triggers: ['proposal', 'self-improve', 'pr'], defaultSize: { w: 6, h: 6 }, source: 'system:self-proposals' },\n { id: 'system-overview', name: 'Overview', category: 'system', tags: ['overview', 'stats', 'dashboard'], description: 'System overview and activity dashboard', triggers: ['overview', 'stats', 'dashboard'], defaultSize: { w: 6, h: 5 }, source: 'system:overview' },\n { id: 'system-sessions', name: 'Sessions', category: 'system', tags: ['session', 'chat', 'history'], description: 'Browse and manage chat sessions', triggers: ['session', 'chat', 'history'], defaultSize: { w: 6, h: 5 }, source: 'system:sessions' },\n { id: 'system-watch', name: 'Watch', category: 'system', tags: ['watch', 'monitor', 'live'], description: 'Live organism and drive activity monitor', triggers: ['watch', 'monitor', 'live', 'activity'], defaultSize: { w: 8, h: 7 }, source: 'system:watch' },\n];\n\nlet templateCache: Map<string, WidgetTemplate> | null = null;\n\nfunction loadTemplates(): Map<string, WidgetTemplate> {\n if (templateCache) return templateCache;\n const map = new Map<string, WidgetTemplate>();\n if (!existsSync(TEMPLATES_DIR)) {\n logger.warn(COMPONENT, `Templates dir missing: ${TEMPLATES_DIR}`);\n templateCache = map;\n return map;\n }\n const walk = (dir: string): void => {\n let entries: string[] = [];\n try { entries = readdirSync(dir); } catch { return; }\n for (const name of entries) {\n const full = join(dir, name);\n let stat;\n try { stat = statSync(full); } catch { continue; }\n if (stat.isDirectory()) { walk(full); continue; }\n if (!name.endsWith('.json')) continue;\n try {\n const raw = readFileSync(full, 'utf-8');\n const t = JSON.parse(raw) as WidgetTemplate;\n if (!t.id || !t.source) {\n logger.warn(COMPONENT, `Skipped malformed template: ${full}`);\n continue;\n }\n if (map.has(t.id)) {\n logger.warn(COMPONENT, `Duplicate template id \"${t.id}\" — keeping first.`);\n continue;\n }\n // Fill defaults so search/list don't have to null-check\n t.tags = Array.isArray(t.tags) ? t.tags : [];\n t.triggers = Array.isArray(t.triggers) ? t.triggers : [];\n t.description = t.description || '';\n t.category = t.category || 'misc';\n map.set(t.id, t);\n } catch (e) {\n logger.warn(COMPONENT, `Failed to parse ${full}: ${(e as Error).message}`);\n }\n }\n };\n walk(TEMPLATES_DIR);\n // Merge system widgets into the gallery\n for (const sw of SYSTEM_WIDGET_TEMPLATES) {\n if (!map.has(sw.id)) {\n map.set(sw.id, sw);\n }\n }\n templateCache = map;\n logger.info(COMPONENT, `Loaded ${map.size} widget templates (${map.size - SYSTEM_WIDGET_TEMPLATES.length} JSON + ${SYSTEM_WIDGET_TEMPLATES.length} system) from ${TEMPLATES_DIR}`);\n return map;\n}\n\n/** Drop the cache so the next call reloads from disk (useful for dev). */\nexport function reloadTemplates(): number {\n templateCache = null;\n return loadTemplates().size;\n}\n\nfunction tokenize(s: string): string[] {\n return s.toLowerCase().replace(/[^a-z0-9\\s]+/g, ' ').split(/\\s+/).filter(Boolean);\n}\n\ninterface ScoredTemplate {\n template: WidgetTemplate;\n score: number;\n matched: string[];\n}\n\nfunction scoreTemplate(t: WidgetTemplate, queryTokens: Set<string>): ScoredTemplate {\n let score = 0;\n const matched = new Set<string>();\n\n // Trigger phrases get the heaviest weight (they're hand-curated intents)\n for (const trigger of t.triggers) {\n const triggerLower = trigger.toLowerCase();\n // Whole-phrase match in original query\n const queryString = Array.from(queryTokens).join(' ');\n if (queryString.includes(triggerLower)) {\n score += 10;\n matched.add(trigger);\n } else {\n const triggerTokens = tokenize(trigger);\n for (const tk of triggerTokens) {\n if (queryTokens.has(tk)) { score += 3; matched.add(trigger); }\n }\n }\n }\n\n // Tags: medium weight\n for (const tag of t.tags) {\n if (queryTokens.has(tag.toLowerCase())) { score += 2; matched.add(tag); }\n }\n\n // Name tokens: weight 4 each (a name match is strong signal)\n for (const tk of tokenize(t.name)) {\n if (queryTokens.has(tk)) { score += 4; matched.add(t.name); }\n }\n\n // Description: weight 1 (last-resort match)\n for (const tk of tokenize(t.description)) {\n if (queryTokens.has(tk)) { score += 1; }\n }\n\n // Category: weight 1\n if (queryTokens.has(t.category.toLowerCase())) { score += 1; matched.add(`category:${t.category}`); }\n\n return { template: t, score, matched: Array.from(matched) };\n}\n\nexport function searchGallery(query: string, limit = 5): ScoredTemplate[] {\n const map = loadTemplates();\n const queryTokens = new Set(tokenize(query));\n if (queryTokens.size === 0) return [];\n const scored: ScoredTemplate[] = [];\n for (const t of map.values()) {\n const s = scoreTemplate(t, queryTokens);\n if (s.score > 0) scored.push(s);\n }\n scored.sort((a, b) => b.score - a.score);\n return scored.slice(0, limit);\n}\n\nexport function getTemplate(id: string, fill?: Record<string, string>): WidgetTemplate | null {\n const t = loadTemplates().get(id);\n if (!t) return null;\n if (!fill || Object.keys(fill).length === 0) return t;\n let source = t.source;\n for (const [key, value] of Object.entries(fill)) {\n // Replace both REPLACE_WITH_X and {{X}} forms; agent may use either.\n // Escape backslash, single-quote, AND backtick (kimi review: backticks\n // could break out of template literals if a value contained one).\n const safe = String(value)\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"\\\\'\")\n .replace(/`/g, '\\\\`');\n source = source.split(`REPLACE_WITH_${key}`).join(safe);\n source = source.split(`{{${key}}}`).join(safe);\n }\n return { ...t, source };\n}\n\nexport function listTemplates(category?: string): Array<Omit<WidgetTemplate, 'source'>> {\n const map = loadTemplates();\n const out: Array<Omit<WidgetTemplate, 'source'>> = [];\n for (const t of map.values()) {\n if (category && t.category !== category) continue;\n // Strip `source` so list calls stay token-light. Agent calls gallery_get to fetch source.\n const { source: _omit, ...rest } = t;\n out.push(rest);\n }\n out.sort((a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name));\n return out;\n}\n\nexport function listCategories(): Array<{ category: string; count: number }> {\n const map = loadTemplates();\n const counts = new Map<string, number>();\n for (const t of map.values()) {\n counts.set(t.category, (counts.get(t.category) ?? 0) + 1);\n }\n return Array.from(counts.entries())\n .map(([category, count]) => ({ category, count }))\n .sort((a, b) => a.category.localeCompare(b.category));\n}\n\nexport function registerWidgetGallerySkill(): void {\n // Eager-load so the count appears in startup logs.\n loadTemplates();\n\n registerSkill(\n {\n name: 'widget_gallery',\n description: 'Curated library of pre-built canvas widget templates (timers, trackers, dashboards, automations, smart-home controls, agent-employee panels, software-builder skeletons). The canvas chat agent should ALWAYS call gallery_search FIRST when the user asks for a widget. Only generate from scratch when no template matches.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'gallery_search',\n description: 'Search the widget template gallery for matches. Returns top scored templates with id, name, category, description, and matched signals. ALWAYS call this FIRST when the user wants a new widget — only generate from scratch when no result scores well.',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'User intent — phrase, keyword, or full request. Example: \"stock tracker for AAPL\", \"pomodoro\", \"control my smart lights\".' },\n limit: { type: 'number', description: 'Max results (default 5).' },\n },\n required: ['query'],\n },\n execute: async (args: Record<string, unknown>) => {\n const query = String(args.query ?? '');\n const limit = typeof args.limit === 'number' ? args.limit : undefined;\n const results = searchGallery(query, limit ?? 5);\n if (results.length === 0) {\n return JSON.stringify({\n query,\n results: [],\n hint: 'No matches. Generate a custom widget from scratch.',\n });\n }\n return JSON.stringify({\n query,\n results: results.map(r => ({\n id: r.template.id,\n name: r.template.name,\n category: r.template.category,\n description: r.template.description,\n defaultSize: r.template.defaultSize,\n placeholders: r.template.placeholders ?? [],\n score: r.score,\n matched: r.matched,\n })),\n hint: 'Pick the best match, call gallery_get with its id and a `fill` map of placeholder values from the user request.',\n });\n },\n },\n );\n\n registerSkill(\n {\n name: 'widget_gallery_get',\n description: 'Fetch the full source of a gallery template, with placeholders replaced.',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'gallery_get',\n description: 'Fetch a widget template by id with placeholder values filled in. Returns the full React component source ready to drop into the canvas.',\n parameters: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Template id from gallery_search results.' },\n fill: {\n type: 'object',\n description: 'Placeholder values (key = placeholder name without REPLACE_WITH_ prefix, value = string to insert). Example: {\"SYMBOL\": \"AAPL\"} → REPLACE_WITH_SYMBOL becomes \"AAPL\".',\n additionalProperties: { type: 'string' },\n },\n },\n required: ['id'],\n },\n execute: async (args: Record<string, unknown>) => {\n const id = String(args.id ?? '');\n const fill = (args.fill && typeof args.fill === 'object') ? args.fill as Record<string, string> : undefined;\n const t = getTemplate(id, fill);\n if (!t) return JSON.stringify({ error: `Template not found: ${id}` });\n // v5.4.2: Prepend size metadata as a leading comment so the canvas\n // frontend can extract defaultSize from the source (Bug #3 fix).\n // The frontend parses `// __WIDGET_META__ w=6 h=6` on _____react gate.\n const w = t.defaultSize?.w ?? 4;\n const h = t.defaultSize?.h ?? 4;\n const metaComment = `// __WIDGET_META__ w=${w} h=${h}\\n`;\n const sourceWithMeta = metaComment + (t.source || '');\n return JSON.stringify({\n id: t.id,\n name: t.name,\n category: t.category,\n defaultSize: t.defaultSize,\n source: sourceWithMeta,\n placeholders: t.placeholders ?? [],\n });\n },\n },\n );\n\n registerSkill(\n {\n name: 'widget_gallery_list',\n description: 'List all gallery templates (optionally filtered by category).',\n version: '1.0.0',\n source: 'bundled',\n enabled: true,\n },\n {\n name: 'gallery_list',\n description: 'List all widget gallery templates. Filter by category. Returns metadata (id, name, category, description, tags, triggers) without source — call gallery_get to fetch source.',\n parameters: {\n type: 'object',\n properties: {\n category: { type: 'string', description: 'Optional category filter (e.g. \"finance\", \"productivity\", \"automation\", \"smart-home\", \"agents\").' },\n },\n },\n execute: async (args: Record<string, unknown>) => {\n const category = typeof args.category === 'string' ? args.category : undefined;\n const items = listTemplates(category);\n return JSON.stringify({\n count: items.length,\n categories: listCategories(),\n templates: items,\n });\n },\n },\n );\n}\n"],"mappings":"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","names":[]}
@@ -4,7 +4,7 @@ import { join, dirname } from "path";
4
4
  import vm from "vm";
5
5
  import { TITAN_HOME, TITAN_SKILLS_DIR } from "../utils/constants.js";
6
6
  import { registerTool } from "../agent/toolRunner.js";
7
- import { ensureDir } from "../utils/helpers.js";
7
+ import { mkdirIfNotExists } from "../utils/helpers.js";
8
8
  import logger from "../utils/logger.js";
9
9
  import { loadConfig } from "../config/config.js";
10
10
  const COMPONENT = "Skills";
@@ -77,16 +77,27 @@ function setSkillEnabled(skillName, enabled) {
77
77
  }
78
78
  saveDisabledSkills(disabled);
79
79
  }
80
+ let _disabledSkillsCache = null;
81
+ let _disabledSkillsCacheAt = 0;
82
+ const DISABLED_SKILLS_CACHE_TTL_MS = 3e4;
80
83
  function loadDisabledSkills() {
84
+ const now = Date.now();
85
+ if (_disabledSkillsCache !== null && now - _disabledSkillsCacheAt < DISABLED_SKILLS_CACHE_TTL_MS) {
86
+ return _disabledSkillsCache;
87
+ }
88
+ let result = [];
81
89
  try {
82
90
  if (existsSync(DISABLED_SKILLS_PATH)) {
83
- return JSON.parse(readFileSync(DISABLED_SKILLS_PATH, "utf-8"));
91
+ result = JSON.parse(readFileSync(DISABLED_SKILLS_PATH, "utf-8"));
84
92
  }
85
93
  } catch {
86
94
  }
87
- return [];
95
+ _disabledSkillsCache = result;
96
+ _disabledSkillsCacheAt = now;
97
+ return result;
88
98
  }
89
99
  function saveDisabledSkills(disabled) {
100
+ _disabledSkillsCache = null;
90
101
  try {
91
102
  const dir = dirname(DISABLED_SKILLS_PATH);
92
103
  if (!existsSync(dir)) {
@@ -98,7 +109,7 @@ function saveDisabledSkills(disabled) {
98
109
  }
99
110
  }
100
111
  function discoverWorkspaceSkills() {
101
- ensureDir(TITAN_SKILLS_DIR);
112
+ mkdirIfNotExists(TITAN_SKILLS_DIR);
102
113
  const discovered = [];
103
114
  if (!existsSync(TITAN_SKILLS_DIR)) return discovered;
104
115
  const entries = readdirSync(TITAN_SKILLS_DIR, { withFileTypes: true });