@elizaos/ui 2.0.0-alpha.192 → 2.0.0-alpha.196

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 (273) hide show
  1. package/apps/app-companion/src/components/companion/companion-app.js +2 -2
  2. package/apps/app-lifeops/src/action.d.ts.map +1 -1
  3. package/apps/app-lifeops/src/action.js +21 -2
  4. package/apps/app-lifeops/src/actions/app-blocker.d.ts +3 -1
  5. package/apps/app-lifeops/src/actions/app-blocker.d.ts.map +1 -1
  6. package/apps/app-lifeops/src/actions/app-blocker.js +2 -0
  7. package/apps/app-lifeops/src/actions/calendar.d.ts.map +1 -1
  8. package/apps/app-lifeops/src/actions/calendar.js +140 -7
  9. package/apps/app-lifeops/src/actions/computer-use.d.ts.map +1 -1
  10. package/apps/app-lifeops/src/actions/computer-use.js +248 -17
  11. package/apps/app-lifeops/src/actions/cross-channel-send.d.ts +3 -1
  12. package/apps/app-lifeops/src/actions/cross-channel-send.d.ts.map +1 -1
  13. package/apps/app-lifeops/src/actions/cross-channel-send.js +1 -0
  14. package/apps/app-lifeops/src/actions/dossier.d.ts.map +1 -1
  15. package/apps/app-lifeops/src/actions/dossier.js +51 -34
  16. package/apps/app-lifeops/src/actions/gmail.d.ts.map +1 -1
  17. package/apps/app-lifeops/src/actions/gmail.js +5 -0
  18. package/apps/app-lifeops/src/actions/inbox.d.ts.map +1 -1
  19. package/apps/app-lifeops/src/actions/inbox.js +8 -1
  20. package/apps/app-lifeops/src/actions/life.d.ts.map +1 -1
  21. package/apps/app-lifeops/src/actions/life.extractor.d.ts.map +1 -1
  22. package/apps/app-lifeops/src/actions/life.extractor.js +103 -4
  23. package/apps/app-lifeops/src/actions/life.js +8 -0
  24. package/apps/app-lifeops/src/actions/non-actionable-request.d.ts +6 -0
  25. package/apps/app-lifeops/src/actions/non-actionable-request.d.ts.map +1 -0
  26. package/apps/app-lifeops/src/actions/non-actionable-request.js +33 -0
  27. package/apps/app-lifeops/src/actions/password-manager.d.ts.map +1 -1
  28. package/apps/app-lifeops/src/actions/password-manager.js +11 -2
  29. package/apps/app-lifeops/src/actions/relationships.d.ts +3 -1
  30. package/apps/app-lifeops/src/actions/relationships.d.ts.map +1 -1
  31. package/apps/app-lifeops/src/actions/relationships.js +167 -11
  32. package/apps/app-lifeops/src/actions/remote-desktop.js +1 -1
  33. package/apps/app-lifeops/src/actions/screen-time.d.ts.map +1 -1
  34. package/apps/app-lifeops/src/actions/screen-time.js +8 -2
  35. package/apps/app-lifeops/src/actions/start-remote-session.js +1 -1
  36. package/apps/app-lifeops/src/actions/subscriptions.d.ts +5 -0
  37. package/apps/app-lifeops/src/actions/subscriptions.d.ts.map +1 -0
  38. package/apps/app-lifeops/src/actions/subscriptions.js +251 -0
  39. package/apps/app-lifeops/src/actions/twilio-call.d.ts.map +1 -1
  40. package/apps/app-lifeops/src/actions/twilio-call.js +48 -3
  41. package/apps/app-lifeops/src/actions/update-owner-profile.d.ts.map +1 -1
  42. package/apps/app-lifeops/src/actions/update-owner-profile.js +2 -1
  43. package/apps/app-lifeops/src/actions/website-blocker.d.ts +6 -2
  44. package/apps/app-lifeops/src/actions/website-blocker.d.ts.map +1 -1
  45. package/apps/app-lifeops/src/actions/website-blocker.js +38 -0
  46. package/apps/app-lifeops/src/actions/x-read.d.ts.map +1 -1
  47. package/apps/app-lifeops/src/actions/x-read.js +27 -3
  48. package/apps/app-lifeops/src/dossier/action.d.ts.map +1 -1
  49. package/apps/app-lifeops/src/dossier/action.js +8 -7
  50. package/apps/app-lifeops/src/followup/actions/listOverdueFollowups.d.ts.map +1 -1
  51. package/apps/app-lifeops/src/followup/actions/listOverdueFollowups.js +1 -0
  52. package/apps/app-lifeops/src/followup/actions/markFollowupDone.d.ts.map +1 -1
  53. package/apps/app-lifeops/src/followup/actions/markFollowupDone.js +2 -1
  54. package/apps/app-lifeops/src/followup/actions/setFollowupThreshold.d.ts.map +1 -1
  55. package/apps/app-lifeops/src/followup/actions/setFollowupThreshold.js +2 -1
  56. package/apps/app-lifeops/src/lifeops/calendly-client.d.ts.map +1 -1
  57. package/apps/app-lifeops/src/lifeops/calendly-client.js +4 -2
  58. package/apps/app-lifeops/src/lifeops/discord-browser-scraper.d.ts.map +1 -1
  59. package/apps/app-lifeops/src/lifeops/discord-browser-scraper.js +6 -5
  60. package/apps/app-lifeops/src/lifeops/password-manager-bridge.d.ts +1 -1
  61. package/apps/app-lifeops/src/lifeops/password-manager-bridge.d.ts.map +1 -1
  62. package/apps/app-lifeops/src/lifeops/password-manager-bridge.js +84 -6
  63. package/apps/app-lifeops/src/lifeops/remote-desktop.d.ts.map +1 -1
  64. package/apps/app-lifeops/src/lifeops/remote-desktop.js +30 -0
  65. package/apps/app-lifeops/src/lifeops/repository.d.ts +15 -0
  66. package/apps/app-lifeops/src/lifeops/repository.d.ts.map +1 -1
  67. package/apps/app-lifeops/src/lifeops/repository.js +330 -1
  68. package/apps/app-lifeops/src/lifeops/service-mixin-discord.js +222 -42
  69. package/apps/app-lifeops/src/lifeops/service-mixin-subscriptions.d.ts +2 -0
  70. package/apps/app-lifeops/src/lifeops/service-mixin-subscriptions.d.ts.map +1 -0
  71. package/apps/app-lifeops/src/lifeops/service-mixin-subscriptions.js +763 -0
  72. package/apps/app-lifeops/src/lifeops/service.d.ts +64 -1
  73. package/apps/app-lifeops/src/lifeops/service.d.ts.map +1 -1
  74. package/apps/app-lifeops/src/lifeops/service.js +2 -1
  75. package/apps/app-lifeops/src/lifeops/subscriptions-playbooks.d.ts +54 -0
  76. package/apps/app-lifeops/src/lifeops/subscriptions-playbooks.d.ts.map +1 -0
  77. package/apps/app-lifeops/src/lifeops/subscriptions-playbooks.js +213 -0
  78. package/apps/app-lifeops/src/lifeops/subscriptions-types.d.ts +80 -0
  79. package/apps/app-lifeops/src/lifeops/subscriptions-types.d.ts.map +1 -0
  80. package/apps/app-lifeops/src/lifeops/twilio.d.ts.map +1 -1
  81. package/apps/app-lifeops/src/lifeops/twilio.js +4 -2
  82. package/apps/app-lifeops/src/lifeops/whatsapp-client.d.ts.map +1 -1
  83. package/apps/app-lifeops/src/lifeops/whatsapp-client.js +4 -2
  84. package/apps/app-lifeops/src/lifeops/x-poster.d.ts.map +1 -1
  85. package/apps/app-lifeops/src/lifeops/x-poster.js +12 -6
  86. package/apps/app-lifeops/src/lifeops/x-reader.d.ts.map +1 -1
  87. package/apps/app-lifeops/src/lifeops/x-reader.js +7 -5
  88. package/apps/app-lifeops/src/plugin.d.ts.map +1 -1
  89. package/apps/app-lifeops/src/plugin.js +31 -24
  90. package/apps/app-lifeops/src/provider.d.ts.map +1 -1
  91. package/apps/app-lifeops/src/provider.js +4 -4
  92. package/apps/app-lifeops/src/providers/inbox-triage.js +2 -2
  93. package/apps/app-lifeops/src/providers/lifeops.d.ts.map +1 -1
  94. package/apps/app-lifeops/src/providers/lifeops.js +19 -10
  95. package/apps/app-lifeops/src/providers/website-blocker.js +1 -1
  96. package/apps/app-lifeops/src/public.d.ts +4 -0
  97. package/apps/app-lifeops/src/public.d.ts.map +1 -0
  98. package/apps/app-lifeops/src/public.js +3 -0
  99. package/apps/app-lifeops/src/routes/plugin.d.ts +18 -0
  100. package/apps/app-lifeops/src/routes/plugin.d.ts.map +1 -0
  101. package/apps/app-lifeops/src/routes/plugin.js +270 -0
  102. package/apps/app-lifeops/src/website-blocker/chat-integration/actions/blockUntilTaskComplete.d.ts.map +1 -1
  103. package/apps/app-lifeops/src/website-blocker/chat-integration/actions/blockUntilTaskComplete.js +18 -2
  104. package/package.json +1 -1
  105. package/packages/agent/src/actions/browser-session.d.ts +3 -0
  106. package/packages/agent/src/actions/browser-session.d.ts.map +1 -0
  107. package/packages/agent/src/actions/browser-session.js +207 -0
  108. package/packages/agent/src/actions/context-signal.d.ts +1 -1
  109. package/packages/agent/src/actions/context-signal.d.ts.map +1 -1
  110. package/packages/agent/src/actions/context-signal.js +1 -1
  111. package/packages/agent/src/actions/extract-page.d.ts +3 -0
  112. package/packages/agent/src/actions/extract-page.d.ts.map +1 -0
  113. package/packages/agent/src/actions/extract-page.js +124 -0
  114. package/packages/agent/src/actions/grounded-action-reply.js +1 -1
  115. package/packages/agent/src/actions/recent-conversation-texts.d.ts +9 -0
  116. package/packages/agent/src/actions/recent-conversation-texts.d.ts.map +1 -0
  117. package/packages/agent/src/actions/recent-conversation-texts.js +81 -0
  118. package/packages/agent/src/actions/set-user-name.js +1 -1
  119. package/packages/agent/src/actions/web-search.d.ts +4 -10
  120. package/packages/agent/src/actions/web-search.d.ts.map +1 -1
  121. package/packages/agent/src/actions/web-search.js +89 -19
  122. package/packages/agent/src/api/binance-skill-helpers.d.ts.map +1 -1
  123. package/packages/agent/src/api/binance-skill-helpers.js +1 -1
  124. package/packages/agent/src/api/chat-augmentation.d.ts +1 -20
  125. package/packages/agent/src/api/chat-augmentation.d.ts.map +1 -1
  126. package/packages/agent/src/api/chat-augmentation.js +2 -110
  127. package/packages/agent/src/api/conversation-metadata.d.ts +9 -0
  128. package/packages/agent/src/api/conversation-metadata.d.ts.map +1 -0
  129. package/packages/agent/src/api/conversation-metadata.js +98 -0
  130. package/packages/agent/src/api/conversation-routes.d.ts.map +1 -1
  131. package/packages/agent/src/api/conversation-routes.js +40 -9
  132. package/packages/agent/src/api/permissions-routes.js +1 -1
  133. package/packages/agent/src/api/server-helpers-auth.js +1 -1
  134. package/packages/agent/src/api/server-helpers.d.ts +2 -1
  135. package/packages/agent/src/api/server-helpers.d.ts.map +1 -1
  136. package/packages/agent/src/api/server-helpers.js +2 -110
  137. package/packages/agent/src/api/server-types.d.ts +14 -0
  138. package/packages/agent/src/api/server-types.d.ts.map +1 -1
  139. package/packages/agent/src/api/server.d.ts.map +1 -1
  140. package/packages/agent/src/api/server.js +3 -13
  141. package/packages/agent/src/api/workbench-routes.d.ts.map +1 -1
  142. package/packages/agent/src/api/workbench-routes.js +0 -14
  143. package/packages/agent/src/auth/credentials.d.ts.map +1 -1
  144. package/packages/agent/src/auth/credentials.js +8 -0
  145. package/packages/agent/src/providers/automation-terminal-bridge.d.ts +3 -0
  146. package/packages/agent/src/providers/automation-terminal-bridge.d.ts.map +1 -0
  147. package/packages/agent/src/providers/automation-terminal-bridge.js +71 -0
  148. package/packages/agent/src/providers/recent-conversations.d.ts.map +1 -1
  149. package/packages/agent/src/providers/recent-conversations.js +5 -0
  150. package/packages/agent/src/providers/relevant-conversations.d.ts.map +1 -1
  151. package/packages/agent/src/providers/relevant-conversations.js +5 -0
  152. package/packages/agent/src/providers/user-name.d.ts.map +1 -1
  153. package/packages/agent/src/providers/user-name.js +1 -1
  154. package/packages/agent/src/runtime/eliza-plugin.d.ts.map +1 -1
  155. package/packages/agent/src/runtime/eliza-plugin.js +6 -0
  156. package/packages/agent/src/runtime/plugin-resolver.d.ts.map +1 -1
  157. package/packages/agent/src/runtime/plugin-resolver.js +115 -0
  158. package/packages/agent/src/services/browser-workspace-types.d.ts +5 -1
  159. package/packages/agent/src/services/browser-workspace-types.d.ts.map +1 -1
  160. package/packages/agent/src/services/browser-workspace.d.ts.map +1 -1
  161. package/packages/agent/src/services/browser-workspace.js +178 -7
  162. package/packages/agent/src/services/escalation.d.ts +0 -11
  163. package/packages/agent/src/services/escalation.d.ts.map +1 -1
  164. package/packages/agent/src/services/escalation.js +19 -58
  165. package/packages/agent/src/services/hosted-tools.d.ts +70 -0
  166. package/packages/agent/src/services/hosted-tools.d.ts.map +1 -0
  167. package/packages/agent/src/services/hosted-tools.js +87 -0
  168. package/packages/agent/src/services/owner-name.d.ts +4 -0
  169. package/packages/agent/src/services/owner-name.d.ts.map +1 -0
  170. package/packages/agent/src/services/owner-name.js +46 -0
  171. package/packages/agent/src/services/registry-client-queries.d.ts +1 -1
  172. package/packages/agent/src/services/registry-client-queries.js +1 -1
  173. package/packages/agent/src/services/registry-client-types.d.ts +1 -1
  174. package/packages/app-core/src/api/client-automations.d.ts +8 -0
  175. package/packages/app-core/src/api/client-automations.d.ts.map +1 -0
  176. package/packages/app-core/src/api/client-automations.js +7 -0
  177. package/packages/app-core/src/api/client-browser-workspace.d.ts +3 -0
  178. package/packages/app-core/src/api/client-browser-workspace.d.ts.map +1 -1
  179. package/packages/app-core/src/api/client-browser-workspace.js +3 -0
  180. package/packages/app-core/src/api/client-chat.d.ts +8 -2
  181. package/packages/app-core/src/api/client-chat.d.ts.map +1 -1
  182. package/packages/app-core/src/api/client-chat.js +14 -1
  183. package/packages/app-core/src/api/client-types-chat.d.ts +4 -0
  184. package/packages/app-core/src/api/client-types-chat.d.ts.map +1 -1
  185. package/packages/app-core/src/api/client-types-config.d.ts +67 -2
  186. package/packages/app-core/src/api/client-types-config.d.ts.map +1 -1
  187. package/packages/app-core/src/api/client.d.ts +1 -0
  188. package/packages/app-core/src/api/client.d.ts.map +1 -1
  189. package/packages/app-core/src/api/client.js +1 -0
  190. package/packages/app-core/src/components/apps/helpers.d.ts +1 -1
  191. package/packages/app-core/src/components/apps/helpers.d.ts.map +1 -1
  192. package/packages/app-core/src/components/apps/helpers.js +5 -9
  193. package/packages/app-core/src/components/apps/internal-tool-apps.d.ts.map +1 -1
  194. package/packages/app-core/src/components/apps/internal-tool-apps.js +10 -1
  195. package/packages/app-core/src/components/conversations/conversation-sidebar-model.d.ts.map +1 -1
  196. package/packages/app-core/src/components/conversations/conversation-sidebar-model.js +6 -0
  197. package/packages/app-core/src/components/pages/AutomationRoomChatPane.d.ts +16 -0
  198. package/packages/app-core/src/components/pages/AutomationRoomChatPane.d.ts.map +1 -0
  199. package/packages/app-core/src/components/pages/AutomationRoomChatPane.js +202 -0
  200. package/packages/app-core/src/components/pages/AutomationsView.d.ts +1 -4
  201. package/packages/app-core/src/components/pages/AutomationsView.d.ts.map +1 -1
  202. package/packages/app-core/src/components/pages/AutomationsView.js +565 -265
  203. package/packages/app-core/src/components/pages/BrowserWorkspaceView.d.ts.map +1 -1
  204. package/packages/app-core/src/components/pages/BrowserWorkspaceView.js +118 -24
  205. package/packages/app-core/src/components/pages/automation-conversations.d.ts +15 -0
  206. package/packages/app-core/src/components/pages/automation-conversations.d.ts.map +1 -0
  207. package/packages/app-core/src/components/pages/automation-conversations.js +190 -0
  208. package/packages/app-core/src/components/pages/plugin-list-utils.js +4 -4
  209. package/packages/app-core/src/config/boot-config-store.d.ts +1 -1
  210. package/packages/app-core/src/config/boot-config-store.d.ts.map +1 -1
  211. package/packages/app-core/src/state/useOnboardingCallbacks.d.ts +14 -0
  212. package/packages/app-core/src/state/useOnboardingCallbacks.d.ts.map +1 -1
  213. package/packages/app-core/src/state/useOnboardingCallbacks.js +1 -1
  214. package/packages/app-core/src/state/usePluginsSkillsState.d.ts +2 -2
  215. package/packages/app-core/src/utils/desktop-workspace.d.ts +2 -2
  216. package/packages/app-core/src/utils/desktop-workspace.d.ts.map +1 -1
  217. package/packages/app-core/src/utils/desktop-workspace.js +7 -2
  218. package/packages/shared/src/contracts/lifeops.d.ts +26 -0
  219. package/packages/shared/src/contracts/lifeops.d.ts.map +1 -1
  220. package/packages/shared/src/contracts/lifeops.js +28 -0
  221. package/packages/typescript/src/actions.d.ts.map +1 -1
  222. package/packages/typescript/src/actions.js +12 -0
  223. package/packages/typescript/src/features/advanced-capabilities/actions/scheduleFollowUp.d.ts.map +1 -1
  224. package/packages/typescript/src/features/advanced-capabilities/actions/scheduleFollowUp.js +8 -2
  225. package/packages/typescript/src/features/advanced-capabilities/providers/settings.d.ts.map +1 -1
  226. package/packages/typescript/src/features/advanced-capabilities/providers/settings.js +11 -3
  227. package/packages/typescript/src/features/basic-capabilities/providers/actions.d.ts.map +1 -1
  228. package/packages/typescript/src/features/basic-capabilities/providers/actions.js +23 -1
  229. package/packages/typescript/src/features/basic-capabilities/providers/non-actionable-chatter.d.ts +5 -0
  230. package/packages/typescript/src/features/basic-capabilities/providers/non-actionable-chatter.d.ts.map +1 -0
  231. package/packages/typescript/src/features/basic-capabilities/providers/non-actionable-chatter.js +22 -0
  232. package/packages/typescript/src/features/basic-capabilities/providers/providers.d.ts.map +1 -1
  233. package/packages/typescript/src/features/basic-capabilities/providers/providers.js +10 -5
  234. package/packages/typescript/src/features/knowledge/documents-provider.d.ts.map +1 -1
  235. package/packages/typescript/src/features/knowledge/documents-provider.js +1 -0
  236. package/packages/typescript/src/features/shared/schedule-follow-up-response.d.ts +10 -0
  237. package/packages/typescript/src/features/shared/schedule-follow-up-response.d.ts.map +1 -0
  238. package/packages/typescript/src/features/shared/schedule-follow-up-response.js +49 -0
  239. package/packages/typescript/src/features/trajectories/TrajectoriesService.d.ts +1 -0
  240. package/packages/typescript/src/features/trajectories/TrajectoriesService.d.ts.map +1 -1
  241. package/packages/typescript/src/features/trajectories/TrajectoriesService.js +21 -2
  242. package/packages/typescript/src/features/trust/providers/settings.d.ts.map +1 -1
  243. package/packages/typescript/src/features/trust/providers/settings.js +11 -1
  244. package/packages/typescript/src/prompts.d.ts +4 -4
  245. package/packages/typescript/src/prompts.d.ts.map +1 -1
  246. package/packages/typescript/src/prompts.js +10 -2
  247. package/packages/typescript/src/services/message.d.ts +1 -0
  248. package/packages/typescript/src/services/message.d.ts.map +1 -1
  249. package/packages/typescript/src/services/message.js +483 -18
  250. package/packages/typescript/src/types/components.d.ts +6 -0
  251. package/packages/typescript/src/types/components.d.ts.map +1 -1
  252. package/packages/typescript/src/utils/context-routing.d.ts.map +1 -1
  253. package/packages/typescript/src/utils/context-routing.js +5 -1
  254. package/packages/typescript/src/utils/toon.d.ts.map +1 -1
  255. package/packages/typescript/src/utils/toon.js +23 -2
  256. package/packages/typescript/src/utils.d.ts +1 -0
  257. package/packages/typescript/src/utils.d.ts.map +1 -1
  258. package/packages/typescript/src/utils.js +1 -0
  259. package/apps/app-lifeops/src/types/app-blocker-settings-card.d.ts +0 -2
  260. package/apps/app-lifeops/src/types/app-blocker-settings-card.d.ts.map +0 -1
  261. package/apps/app-lifeops/src/types/index.d.ts +0 -3
  262. package/apps/app-lifeops/src/types/index.d.ts.map +0 -1
  263. package/apps/app-lifeops/src/types/index.js +0 -1
  264. package/apps/app-lifeops/src/types/website-blocker-settings-card.d.ts +0 -2
  265. package/apps/app-lifeops/src/types/website-blocker-settings-card.d.ts.map +0 -1
  266. package/apps/app-lifeops/src/types/website-blocker-settings-card.js +0 -1
  267. package/packages/agent/src/contracts/lifeops.d.ts +0 -2
  268. package/packages/agent/src/contracts/lifeops.d.ts.map +0 -1
  269. package/packages/agent/src/contracts/lifeops.js +0 -1
  270. package/packages/app-core/src/components/pages/N8nWorkflowsPanel.d.ts +0 -18
  271. package/packages/app-core/src/components/pages/N8nWorkflowsPanel.d.ts.map +0 -1
  272. package/packages/app-core/src/components/pages/N8nWorkflowsPanel.js +0 -542
  273. /package/apps/app-lifeops/src/{types/app-blocker-settings-card.js → lifeops/subscriptions-types.js} +0 -0
@@ -1,23 +1,27 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /**
3
- * AutomationsView — unified view for tasks and scheduled automations.
4
- *
5
- * Combines the previous "Heartbeats" (triggers) and "Tasks" views into a
6
- * single /automations route with filter tabs.
3
+ * AutomationsView — unified list/detail UI for coordinator and workflow automations.
7
4
  */
8
5
  import { Button, FieldLabel, Input, PageLayout, PagePanel, Sidebar, SidebarCollapsedActionButton, SidebarContent, SidebarPanel, SidebarScrollRegion, StatusBadge, Textarea, } from "@elizaos/ui";
9
- import { CheckCircle2, Circle, Clock3, ListTodo, Plus, Settings, Zap, } from "lucide-react";
6
+ import { CheckCircle2, Circle, Clock3, GitBranch, Mail, Plus, Settings, Signal, SquareTerminal, Workflow, Zap, } from "lucide-react";
10
7
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
11
8
  import { client } from "../../api";
12
9
  import { useApp } from "../../state";
13
10
  import { confirmDesktopAction } from "../../utils";
14
11
  import { formatDateTime, formatDurationMs } from "../../utils/format";
15
12
  import { WidgetHost } from "../../widgets";
13
+ import { AutomationRoomChatPane } from "./AutomationRoomChatPane";
16
14
  import { HeartbeatForm } from "./HeartbeatForm";
17
15
  import { buildCreateRequest, buildUpdateRequest, emptyForm, formFromTrigger, loadUserTemplates, localizedExecutionStatus, railMonogram, saveUserTemplates, scheduleLabel, toneForLastStatus, validateForm, } from "./heartbeat-utils";
18
- import { N8nWorkflowsPanel } from "./N8nWorkflowsPanel";
19
- // ── System task detection ─────────────────────────────────────────
20
- /** Runtime-internal task names that are not user-created. */
16
+ import { buildAutomationResponseRoutingMetadata, buildCoordinatorConversationMetadata, buildCoordinatorTriggerConversationMetadata, buildWorkflowConversationMetadata, buildWorkflowDraftConversationMetadata, getAutomationBridgeConversationId, resolveAutomationConversation, } from "./automation-conversations";
17
+ const WORKFLOW_DRAFT_TITLE = "New Workflow Draft";
18
+ const WORKFLOW_SYSTEM_ADDENDUM = "You are in a workflow-specific automation room. Focus only on this " +
19
+ "workflow. Use the linked terminal conversation only when it directly " +
20
+ "informs the workflow. Request keys and connector setup when needed, and " +
21
+ "prefer owner-scoped LifeOps integrations for personal services.";
22
+ const COORDINATOR_SYSTEM_ADDENDUM = "You are in a workflow-specific automation room for a coordinator " +
23
+ "automation. Focus only on this automation. Use the linked terminal " +
24
+ "conversation only when it directly informs the automation.";
21
25
  const SYSTEM_TASK_NAMES = new Set([
22
26
  "EMBEDDING_DRAIN",
23
27
  "PROACTIVE_AGENT",
@@ -25,123 +29,239 @@ const SYSTEM_TASK_NAMES = new Set([
25
29
  "TRIGGER_DISPATCH",
26
30
  "heartbeat",
27
31
  ]);
32
+ const NODE_CLASS_ORDER = [
33
+ "agent",
34
+ "action",
35
+ "context",
36
+ "integration",
37
+ "trigger",
38
+ "flow-control",
39
+ ];
40
+ function createWorkflowDraftId() {
41
+ return globalThis.crypto.randomUUID();
42
+ }
28
43
  function isSystemTask(task) {
29
- if (SYSTEM_TASK_NAMES.has(task.name))
44
+ if (SYSTEM_TASK_NAMES.has(task.name)) {
30
45
  return true;
31
- // Tasks with queue+repeat tags are runtime-internal
46
+ }
32
47
  const tags = new Set(task.tags ?? []);
33
- if (tags.has("queue") && tags.has("repeat"))
34
- return true;
35
- return false;
48
+ return tags.has("queue") && tags.has("repeat");
36
49
  }
37
- /**
38
- * Deduplicate system tasks by name — keep only one per name,
39
- * preferring the one with a description.
40
- */
41
- function deduplicateSystemTasks(tasks) {
42
- const byName = new Map();
43
- for (const task of tasks) {
44
- const existing = byName.get(task.name);
45
- if (!existing || (task.description && !existing.description)) {
46
- byName.set(task.name, task);
50
+ function getSelectionKind(item) {
51
+ if (!item)
52
+ return null;
53
+ if (item.type === "n8n_workflow")
54
+ return "workflow";
55
+ if (item.task)
56
+ return "task";
57
+ if (item.trigger)
58
+ return "trigger";
59
+ return null;
60
+ }
61
+ function getAutomationSearchText(item) {
62
+ return [item.title, item.description]
63
+ .map((value) => value.trim().toLowerCase())
64
+ .filter(Boolean)
65
+ .join("\n");
66
+ }
67
+ function getAutomationBridgeIdForItem(item, activeConversationId, conversations) {
68
+ return (item?.room?.terminalBridgeConversationId ??
69
+ item?.room?.sourceConversationId ??
70
+ getAutomationBridgeConversationId(activeConversationId, conversations));
71
+ }
72
+ function getWorkflowNodeCount(item) {
73
+ return item.workflow?.nodeCount ?? item.workflow?.nodes?.length ?? 0;
74
+ }
75
+ function getAutomationIndicatorTone(item) {
76
+ if (item.type === "n8n_workflow") {
77
+ return item.enabled ? "accent" : undefined;
78
+ }
79
+ if (item.task) {
80
+ return item.task.isCompleted ? undefined : "accent";
81
+ }
82
+ if (item.trigger) {
83
+ return item.trigger.enabled ? "accent" : undefined;
84
+ }
85
+ return undefined;
86
+ }
87
+ function buildTriggerSchedulePrompt(trigger) {
88
+ if (trigger.triggerType === "interval") {
89
+ return `Schedule: interval every ${trigger.intervalMs ?? 0}ms.`;
90
+ }
91
+ if (trigger.triggerType === "once") {
92
+ return `Schedule: run once at ${trigger.scheduledAtIso ?? "an unspecified time"}.`;
93
+ }
94
+ if (trigger.triggerType === "cron") {
95
+ return `Schedule: cron ${trigger.cronExpression ?? ""}.`;
96
+ }
97
+ return `Schedule type: ${trigger.triggerType}.`;
98
+ }
99
+ function buildWorkflowCompilationPrompt(item) {
100
+ const lines = [
101
+ "Compile this coordinator automation into an n8n workflow.",
102
+ `Automation title: ${item.title}`,
103
+ `Description: ${item.description || "No additional description provided."}`,
104
+ "Keep the workflow in this dedicated automation room.",
105
+ "Use runtime actions and providers as workflow nodes when they fit the job.",
106
+ "Use owner-scoped LifeOps nodes for Gmail, Calendar, Signal, Telegram, Discord, and GitHub when they are set up. If not, request the required setup or keys.",
107
+ ];
108
+ if (item.task) {
109
+ lines.push(`Task description: ${item.task.description || "No task description."}`);
110
+ }
111
+ if (item.trigger) {
112
+ lines.push(`Coordinator instructions: ${item.trigger.instructions}`);
113
+ lines.push(buildTriggerSchedulePrompt(item.trigger));
114
+ }
115
+ if (item.schedules.length > 0) {
116
+ lines.push("Existing schedules:");
117
+ for (const schedule of item.schedules) {
118
+ lines.push(`- ${buildTriggerSchedulePrompt(schedule)}`);
47
119
  }
48
120
  }
49
- return [...byName.values()];
121
+ lines.push("Ask follow-up questions only when workflow intent is genuinely ambiguous.");
122
+ return lines.join("\n");
123
+ }
124
+ function getNodeClassLabel(className) {
125
+ switch (className) {
126
+ case "agent":
127
+ return "Agent";
128
+ case "action":
129
+ return "Actions";
130
+ case "context":
131
+ return "Context";
132
+ case "integration":
133
+ return "Integrations";
134
+ case "trigger":
135
+ return "Triggers";
136
+ case "flow-control":
137
+ return "Flow Control";
138
+ default:
139
+ return className;
140
+ }
141
+ }
142
+ function getNodeIcon(node) {
143
+ if (node.source === "lifeops") {
144
+ if (node.id === "lifeops:gmail")
145
+ return _jsx(Mail, { className: "h-3.5 w-3.5" });
146
+ if (node.id === "lifeops:signal")
147
+ return _jsx(Signal, { className: "h-3.5 w-3.5" });
148
+ if (node.id === "lifeops:github") {
149
+ return _jsx(GitBranch, { className: "h-3.5 w-3.5" });
150
+ }
151
+ }
152
+ if (node.class === "agent") {
153
+ return _jsx(SquareTerminal, { className: "h-3.5 w-3.5" });
154
+ }
155
+ if (node.class === "integration") {
156
+ return _jsx(Workflow, { className: "h-3.5 w-3.5" });
157
+ }
158
+ if (node.class === "context") {
159
+ return _jsx(Settings, { className: "h-3.5 w-3.5" });
160
+ }
161
+ if (node.class === "trigger") {
162
+ return _jsx(Clock3, { className: "h-3.5 w-3.5" });
163
+ }
164
+ return _jsx(Zap, { className: "h-3.5 w-3.5" });
50
165
  }
51
- // ── View controller hook ──────────────────────────────────────────
52
166
  function useAutomationsViewController() {
53
- const { triggers = [], triggersLoaded = false, triggersLoading = false, triggersSaving = false, triggerRunsById = {}, triggerHealth: _triggerHealth = null, triggerError = null, loadTriggers = async () => { }, createTrigger = async () => null, updateTrigger = async () => null, deleteTrigger = async () => true, runTriggerNow = async () => true, loadTriggerRuns = async () => { }, loadTriggerHealth = async () => { }, ensureTriggersLoaded = async () => {
167
+ const { triggers = [], triggersLoaded = false, triggersLoading = false, triggersSaving = false, triggerRunsById = {}, triggerError = null, loadTriggers = async () => { }, createTrigger = async () => null, updateTrigger = async () => null, deleteTrigger = async () => true, runTriggerNow = async () => true, loadTriggerRuns = async () => { }, loadTriggerHealth = async () => { }, ensureTriggersLoaded = async () => {
54
168
  await loadTriggers(triggersLoaded ? { silent: true } : undefined);
55
169
  }, t, uiLanguage, } = useApp();
56
- // ── Workbench tasks state ───────────────────────────────────────
57
- const [workbenchTasks, setWorkbenchTasks] = useState([]);
58
- const [tasksLoading, setTasksLoading] = useState(false);
59
- const [_tasksLoaded, setTasksLoaded] = useState(false);
60
170
  const [taskError, setTaskError] = useState(null);
61
171
  const [taskSaving, setTaskSaving] = useState(false);
62
- const loadWorkbenchTasks = useCallback(async () => {
63
- setTasksLoading(true);
172
+ const [form, setForm] = useState(emptyForm);
173
+ const [editingId, setEditingId] = useState(null);
174
+ const [selectedItemId, setSelectedItemId] = useState(null);
175
+ const [selectedItemKind, setSelectedItemKind] = useState(null);
176
+ const [formError, setFormError] = useState(null);
177
+ const [editorOpen, setEditorOpen] = useState(false);
178
+ const [editorMode, setEditorMode] = useState("trigger");
179
+ const [userTemplates, setUserTemplates] = useState(loadUserTemplates);
180
+ const [templateNotice, setTemplateNotice] = useState(null);
181
+ const [taskFormName, setTaskFormName] = useState("");
182
+ const [taskFormDescription, setTaskFormDescription] = useState("");
183
+ const [editingTaskId, setEditingTaskId] = useState(null);
184
+ const [filter, setFilter] = useState("all");
185
+ const [automationItems, setAutomationItems] = useState([]);
186
+ const [automationNodes, setAutomationNodes] = useState([]);
187
+ const [automationsLoading, setAutomationsLoading] = useState(false);
188
+ const [automationsLoaded, setAutomationsLoaded] = useState(false);
189
+ const [automationsError, setAutomationsError] = useState(null);
190
+ const [n8nStatus, setN8nStatus] = useState(null);
191
+ const [workflowFetchError, setWorkflowFetchError] = useState(null);
192
+ const didBootstrapDataRef = useRef(false);
193
+ const lastSelectedIdRef = useRef(null);
194
+ const refreshAutomations = useCallback(async () => {
195
+ setAutomationsLoading(true);
64
196
  try {
65
- const data = await client.listWorkbenchTasks();
66
- setWorkbenchTasks(data.tasks ?? []);
67
- setTaskError(null);
197
+ const [automationData, nodeCatalog] = await Promise.all([
198
+ client.listAutomations(),
199
+ client.getAutomationNodeCatalog(),
200
+ ]);
201
+ setAutomationItems(automationData.automations ?? []);
202
+ setAutomationNodes(nodeCatalog.nodes ?? []);
203
+ setN8nStatus(automationData.n8nStatus ?? null);
204
+ setWorkflowFetchError(automationData.workflowFetchError ?? null);
205
+ setAutomationsError(null);
206
+ return automationData;
68
207
  }
69
- catch (err) {
70
- setTaskError(err instanceof Error ? err.message : "Failed to load tasks");
208
+ catch (error) {
209
+ setAutomationsError(error instanceof Error ? error.message : "Failed to load automations");
210
+ return null;
71
211
  }
72
212
  finally {
73
- setTasksLoaded(true);
74
- setTasksLoading(false);
213
+ setAutomationsLoaded(true);
214
+ setAutomationsLoading(false);
75
215
  }
76
216
  }, []);
77
217
  const createWorkbenchTask = useCallback(async (data) => {
78
218
  setTaskSaving(true);
79
219
  try {
80
220
  const res = await client.createWorkbenchTask(data);
81
- const created = res.task;
82
- setWorkbenchTasks((prev) => [...prev, created]);
83
221
  setTaskError(null);
84
- return created;
222
+ await refreshAutomations();
223
+ return res.task;
85
224
  }
86
- catch (err) {
87
- setTaskError(err instanceof Error ? err.message : "Failed to create task");
225
+ catch (error) {
226
+ setTaskError(error instanceof Error ? error.message : "Failed to create task");
88
227
  return null;
89
228
  }
90
229
  finally {
91
230
  setTaskSaving(false);
92
231
  }
93
- }, []);
232
+ }, [refreshAutomations]);
94
233
  const updateWorkbenchTask = useCallback(async (id, data) => {
95
234
  setTaskSaving(true);
96
235
  try {
97
236
  const res = await client.updateWorkbenchTask(id, data);
98
- const updated = res.task;
99
- setWorkbenchTasks((prev) => prev.map((item) => (item.id === updated.id ? updated : item)));
100
237
  setTaskError(null);
101
- return updated;
238
+ await refreshAutomations();
239
+ return res.task;
102
240
  }
103
- catch (err) {
104
- setTaskError(err instanceof Error ? err.message : "Failed to update task");
241
+ catch (error) {
242
+ setTaskError(error instanceof Error ? error.message : "Failed to update task");
105
243
  return null;
106
244
  }
107
245
  finally {
108
246
  setTaskSaving(false);
109
247
  }
110
- }, []);
248
+ }, [refreshAutomations]);
111
249
  const deleteWorkbenchTask = useCallback(async (id) => {
112
250
  setTaskSaving(true);
113
251
  try {
114
252
  await client.deleteWorkbenchTask(id);
115
- setWorkbenchTasks((prev) => prev.filter((item) => item.id !== id));
116
253
  setTaskError(null);
254
+ await refreshAutomations();
117
255
  return true;
118
256
  }
119
- catch (err) {
120
- setTaskError(err instanceof Error ? err.message : "Failed to delete task");
257
+ catch (error) {
258
+ setTaskError(error instanceof Error ? error.message : "Failed to delete task");
121
259
  return false;
122
260
  }
123
261
  finally {
124
262
  setTaskSaving(false);
125
263
  }
126
- }, []);
127
- // ── Trigger form state (reused from HeartbeatsView) ─────────────
128
- const [form, setForm] = useState(emptyForm);
129
- const [editingId, setEditingId] = useState(null);
130
- const [selectedItemId, setSelectedItemId] = useState(null);
131
- const [selectedItemKind, setSelectedItemKind] = useState(null);
132
- const [formError, setFormError] = useState(null);
133
- const [editorOpen, setEditorOpen] = useState(false);
134
- const [editorMode, setEditorMode] = useState("trigger");
135
- const lastSelectedIdRef = useRef(null);
136
- const [userTemplates, setUserTemplates] = useState(loadUserTemplates);
137
- const [templateNotice, setTemplateNotice] = useState(null);
138
- const didBootstrapDataRef = useRef(false);
139
- // ── Task create form state ──────────────────────────────────────
140
- const [taskFormName, setTaskFormName] = useState("");
141
- const [taskFormDescription, setTaskFormDescription] = useState("");
142
- const [editingTaskId, setEditingTaskId] = useState(null);
143
- // ── Filter state ────────────────────────────────────────────────
144
- const [filter, setFilter] = useState("all");
264
+ }, [refreshAutomations]);
145
265
  const saveFormAsTemplate = useCallback(() => {
146
266
  const name = form.displayName.trim();
147
267
  if (!name)
@@ -153,35 +273,30 @@ function useAutomationsViewController() {
153
273
  interval: form.durationValue || "1",
154
274
  unit: form.durationUnit,
155
275
  };
156
- setUserTemplates((prev) => {
157
- const next = [...prev, template];
276
+ setUserTemplates((previous) => {
277
+ const next = [...previous, template];
158
278
  saveUserTemplates(next);
159
279
  return next;
160
280
  });
161
281
  }, [form]);
162
282
  const deleteUserTemplate = useCallback((id) => {
163
- setUserTemplates((prev) => {
164
- const next = prev.filter((t) => t.id !== id);
283
+ setUserTemplates((previous) => {
284
+ const next = previous.filter((template) => template.id !== id);
165
285
  saveUserTemplates(next);
166
286
  return next;
167
287
  });
168
288
  }, []);
169
- // ── Bootstrap data ──────────────────────────────────────────────
170
289
  useEffect(() => {
171
290
  if (didBootstrapDataRef.current)
172
291
  return;
173
292
  didBootstrapDataRef.current = true;
174
293
  void loadTriggerHealth();
175
294
  void ensureTriggersLoaded();
176
- void loadWorkbenchTasks();
177
- }, [ensureTriggersLoaded, loadTriggerHealth, loadWorkbenchTasks]);
178
- // ── Cross-component filter navigation ─────────────────────────
179
- // HeartbeatForm dispatches this event when the user clicks "Go to Workflows"
180
- // from the workflow-unavailable state inside the kind section.
295
+ void refreshAutomations();
296
+ }, [ensureTriggersLoaded, loadTriggerHealth, refreshAutomations]);
181
297
  useEffect(() => {
182
298
  const handler = (event) => {
183
- const detail = event
184
- .detail;
299
+ const detail = event.detail;
185
300
  if (detail?.filter) {
186
301
  setFilter(detail.filter);
187
302
  }
@@ -189,63 +304,19 @@ function useAutomationsViewController() {
189
304
  window.addEventListener("milady:automations:setFilter", handler);
190
305
  return () => window.removeEventListener("milady:automations:setFilter", handler);
191
306
  }, []);
192
- // ── Build unified items list ────────────────────────────────────
193
- const allItems = useMemo(() => {
194
- const items = [];
195
- for (const trigger of triggers) {
196
- items.push({
197
- kind: "trigger",
198
- id: `trigger:${trigger.id}`,
199
- name: trigger.displayName,
200
- trigger,
201
- });
202
- }
203
- // Separate user tasks from system tasks
204
- const userTasks = [];
205
- const systemTasks = [];
206
- for (const task of workbenchTasks) {
207
- if (isSystemTask(task)) {
208
- systemTasks.push(task);
209
- }
210
- else {
211
- userTasks.push(task);
212
- }
213
- }
214
- // Add user-created tasks
215
- for (const task of userTasks) {
216
- items.push({
217
- kind: "task",
218
- id: `task:${task.id}`,
219
- name: task.name,
220
- task,
221
- system: false,
222
- });
223
- }
224
- // Add deduplicated system tasks
225
- for (const task of deduplicateSystemTasks(systemTasks)) {
226
- items.push({
227
- kind: "task",
228
- id: `task:${task.id}`,
229
- name: task.name,
230
- task,
231
- system: true,
232
- });
233
- }
234
- return items;
235
- }, [triggers, workbenchTasks]);
307
+ const allItems = automationItems;
236
308
  const filteredItems = useMemo(() => {
237
309
  switch (filter) {
310
+ case "coordinator":
311
+ return allItems.filter((item) => item.type === "coordinator_text");
312
+ case "workflows":
313
+ return allItems.filter((item) => item.type === "n8n_workflow");
238
314
  case "scheduled":
239
- return allItems.filter((item) => item.kind === "trigger");
240
- case "my-tasks":
241
- return allItems.filter((item) => item.kind === "task" && !item.system);
242
- case "system":
243
- return allItems.filter((item) => (item.kind === "task" && item.system) || item.kind === "trigger");
315
+ return allItems.filter((item) => item.schedules.length > 0);
244
316
  default:
245
317
  return allItems;
246
318
  }
247
319
  }, [allItems, filter]);
248
- // Clear stale selection
249
320
  useEffect(() => {
250
321
  if (!selectedItemId)
251
322
  return;
@@ -253,31 +324,30 @@ function useAutomationsViewController() {
253
324
  setSelectedItemId(null);
254
325
  setSelectedItemKind(null);
255
326
  }
256
- }, [selectedItemId, allItems]);
327
+ }, [allItems, selectedItemId]);
257
328
  useEffect(() => {
258
329
  if (selectedItemId) {
259
330
  lastSelectedIdRef.current = selectedItemId;
260
331
  }
261
332
  }, [selectedItemId]);
262
- // Auto-select first item
263
333
  useEffect(() => {
264
334
  if (editorOpen ||
265
335
  editingId ||
266
336
  editingTaskId ||
267
337
  selectedItemId ||
268
- allItems.length === 0)
338
+ allItems.length === 0) {
269
339
  return;
340
+ }
270
341
  const preferred = lastSelectedIdRef.current;
271
342
  const next = preferred && allItems.some((item) => item.id === preferred)
272
343
  ? preferred
273
344
  : (allItems[0]?.id ?? null);
274
- if (next) {
275
- const item = allItems.find((i) => i.id === next);
276
- setSelectedItemId(next);
277
- setSelectedItemKind(item?.kind ?? null);
278
- }
279
- }, [editorOpen, editingId, editingTaskId, selectedItemId, allItems]);
280
- // Escape key
345
+ if (!next)
346
+ return;
347
+ const item = allItems.find((candidate) => candidate.id === next) ?? null;
348
+ setSelectedItemId(next);
349
+ setSelectedItemKind(getSelectionKind(item));
350
+ }, [allItems, editingId, editingTaskId, editorOpen, selectedItemId]);
281
351
  useEffect(() => {
282
352
  if (!editorOpen)
283
353
  return undefined;
@@ -295,7 +365,6 @@ function useAutomationsViewController() {
295
365
  window.addEventListener("keydown", onKeyDown);
296
366
  return () => window.removeEventListener("keydown", onKeyDown);
297
367
  }, [editorOpen]);
298
- // ── Editor helpers ──────────────────────────────────────────────
299
368
  const resetEditor = () => {
300
369
  setForm(emptyForm);
301
370
  setEditingId(null);
@@ -349,6 +418,7 @@ function useAutomationsViewController() {
349
418
  if (updated) {
350
419
  setSelectedItemId(`trigger:${updated.id}`);
351
420
  setSelectedItemKind("trigger");
421
+ await refreshAutomations();
352
422
  closeEditor();
353
423
  }
354
424
  return;
@@ -358,6 +428,7 @@ function useAutomationsViewController() {
358
428
  setSelectedItemId(`trigger:${created.id}`);
359
429
  setSelectedItemKind("trigger");
360
430
  void loadTriggerRuns(created.id);
431
+ await refreshAutomations();
361
432
  closeEditor();
362
433
  }
363
434
  };
@@ -409,6 +480,7 @@ function useAutomationsViewController() {
409
480
  setSelectedItemId(null);
410
481
  setSelectedItemKind(null);
411
482
  }
483
+ await refreshAutomations();
412
484
  closeEditor();
413
485
  };
414
486
  const onDeleteTask = async (taskId) => {
@@ -436,6 +508,8 @@ function useAutomationsViewController() {
436
508
  setSelectedItemId(`trigger:${triggerId}`);
437
509
  setSelectedItemKind("trigger");
438
510
  await runTriggerNow(triggerId);
511
+ await loadTriggerRuns(triggerId);
512
+ await refreshAutomations();
439
513
  };
440
514
  const onToggleTriggerEnabled = async (triggerId, currentlyEnabled) => {
441
515
  const updated = await updateTrigger(triggerId, {
@@ -444,11 +518,13 @@ function useAutomationsViewController() {
444
518
  if (updated && editingId === updated.id) {
445
519
  setForm(formFromTrigger(updated));
446
520
  }
521
+ await refreshAutomations();
447
522
  };
448
523
  const onToggleTaskCompleted = async (taskId, currentlyCompleted) => {
449
- await updateWorkbenchTask(taskId, { isCompleted: !currentlyCompleted });
524
+ await updateWorkbenchTask(taskId, {
525
+ isCompleted: !currentlyCompleted,
526
+ });
450
527
  };
451
- // ── Resolved selection ──────────────────────────────────────────
452
528
  const resolvedSelectedItem = useMemo(() => {
453
529
  if (editorOpen || editingId || editingTaskId)
454
530
  return null;
@@ -456,40 +532,36 @@ function useAutomationsViewController() {
456
532
  return allItems.find((item) => item.id === selectedItemId) ?? null;
457
533
  }
458
534
  return allItems[0] ?? null;
459
- }, [editorOpen, editingId, editingTaskId, selectedItemId, allItems]);
535
+ }, [allItems, editingId, editingTaskId, editorOpen, selectedItemId]);
460
536
  const modalTitle = editorMode === "trigger"
461
537
  ? editingId
462
538
  ? t("heartbeatsview.editTitle", {
463
539
  name: form.displayName.trim() || "Task",
464
540
  defaultValue: "Edit {{name}}",
465
541
  })
466
- : "New Task"
542
+ : "New Schedule"
467
543
  : editingTaskId
468
- ? "Edit Task"
469
- : "New Task";
544
+ ? "Edit Coordinator"
545
+ : "New Coordinator";
470
546
  const editorEnabled = editingId != null
471
547
  ? (triggers.find((trigger) => trigger.id === editingId)?.enabled ??
472
548
  form.enabled)
473
549
  : form.enabled;
474
550
  const hasItems = allItems.length > 0;
475
- const isLoading = triggersLoading || tasksLoading;
476
- const combinedError = triggerError || taskError;
551
+ const isLoading = triggersLoading || automationsLoading;
552
+ const combinedError = automationsError || triggerError || taskError;
477
553
  const showFirstRunEmptyState = !isLoading && !combinedError && !hasItems;
478
554
  const showDetailPane = Boolean(editorOpen || editingId || editingTaskId || resolvedSelectedItem);
479
555
  return {
480
- // Filter
481
556
  filter,
482
557
  setFilter,
483
- // Items
484
558
  allItems,
485
559
  filteredItems,
486
- // Selection
487
560
  selectedItemId,
488
561
  selectedItemKind,
489
562
  setSelectedItemId,
490
563
  setSelectedItemKind,
491
564
  resolvedSelectedItem,
492
- // Trigger editor
493
565
  form,
494
566
  setForm,
495
567
  setField,
@@ -498,7 +570,6 @@ function useAutomationsViewController() {
498
570
  editorOpen,
499
571
  setEditorOpen,
500
572
  editorMode,
501
- setEditorMode,
502
573
  formError,
503
574
  setFormError,
504
575
  editorEnabled,
@@ -506,7 +577,6 @@ function useAutomationsViewController() {
506
577
  templateNotice,
507
578
  setTemplateNotice,
508
579
  userTemplates,
509
- // Task editor
510
580
  taskFormName,
511
581
  setTaskFormName,
512
582
  taskFormDescription,
@@ -514,7 +584,6 @@ function useAutomationsViewController() {
514
584
  editingTaskId,
515
585
  setEditingTaskId,
516
586
  taskSaving,
517
- // Actions
518
587
  closeEditor,
519
588
  openCreateTrigger,
520
589
  openCreateTask,
@@ -530,13 +599,17 @@ function useAutomationsViewController() {
530
599
  saveFormAsTemplate,
531
600
  deleteUserTemplate,
532
601
  loadTriggerRuns,
533
- // Data
602
+ refreshAutomations,
603
+ automationNodes,
604
+ automationsLoading,
605
+ automationsLoaded,
606
+ automationsError,
607
+ n8nStatus,
608
+ workflowFetchError,
534
609
  triggers,
535
- workbenchTasks,
536
610
  triggerRunsById,
537
611
  triggersSaving,
538
612
  triggersLoading,
539
- tasksLoading,
540
613
  triggerError,
541
614
  taskError,
542
615
  hasItems,
@@ -544,7 +617,6 @@ function useAutomationsViewController() {
544
617
  combinedError,
545
618
  showFirstRunEmptyState,
546
619
  showDetailPane,
547
- // I18n
548
620
  t,
549
621
  uiLanguage,
550
622
  };
@@ -557,39 +629,104 @@ function useAutomationsViewContext() {
557
629
  }
558
630
  return context;
559
631
  }
560
- // ── Filter tabs ───────────────────────────────────────────────────
561
632
  function FilterTabs() {
562
633
  const { filter, setFilter, allItems } = useAutomationsViewContext();
563
- const triggerCount = allItems.filter((i) => i.kind === "trigger").length;
564
- const _userTaskCount = allItems.filter((i) => i.kind === "task" && !i.system).length;
565
- const _systemCount = allItems.filter((i) => i.kind === "task" && i.system).length + triggerCount;
566
634
  const filters = [
567
- { key: "all", label: "All" },
568
- { key: "scheduled", label: "Scheduled" },
569
- { key: "system", label: "System" },
570
- { key: "workflows", label: "Workflows" },
635
+ { key: "all", label: "All", count: allItems.length },
636
+ {
637
+ key: "coordinator",
638
+ label: "Coordinator",
639
+ count: allItems.filter((item) => item.type === "coordinator_text").length,
640
+ },
641
+ {
642
+ key: "workflows",
643
+ label: "Workflows",
644
+ count: allItems.filter((item) => item.type === "n8n_workflow").length,
645
+ },
646
+ {
647
+ key: "scheduled",
648
+ label: "Scheduled",
649
+ count: allItems.filter((item) => item.schedules.length > 0).length,
650
+ },
571
651
  ];
572
- return (_jsx("div", { className: "flex gap-1 px-1 pb-2", children: filters.map(({ key, label }) => (_jsx("button", { type: "button", onClick: () => setFilter(key), className: `rounded-lg px-2.5 py-1 text-xs font-medium transition-colors ${filter === key
652
+ return (_jsx("div", { className: "flex gap-1 px-1 pb-2", children: filters.map(({ key, label, count }) => (_jsxs("button", { type: "button", onClick: () => setFilter(key), className: `rounded-lg px-2.5 py-1 text-xs font-medium transition-colors ${filter === key
573
653
  ? "bg-accent/15 text-accent"
574
- : "text-muted hover:text-txt hover:bg-bg/50"}`, children: label }, key))) }));
654
+ : "text-muted hover:bg-bg/50 hover:text-txt"}`, children: [label, " ", _jsx("span", { className: filter === key ? "text-accent/80" : "text-muted/70", children: count })] }, key))) }));
575
655
  }
576
- // ── Task form (inline in editor) ──────────────────────────────────
577
656
  function TaskForm() {
578
657
  const { taskFormName, setTaskFormName, taskFormDescription, setTaskFormDescription, editingTaskId, formError, taskSaving, onSubmitTask, onDeleteTask, closeEditor, modalTitle, t, } = useAutomationsViewContext();
579
- return (_jsxs(PagePanel, { variant: "padded", className: "space-y-5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-lg font-semibold text-txt", children: modalTitle }), _jsx(Button, { variant: "ghost", size: "sm", onClick: closeEditor, children: t("common.cancel") })] }), formError && (_jsx("div", { className: "rounded-lg border border-danger/20 bg-danger/10 p-3 text-sm text-danger", children: formError })), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx(FieldLabel, { children: "Name" }), _jsx(Input, { value: taskFormName, onChange: (e) => setTaskFormName(e.target.value), placeholder: "Task name...", autoFocus: true })] }), _jsxs("div", { children: [_jsx(FieldLabel, { children: "Description" }), _jsx(Textarea, { value: taskFormDescription, onChange: (e) => setTaskFormDescription(e.target.value), placeholder: "What should be done...", rows: 4 })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "default", size: "sm", disabled: taskSaving || !taskFormName.trim(), onClick: () => void onSubmitTask(), children: editingTaskId
580
- ? t("triggersview.Save", { defaultValue: "Save" })
581
- : "Create Task" }), editingTaskId && (_jsx(Button, { variant: "outline", size: "sm", className: "border-danger/30 text-danger hover:bg-danger/10", onClick: () => void onDeleteTask(editingTaskId), children: t("triggersview.Delete") }))] })] }));
658
+ return (_jsxs(PagePanel, { variant: "padded", className: "space-y-5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-lg font-semibold text-txt", children: modalTitle }), _jsx(Button, { variant: "ghost", size: "sm", onClick: closeEditor, children: t("common.cancel") })] }), formError && (_jsx("div", { className: "rounded-lg border border-danger/20 bg-danger/10 p-3 text-sm text-danger", children: formError })), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx(FieldLabel, { children: "Name" }), _jsx(Input, { value: taskFormName, onChange: (event) => setTaskFormName(event.target.value), placeholder: "Coordinator automation name...", autoFocus: true })] }), _jsxs("div", { children: [_jsx(FieldLabel, { children: "Description" }), _jsx(Textarea, { value: taskFormDescription, onChange: (event) => setTaskFormDescription(event.target.value), placeholder: "What should the coordinator do...", rows: 4 })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "default", size: "sm", disabled: taskSaving || !taskFormName.trim(), onClick: () => void onSubmitTask(), children: editingTaskId ? "Save Coordinator" : "Create Coordinator" }), editingTaskId && (_jsx(Button, { variant: "outline", size: "sm", className: "border-danger/30 text-danger hover:bg-danger/10", onClick: () => void onDeleteTask(editingTaskId), children: t("triggersview.Delete") }))] })] }));
659
+ }
660
+ function WorkflowRuntimeNotice({ status, workflowFetchError, busy, onRefresh, onStartLocal, }) {
661
+ if (!status && !workflowFetchError) {
662
+ return null;
663
+ }
664
+ if (status?.mode === "disabled") {
665
+ return (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-border/30 bg-bg/30", children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-sm font-semibold text-txt", children: "Workflow execution needs n8n." }), _jsx("p", { className: "text-sm text-muted", children: "Coordinator automations stay usable without n8n. Workflow automations become deployable once Eliza Cloud or local n8n is available." })] }), status.platform !== "mobile" && (_jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onStartLocal, children: "Enable Local n8n" }))] }) }));
666
+ }
667
+ if (workflowFetchError) {
668
+ return (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-danger/20 bg-danger/5", children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-sm font-semibold text-danger", children: "Workflow backend unavailable" }), _jsx("p", { className: "text-sm text-danger/90", children: workflowFetchError })] }), _jsxs("div", { className: "flex items-center gap-2", children: [status?.mode === "local" && status.status !== "ready" && (_jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onStartLocal, children: "Start Local n8n" })), _jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onRefresh, children: "Refresh" })] })] }) }));
669
+ }
670
+ if (status?.mode === "local" && status.status !== "ready") {
671
+ return (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-warning/20 bg-warning/5", children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "text-sm font-semibold text-warning", children: ["Local n8n is ", status.status, "."] }), _jsx("p", { className: "text-sm text-muted", children: "Draft rooms still work. Workflow deploy, activate, and delete operations resume when local n8n is ready." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onStartLocal, children: "Start Local n8n" }), _jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onRefresh, children: "Refresh" })] })] }) }));
672
+ }
673
+ if (status?.mode === "cloud" && status.cloudHealth === "degraded") {
674
+ return (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-warning/20 bg-warning/5", children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-sm font-semibold text-warning", children: "Eliza Cloud workflow gateway is degraded." }), _jsx("p", { className: "text-sm text-muted", children: "Chat rooms remain usable while workflow execution and sync may be delayed." })] }), _jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: onRefresh, children: "Refresh" })] }) }));
675
+ }
676
+ return null;
582
677
  }
583
- // ── Trigger detail pane (read-only view) ──────────────────────────
584
- function TriggerDetailPane({ trigger }) {
585
- const { openEditTrigger, onRunSelectedTrigger, onToggleTriggerEnabled, loadTriggerRuns, triggerRunsById, t, uiLanguage, setForm, setEditorOpen, setEditingId, setSelectedItemId, setSelectedItemKind, setFilter, } = useAutomationsViewContext();
678
+ function AutomationNodePalette({ nodes, title, subtitle, }) {
679
+ const groupedNodes = useMemo(() => NODE_CLASS_ORDER.map((className) => ({
680
+ className,
681
+ nodes: nodes.filter((node) => node.class === className),
682
+ })).filter((group) => group.nodes.length > 0), [nodes]);
683
+ return (_jsxs(PagePanel, { variant: "padded", className: "space-y-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: title }), _jsx("p", { className: "text-sm text-muted", children: subtitle })] }), _jsxs("div", { className: "flex flex-wrap gap-2 text-xs", children: [_jsxs("span", { className: "rounded-full bg-bg/40 px-2.5 py-1 text-muted", children: [nodes.length, " total"] }), _jsxs("span", { className: "rounded-full bg-ok/10 px-2.5 py-1 text-ok", children: [nodes.filter((node) => node.availability === "enabled").length, " enabled"] }), _jsxs("span", { className: "rounded-full bg-warning/10 px-2.5 py-1 text-warning", children: [nodes.filter((node) => node.availability === "disabled").length, " setup required"] })] }), _jsx("div", { className: "space-y-4", children: groupedNodes.map((group) => (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: getNodeClassLabel(group.className) }), _jsx("div", { className: "grid gap-3 xl:grid-cols-2", children: group.nodes.map((node) => (_jsx("div", { className: `rounded-xl border px-4 py-3 ${node.availability === "enabled"
684
+ ? "border-border/30 bg-bg/25"
685
+ : "border-warning/20 bg-warning/5"}`, children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: `mt-0.5 rounded-lg p-2 ${node.availability === "enabled"
686
+ ? "bg-accent/10 text-accent"
687
+ : "bg-warning/10 text-warning"}`, children: getNodeIcon(node) }), _jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "text-sm font-semibold text-txt", children: node.label }), _jsx(StatusBadge, { label: node.availability === "enabled" ? "Ready" : "Setup", variant: node.availability === "enabled" ? "success" : "warning", withDot: true }), node.ownerScoped && (_jsx("span", { className: "rounded-full bg-bg/40 px-2 py-0.5 text-[11px] text-muted", children: "Owner scoped" }))] }), _jsx("p", { className: "text-sm text-muted", children: node.description }), node.disabledReason && (_jsx("p", { className: "text-xs text-warning", children: node.disabledReason }))] })] }) }, node.id))) })] }, group.className))) })] }));
688
+ }
689
+ function TaskAutomationDetailPane({ automation, nodes, onAutomationMutated, onPromoteToWorkflow, }) {
690
+ const { activeConversationId, conversations } = useApp();
691
+ const { openEditTask, onDeleteTask, onToggleTaskCompleted, t } = useAutomationsViewContext();
692
+ const task = automation.task;
693
+ const [chatCollapsed, setChatCollapsed] = useState(false);
694
+ const composerRef = useRef(null);
695
+ if (!task) {
696
+ return null;
697
+ }
698
+ const bridgeConversationId = getAutomationBridgeIdForItem(automation, activeConversationId, conversations);
699
+ const metadata = buildCoordinatorConversationMetadata(task.id, bridgeConversationId);
700
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between", children: [_jsxs("div", { className: "max-w-3xl space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(FieldLabel, { variant: "kicker", children: automation.system ? (_jsxs(_Fragment, { children: [_jsx(Settings, { className: "mr-1.5 inline h-3.5 w-3.5" }), "System Automation"] })) : (_jsxs(_Fragment, { children: [_jsx(SquareTerminal, { className: "mr-1.5 inline h-3.5 w-3.5" }), "Coordinator Automation"] })) }), _jsx(StatusBadge, { label: automation.system
701
+ ? "System"
702
+ : task.isCompleted
703
+ ? "Completed"
704
+ : "Active", variant: automation.system
705
+ ? "muted"
706
+ : task.isCompleted
707
+ ? "muted"
708
+ : "success", withDot: true })] }), _jsx("h2", { className: "text-2xl font-semibold text-txt sm:text-[2rem]", children: automation.title }), automation.description && (_jsx("p", { className: "text-sm leading-relaxed text-muted", children: automation.description })), task.tags.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1.5", children: task.tags.map((tag) => (_jsx("span", { className: "rounded-md bg-bg/50 px-2 py-0.5 text-xs text-muted", children: tag }, tag))) }))] }), !automation.system && (_jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 lg:justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", className: `h-8 px-3 text-xs ${task.isCompleted
709
+ ? "border-ok/30 text-ok hover:bg-ok/10"
710
+ : "border-accent/30 text-accent hover:bg-accent/10"}`, onClick: () => void onToggleTaskCompleted(task.id, task.isCompleted), children: task.isCompleted ? "Reopen" : "Complete" }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => void onPromoteToWorkflow(automation), children: [_jsx(GitBranch, { className: "mr-1.5 h-3.5 w-3.5" }), "Compile to Workflow"] }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => openEditTask(task), children: t("triggersview.Edit") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs border-danger/30 text-danger hover:bg-danger/10", onClick: () => void onDeleteTask(task.id), children: t("triggersview.Delete") })] }))] }), !automation.system && (_jsx(AutomationRoomChatPane, { assistantLabel: t("automations.chat.assistantLabel"), collapsed: chatCollapsed, composerRef: composerRef, metadata: metadata, onAutomationMutated: onAutomationMutated, onToggleCollapse: () => setChatCollapsed((value) => !value), placeholder: "Ask the coordinator to plan or execute this automation.", systemAddendum: COORDINATOR_SYSTEM_ADDENDUM, title: automation.title })), _jsx(AutomationNodePalette, { nodes: nodes, title: "Available Automation Nodes", subtitle: "These are the runtime capabilities the coordinator can reference while building or converting this automation." })] }));
711
+ }
712
+ function TriggerAutomationDetailPane({ automation, nodes, onAutomationMutated, onPromoteToWorkflow, }) {
713
+ const { activeConversationId, conversations } = useApp();
714
+ const { t, uiLanguage, openEditTrigger, onRunSelectedTrigger, onToggleTriggerEnabled, loadTriggerRuns, triggerRunsById, setForm, setEditorOpen, setEditingId, setSelectedItemId, setSelectedItemKind, } = useAutomationsViewContext();
715
+ const trigger = automation.trigger;
716
+ const [chatCollapsed, setChatCollapsed] = useState(false);
717
+ const composerRef = useRef(null);
718
+ if (!trigger) {
719
+ return null;
720
+ }
721
+ const bridgeConversationId = getAutomationBridgeIdForItem(automation, activeConversationId, conversations);
722
+ const metadata = buildCoordinatorTriggerConversationMetadata(trigger.id, bridgeConversationId);
586
723
  const selectedRuns = triggerRunsById[trigger.id] ?? [];
587
724
  const hasLoadedRuns = Object.hasOwn(triggerRunsById, trigger.id);
588
725
  useEffect(() => {
589
726
  if (!hasLoadedRuns) {
590
727
  void loadTriggerRuns(trigger.id);
591
728
  }
592
- }, [trigger.id, hasLoadedRuns, loadTriggerRuns]);
729
+ }, [hasLoadedRuns, loadTriggerRuns, trigger.id]);
593
730
  const { failureCount, successCount } = selectedRuns.reduce((counts, run) => {
594
731
  const tone = toneForLastStatus(run.status);
595
732
  if (tone === "success")
@@ -598,13 +735,9 @@ function TriggerDetailPane({ trigger }) {
598
735
  counts.failureCount += 1;
599
736
  return counts;
600
737
  }, { failureCount: 0, successCount: 0 });
601
- return (_jsxs("div", { className: "w-full", children: [_jsxs("div", { className: "mb-8 flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between", children: [_jsxs("div", { className: "max-w-3xl space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(FieldLabel, { variant: "kicker", children: [_jsx(Clock3, { className: "mr-1.5 inline h-3.5 w-3.5" }), "Scheduled Task"] }), _jsx(StatusBadge, { label: trigger.enabled
602
- ? t("appsview.Active")
603
- : t("heartbeatsview.statusPaused"), variant: trigger.enabled ? "success" : "muted", withDot: true })] }), _jsx("h2", { className: "text-2xl font-semibold text-txt sm:text-[2rem]", children: trigger.displayName }), trigger.kind === "workflow" ? (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm text-txt", children: [_jsx(Zap, { className: "h-4 w-4 shrink-0 text-accent" }), _jsx("span", { children: t("automations.runsWorkflow", {
604
- name: trigger.workflowName ?? trigger.workflowId ?? "",
605
- }) })] }), _jsx("button", { type: "button", className: "text-xs font-medium text-accent underline-offset-2 hover:underline", onClick: () => setFilter("workflows"), children: t("automations.openInWorkflowsTab") })] })) : (_jsx("p", { className: "text-sm leading-relaxed text-muted sm:text-sm", children: trigger.instructions }))] }), _jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 lg:justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", className: `h-8 px-3 text-xs ${trigger.enabled ? "border-warning/30 text-warning hover:bg-warning/10" : "border-ok/30 text-ok hover:bg-ok/10"}`, onClick: () => void onToggleTriggerEnabled(trigger.id, trigger.enabled), children: trigger.enabled
606
- ? t("heartbeatsview.pause")
607
- : t("heartbeatsview.resume") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => openEditTrigger(trigger), children: t("triggersview.Edit") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => {
738
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between", children: [_jsxs("div", { className: "max-w-3xl space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(FieldLabel, { variant: "kicker", children: [_jsx(Clock3, { className: "mr-1.5 inline h-3.5 w-3.5" }), "Scheduled Coordinator Automation"] }), _jsx(StatusBadge, { label: trigger.enabled ? "Active" : "Paused", variant: trigger.enabled ? "success" : "muted", withDot: true })] }), _jsx("h2", { className: "text-2xl font-semibold text-txt sm:text-[2rem]", children: automation.title }), _jsx("p", { className: "text-sm leading-relaxed text-muted", children: automation.description })] }), _jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 lg:justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", className: `h-8 px-3 text-xs ${trigger.enabled
739
+ ? "border-warning/30 text-warning hover:bg-warning/10"
740
+ : "border-ok/30 text-ok hover:bg-ok/10"}`, onClick: () => void onToggleTriggerEnabled(trigger.id, trigger.enabled), children: trigger.enabled ? "Pause" : "Resume" }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => void onPromoteToWorkflow(automation), children: [_jsx(GitBranch, { className: "mr-1.5 h-3.5 w-3.5" }), "Compile to Workflow"] }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => openEditTrigger(trigger), children: t("triggersview.Edit") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => {
608
741
  setForm({
609
742
  ...formFromTrigger(trigger),
610
743
  displayName: `${trigger.displayName} (copy)`,
@@ -613,108 +746,275 @@ function TriggerDetailPane({ trigger }) {
613
746
  setEditingId(null);
614
747
  setSelectedItemId(null);
615
748
  setSelectedItemKind(null);
616
- }, children: t("heartbeatsview.duplicate") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => void onRunSelectedTrigger(trigger.id), children: t("triggersview.RunNow") })] })] }), _jsxs("dl", { className: "mb-8 grid gap-4 text-sm sm:grid-cols-2 xl:grid-cols-4", children: [_jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: t("heartbeatsview.schedule") }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: scheduleLabel(trigger, t, uiLanguage) })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: t("triggersview.LastRun") }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: formatDateTime(trigger.lastRunAtIso, {
617
- fallback: t("heartbeatsview.notYetRun"),
749
+ }, children: t("heartbeatsview.duplicate") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => void onRunSelectedTrigger(trigger.id), children: t("triggersview.RunNow") })] })] }), _jsxs("dl", { className: "grid gap-4 text-sm sm:grid-cols-2 xl:grid-cols-4", children: [_jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Schedule" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: scheduleLabel(trigger, t, uiLanguage) })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Last Run" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: formatDateTime(trigger.lastRunAtIso, {
750
+ fallback: "Not yet run",
618
751
  locale: uiLanguage,
619
- }) })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: t("heartbeatsview.nextRun") }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: formatDateTime(trigger.nextRunAtMs, {
620
- fallback: t("heartbeatsview.notScheduled"),
752
+ }) })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Next Run" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: formatDateTime(trigger.nextRunAtMs, {
753
+ fallback: "Not scheduled",
621
754
  locale: uiLanguage,
622
- }) })] }), hasLoadedRuns && selectedRuns.length > 0 && (_jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: t("heartbeatsview.runStats") }), _jsxs("dd", { className: "mt-1 flex items-center gap-2 text-sm font-medium", children: [_jsx("span", { className: "text-txt", children: t("heartbeatsview.runCountPlural", {
623
- count: selectedRuns.length,
624
- }) }), successCount > 0 && (_jsxs("span", { className: "text-ok", children: [successCount, " \u2713"] })), failureCount > 0 && (_jsxs("span", { className: "text-danger", children: [failureCount, " \u2717"] }))] })] }))] }), _jsxs(PagePanel, { variant: "padded", className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: t("triggersview.RunHistory") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-7 px-3 text-xs-tight", onClick: () => void loadTriggerRuns(trigger.id), children: t("common.refresh") })] }), !hasLoadedRuns ? (_jsxs("div", { className: "flex items-center gap-2 py-6 text-sm text-muted/70", children: [_jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-muted/30 border-t-muted/80" }), t("databaseview.Loading")] })) : selectedRuns.length === 0 ? (_jsx("div", { className: "py-8 text-center text-sm text-muted/60", children: t("heartbeatsview.noRunsYetMessage") })) : (_jsx("div", { className: "space-y-2", children: selectedRuns.map((run) => (_jsxs("div", { className: "rounded-lg border border-border/30 bg-bg/30 px-4 py-3", children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [_jsx(StatusBadge, { label: localizedExecutionStatus(run.status, t), variant: toneForLastStatus(run.status) }), _jsx("span", { className: "font-mono text-xs-tight text-muted/70", children: formatDateTime(run.startedAt, { locale: uiLanguage }) })] }), _jsxs("div", { className: "text-xs-tight text-muted/80", children: [formatDurationMs(run.latencyMs, { t }), " \u00B7", " ", _jsx("span", { className: "rounded bg-bg/40 px-1 py-0.5 font-mono text-muted/60", children: run.source })] }), run.error && (_jsx("div", { className: "mt-2 whitespace-pre-wrap rounded-lg border border-danger/20 bg-danger/10 p-2 font-mono text-xs text-danger/90", children: run.error }))] }, run.triggerRunId))) }))] })] }));
755
+ }) })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Runs" }), _jsxs("dd", { className: "mt-1 flex items-center gap-2 text-sm font-medium", children: [_jsx("span", { className: "text-txt", children: selectedRuns.length }), successCount > 0 && _jsxs("span", { className: "text-ok", children: [successCount, " \u2713"] }), failureCount > 0 && (_jsxs("span", { className: "text-danger", children: [failureCount, " \u2717"] }))] })] })] }), _jsxs(PagePanel, { variant: "padded", className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: "Run History" }), _jsx(Button, { variant: "outline", size: "sm", className: "h-7 px-3 text-xs-tight", onClick: () => void loadTriggerRuns(trigger.id), children: t("common.refresh") })] }), !hasLoadedRuns ? (_jsxs("div", { className: "flex items-center gap-2 py-6 text-sm text-muted/70", children: [_jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-muted/30 border-t-muted/80" }), t("databaseview.Loading")] })) : selectedRuns.length === 0 ? (_jsx("div", { className: "py-8 text-center text-sm text-muted/60", children: t("heartbeatsview.noRunsYetMessage") })) : (_jsx("div", { className: "space-y-2", children: selectedRuns.map((run) => (_jsxs("div", { className: "rounded-lg border border-border/30 bg-bg/30 px-4 py-3", children: [_jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [_jsx(StatusBadge, { label: localizedExecutionStatus(run.status, t), variant: toneForLastStatus(run.status) }), _jsx("span", { className: "font-mono text-xs-tight text-muted/70", children: formatDateTime(run.startedAt, { locale: uiLanguage }) })] }), _jsxs("div", { className: "text-xs-tight text-muted/80", children: [formatDurationMs(run.latencyMs, { t }), " \u00B7", " ", _jsx("span", { className: "rounded bg-bg/40 px-1 py-0.5 font-mono text-muted/60", children: run.source })] }), run.error && (_jsx("div", { className: "mt-2 whitespace-pre-wrap rounded-lg border border-danger/20 bg-danger/10 p-2 font-mono text-xs text-danger/90", children: run.error }))] }, run.triggerRunId))) }))] }), _jsx(AutomationRoomChatPane, { assistantLabel: t("automations.chat.assistantLabel"), collapsed: chatCollapsed, composerRef: composerRef, metadata: metadata, onAutomationMutated: onAutomationMutated, onToggleCollapse: () => setChatCollapsed((value) => !value), placeholder: "Ask the coordinator to refine or convert this scheduled automation.", systemAddendum: COORDINATOR_SYSTEM_ADDENDUM, title: automation.title }), _jsx(AutomationNodePalette, { nodes: nodes, title: "Available Automation Nodes", subtitle: "These nodes stay visible even when setup is missing so you can design the workflow shape before connecting services." })] }));
625
756
  }
626
- // ── Task detail pane ──────────────────────────────────────────────
627
- function TaskDetailPane({ task, system, }) {
628
- const { openEditTask, onDeleteTask, onToggleTaskCompleted, t } = useAutomationsViewContext();
629
- return (_jsx("div", { className: "w-full", children: _jsxs("div", { className: "mb-8 flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between", children: [_jsxs("div", { className: "max-w-3xl space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(FieldLabel, { variant: "kicker", children: system ? (_jsxs(_Fragment, { children: [_jsx(Settings, { className: "mr-1.5 inline h-3.5 w-3.5" }), "System Automation"] })) : (_jsxs(_Fragment, { children: [_jsx(ListTodo, { className: "mr-1.5 inline h-3.5 w-3.5" }), "Task"] })) }), _jsx(StatusBadge, { label: system ? "System" : task.isCompleted ? "Completed" : "Active", variant: system ? "muted" : task.isCompleted ? "muted" : "success", withDot: true })] }), _jsx("h2", { className: "text-2xl font-semibold text-txt sm:text-[2rem]", children: task.name }), task.description && (_jsx("p", { className: "text-sm leading-relaxed text-muted sm:text-sm", children: task.description })), task.tags && task.tags.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1.5", children: task.tags.map((tag) => (_jsx("span", { className: "rounded-md bg-bg/50 px-2 py-0.5 text-xs text-muted", children: tag }, tag))) }))] }), !system && (_jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 lg:justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", className: `h-8 px-3 text-xs ${task.isCompleted ? "border-ok/30 text-ok hover:bg-ok/10" : "border-accent/30 text-accent hover:bg-accent/10"}`, onClick: () => void onToggleTaskCompleted(task.id, task.isCompleted), children: task.isCompleted ? "Reopen" : "Complete" }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs", onClick: () => openEditTask(task), children: t("triggersview.Edit") }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs border-danger/30 text-danger hover:bg-danger/10", onClick: () => void onDeleteTask(task.id), children: t("triggersview.Delete") })] }))] }) }));
757
+ function WorkflowAutomationDetailPane({ automation, nodes, n8nStatus, workflowFetchError, workflowBusyId, workflowOpsBusy, onConversationResolved, onDeleteWorkflow, onRefreshWorkflows, onStartLocalN8n, onToggleWorkflowActive, onWorkflowMutated, }) {
758
+ const { activeConversationId, conversations, t, uiLanguage } = useApp();
759
+ const [chatCollapsed, setChatCollapsed] = useState(false);
760
+ const composerRef = useRef(null);
761
+ const bridgeConversationId = getAutomationBridgeIdForItem(automation, activeConversationId, conversations);
762
+ const metadata = automation.workflowId && !automation.isDraft
763
+ ? buildWorkflowConversationMetadata(automation.workflowId, automation.title, bridgeConversationId)
764
+ : buildWorkflowDraftConversationMetadata(automation.draftId ?? createWorkflowDraftId(), bridgeConversationId);
765
+ const nodeCount = getWorkflowNodeCount(automation);
766
+ const busy = workflowOpsBusy || (automation.workflowId != null && workflowBusyId === automation.workflowId);
767
+ useEffect(() => {
768
+ setChatCollapsed(false);
769
+ }, [automation.id]);
770
+ return (_jsxs("div", { className: "space-y-6", children: [_jsx(WorkflowRuntimeNotice, { status: n8nStatus, workflowFetchError: workflowFetchError, busy: busy, onRefresh: () => void onRefreshWorkflows(), onStartLocal: () => void onStartLocalN8n() }), _jsx(AutomationRoomChatPane, { assistantLabel: t("automations.chat.assistantLabel"), collapsed: chatCollapsed, composerRef: composerRef, metadata: metadata, onConversationResolved: onConversationResolved, onAutomationMutated: onWorkflowMutated, onToggleCollapse: () => setChatCollapsed((value) => !value), placeholder: automation.isDraft
771
+ ? "Describe the workflow you want to build."
772
+ : "Refine or debug this workflow with the automation agent.", systemAddendum: WORKFLOW_SYSTEM_ADDENDUM, title: automation.title || WORKFLOW_DRAFT_TITLE }), _jsxs(PagePanel, { variant: "padded", className: "space-y-5", children: [_jsxs("div", { className: "flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between", children: [_jsxs("div", { className: "max-w-3xl space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(FieldLabel, { variant: "kicker", children: [_jsx(Workflow, { className: "mr-1.5 inline h-3.5 w-3.5" }), automation.isDraft ? "Workflow Draft" : "Workflow Automation"] }), _jsx(StatusBadge, { label: automation.isDraft
773
+ ? "Draft"
774
+ : automation.enabled
775
+ ? "Active"
776
+ : "Paused", variant: automation.isDraft
777
+ ? "warning"
778
+ : automation.enabled
779
+ ? "success"
780
+ : "muted", withDot: true }), automation.hasBackingWorkflow ? (_jsx("span", { className: "rounded-full bg-ok/10 px-2 py-0.5 text-[11px] text-ok", children: "Backed by n8n" })) : (_jsx("span", { className: "rounded-full bg-warning/10 px-2 py-0.5 text-[11px] text-warning", children: "Room only" }))] }), _jsx("h2", { className: "text-2xl font-semibold text-txt sm:text-[2rem]", children: automation.title }), _jsx("p", { className: "text-sm leading-relaxed text-muted", children: automation.description ||
781
+ (automation.isDraft
782
+ ? "Develop this workflow in chat, then have the agent create and deploy the backing n8n workflow."
783
+ : "Workflow automation.") })] }), automation.workflow && automation.workflowId && (_jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 lg:justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", className: `h-8 px-3 text-xs ${automation.workflow.active
784
+ ? "border-warning/30 text-warning hover:bg-warning/10"
785
+ : "border-ok/30 text-ok hover:bg-ok/10"}`, disabled: busy, onClick: () => void onToggleWorkflowActive(automation), children: automation.workflow.active ? "Deactivate" : "Activate" }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 px-3 text-xs border-danger/30 text-danger hover:bg-danger/10", disabled: busy, onClick: () => void onDeleteWorkflow(automation), children: "Delete Workflow" })] }))] }), _jsxs("dl", { className: "grid gap-4 text-sm sm:grid-cols-2 xl:grid-cols-4", children: [_jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Workflow ID" }), _jsx("dd", { className: "mt-1 break-all font-mono text-xs text-txt", children: automation.workflowId ?? automation.draftId ?? "Pending" })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Nodes" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: nodeCount })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Attached Schedules" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: automation.schedules.length })] }), _jsxs(PagePanel.SummaryCard, { className: "px-4 py-4", children: [_jsx("dt", { className: "text-xs-tight font-semibold uppercase tracking-wider text-muted", children: "Updated" }), _jsx("dd", { className: "mt-1 font-medium text-txt", children: formatDateTime(automation.updatedAt, {
786
+ fallback: "Unknown",
787
+ locale: uiLanguage,
788
+ }) })] })] }), automation.schedules.length > 0 && (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: "Workflow Schedules" }), _jsx("div", { className: "space-y-2", children: automation.schedules.map((schedule) => (_jsxs("div", { className: "rounded-lg border border-border/30 bg-bg/20 px-4 py-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "font-medium text-txt", children: schedule.displayName }), _jsx(StatusBadge, { label: schedule.enabled ? "Active" : "Paused", variant: schedule.enabled ? "success" : "muted", withDot: true })] }), _jsx("div", { className: "mt-1 text-sm text-muted", children: scheduleLabel(schedule, t, uiLanguage) })] }, schedule.id))) })] })), automation.workflow?.nodes && automation.workflow.nodes.length > 0 && (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: "Backing Workflow Graph" }), _jsx("div", { className: "space-y-2", children: automation.workflow.nodes.map((node) => (_jsxs("div", { className: "flex items-center justify-between rounded-lg border border-border/30 bg-bg/20 px-4 py-3 text-sm", children: [_jsx("span", { className: "font-medium text-txt", children: node.name ?? "Unnamed node" }), _jsx("span", { className: "font-mono text-xs text-muted", children: node.type?.split(".").pop() ?? "node" })] }, node.id ?? `${node.name}-${node.type}`))) })] }))] }), _jsx(AutomationNodePalette, { nodes: nodes, title: "Workflow Node Catalog", subtitle: "Runtime actions, providers, code-agent nodes, and owner-scoped LifeOps integrations are available here as workflow building blocks." })] }));
789
+ }
790
+ function AutomationSidebarItem({ item, selected, onClick, onDoubleClick, }) {
791
+ const { t, uiLanguage } = useAutomationsViewContext();
792
+ if (item.type === "n8n_workflow") {
793
+ const nodeCount = getWorkflowNodeCount(item);
794
+ return (_jsx(SidebarContent.Item, { onClick: onClick, onDoubleClick: onDoubleClick, active: selected, className: "h-auto", children: _jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 truncate", children: [_jsx(Workflow, { className: "h-3 w-3 shrink-0 text-muted/60" }), _jsx("span", { className: "truncate text-sm font-semibold text-txt", children: item.title })] }), _jsx(StatusBadge, { label: item.isDraft ? "Draft" : item.enabled ? "Active" : "Paused", variant: item.isDraft ? "warning" : item.enabled ? "success" : "muted", withDot: true })] }), _jsxs("div", { className: "mt-0.5 flex items-center justify-between gap-2 text-xs-tight text-muted", children: [_jsx("span", { className: "truncate", children: item.hasBackingWorkflow
795
+ ? `${nodeCount} workflow nodes`
796
+ : "Room draft or workflow shadow" }), item.schedules.length > 0 && (_jsxs("span", { children: [item.schedules.length, " schedule(s)"] }))] })] }) }));
797
+ }
798
+ if (item.trigger) {
799
+ const trigger = item.trigger;
800
+ return (_jsx(SidebarContent.Item, { onClick: onClick, onDoubleClick: onDoubleClick, active: selected, className: "h-auto", children: _jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 truncate", children: [_jsx(Clock3, { className: "h-3 w-3 shrink-0 text-muted/60" }), _jsx("span", { className: "truncate text-sm font-semibold text-txt", children: item.title })] }), _jsx(StatusBadge, { label: trigger.enabled ? "Active" : "Paused", variant: trigger.enabled ? "success" : "muted", withDot: true })] }), _jsxs("div", { className: "mt-0.5 flex items-center justify-between gap-2 text-xs-tight text-muted", children: [_jsx("span", { className: "truncate", children: scheduleLabel(trigger, t, uiLanguage) }), trigger.lastStatus && (_jsx(StatusBadge, { label: localizedExecutionStatus(trigger.lastStatus, t), variant: toneForLastStatus(trigger.lastStatus) }))] })] }) }));
801
+ }
802
+ if (item.task) {
803
+ const task = item.task;
804
+ return (_jsx(SidebarContent.Item, { onClick: onClick, onDoubleClick: onDoubleClick, active: selected, className: `h-auto ${item.system ? "opacity-60" : ""}`, children: _jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 truncate", children: [item.system ? (_jsx(Settings, { className: "h-3 w-3 shrink-0 text-muted/50" })) : task.isCompleted ? (_jsx(CheckCircle2, { className: "h-3 w-3 shrink-0 text-ok/60" })) : (_jsx(Circle, { className: "h-3 w-3 shrink-0 text-muted/60" })), _jsx("span", { className: `truncate text-sm font-semibold ${item.system
805
+ ? "text-muted"
806
+ : task.isCompleted
807
+ ? "text-muted line-through"
808
+ : "text-txt"}`, children: item.title })] }), _jsx(StatusBadge, { label: item.system ? "System" : task.isCompleted ? "Done" : "Active", variant: item.system ? "muted" : task.isCompleted ? "muted" : "success", withDot: true })] }), item.description && (_jsx("div", { className: "mt-0.5 truncate text-xs-tight text-muted", children: item.description }))] }) }));
809
+ }
810
+ return null;
630
811
  }
631
- // ── Main layout ───────────────────────────────────────────────────
632
812
  function AutomationsLayout() {
813
+ const { activeConversationId, conversations, } = useApp();
633
814
  const ctx = useAutomationsViewContext();
634
- const { closeEditor, deleteUserTemplate, editorEnabled, editingId, editingTaskId, editorOpen, editorMode, form, formError, loadTriggerRuns, modalTitle, onDeleteTrigger, onRunSelectedTrigger, onSubmitTrigger, onToggleTriggerEnabled, openCreateTrigger, openCreateTask, openEditTrigger, saveFormAsTemplate, selectedItemId, selectedItemKind, setEditingId, setEditorOpen, setField, setForm, setFormError, setSelectedItemId, setSelectedItemKind, setTemplateNotice, showDetailPane, showFirstRunEmptyState, resolvedSelectedItem, t, templateNotice, triggers, filteredItems, triggerError, taskError, triggerRunsById, triggersLoading, tasksLoading, triggersSaving, uiLanguage, userTemplates, filter, } = ctx;
635
- // Ref forwarded to the n8n scoped chat composer so "New workflow" can focus it.
815
+ const { closeEditor, editorEnabled, editingId, editingTaskId, editorOpen, editorMode, form, formError, loadTriggerRuns, modalTitle, onDeleteTrigger, onRunSelectedTrigger, onSubmitTrigger, onToggleTriggerEnabled, openCreateTrigger, openCreateTask, openEditTrigger, saveFormAsTemplate, selectedItemId, setEditingId, setEditorOpen, setField, setFilter, setForm, setFormError, setSelectedItemId, setSelectedItemKind, showDetailPane, showFirstRunEmptyState, resolvedSelectedItem, t, templateNotice, triggers, filteredItems, triggerRunsById, triggersLoading, triggersSaving, uiLanguage, automationNodes, combinedError, isLoading, n8nStatus, workflowFetchError, } = ctx;
636
816
  const workflowComposerRef = useRef(null);
637
- const focusWorkflowComposer = useCallback((seed = "Create a new workflow that ") => {
638
- const ta = workflowComposerRef.current;
639
- if (!ta)
640
- return;
641
- ta.focus();
642
- if (!ta.value.startsWith(seed)) {
643
- const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value")?.set;
644
- nativeSetter?.call(ta, seed);
645
- ta.dispatchEvent(new Event("input", { bubbles: true }));
646
- }
647
- }, []);
648
817
  const [searchQuery, setSearchQuery] = useState("");
649
- const searchLabel = "Search tasks";
818
+ const [pageNotice, setPageNotice] = useState(null);
819
+ const [workflowBusyId, setWorkflowBusyId] = useState(null);
820
+ const [workflowOpsBusy, setWorkflowOpsBusy] = useState(false);
821
+ const [activeWorkflowConversation, setActiveWorkflowConversation] = useState(null);
650
822
  const normalizedSearchQuery = searchQuery.trim().toLowerCase();
651
823
  const visibleItems = useMemo(() => {
652
824
  if (!normalizedSearchQuery)
653
825
  return filteredItems;
654
- return filteredItems.filter((item) => item.name.toLowerCase().includes(normalizedSearchQuery));
655
- }, [normalizedSearchQuery, filteredItems]);
826
+ return filteredItems.filter((item) => getAutomationSearchText(item).includes(normalizedSearchQuery));
827
+ }, [filteredItems, normalizedSearchQuery]);
656
828
  const mobileSidebarLabel = editorOpen || editingId || editingTaskId
657
829
  ? modalTitle
658
- : (resolvedSelectedItem?.name ?? "Automations");
659
- const selectItem = (item) => {
830
+ : (resolvedSelectedItem?.title ?? "Automations");
831
+ const selectItem = useCallback((item) => {
660
832
  setSelectedItemId(item.id);
661
- setSelectedItemKind(item.kind);
833
+ setSelectedItemKind(getSelectionKind(item));
662
834
  setEditorOpen(false);
663
835
  setEditingId(null);
664
- if (item.kind === "trigger") {
836
+ ctx.setEditingTaskId(null);
837
+ if (item.trigger) {
665
838
  void loadTriggerRuns(item.trigger.id);
666
839
  }
667
- };
668
- const automationsSidebar = (_jsx(Sidebar, { testId: "automations-sidebar", collapsible: true, contentIdentity: "automations", collapseButtonTestId: "automations-sidebar-collapse-toggle", expandButtonTestId: "automations-sidebar-expand-toggle", collapseButtonAriaLabel: "Collapse tasks", expandButtonAriaLabel: "Expand tasks", header: null, collapsedRailAction: _jsx(SidebarCollapsedActionButton, { "aria-label": "New task", onClick: openCreateTrigger, children: _jsx(Plus, { className: "h-4 w-4" }) }), collapsedRailItems: visibleItems.map((item) => {
669
- const isActive = item.id === selectedItemId;
670
- return (_jsx(SidebarContent.RailItem, { "aria-label": item.name, title: item.name, active: isActive, indicatorTone: item.kind === "trigger"
671
- ? item.trigger.enabled
672
- ? "accent"
673
- : undefined
674
- : item.task.isCompleted
675
- ? undefined
676
- : "accent", onClick: () => selectItem(item), children: railMonogram(item.name) }, item.id));
677
- }), children: _jsx(SidebarScrollRegion, { children: _jsxs(SidebarPanel, { children: [_jsxs("div", { className: "mb-3 flex items-center gap-2", children: [filter !== "workflows" && (_jsx("input", { type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: searchLabel, "aria-label": searchLabel, autoComplete: "off", spellCheck: false, className: "min-w-0 flex-1 rounded-lg border border-border/30 bg-bg/30 px-3 py-1.5 text-sm text-txt placeholder:text-muted/50 focus:border-accent/40 focus:outline-none" })), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 shrink-0 gap-1 px-3 text-xs font-medium", onClick: filter === "workflows"
678
- ? () => focusWorkflowComposer()
679
- : openCreateTrigger, children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), filter === "workflows"
680
- ? t("automations.n8n.newWorkflow")
681
- : "New"] })] }), _jsx(FilterTabs, {}), filter !== "workflows" && (triggersLoading || tasksLoading) && (_jsx(SidebarContent.Notice, { icon: _jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-muted/30 border-t-muted/80" }), children: t("common.loading") })), filter !== "workflows" &&
682
- normalizedSearchQuery &&
683
- visibleItems.length === 0 ? (_jsx(SidebarContent.EmptyState, { className: "px-4 py-6", children: "No matching items" })) : filter !== "workflows" ? (visibleItems.map((item) => {
684
- const isActive = selectedItemId === item.id;
685
- if (item.kind === "trigger") {
686
- const trigger = item.trigger;
687
- return (_jsx(SidebarContent.Item, { onClick: () => selectItem(item), onDoubleClick: () => {
688
- openEditTrigger(trigger);
689
- void loadTriggerRuns(trigger.id);
690
- }, active: isActive, className: "h-auto", children: _jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 truncate", children: [_jsx(Clock3, { className: "h-3 w-3 shrink-0 text-muted/60" }), _jsx("span", { className: "truncate text-sm font-semibold text-txt", children: trigger.displayName })] }), _jsx(StatusBadge, { label: trigger.enabled
691
- ? t("appsview.Active")
692
- : t("heartbeatsview.statusPaused"), variant: trigger.enabled ? "success" : "muted", withDot: true })] }), _jsxs("div", { className: "mt-0.5 flex items-center justify-between gap-2 text-xs-tight text-muted", children: [_jsx("span", { className: "truncate", children: scheduleLabel(trigger, t, uiLanguage) }), trigger.lastStatus && (_jsx(StatusBadge, { label: localizedExecutionStatus(trigger.lastStatus, t), variant: toneForLastStatus(trigger.lastStatus) }))] })] }) }, item.id));
693
- }
694
- // Task item
695
- const task = item.task;
696
- const isSys = item.system;
697
- return (_jsx(SidebarContent.Item, { onClick: () => selectItem(item), onDoubleClick: isSys ? undefined : () => ctx.openEditTask(task), active: isActive, className: `h-auto ${isSys ? "opacity-60" : ""}`, children: _jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 truncate", children: [isSys ? (_jsx(Settings, { className: "h-3 w-3 shrink-0 text-muted/50" })) : task.isCompleted ? (_jsx(CheckCircle2, { className: "h-3 w-3 shrink-0 text-ok/60" })) : (_jsx(Circle, { className: "h-3 w-3 shrink-0 text-muted/60" })), _jsx("span", { className: `truncate text-sm font-semibold ${isSys ? "text-muted" : task.isCompleted ? "text-muted line-through" : "text-txt"}`, children: task.name })] }), _jsx(StatusBadge, { label: isSys
698
- ? "System"
699
- : task.isCompleted
700
- ? "Done"
701
- : "Active", variant: isSys
702
- ? "muted"
703
- : task.isCompleted
704
- ? "muted"
705
- : "success", withDot: true })] }), task.description && (_jsx("div", { className: "mt-0.5 truncate text-xs-tight text-muted", children: task.description }))] }) }, item.id));
706
- })) : null] }) }) }));
840
+ }, [ctx, loadTriggerRuns, setEditingId, setEditorOpen, setSelectedItemId, setSelectedItemKind]);
841
+ const findAutomationForConversation = useCallback((data, conversationId) => data?.automations.find((item) => item.room?.conversationId === conversationId) ?? null, []);
842
+ const refreshAutomationsWithDraftBinding = useCallback(async (draftConversation) => {
843
+ const previousWorkflowIds = new Set(ctx.allItems
844
+ .filter((item) => item.type === "n8n_workflow" &&
845
+ item.workflowId != null &&
846
+ !item.isDraft)
847
+ .map((item) => item.workflowId));
848
+ const data = await ctx.refreshAutomations();
849
+ if (!draftConversation ||
850
+ draftConversation.metadata?.scope !== "automation-workflow-draft" ||
851
+ draftConversation.metadata.automationType !== "n8n_workflow") {
852
+ return data;
853
+ }
854
+ const createdWorkflows = data?.automations.filter((item) => item.type === "n8n_workflow" &&
855
+ item.workflowId != null &&
856
+ !item.isDraft &&
857
+ !previousWorkflowIds.has(item.workflowId)) ?? [];
858
+ if (createdWorkflows.length !== 1) {
859
+ return data;
860
+ }
861
+ const createdWorkflow = createdWorkflows[0];
862
+ const reboundMetadata = buildWorkflowConversationMetadata(createdWorkflow.workflowId, createdWorkflow.title, draftConversation.metadata.terminalBridgeConversationId);
863
+ const { conversation } = await client.updateConversation(draftConversation.id, {
864
+ title: createdWorkflow.title,
865
+ metadata: reboundMetadata,
866
+ });
867
+ setActiveWorkflowConversation(conversation);
868
+ return await ctx.refreshAutomations();
869
+ }, [ctx]);
870
+ const createWorkflowDraft = useCallback(async (options) => {
871
+ setPageNotice(null);
872
+ const draftId = createWorkflowDraftId();
873
+ const bridgeConversationId = getAutomationBridgeIdForItem(resolvedSelectedItem, activeConversationId, conversations);
874
+ const metadata = buildWorkflowDraftConversationMetadata(draftId, bridgeConversationId);
875
+ try {
876
+ const conversation = await resolveAutomationConversation({
877
+ title: options?.title?.trim() || WORKFLOW_DRAFT_TITLE,
878
+ metadata,
879
+ });
880
+ setActiveWorkflowConversation(conversation);
881
+ if (options?.initialPrompt?.trim()) {
882
+ await client.sendConversationMessage(conversation.id, `[SYSTEM]${WORKFLOW_SYSTEM_ADDENDUM}[/SYSTEM]\n\n${options.initialPrompt.trim()}`, "DM", undefined, undefined, buildAutomationResponseRoutingMetadata(metadata));
883
+ }
884
+ const data = options?.initialPrompt
885
+ ? await refreshAutomationsWithDraftBinding(conversation)
886
+ : await ctx.refreshAutomations();
887
+ const resolvedItem = findAutomationForConversation(data, conversation.id);
888
+ setFilter("workflows");
889
+ setSelectedItemId(resolvedItem?.id ?? `workflow-draft:${draftId}`);
890
+ setSelectedItemKind("workflow");
891
+ setEditorOpen(false);
892
+ setEditingId(null);
893
+ ctx.setEditingTaskId(null);
894
+ window.requestAnimationFrame(() => {
895
+ workflowComposerRef.current?.focus();
896
+ });
897
+ }
898
+ catch (error) {
899
+ setPageNotice(error instanceof Error
900
+ ? error.message
901
+ : "Failed to create the workflow draft room.");
902
+ }
903
+ }, [
904
+ activeConversationId,
905
+ conversations,
906
+ ctx,
907
+ findAutomationForConversation,
908
+ refreshAutomationsWithDraftBinding,
909
+ resolvedSelectedItem,
910
+ setEditingId,
911
+ setEditorOpen,
912
+ setFilter,
913
+ setSelectedItemId,
914
+ setSelectedItemKind,
915
+ ]);
916
+ const promoteAutomationToWorkflow = useCallback(async (item) => {
917
+ await createWorkflowDraft({
918
+ title: `${item.title} Workflow`,
919
+ initialPrompt: buildWorkflowCompilationPrompt(item),
920
+ });
921
+ }, [createWorkflowDraft]);
922
+ const handleWorkflowMutated = useCallback(() => {
923
+ void refreshAutomationsWithDraftBinding(activeWorkflowConversation);
924
+ }, [activeWorkflowConversation, refreshAutomationsWithDraftBinding]);
925
+ const handleRefreshWorkflows = useCallback(async () => {
926
+ setPageNotice(null);
927
+ const data = await refreshAutomationsWithDraftBinding(activeWorkflowConversation);
928
+ if (!data && ctx.automationsError) {
929
+ setPageNotice(ctx.automationsError);
930
+ }
931
+ }, [activeWorkflowConversation, ctx.automationsError, refreshAutomationsWithDraftBinding]);
932
+ const handleStartLocalN8n = useCallback(async () => {
933
+ setWorkflowOpsBusy(true);
934
+ setPageNotice(null);
935
+ try {
936
+ await client.startN8nSidecar();
937
+ await ctx.refreshAutomations();
938
+ }
939
+ catch (error) {
940
+ setPageNotice(error instanceof Error ? error.message : "Failed to start local n8n.");
941
+ }
942
+ finally {
943
+ setWorkflowOpsBusy(false);
944
+ }
945
+ }, [ctx]);
946
+ const handleToggleWorkflowActive = useCallback(async (item) => {
947
+ if (!item.workflowId || !item.workflow) {
948
+ return;
949
+ }
950
+ setWorkflowBusyId(item.workflowId);
951
+ setPageNotice(null);
952
+ try {
953
+ if (item.workflow.active) {
954
+ await client.deactivateN8nWorkflow(item.workflowId);
955
+ }
956
+ else {
957
+ await client.activateN8nWorkflow(item.workflowId);
958
+ }
959
+ await ctx.refreshAutomations();
960
+ }
961
+ catch (error) {
962
+ setPageNotice(error instanceof Error
963
+ ? error.message
964
+ : "Failed to update workflow state.");
965
+ }
966
+ finally {
967
+ setWorkflowBusyId(null);
968
+ }
969
+ }, [ctx]);
970
+ const handleDeleteWorkflow = useCallback(async (item) => {
971
+ if (!item.workflowId) {
972
+ return;
973
+ }
974
+ const confirmed = await confirmDesktopAction({
975
+ title: "Delete Workflow",
976
+ message: `Delete ${item.title}?`,
977
+ confirmLabel: "Delete Workflow",
978
+ cancelLabel: t("common.cancel"),
979
+ type: "warning",
980
+ });
981
+ if (!confirmed)
982
+ return;
983
+ setWorkflowBusyId(item.workflowId);
984
+ setPageNotice(null);
985
+ try {
986
+ await client.deleteN8nWorkflow(item.workflowId);
987
+ await ctx.refreshAutomations();
988
+ }
989
+ catch (error) {
990
+ setPageNotice(error instanceof Error ? error.message : "Failed to delete workflow.");
991
+ }
992
+ finally {
993
+ setWorkflowBusyId(null);
994
+ }
995
+ }, [ctx, t]);
996
+ const automationsSidebar = (_jsx(Sidebar, { testId: "automations-sidebar", collapsible: true, contentIdentity: "automations", collapseButtonTestId: "automations-sidebar-collapse-toggle", expandButtonTestId: "automations-sidebar-expand-toggle", collapseButtonAriaLabel: "Collapse automations", expandButtonAriaLabel: "Expand automations", header: null, collapsedRailAction: _jsx(SidebarCollapsedActionButton, { "aria-label": "New coordinator automation", onClick: openCreateTask, children: _jsx(Plus, { className: "h-4 w-4" }) }), collapsedRailItems: visibleItems.map((item) => (_jsx(SidebarContent.RailItem, { "aria-label": item.title, title: item.title, active: item.id === selectedItemId, indicatorTone: getAutomationIndicatorTone(item), onClick: () => selectItem(item), children: railMonogram(item.title) }, item.id))), children: _jsx(SidebarScrollRegion, { children: _jsxs(SidebarPanel, { children: [_jsxs("div", { className: "mb-3 space-y-2", children: [_jsx("input", { type: "text", value: searchQuery, onChange: (event) => setSearchQuery(event.target.value), placeholder: "Search automations", "aria-label": "Search automations", autoComplete: "off", spellCheck: false, className: "w-full rounded-lg border border-border/30 bg-bg/30 px-3 py-1.5 text-sm text-txt placeholder:text-muted/50 focus:border-accent/40 focus:outline-none" }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1 px-3 text-xs font-medium", onClick: openCreateTask, children: [_jsx(SquareTerminal, { className: "h-3.5 w-3.5" }), "Coordinator"] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1 px-3 text-xs font-medium", onClick: openCreateTrigger, children: [_jsx(Clock3, { className: "h-3.5 w-3.5" }), "Schedule"] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1 px-3 text-xs font-medium", onClick: () => void createWorkflowDraft(), children: [_jsx(Workflow, { className: "h-3.5 w-3.5" }), "Workflow"] })] })] }), _jsx(FilterTabs, {}), isLoading && (_jsx(SidebarContent.Notice, { icon: _jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-muted/30 border-t-muted/80" }), children: t("common.loading") })), !isLoading && normalizedSearchQuery && visibleItems.length === 0 ? (_jsx(SidebarContent.EmptyState, { className: "px-4 py-6", children: "No matching automations" })) : (visibleItems.map((item) => (_jsx(AutomationSidebarItem, { item: item, selected: selectedItemId === item.id, onClick: () => selectItem(item), onDoubleClick: item.task && !item.system
997
+ ? () => ctx.openEditTask(item.task)
998
+ : item.trigger
999
+ ? () => {
1000
+ ctx.openEditTrigger(item.trigger);
1001
+ void loadTriggerRuns(item.trigger.id);
1002
+ }
1003
+ : undefined }, item.id))))] }) }) }));
707
1004
  return (_jsx(PageLayout, { className: "h-full bg-transparent", "data-testid": "automations-shell", sidebar: automationsSidebar, contentInnerClassName: "mx-auto w-full max-w-[96rem]", footer: _jsx(WidgetHost, { slot: "automations", className: "py-3" }), mobileSidebarLabel: mobileSidebarLabel, children: _jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [showDetailPane ? (_jsx("button", { type: "button", className: "mb-3 flex items-center gap-2 rounded-2xl border border-border/30 bg-bg/25 px-4 py-3 text-base font-medium text-muted hover:text-txt md:hidden", onClick: () => {
708
1005
  setSelectedItemId(null);
709
1006
  setSelectedItemKind(null);
710
1007
  setEditorOpen(false);
711
1008
  setEditingId(null);
712
1009
  ctx.setEditingTaskId(null);
713
- }, children: "\u2190 Back" })) : null, filter === "workflows" ? (_jsx(N8nWorkflowsPanel, { composerRef: workflowComposerRef })) : editorOpen || editingId || editingTaskId ? (editorMode === "task" || editingTaskId ? (_jsx(TaskForm, {})) : (_jsx(HeartbeatForm, { form: form, editingId: editingId, editorEnabled: editorEnabled, modalTitle: modalTitle, formError: formError, triggersSaving: triggersSaving, templateNotice: templateNotice, triggers: triggers, triggerRunsById: triggerRunsById, t: t, selectedTriggerId: editingId, setField: setField, setForm: setForm, setFormError: setFormError, closeEditor: closeEditor, onSubmit: onSubmitTrigger, onDelete: onDeleteTrigger, onRunSelectedTrigger: onRunSelectedTrigger, onToggleTriggerEnabled: onToggleTriggerEnabled, saveFormAsTemplate: saveFormAsTemplate, loadTriggerRuns: loadTriggerRuns }))) : resolvedSelectedItem ? (resolvedSelectedItem.kind === "trigger" ? (_jsx(TriggerDetailPane, { trigger: resolvedSelectedItem.trigger })) : (_jsx(TaskDetailPane, { task: resolvedSelectedItem.task, system: resolvedSelectedItem.system }))) : (_jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center px-8 py-10 text-center", children: _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-lg font-semibold text-txt-strong", children: showFirstRunEmptyState
1010
+ }, children: "\u2190 Back" })) : null, (pageNotice || combinedError) && (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-danger/20 bg-danger/5", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("p", { className: "text-sm text-danger", children: pageNotice ?? combinedError }), pageNotice && (_jsx(Button, { variant: "ghost", size: "sm", className: "text-danger hover:bg-danger/10", onClick: () => setPageNotice(null), children: "Dismiss" }))] }) })), editorOpen || editingId || editingTaskId ? (editorMode === "task" || editingTaskId ? (_jsx(TaskForm, {})) : (_jsx(HeartbeatForm, { form: form, editingId: editingId, editorEnabled: editorEnabled, modalTitle: modalTitle, formError: formError, triggersSaving: triggersSaving, templateNotice: templateNotice, triggers: triggers, triggerRunsById: triggerRunsById, t: t, selectedTriggerId: editingId, setField: setField, setForm: setForm, setFormError: setFormError, closeEditor: closeEditor, onSubmit: onSubmitTrigger, onDelete: onDeleteTrigger, onRunSelectedTrigger: onRunSelectedTrigger, onToggleTriggerEnabled: onToggleTriggerEnabled, saveFormAsTemplate: saveFormAsTemplate, loadTriggerRuns: loadTriggerRuns }))) : resolvedSelectedItem?.type === "n8n_workflow" ? (_jsx(WorkflowAutomationDetailPane, { automation: resolvedSelectedItem, nodes: automationNodes, n8nStatus: n8nStatus, workflowFetchError: workflowFetchError, workflowBusyId: workflowBusyId, workflowOpsBusy: workflowOpsBusy, onConversationResolved: setActiveWorkflowConversation, onDeleteWorkflow: handleDeleteWorkflow, onRefreshWorkflows: handleRefreshWorkflows, onStartLocalN8n: handleStartLocalN8n, onToggleWorkflowActive: handleToggleWorkflowActive, onWorkflowMutated: handleWorkflowMutated })) : resolvedSelectedItem?.trigger ? (_jsx(TriggerAutomationDetailPane, { automation: resolvedSelectedItem, nodes: automationNodes, onAutomationMutated: () => {
1011
+ void ctx.refreshAutomations();
1012
+ }, onPromoteToWorkflow: promoteAutomationToWorkflow })) : resolvedSelectedItem?.task ? (_jsx(TaskAutomationDetailPane, { automation: resolvedSelectedItem, nodes: automationNodes, onAutomationMutated: () => {
1013
+ void ctx.refreshAutomations();
1014
+ }, onPromoteToWorkflow: promoteAutomationToWorkflow })) : (_jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center px-8 py-10 text-center", children: _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-lg font-semibold text-txt-strong", children: showFirstRunEmptyState
714
1015
  ? "Create your first automation"
715
- : "Select an item" }), showFirstRunEmptyState && (_jsx("p", { className: "text-sm text-muted", children: "Schedule recurring automations or create tasks to track work." }))] }) }))] }) }));
1016
+ : "Select an automation" }), showFirstRunEmptyState && (_jsx("p", { className: "text-sm text-muted", children: "Build a coordinator automation, schedule recurring work, or create an n8n workflow room." }))] }) }))] }) }));
716
1017
  }
717
- // ── Exports ───────────────────────────────────────────────────────
718
1018
  export function AutomationsView() {
719
1019
  const controller = useAutomationsViewController();
720
1020
  return (_jsx(AutomationsViewContext.Provider, { value: controller, children: _jsx(AutomationsLayout, {}) }));