@swarmclawai/swarmclaw 0.7.2 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/README.md +116 -50
  2. package/bin/package-manager.js +157 -0
  3. package/bin/package-manager.test.js +90 -0
  4. package/bin/server-cmd.js +38 -7
  5. package/bin/swarmclaw.js +54 -4
  6. package/bin/update-cmd.js +48 -10
  7. package/bin/update-cmd.test.js +55 -0
  8. package/package.json +8 -3
  9. package/scripts/postinstall.mjs +26 -0
  10. package/src/app/api/agents/[id]/route.ts +43 -0
  11. package/src/app/api/agents/[id]/thread/route.ts +39 -8
  12. package/src/app/api/agents/route.ts +35 -2
  13. package/src/app/api/auth/route.ts +77 -8
  14. package/src/app/api/chatrooms/[id]/chat/route.ts +22 -6
  15. package/src/app/api/chatrooms/[id]/pins/route.ts +2 -1
  16. package/src/app/api/chatrooms/[id]/reactions/route.ts +2 -1
  17. package/src/app/api/chatrooms/[id]/route.ts +6 -0
  18. package/src/app/api/chats/[id]/browser/route.ts +5 -1
  19. package/src/app/api/chats/[id]/chat/route.ts +7 -3
  20. package/src/app/api/chats/[id]/messages/route.ts +19 -13
  21. package/src/app/api/chats/[id]/route.ts +30 -0
  22. package/src/app/api/chats/[id]/stop/route.ts +6 -1
  23. package/src/app/api/chats/heartbeat/route.ts +2 -1
  24. package/src/app/api/chats/route.ts +23 -1
  25. package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
  26. package/src/app/api/connectors/doctor/route.ts +13 -0
  27. package/src/app/api/external-agents/[id]/heartbeat/route.ts +33 -0
  28. package/src/app/api/external-agents/[id]/route.ts +31 -0
  29. package/src/app/api/external-agents/register/route.ts +3 -0
  30. package/src/app/api/external-agents/route.ts +66 -0
  31. package/src/app/api/files/open/route.ts +16 -14
  32. package/src/app/api/gateways/[id]/health/route.ts +28 -0
  33. package/src/app/api/gateways/[id]/route.ts +79 -0
  34. package/src/app/api/gateways/route.ts +57 -0
  35. package/src/app/api/memory/maintenance/route.ts +11 -1
  36. package/src/app/api/openclaw/agent-files/route.ts +27 -4
  37. package/src/app/api/openclaw/gateway/route.ts +10 -7
  38. package/src/app/api/openclaw/skills/route.ts +12 -4
  39. package/src/app/api/plugins/dependencies/route.ts +24 -0
  40. package/src/app/api/plugins/install/route.ts +15 -92
  41. package/src/app/api/plugins/route.ts +3 -26
  42. package/src/app/api/plugins/settings/route.ts +17 -12
  43. package/src/app/api/plugins/ui/route.ts +1 -0
  44. package/src/app/api/providers/[id]/discover-models/route.ts +27 -0
  45. package/src/app/api/schedules/[id]/route.ts +38 -9
  46. package/src/app/api/schedules/route.ts +51 -28
  47. package/src/app/api/settings/route.ts +55 -17
  48. package/src/app/api/setup/doctor/route.ts +6 -4
  49. package/src/app/api/tasks/[id]/route.ts +16 -6
  50. package/src/app/api/tasks/bulk/route.ts +3 -3
  51. package/src/app/api/tasks/route.ts +9 -4
  52. package/src/app/api/webhooks/[id]/route.ts +8 -1
  53. package/src/app/page.tsx +135 -17
  54. package/src/cli/binary.test.js +142 -0
  55. package/src/cli/index.js +38 -11
  56. package/src/cli/index.test.js +195 -0
  57. package/src/cli/index.ts +21 -12
  58. package/src/cli/server-cmd.test.js +59 -0
  59. package/src/cli/spec.js +20 -2
  60. package/src/components/agents/agent-card.tsx +15 -12
  61. package/src/components/agents/agent-chat-list.tsx +101 -1
  62. package/src/components/agents/agent-list.tsx +46 -9
  63. package/src/components/agents/agent-sheet.tsx +456 -23
  64. package/src/components/agents/inspector-panel.tsx +110 -49
  65. package/src/components/agents/sandbox-env-panel.tsx +4 -1
  66. package/src/components/auth/access-key-gate.tsx +36 -97
  67. package/src/components/auth/setup-wizard.tsx +970 -275
  68. package/src/components/chat/chat-area.tsx +70 -27
  69. package/src/components/chat/chat-card.tsx +6 -21
  70. package/src/components/chat/chat-header.tsx +263 -366
  71. package/src/components/chat/chat-list.tsx +62 -26
  72. package/src/components/chat/checkpoint-timeline.tsx +1 -1
  73. package/src/components/chat/message-list.tsx +145 -19
  74. package/src/components/chatrooms/chatroom-input.tsx +96 -33
  75. package/src/components/chatrooms/chatroom-list.tsx +141 -72
  76. package/src/components/chatrooms/chatroom-message.tsx +7 -6
  77. package/src/components/chatrooms/chatroom-sheet.tsx +13 -1
  78. package/src/components/chatrooms/chatroom-tool-request-banner.tsx +5 -2
  79. package/src/components/chatrooms/chatroom-view.tsx +422 -209
  80. package/src/components/chatrooms/reaction-picker.tsx +38 -33
  81. package/src/components/connectors/connector-list.tsx +265 -127
  82. package/src/components/connectors/connector-sheet.tsx +217 -0
  83. package/src/components/gateways/gateway-sheet.tsx +567 -0
  84. package/src/components/home/home-view.tsx +128 -4
  85. package/src/components/input/chat-input.tsx +135 -86
  86. package/src/components/layout/app-layout.tsx +385 -194
  87. package/src/components/layout/mobile-header.tsx +26 -8
  88. package/src/components/memory/memory-browser.tsx +71 -6
  89. package/src/components/memory/memory-card.tsx +18 -0
  90. package/src/components/memory/memory-detail.tsx +58 -31
  91. package/src/components/memory/memory-sheet.tsx +32 -4
  92. package/src/components/plugins/plugin-list.tsx +15 -3
  93. package/src/components/plugins/plugin-sheet.tsx +118 -9
  94. package/src/components/projects/project-detail.tsx +189 -1
  95. package/src/components/providers/provider-list.tsx +158 -2
  96. package/src/components/providers/provider-sheet.tsx +81 -70
  97. package/src/components/shared/agent-picker-list.tsx +2 -2
  98. package/src/components/shared/bottom-sheet.tsx +31 -15
  99. package/src/components/shared/command-palette.tsx +111 -24
  100. package/src/components/shared/confirm-dialog.tsx +45 -30
  101. package/src/components/shared/model-combobox.tsx +90 -8
  102. package/src/components/shared/settings/plugin-manager.tsx +20 -4
  103. package/src/components/shared/settings/section-capability-policy.tsx +105 -0
  104. package/src/components/shared/settings/section-heartbeat.tsx +88 -6
  105. package/src/components/shared/settings/section-orchestrator.tsx +6 -3
  106. package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
  107. package/src/components/shared/settings/section-secrets.tsx +6 -6
  108. package/src/components/shared/settings/section-user-preferences.tsx +1 -1
  109. package/src/components/shared/settings/section-voice.tsx +5 -1
  110. package/src/components/shared/settings/section-web-search.tsx +10 -2
  111. package/src/components/shared/settings/settings-page.tsx +248 -47
  112. package/src/components/tasks/approvals-panel.tsx +211 -18
  113. package/src/components/tasks/task-board.tsx +242 -46
  114. package/src/components/ui/dialog.tsx +2 -2
  115. package/src/components/usage/metrics-dashboard.tsx +74 -1
  116. package/src/components/wallets/wallet-approval-dialog.tsx +59 -54
  117. package/src/components/wallets/wallet-panel.tsx +17 -5
  118. package/src/components/webhooks/webhook-sheet.tsx +7 -7
  119. package/src/lib/auth.ts +17 -0
  120. package/src/lib/chat-streaming-state.test.ts +108 -0
  121. package/src/lib/chat-streaming-state.ts +108 -0
  122. package/src/lib/heartbeat-defaults.ts +48 -0
  123. package/src/lib/memory-presentation.ts +59 -0
  124. package/src/lib/openclaw-agent-id.test.ts +14 -0
  125. package/src/lib/openclaw-agent-id.ts +31 -0
  126. package/src/lib/provider-model-discovery-client.ts +29 -0
  127. package/src/lib/providers/index.ts +12 -5
  128. package/src/lib/runtime-loop.ts +105 -3
  129. package/src/lib/safe-storage.ts +6 -1
  130. package/src/lib/server/agent-assignment.test.ts +112 -0
  131. package/src/lib/server/agent-assignment.ts +169 -0
  132. package/src/lib/server/agent-runtime-config.test.ts +141 -0
  133. package/src/lib/server/agent-runtime-config.ts +277 -0
  134. package/src/lib/server/approval-connector-notify.test.ts +253 -0
  135. package/src/lib/server/approvals-auto-approve.test.ts +264 -0
  136. package/src/lib/server/approvals.ts +483 -75
  137. package/src/lib/server/autonomy-runtime.test.ts +341 -0
  138. package/src/lib/server/browser-state.test.ts +118 -0
  139. package/src/lib/server/browser-state.ts +123 -0
  140. package/src/lib/server/build-llm.test.ts +44 -0
  141. package/src/lib/server/build-llm.ts +11 -4
  142. package/src/lib/server/builtin-plugins.ts +34 -0
  143. package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
  144. package/src/lib/server/chat-execution-tool-events.test.ts +219 -0
  145. package/src/lib/server/chat-execution.ts +402 -125
  146. package/src/lib/server/chatroom-health.test.ts +26 -0
  147. package/src/lib/server/chatroom-health.ts +2 -3
  148. package/src/lib/server/chatroom-helpers.test.ts +74 -2
  149. package/src/lib/server/chatroom-helpers.ts +144 -11
  150. package/src/lib/server/chatroom-session-persistence.test.ts +87 -0
  151. package/src/lib/server/connectors/discord.ts +175 -11
  152. package/src/lib/server/connectors/doctor.test.ts +80 -0
  153. package/src/lib/server/connectors/doctor.ts +116 -0
  154. package/src/lib/server/connectors/manager.ts +994 -130
  155. package/src/lib/server/connectors/policy.test.ts +222 -0
  156. package/src/lib/server/connectors/policy.ts +452 -0
  157. package/src/lib/server/connectors/slack.ts +189 -10
  158. package/src/lib/server/connectors/telegram.ts +65 -15
  159. package/src/lib/server/connectors/thread-context.test.ts +44 -0
  160. package/src/lib/server/connectors/thread-context.ts +72 -0
  161. package/src/lib/server/connectors/types.ts +41 -11
  162. package/src/lib/server/daemon-state.ts +62 -3
  163. package/src/lib/server/data-dir.ts +13 -0
  164. package/src/lib/server/delegation-jobs.test.ts +140 -0
  165. package/src/lib/server/delegation-jobs.ts +248 -0
  166. package/src/lib/server/document-utils.test.ts +47 -0
  167. package/src/lib/server/document-utils.ts +397 -0
  168. package/src/lib/server/eval/agent-regression.test.ts +47 -0
  169. package/src/lib/server/eval/agent-regression.ts +1742 -0
  170. package/src/lib/server/eval/runner.ts +11 -1
  171. package/src/lib/server/eval/store.ts +2 -1
  172. package/src/lib/server/heartbeat-service.ts +23 -43
  173. package/src/lib/server/heartbeat-source.test.ts +22 -0
  174. package/src/lib/server/heartbeat-source.ts +7 -0
  175. package/src/lib/server/identity-continuity.test.ts +77 -0
  176. package/src/lib/server/identity-continuity.ts +127 -0
  177. package/src/lib/server/mailbox-utils.ts +347 -0
  178. package/src/lib/server/main-agent-loop.ts +31 -964
  179. package/src/lib/server/memory-db.ts +4 -6
  180. package/src/lib/server/memory-tiers.ts +40 -0
  181. package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
  182. package/src/lib/server/openclaw-agent-resolver.ts +128 -0
  183. package/src/lib/server/openclaw-exec-config.ts +6 -5
  184. package/src/lib/server/openclaw-gateway.ts +123 -36
  185. package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
  186. package/src/lib/server/openclaw-skills-normalize.ts +136 -0
  187. package/src/lib/server/openclaw-sync.ts +3 -2
  188. package/src/lib/server/orchestrator-lg.ts +18 -8
  189. package/src/lib/server/orchestrator.ts +5 -4
  190. package/src/lib/server/playwright-proxy.mjs +27 -3
  191. package/src/lib/server/plugins.test.ts +215 -0
  192. package/src/lib/server/plugins.ts +832 -69
  193. package/src/lib/server/provider-health.ts +33 -3
  194. package/src/lib/server/provider-model-discovery.ts +481 -0
  195. package/src/lib/server/queue.ts +4 -21
  196. package/src/lib/server/runtime-settings.test.ts +119 -0
  197. package/src/lib/server/runtime-settings.ts +12 -92
  198. package/src/lib/server/schedule-normalization.ts +187 -0
  199. package/src/lib/server/scheduler.ts +2 -0
  200. package/src/lib/server/session-archive-memory.test.ts +85 -0
  201. package/src/lib/server/session-archive-memory.ts +230 -0
  202. package/src/lib/server/session-mailbox.ts +8 -18
  203. package/src/lib/server/session-reset-policy.test.ts +99 -0
  204. package/src/lib/server/session-reset-policy.ts +311 -0
  205. package/src/lib/server/session-run-manager.ts +33 -80
  206. package/src/lib/server/session-tools/autonomy-tools.test.ts +128 -0
  207. package/src/lib/server/session-tools/calendar.ts +2 -12
  208. package/src/lib/server/session-tools/connector.ts +109 -8
  209. package/src/lib/server/session-tools/context.ts +14 -2
  210. package/src/lib/server/session-tools/crawl.ts +447 -0
  211. package/src/lib/server/session-tools/crud.ts +96 -34
  212. package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
  213. package/src/lib/server/session-tools/delegate.ts +406 -20
  214. package/src/lib/server/session-tools/discovery-approvals.test.ts +170 -0
  215. package/src/lib/server/session-tools/discovery.ts +40 -12
  216. package/src/lib/server/session-tools/document.ts +283 -0
  217. package/src/lib/server/session-tools/email.ts +1 -3
  218. package/src/lib/server/session-tools/extract.ts +137 -0
  219. package/src/lib/server/session-tools/file-normalize.test.ts +98 -0
  220. package/src/lib/server/session-tools/file-send.test.ts +84 -1
  221. package/src/lib/server/session-tools/file.ts +243 -24
  222. package/src/lib/server/session-tools/http.ts +9 -3
  223. package/src/lib/server/session-tools/human-loop.ts +227 -0
  224. package/src/lib/server/session-tools/image-gen.ts +1 -3
  225. package/src/lib/server/session-tools/index.ts +87 -2
  226. package/src/lib/server/session-tools/mailbox.ts +276 -0
  227. package/src/lib/server/session-tools/manage-schedules.test.ts +137 -0
  228. package/src/lib/server/session-tools/memory.ts +35 -3
  229. package/src/lib/server/session-tools/monitor.ts +162 -12
  230. package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
  231. package/src/lib/server/session-tools/openclaw-nodes.test.ts +111 -0
  232. package/src/lib/server/session-tools/openclaw-nodes.ts +86 -20
  233. package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
  234. package/src/lib/server/session-tools/platform.ts +142 -4
  235. package/src/lib/server/session-tools/plugin-creator.ts +95 -25
  236. package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
  237. package/src/lib/server/session-tools/replicate.ts +1 -3
  238. package/src/lib/server/session-tools/sandbox.ts +51 -92
  239. package/src/lib/server/session-tools/schedule.ts +20 -10
  240. package/src/lib/server/session-tools/session-info.ts +58 -4
  241. package/src/lib/server/session-tools/session-tools-wiring.test.ts +54 -17
  242. package/src/lib/server/session-tools/shell.ts +2 -2
  243. package/src/lib/server/session-tools/subagent.ts +195 -27
  244. package/src/lib/server/session-tools/table.ts +587 -0
  245. package/src/lib/server/session-tools/wallet.ts +13 -10
  246. package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
  247. package/src/lib/server/session-tools/web.ts +947 -108
  248. package/src/lib/server/storage.ts +255 -10
  249. package/src/lib/server/stream-agent-chat.test.ts +61 -0
  250. package/src/lib/server/stream-agent-chat.ts +185 -25
  251. package/src/lib/server/structured-extract.test.ts +72 -0
  252. package/src/lib/server/structured-extract.ts +373 -0
  253. package/src/lib/server/task-mention.test.ts +16 -2
  254. package/src/lib/server/task-mention.ts +61 -11
  255. package/src/lib/server/tool-aliases.ts +80 -12
  256. package/src/lib/server/tool-capability-policy.ts +7 -1
  257. package/src/lib/server/tool-retry.ts +2 -0
  258. package/src/lib/server/watch-jobs.test.ts +173 -0
  259. package/src/lib/server/watch-jobs.ts +532 -0
  260. package/src/lib/server/ws-hub.ts +5 -3
  261. package/src/lib/setup-defaults.ts +352 -11
  262. package/src/lib/tool-definitions.ts +3 -4
  263. package/src/lib/validation/schemas.test.ts +26 -0
  264. package/src/lib/validation/schemas.ts +62 -1
  265. package/src/lib/ws-client.ts +14 -12
  266. package/src/proxy.ts +5 -5
  267. package/src/stores/use-app-store.ts +43 -7
  268. package/src/stores/use-chat-store.ts +31 -2
  269. package/src/stores/use-chatroom-store.ts +153 -26
  270. package/src/types/index.ts +470 -44
  271. package/src/app/api/chats/[id]/main-loop/route.ts +0 -94
  272. package/src/components/chat/new-chat-sheet.tsx +0 -253
  273. package/src/lib/server/main-session.ts +0 -17
  274. package/src/lib/server/session-run-manager.test.ts +0 -26
@@ -22,12 +22,20 @@ import { Dropdown, DropdownItem } from '@/components/shared/dropdown'
22
22
  import { ConfirmDialog } from '@/components/shared/confirm-dialog'
23
23
  import { speak } from '@/lib/tts'
24
24
  import { api } from '@/lib/api-client'
25
+ import { messagesDiffer } from '@/lib/chat-streaming-state'
25
26
 
26
- const PROMPT_SUGGESTIONS = [
27
+ const DIRECT_PROMPT_SUGGESTIONS = [
27
28
  { text: 'What can you help me with?', icon: 'book', gradient: 'from-[#6366F1]/10 to-[#818CF8]/5' },
29
+ { text: 'Help me choose the right agent for this', icon: 'bot', gradient: 'from-[#34D399]/10 to-[#6EE7B7]/5' },
28
30
  { text: 'Help me set up a new connector', icon: 'link', gradient: 'from-[#EC4899]/10 to-[#F472B6]/5' },
29
- { text: 'Create a new agent for me', icon: 'bot', gradient: 'from-[#34D399]/10 to-[#6EE7B7]/5' },
30
- { text: 'Schedule a recurring task', icon: 'check', gradient: 'from-[#F59E0B]/10 to-[#FBBF24]/5' },
31
+ { text: 'Summarize what needs attention in this workspace', icon: 'check', gradient: 'from-[#F59E0B]/10 to-[#FBBF24]/5' },
32
+ ]
33
+
34
+ const AGENT_PROMPT_SUGGESTIONS = [
35
+ { text: 'Give me a quick overview of what you can help with', icon: 'book', gradient: 'from-[#6366F1]/10 to-[#818CF8]/5' },
36
+ { text: 'Review what needs attention right now', icon: 'check', gradient: 'from-[#F59E0B]/10 to-[#FBBF24]/5' },
37
+ { text: 'Summarize our recent context before we continue', icon: 'link', gradient: 'from-[#EC4899]/10 to-[#F472B6]/5' },
38
+ { text: 'Help me map the next best step', icon: 'bot', gradient: 'from-[#34D399]/10 to-[#6EE7B7]/5' },
31
39
  ]
32
40
 
33
41
  export function ChatArea() {
@@ -52,6 +60,10 @@ export function ChatArea() {
52
60
  const sidebarOpen = useAppStore((s) => s.sidebarOpen)
53
61
  const setSidebarOpen = useAppStore((s) => s.setSidebarOpen)
54
62
  const currentAgent = session?.agentId ? agents[session.agentId] ?? null : null
63
+ const promptSuggestions = useMemo(
64
+ () => (currentAgent ? AGENT_PROMPT_SUGGESTIONS : DIRECT_PROMPT_SUGGESTIONS),
65
+ [currentAgent],
66
+ )
55
67
 
56
68
  const voice = useVoiceConversation()
57
69
  const handleVoiceToggle = useCallback(() => {
@@ -68,6 +80,7 @@ export function ChatArea() {
68
80
  const [messagesLoading, setMessagesLoading] = useState(true)
69
81
  const [connectorFilter, setConnectorFilter] = useState<string | null>(null)
70
82
  const [pluginChatActions, setPluginChatActions] = useState<Array<{ id: string; label: string; action: string; value: string; tooltip?: string }>>([])
83
+ const sessionHasBrowserPlugin = session?.plugins?.includes('browser') === true
71
84
 
72
85
  useEffect(() => {
73
86
  if (sessionId) {
@@ -101,52 +114,71 @@ export function ChatArea() {
101
114
 
102
115
  useEffect(() => {
103
116
  if (!sessionId) return
117
+ let cancelled = false
118
+ const requestedSessionId = sessionId
104
119
  const chatState = useChatStore.getState()
105
- const preserveLocalStream = chatState.streaming && chatState.streamingSessionId === sessionId
106
- // Clear stale state from the previous session, but keep active local stream state for this session.
120
+ const preserveLocalStream = chatState.streaming && chatState.streamingSessionId === requestedSessionId
121
+ // Keep the previous thread visible while the next one loads to avoid blank flashes.
107
122
  setMessagesLoading(true)
108
- setMessages([])
109
123
  if (!preserveLocalStream) {
110
124
  useChatStore.setState({ streaming: false, streamingSessionId: null, streamText: '', toolEvents: [] })
111
125
  }
112
- fetchMessagesPaginated(sessionId, 100).then((data) => {
126
+ fetchMessagesPaginated(requestedSessionId, 100).then((data) => {
127
+ if (cancelled || useAppStore.getState().currentSessionId !== requestedSessionId) return
113
128
  setMessages(data.messages)
114
129
  useChatStore.setState({ hasMoreMessages: data.hasMore, totalMessages: data.total })
115
130
  }).catch((err) => {
131
+ if (cancelled || useAppStore.getState().currentSessionId !== requestedSessionId) return
116
132
  console.error('Failed to load messages:', err)
117
- setMessages(session?.messages || [])
133
+ const fallbackSession = useAppStore.getState().sessions[requestedSessionId]
134
+ setMessages(fallbackSession?.messages || [])
118
135
  }).finally(() => {
136
+ if (cancelled || useAppStore.getState().currentSessionId !== requestedSessionId) return
119
137
  setMessagesLoading(false)
120
138
  })
121
139
  // If server reports session is still active, show streaming state
122
140
  if (session?.active) {
123
- useChatStore.setState({ streaming: true, streamingSessionId: sessionId, streamText: '' })
141
+ useChatStore.setState({ streaming: true, streamingSessionId: requestedSessionId, streamText: '' })
124
142
  }
125
143
  // Refresh active state from server so returning to a session restores typing indicator.
126
144
  loadSessions().then(() => {
127
- const refreshed = useAppStore.getState().sessions[sessionId]
145
+ if (cancelled || useAppStore.getState().currentSessionId !== requestedSessionId) return
146
+ const refreshed = useAppStore.getState().sessions[requestedSessionId]
128
147
  if (refreshed?.active) {
129
- useChatStore.setState({ streaming: true, streamingSessionId: sessionId, streamText: '' })
148
+ useChatStore.setState({ streaming: true, streamingSessionId: requestedSessionId, streamText: '' })
130
149
  }
131
150
  }).catch((err) => console.error('Failed to refresh messages:', err))
132
- devServer(sessionId, 'status').then((r) => {
151
+ devServer(requestedSessionId, 'status').then((r) => {
152
+ if (cancelled || useAppStore.getState().currentSessionId !== requestedSessionId) return
133
153
  setDevServer(r.running ? r : null)
134
- }).catch(() => setDevServer(null))
154
+ }).catch(() => {
155
+ if (cancelled || useAppStore.getState().currentSessionId !== requestedSessionId) return
156
+ setDevServer(null)
157
+ })
135
158
  // Check browser status
136
- if (session?.plugins?.includes('browser')) {
137
- checkBrowser(sessionId).then((r) => setBrowserActive(r.active)).catch((err) => { console.error('Browser check failed:', err); setBrowserActive(false) })
159
+ if (sessionHasBrowserPlugin) {
160
+ checkBrowser(requestedSessionId).then((r) => {
161
+ if (cancelled || useAppStore.getState().currentSessionId !== requestedSessionId) return
162
+ setBrowserActive(r.active)
163
+ }).catch((err) => {
164
+ if (cancelled || useAppStore.getState().currentSessionId !== requestedSessionId) return
165
+ console.error('Browser check failed:', err)
166
+ setBrowserActive(false)
167
+ })
138
168
  } else {
139
169
  setBrowserActive(false)
140
170
  }
141
- }, [sessionId])
171
+ return () => {
172
+ cancelled = true
173
+ }
174
+ }, [loadSessions, session?.active, sessionHasBrowserPlugin, sessionId, setDevServer, setMessages])
142
175
 
143
- // Auto-poll messages for orchestrated or server-active sessions
144
- const isOrchestrated = session?.sessionType === 'orchestrated'
176
+ // Auto-poll messages for sessions that are actively running on the server
145
177
  const isServerActive = session?.active === true
146
178
  const isOngoingMonitored = appSettings.loopMode === 'ongoing' && !!session?.plugins?.length
147
- const shouldPollMessages = !!sessionId && (isOrchestrated || isServerActive || isOngoingMonitored)
148
- const messagesLenRef = useRef(messages.length)
149
- messagesLenRef.current = messages.length
179
+ const shouldPollMessages = !!sessionId && (isServerActive || isOngoingMonitored)
180
+ const messagesRef = useRef(messages)
181
+ messagesRef.current = messages
150
182
  const isServerActiveRef = useRef(isServerActive)
151
183
  isServerActiveRef.current = isServerActive
152
184
  const ttsEnabledRef = useRef(ttsEnabled)
@@ -156,8 +188,9 @@ export function ChatArea() {
156
188
  if (!sessionId) return
157
189
  try {
158
190
  const msgs = await fetchMessages(sessionId)
159
- if (msgs.length !== messagesLenRef.current) {
160
- const newMsgs = msgs.length > messagesLenRef.current ? msgs.slice(messagesLenRef.current) : []
191
+ const previous = messagesRef.current
192
+ if (messagesDiffer(msgs, previous)) {
193
+ const newMsgs = msgs.length > previous.length ? msgs.slice(previous.length) : []
161
194
  setMessages(msgs)
162
195
  if (ttsEnabledRef.current && typeof document !== 'undefined' && document.visibilityState === 'visible') {
163
196
  const latestAssistant = [...newMsgs].reverse().find((m) => {
@@ -385,15 +418,19 @@ export function ChatArea() {
385
418
  <h1 className="font-display text-[28px] md:text-[36px] font-800 leading-[1.1] tracking-[-0.04em] mb-3">
386
419
  Hi{currentUser ? ', ' : ' '}<span className="text-accent-bright">{currentUser || 'there'}</span>
387
420
  <br />
388
- <span className="text-text-2">How can I help?</span>
421
+ <span className="text-text-2">
422
+ {currentAgent ? `Start with ${currentAgent.name}` : 'Start the conversation'}
423
+ </span>
389
424
  </h1>
390
425
  <p className="text-[13px] text-text-3 mt-2">
391
- Pick a prompt or type your own below
426
+ {currentAgent
427
+ ? `Ask ${currentAgent.name} anything, hand over work, or start with one of these openers.`
428
+ : 'Pick a prompt or type your own below.'}
392
429
  </p>
393
430
  </div>
394
431
 
395
432
  <div className="relative grid grid-cols-2 md:grid-cols-4 gap-3 max-w-[640px] w-full mb-6">
396
- {PROMPT_SUGGESTIONS.map((prompt, i) => (
433
+ {promptSuggestions.map((prompt, i) => (
397
434
  <button
398
435
  key={prompt.text}
399
436
  onClick={() => handlePrompt(prompt.text)}
@@ -408,7 +445,7 @@ export function ChatArea() {
408
445
  </div>
409
446
  </div>
410
447
  ) : (
411
- <MessageList messages={messages} streaming={streamingForThisSession} connectorFilter={connectorFilter} />
448
+ <MessageList messages={messages} streaming={streamingForThisSession} connectorFilter={connectorFilter} loading={messagesLoading} />
412
449
  )}
413
450
 
414
451
  {voice.active && (
@@ -434,6 +471,12 @@ export function ChatArea() {
434
471
  />
435
472
 
436
473
  <Dropdown open={menuOpen} onClose={() => setMenuOpen(false)}>
474
+ <DropdownItem onClick={() => {
475
+ setMenuOpen(false)
476
+ setDebugOpen(!debugOpen)
477
+ }}>
478
+ {debugOpen ? 'Hide Debug Panel' : 'Show Debug Panel'}
479
+ </DropdownItem>
437
480
  <DropdownItem onClick={() => { setMenuOpen(false); setConfirmClear(true) }}>
438
481
  Clear History
439
482
  </DropdownItem>
@@ -1,5 +1,6 @@
1
1
  'use client'
2
2
 
3
+ import { DEFAULT_HEARTBEAT_INTERVAL_SEC } from '@/lib/heartbeat-defaults'
3
4
  import type { Session } from '@/types'
4
5
  import { api } from '@/lib/api-client'
5
6
  import { useAppStore } from '@/stores/use-app-store'
@@ -66,10 +67,13 @@ export function ChatCard({ session, active, onClick }: Props) {
66
67
  : 'No messages'
67
68
  const providerLabel = PROVIDER_LABELS[session.provider] || session.provider
68
69
  const agent = session.agentId ? agents[session.agentId] : null
70
+ const displayName = session.shortcutForAgentId && agent?.id === session.shortcutForAgentId
71
+ ? agent.name
72
+ : session.name
69
73
  const connector = getSessionConnector(session, connectors)
70
74
  const loopIsOngoing = appSettings.loopMode === 'ongoing'
71
75
  const explicitOptIn = session.heartbeatEnabled === true || agent?.heartbeatEnabled === true
72
- const intervalRaw = session.heartbeatIntervalSec ?? agent?.heartbeatIntervalSec ?? appSettings.heartbeatIntervalSec ?? 120
76
+ const intervalRaw = session.heartbeatIntervalSec ?? agent?.heartbeatIntervalSec ?? appSettings.heartbeatIntervalSec ?? DEFAULT_HEARTBEAT_INTERVAL_SEC
73
77
  const intervalNum = typeof intervalRaw === 'number' ? intervalRaw : Number.parseInt(String(intervalRaw), 10)
74
78
  const intervalEnabled = Number.isFinite(intervalNum) ? intervalNum > 0 : true
75
79
  const heartbeatEnabled =
@@ -109,26 +113,7 @@ export function ChatCard({ session, active, onClick }: Props) {
109
113
  title={`${connector.name} (${connector.platform})`}
110
114
  />
111
115
  )}
112
- <span className="font-display text-[14px] font-600 truncate flex-1 tracking-[-0.01em]">{session.name}</span>
113
- {session.mainLoopState?.status && session.mainLoopState.status !== 'idle' && (
114
- <span className={`shrink-0 flex items-center gap-1 text-[9px] font-600 uppercase tracking-wider px-1.5 py-0.5 rounded-[5px] ${
115
- session.mainLoopState.status === 'progress' ? 'text-blue-400/90 bg-blue-400/[0.08]'
116
- : session.mainLoopState.status === 'blocked' ? 'text-amber-400/90 bg-amber-400/[0.08]'
117
- : 'text-emerald-400/90 bg-emerald-400/[0.08]'
118
- }`}>
119
- <span className={`w-[5px] h-[5px] rounded-full ${
120
- session.mainLoopState.status === 'progress' ? 'bg-blue-400'
121
- : session.mainLoopState.status === 'blocked' ? 'bg-amber-400'
122
- : 'bg-emerald-400'
123
- }`} />
124
- {session.mainLoopState.status}
125
- </span>
126
- )}
127
- {session.sessionType === 'orchestrated' && (
128
- <span className="shrink-0 text-[10px] font-600 uppercase tracking-wider text-amber-400/80 bg-amber-400/[0.08] px-2 py-0.5 rounded-[6px]">
129
- AI
130
- </span>
131
- )}
116
+ <span className="font-display text-[14px] font-600 truncate flex-1 tracking-[-0.01em]">{displayName}</span>
132
117
  {providerLabel && (
133
118
  <span className="shrink-0 text-[10px] font-600 uppercase tracking-wider text-text-3/70 bg-white/[0.03] px-2 py-0.5 rounded-[6px]">
134
119
  {providerLabel}