@swarmclawai/swarmclaw 0.7.1 → 0.7.3

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 (237) hide show
  1. package/README.md +155 -150
  2. package/package.json +1 -1
  3. package/src/app/api/agents/[id]/route.ts +26 -0
  4. package/src/app/api/agents/[id]/thread/route.ts +37 -9
  5. package/src/app/api/agents/route.ts +13 -2
  6. package/src/app/api/auth/route.ts +76 -7
  7. package/src/app/api/chatrooms/[id]/chat/route.ts +7 -2
  8. package/src/app/api/{sessions → chats}/[id]/browser/route.ts +5 -1
  9. package/src/app/api/{sessions → chats}/[id]/chat/route.ts +7 -3
  10. package/src/app/api/{sessions → chats}/[id]/checkpoints/route.ts +1 -1
  11. package/src/app/api/chats/[id]/main-loop/route.ts +13 -0
  12. package/src/app/api/{sessions → chats}/[id]/messages/route.ts +19 -13
  13. package/src/app/api/{sessions → chats}/[id]/restore/route.ts +1 -1
  14. package/src/app/api/{sessions → chats}/[id]/route.ts +22 -52
  15. package/src/app/api/{sessions → chats}/[id]/stop/route.ts +6 -1
  16. package/src/app/api/{sessions → chats}/route.ts +21 -7
  17. package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
  18. package/src/app/api/connectors/doctor/route.ts +13 -0
  19. package/src/app/api/files/open/route.ts +16 -14
  20. package/src/app/api/memory/maintenance/route.ts +11 -1
  21. package/src/app/api/openclaw/agent-files/route.ts +27 -4
  22. package/src/app/api/openclaw/skills/route.ts +11 -3
  23. package/src/app/api/plugins/dependencies/route.ts +24 -0
  24. package/src/app/api/plugins/install/route.ts +15 -92
  25. package/src/app/api/plugins/route.ts +6 -26
  26. package/src/app/api/plugins/settings/route.ts +40 -0
  27. package/src/app/api/plugins/ui/route.ts +1 -0
  28. package/src/app/api/settings/route.ts +49 -7
  29. package/src/app/api/tasks/[id]/route.ts +15 -6
  30. package/src/app/api/tasks/bulk/route.ts +2 -2
  31. package/src/app/api/tasks/route.ts +9 -4
  32. package/src/app/api/usage/route.ts +30 -0
  33. package/src/app/api/webhooks/[id]/route.ts +8 -1
  34. package/src/app/page.tsx +9 -2
  35. package/src/cli/index.js +39 -33
  36. package/src/cli/index.ts +43 -49
  37. package/src/cli/spec.js +29 -27
  38. package/src/components/agents/agent-card.tsx +16 -13
  39. package/src/components/agents/agent-chat-list.tsx +104 -4
  40. package/src/components/agents/agent-list.tsx +54 -22
  41. package/src/components/agents/agent-sheet.tsx +209 -18
  42. package/src/components/agents/cron-job-form.tsx +3 -3
  43. package/src/components/agents/inspector-panel.tsx +110 -50
  44. package/src/components/auth/access-key-gate.tsx +36 -97
  45. package/src/components/auth/setup-wizard.tsx +5 -38
  46. package/src/components/chat/chat-area.tsx +39 -27
  47. package/src/components/{sessions/session-card.tsx → chat/chat-card.tsx} +7 -23
  48. package/src/components/chat/chat-header.tsx +299 -314
  49. package/src/components/{sessions/session-list.tsx → chat/chat-list.tsx} +11 -14
  50. package/src/components/chat/chat-tool-toggles.tsx +26 -17
  51. package/src/components/chat/checkpoint-timeline.tsx +4 -4
  52. package/src/components/chat/message-bubble.tsx +4 -1
  53. package/src/components/chat/message-list.tsx +5 -3
  54. package/src/components/chat/session-debug-panel.tsx +1 -1
  55. package/src/components/chat/tool-request-banner.tsx +3 -3
  56. package/src/components/chatrooms/agent-hover-card.tsx +3 -3
  57. package/src/components/chatrooms/chatroom-tool-request-banner.tsx +2 -2
  58. package/src/components/chatrooms/chatroom-view.tsx +347 -205
  59. package/src/components/connectors/connector-list.tsx +265 -127
  60. package/src/components/connectors/connector-sheet.tsx +218 -1
  61. package/src/components/home/home-view.tsx +129 -5
  62. package/src/components/layout/app-layout.tsx +392 -182
  63. package/src/components/layout/mobile-header.tsx +26 -8
  64. package/src/components/plugins/plugin-list.tsx +487 -254
  65. package/src/components/plugins/plugin-sheet.tsx +236 -13
  66. package/src/components/projects/project-detail.tsx +183 -0
  67. package/src/components/settings/gateway-connection-panel.tsx +1 -1
  68. package/src/components/shared/agent-picker-list.tsx +2 -2
  69. package/src/components/shared/command-palette.tsx +111 -25
  70. package/src/components/shared/settings/plugin-manager.tsx +20 -4
  71. package/src/components/shared/settings/section-capability-policy.tsx +105 -0
  72. package/src/components/shared/settings/section-heartbeat.tsx +78 -1
  73. package/src/components/shared/settings/section-orchestrator.tsx +3 -3
  74. package/src/components/shared/settings/section-providers.tsx +1 -1
  75. package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
  76. package/src/components/shared/settings/section-secrets.tsx +6 -6
  77. package/src/components/shared/settings/section-user-preferences.tsx +1 -1
  78. package/src/components/shared/settings/section-voice.tsx +5 -1
  79. package/src/components/shared/settings/section-web-search.tsx +10 -2
  80. package/src/components/shared/settings/settings-page.tsx +244 -56
  81. package/src/components/tasks/approvals-panel.tsx +205 -18
  82. package/src/components/tasks/task-board.tsx +242 -46
  83. package/src/components/usage/metrics-dashboard.tsx +147 -1
  84. package/src/components/wallets/wallet-panel.tsx +17 -5
  85. package/src/components/webhooks/webhook-sheet.tsx +8 -8
  86. package/src/lib/auth.ts +17 -0
  87. package/src/lib/chat-streaming-state.test.ts +108 -0
  88. package/src/lib/chat-streaming-state.ts +108 -0
  89. package/src/lib/chat.ts +1 -1
  90. package/src/lib/{sessions.ts → chats.ts} +28 -18
  91. package/src/lib/openclaw-agent-id.test.ts +14 -0
  92. package/src/lib/openclaw-agent-id.ts +31 -0
  93. package/src/lib/providers/claude-cli.ts +1 -1
  94. package/src/lib/server/agent-assignment.test.ts +112 -0
  95. package/src/lib/server/agent-assignment.ts +169 -0
  96. package/src/lib/server/approval-connector-notify.test.ts +253 -0
  97. package/src/lib/server/approvals-auto-approve.test.ts +205 -0
  98. package/src/lib/server/approvals.ts +483 -75
  99. package/src/lib/server/autonomy-runtime.test.ts +341 -0
  100. package/src/lib/server/browser-state.test.ts +118 -0
  101. package/src/lib/server/browser-state.ts +123 -0
  102. package/src/lib/server/build-llm.test.ts +36 -0
  103. package/src/lib/server/build-llm.ts +11 -4
  104. package/src/lib/server/builtin-plugins.ts +34 -0
  105. package/src/lib/server/capability-router.ts +10 -8
  106. package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
  107. package/src/lib/server/chat-execution-tool-events.test.ts +134 -0
  108. package/src/lib/server/chat-execution.ts +285 -165
  109. package/src/lib/server/chatroom-health.test.ts +26 -0
  110. package/src/lib/server/chatroom-health.ts +2 -3
  111. package/src/lib/server/chatroom-helpers.test.ts +67 -2
  112. package/src/lib/server/chatroom-helpers.ts +48 -8
  113. package/src/lib/server/connectors/discord.ts +175 -11
  114. package/src/lib/server/connectors/doctor.test.ts +80 -0
  115. package/src/lib/server/connectors/doctor.ts +116 -0
  116. package/src/lib/server/connectors/manager.ts +948 -112
  117. package/src/lib/server/connectors/policy.test.ts +222 -0
  118. package/src/lib/server/connectors/policy.ts +452 -0
  119. package/src/lib/server/connectors/slack.ts +188 -9
  120. package/src/lib/server/connectors/telegram.ts +65 -15
  121. package/src/lib/server/connectors/thread-context.test.ts +44 -0
  122. package/src/lib/server/connectors/thread-context.ts +72 -0
  123. package/src/lib/server/connectors/types.ts +41 -11
  124. package/src/lib/server/cost.ts +34 -1
  125. package/src/lib/server/daemon-state.ts +61 -3
  126. package/src/lib/server/data-dir.ts +13 -0
  127. package/src/lib/server/delegation-jobs.test.ts +140 -0
  128. package/src/lib/server/delegation-jobs.ts +248 -0
  129. package/src/lib/server/document-utils.test.ts +47 -0
  130. package/src/lib/server/document-utils.ts +397 -0
  131. package/src/lib/server/heartbeat-service.ts +14 -40
  132. package/src/lib/server/heartbeat-source.test.ts +22 -0
  133. package/src/lib/server/heartbeat-source.ts +7 -0
  134. package/src/lib/server/identity-continuity.test.ts +77 -0
  135. package/src/lib/server/identity-continuity.ts +127 -0
  136. package/src/lib/server/mailbox-utils.ts +347 -0
  137. package/src/lib/server/main-agent-loop.ts +28 -1103
  138. package/src/lib/server/memory-db.ts +4 -6
  139. package/src/lib/server/memory-tiers.ts +40 -0
  140. package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
  141. package/src/lib/server/openclaw-agent-resolver.ts +128 -0
  142. package/src/lib/server/openclaw-exec-config.ts +5 -6
  143. package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
  144. package/src/lib/server/openclaw-skills-normalize.ts +136 -0
  145. package/src/lib/server/openclaw-sync.ts +3 -2
  146. package/src/lib/server/orchestrator-lg.ts +20 -9
  147. package/src/lib/server/orchestrator.ts +7 -7
  148. package/src/lib/server/playwright-proxy.mjs +27 -3
  149. package/src/lib/server/plugins.test.ts +207 -0
  150. package/src/lib/server/plugins.ts +927 -66
  151. package/src/lib/server/provider-health.ts +38 -6
  152. package/src/lib/server/queue.ts +13 -28
  153. package/src/lib/server/scheduler.ts +2 -0
  154. package/src/lib/server/session-archive-memory.test.ts +85 -0
  155. package/src/lib/server/session-archive-memory.ts +230 -0
  156. package/src/lib/server/session-mailbox.ts +8 -18
  157. package/src/lib/server/session-reset-policy.test.ts +99 -0
  158. package/src/lib/server/session-reset-policy.ts +311 -0
  159. package/src/lib/server/session-run-manager.ts +33 -82
  160. package/src/lib/server/session-tools/autonomy-tools.test.ts +105 -0
  161. package/src/lib/server/session-tools/calendar.ts +366 -0
  162. package/src/lib/server/session-tools/canvas.ts +1 -1
  163. package/src/lib/server/session-tools/chatroom.ts +4 -2
  164. package/src/lib/server/session-tools/connector.ts +114 -10
  165. package/src/lib/server/session-tools/context.ts +21 -5
  166. package/src/lib/server/session-tools/crawl.ts +447 -0
  167. package/src/lib/server/session-tools/crud.ts +74 -28
  168. package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
  169. package/src/lib/server/session-tools/delegate.ts +497 -24
  170. package/src/lib/server/session-tools/discovery.ts +24 -6
  171. package/src/lib/server/session-tools/document.ts +283 -0
  172. package/src/lib/server/session-tools/edit_file.ts +4 -2
  173. package/src/lib/server/session-tools/email.ts +320 -0
  174. package/src/lib/server/session-tools/extract.ts +137 -0
  175. package/src/lib/server/session-tools/file-normalize.test.ts +93 -0
  176. package/src/lib/server/session-tools/file-send.test.ts +84 -1
  177. package/src/lib/server/session-tools/file.ts +241 -25
  178. package/src/lib/server/session-tools/git.ts +1 -1
  179. package/src/lib/server/session-tools/http.ts +1 -1
  180. package/src/lib/server/session-tools/human-loop.ts +227 -0
  181. package/src/lib/server/session-tools/image-gen.ts +380 -0
  182. package/src/lib/server/session-tools/index.ts +130 -50
  183. package/src/lib/server/session-tools/mailbox.ts +276 -0
  184. package/src/lib/server/session-tools/memory.ts +172 -3
  185. package/src/lib/server/session-tools/monitor.ts +151 -8
  186. package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
  187. package/src/lib/server/session-tools/openclaw-nodes.ts +1 -1
  188. package/src/lib/server/session-tools/openclaw-workspace.ts +1 -1
  189. package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
  190. package/src/lib/server/session-tools/platform.ts +148 -7
  191. package/src/lib/server/session-tools/plugin-creator.ts +89 -26
  192. package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
  193. package/src/lib/server/session-tools/replicate.ts +301 -0
  194. package/src/lib/server/session-tools/sample-ui.ts +1 -1
  195. package/src/lib/server/session-tools/sandbox.ts +4 -2
  196. package/src/lib/server/session-tools/schedule.ts +24 -12
  197. package/src/lib/server/session-tools/session-info.ts +43 -7
  198. package/src/lib/server/session-tools/session-tools-wiring.test.ts +31 -17
  199. package/src/lib/server/session-tools/shell.ts +5 -2
  200. package/src/lib/server/session-tools/subagent.ts +194 -28
  201. package/src/lib/server/session-tools/table.ts +587 -0
  202. package/src/lib/server/session-tools/wallet.ts +42 -12
  203. package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
  204. package/src/lib/server/session-tools/web.ts +926 -91
  205. package/src/lib/server/storage.ts +255 -16
  206. package/src/lib/server/stream-agent-chat.ts +116 -268
  207. package/src/lib/server/structured-extract.test.ts +72 -0
  208. package/src/lib/server/structured-extract.ts +373 -0
  209. package/src/lib/server/task-mention.test.ts +16 -2
  210. package/src/lib/server/task-mention.ts +61 -10
  211. package/src/lib/server/tool-aliases.ts +66 -18
  212. package/src/lib/server/tool-capability-policy.test.ts +9 -9
  213. package/src/lib/server/tool-capability-policy.ts +38 -27
  214. package/src/lib/server/tool-retry.ts +2 -0
  215. package/src/lib/server/watch-jobs.test.ts +173 -0
  216. package/src/lib/server/watch-jobs.ts +532 -0
  217. package/src/lib/server/ws-hub.ts +5 -3
  218. package/src/lib/tool-definitions.ts +4 -0
  219. package/src/lib/validation/schemas.test.ts +26 -0
  220. package/src/lib/validation/schemas.ts +10 -1
  221. package/src/lib/ws-client.ts +14 -12
  222. package/src/proxy.ts +5 -5
  223. package/src/stores/use-app-store.ts +5 -11
  224. package/src/stores/use-chat-store.ts +38 -9
  225. package/src/types/index.ts +352 -47
  226. package/src/app/api/sessions/[id]/main-loop/route.ts +0 -94
  227. package/src/components/sessions/new-session-sheet.tsx +0 -253
  228. package/src/lib/server/main-session.ts +0 -24
  229. package/src/lib/server/session-run-manager.test.ts +0 -23
  230. /package/src/app/api/{sessions → chats}/[id]/clear/route.ts +0 -0
  231. /package/src/app/api/{sessions → chats}/[id]/deploy/route.ts +0 -0
  232. /package/src/app/api/{sessions → chats}/[id]/devserver/route.ts +0 -0
  233. /package/src/app/api/{sessions → chats}/[id]/edit-resend/route.ts +0 -0
  234. /package/src/app/api/{sessions → chats}/[id]/fork/route.ts +0 -0
  235. /package/src/app/api/{sessions → chats}/[id]/mailbox/route.ts +0 -0
  236. /package/src/app/api/{sessions → chats}/[id]/retry/route.ts +0 -0
  237. /package/src/app/api/{sessions → chats}/heartbeat/route.ts +0 -0
@@ -3,8 +3,8 @@
3
3
  import { useEffect, useMemo, useState } from 'react'
4
4
  import { useAppStore } from '@/stores/use-app-store'
5
5
  import { useChatStore } from '@/stores/use-chat-store'
6
- import { SessionCard } from './session-card'
7
- import { fetchMessages } from '@/lib/sessions'
6
+ import { ChatCard } from './chat-card'
7
+ import { fetchMessages } from '@/lib/chats'
8
8
  import { toast } from 'sonner'
9
9
  import { Skeleton } from '@/components/shared/skeleton'
10
10
  import { EmptyState } from '@/components/shared/empty-state'
@@ -14,17 +14,17 @@ interface Props {
14
14
  onSelect?: () => void
15
15
  }
16
16
 
17
- type SessionFilter = 'all' | 'active' | 'human' | 'orchestrated'
17
+ type SessionFilter = 'all' | 'active'
18
18
  type SortMode = 'lastActive' | 'name' | 'messages'
19
19
 
20
- export function SessionList({ inSidebar, onSelect }: Props) {
20
+ export function ChatList({ inSidebar, onSelect }: Props) {
21
21
  const sessions = useAppStore((s) => s.sessions)
22
22
  const currentUser = useAppStore((s) => s.currentUser)
23
23
  const currentSessionId = useAppStore((s) => s.currentSessionId)
24
24
  const setCurrentSession = useAppStore((s) => s.setCurrentSession)
25
25
  const loadSessions = useAppStore((s) => s.loadSessions)
26
26
  const loadConnectors = useAppStore((s) => s.loadConnectors)
27
- const setNewSessionOpen = useAppStore((s) => s.setNewSessionOpen)
27
+ const setAgentSheetOpen = useAppStore((s) => s.setAgentSheetOpen)
28
28
  const clearSessions = useAppStore((s) => s.clearSessions)
29
29
  const togglePinSession = useAppStore((s) => s.togglePinSession)
30
30
  const markChatRead = useAppStore((s) => s.markChatRead)
@@ -44,7 +44,6 @@ export function SessionList({ inSidebar, onSelect }: Props) {
44
44
 
45
45
  const allUserSessions = useMemo(() => {
46
46
  return Object.values(sessions).filter((s) => {
47
- if (s.name === '__main__') return false
48
47
  const owner = (s.user || '').toLowerCase()
49
48
  const isPlatformOwned = owner === 'system' || owner === 'connector' || owner === 'swarm'
50
49
  const isCurrentUserOwned = !!currentUser && owner === currentUser.toLowerCase()
@@ -59,8 +58,6 @@ export function SessionList({ inSidebar, onSelect }: Props) {
59
58
  .filter((s) => {
60
59
  if (search && !s.name.toLowerCase().includes(search.toLowerCase())) return false
61
60
  if (typeFilter === 'active' && !s.active) return false
62
- if (typeFilter === 'human' && s.sessionType === 'orchestrated') return false
63
- if (typeFilter === 'orchestrated' && s.sessionType !== 'orchestrated') return false
64
61
  return true
65
62
  })
66
63
  .sort((a, b) => {
@@ -116,8 +113,8 @@ export function SessionList({ inSidebar, onSelect }: Props) {
116
113
  </svg>
117
114
  }
118
115
  title="No chats yet"
119
- subtitle="Create one to start chatting"
120
- action={!inSidebar ? { label: '+ New Chat', onClick: () => setNewSessionOpen(true) } : undefined}
116
+ subtitle="Create an agent to open its persistent thread"
117
+ action={!inSidebar ? { label: '+ New Agent', onClick: () => setAgentSheetOpen(true) } : undefined}
121
118
  />
122
119
  )
123
120
  }
@@ -126,7 +123,7 @@ export function SessionList({ inSidebar, onSelect }: Props) {
126
123
  <div className="flex-1 flex flex-col overflow-y-auto">
127
124
  {/* Filter tabs — always visible when sessions exist */}
128
125
  <div className="flex items-center gap-1 px-4 pt-2 pb-1 shrink-0">
129
- {(['all', 'active', 'human', 'orchestrated'] as SessionFilter[]).map((f) => (
126
+ {(['all', 'active'] as SessionFilter[]).map((f) => (
130
127
  <button
131
128
  key={f}
132
129
  onClick={() => setTypeFilter(f)}
@@ -134,7 +131,7 @@ export function SessionList({ inSidebar, onSelect }: Props) {
134
131
  ${typeFilter === f ? 'bg-accent-soft text-accent-bright' : 'bg-transparent text-text-3 hover:text-text-2'}`}
135
132
  style={{ fontFamily: 'inherit' }}
136
133
  >
137
- {f === 'all' ? 'All' : f === 'active' ? 'Active' : f === 'human' ? 'Human' : 'AI'}
134
+ {f === 'all' ? 'All' : 'Active'}
138
135
  </button>
139
136
  ))}
140
137
  {filtered.length > 0 && (
@@ -186,7 +183,7 @@ export function SessionList({ inSidebar, onSelect }: Props) {
186
183
  <div className="flex flex-col gap-1 px-2 pb-4">
187
184
  {filtered.map((s) => (
188
185
  <div key={s.id} className="group/pin relative">
189
- <SessionCard
186
+ <ChatCard
190
187
  session={s}
191
188
  active={s.id === currentSessionId}
192
189
  onClick={() => handleSelect(s.id)}
@@ -210,7 +207,7 @@ export function SessionList({ inSidebar, onSelect }: Props) {
210
207
  ) : (
211
208
  <div className="flex-1 flex flex-col items-center justify-center gap-3 text-text-3 p-8 text-center">
212
209
  <p className="text-[13px] text-text-3/50">
213
- No {typeFilter === 'orchestrated' ? 'AI' : typeFilter === 'active' ? 'active' : typeFilter} chats{search ? ` matching "${search}"` : ''}
210
+ No {typeFilter === 'active' ? 'active' : typeFilter} chats{search ? ` matching "${search}"` : ''}
214
211
  </p>
215
212
  </div>
216
213
  )}
@@ -3,9 +3,10 @@
3
3
  import { useState, useRef, useEffect } from 'react'
4
4
  import { useAppStore } from '@/stores/use-app-store'
5
5
  import { api } from '@/lib/api-client'
6
- import { AVAILABLE_TOOLS, PLATFORM_TOOLS, TOOL_LABELS } from '@/lib/tool-definitions'
6
+ import { AVAILABLE_TOOLS, PLATFORM_TOOLS } from '@/lib/tool-definitions'
7
7
  import type { ToolDefinition } from '@/lib/tool-definitions'
8
8
  import type { Session } from '@/types'
9
+ import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/tooltip'
9
10
 
10
11
  const TOOL_GROUPS: { label: string; tools: ToolDefinition[] }[] = [
11
12
  { label: 'Plugins', tools: AVAILABLE_TOOLS },
@@ -26,7 +27,7 @@ export function ChatToolToggles({ session }: Props) {
26
27
  const skills = useAppStore((s) => s.skills)
27
28
 
28
29
  const agent = session.agentId ? agents[session.agentId] : null
29
- const sessionTools: string[] = session.tools || []
30
+ const sessionTools: string[] = session.plugins || []
30
31
 
31
32
  // Agent's skill IDs
32
33
  const agentSkillIds: string[] = agent?.skillIds || []
@@ -44,7 +45,7 @@ export function ChatToolToggles({ session }: Props) {
44
45
  const updated = sessionTools.includes(toolId)
45
46
  ? sessionTools.filter((t) => t !== toolId)
46
47
  : [...sessionTools, toolId]
47
- await api('PUT', `/sessions/${session.id}`, { tools: updated })
48
+ await api('PUT', `/chats/${session.id}`, { plugins: updated })
48
49
  loadSessions()
49
50
  }
50
51
 
@@ -69,26 +70,33 @@ export function ChatToolToggles({ session }: Props) {
69
70
  {open && (
70
71
  <div className="absolute top-full left-0 mt-1.5 w-[260px] max-h-[420px] overflow-y-auto rounded-[12px] border border-white/[0.08] shadow-xl z-[120] overflow-hidden"
71
72
  style={{ animation: 'fade-in 0.15s ease', backgroundColor: '#171a2b' }}>
72
-
73
+ <TooltipProvider delayDuration={300}>
73
74
  {TOOL_GROUPS.map((group, gi) => (
74
75
  <div key={group.label} className={`px-3 pb-1 ${gi === 0 ? 'pt-3' : 'pt-1 border-t border-white/[0.04]'}`}>
75
76
  <p className="text-[10px] font-600 text-text-3/60 uppercase tracking-wider mb-2">{group.label}</p>
76
77
  {group.tools.map((tool) => {
77
78
  const enabled = sessionTools.includes(tool.id)
78
79
  return (
79
- <label key={tool.id} className="flex items-center gap-2.5 py-1.5 cursor-pointer" title={tool.description}>
80
- <div
81
- onClick={() => toggleTool(tool.id)}
82
- className={`w-8 h-[18px] rounded-full transition-all duration-200 relative cursor-pointer shrink-0
83
- ${enabled ? 'bg-accent-bright' : 'bg-white/[0.12]'}`}
84
- >
85
- <div className={`absolute top-[2px] w-[14px] h-[14px] rounded-full bg-white transition-all duration-200
86
- ${enabled ? 'left-[16px]' : 'left-[2px]'}`} />
87
- </div>
88
- <span className={`text-[12px] ${enabled ? 'text-text-2' : 'text-text-3/70'}`}>
89
- {tool.label}
90
- </span>
91
- </label>
80
+ <Tooltip key={tool.id}>
81
+ <TooltipTrigger asChild>
82
+ <label className="flex items-center gap-2.5 py-1.5 cursor-pointer">
83
+ <div
84
+ onClick={() => toggleTool(tool.id)}
85
+ className={`w-8 h-[18px] rounded-full transition-all duration-200 relative cursor-pointer shrink-0
86
+ ${enabled ? 'bg-accent-bright' : 'bg-white/[0.12]'}`}
87
+ >
88
+ <div className={`absolute top-[2px] w-[14px] h-[14px] rounded-full bg-white transition-all duration-200
89
+ ${enabled ? 'left-[16px]' : 'left-[2px]'}`} />
90
+ </div>
91
+ <span className={`text-[12px] ${enabled ? 'text-text-2' : 'text-text-3/70'}`}>
92
+ {tool.label}
93
+ </span>
94
+ </label>
95
+ </TooltipTrigger>
96
+ <TooltipContent side="right" sideOffset={8} className="max-w-[200px] bg-[#1e2140] text-text-2 border border-white/[0.08] text-[11px] leading-snug px-2.5 py-1.5">
97
+ {tool.description}
98
+ </TooltipContent>
99
+ </Tooltip>
92
100
  )
93
101
  })}
94
102
  </div>
@@ -110,6 +118,7 @@ export function ChatToolToggles({ session }: Props) {
110
118
  </div>
111
119
  )}
112
120
 
121
+ </TooltipProvider>
113
122
  <div className="px-3 py-2 border-t border-white/[0.04] bg-white/[0.02]">
114
123
  <p className="text-[10px] text-text-3/70">Changes apply to the next message</p>
115
124
  </div>
@@ -26,7 +26,7 @@ export function CheckpointTimeline({ sessionId }: Props) {
26
26
  const load = async () => {
27
27
  setLoading(true)
28
28
  try {
29
- const data = await api<Checkpoint[]>('GET', `/sessions/${sessionId}/checkpoints`)
29
+ const data = await api<Checkpoint[]>('GET', `/chats/${sessionId}/checkpoints`)
30
30
  setCheckpoints(data)
31
31
  } catch (err) {
32
32
  console.error('Failed to load checkpoints', err)
@@ -45,7 +45,7 @@ export function CheckpointTimeline({ sessionId }: Props) {
45
45
 
46
46
  setRestoringId(checkpoint.checkpointId)
47
47
  try {
48
- await api('POST', `/sessions/${sessionId}/restore`, {
48
+ await api('POST', `/chats/${sessionId}/restore`, {
49
49
  checkpointId: checkpoint.checkpointId,
50
50
  timestamp: checkpoint.createdAt
51
51
  })
@@ -67,8 +67,8 @@ export function CheckpointTimeline({ sessionId }: Props) {
67
67
  if (checkpoints.length === 0) {
68
68
  return (
69
69
  <div className="p-8 text-center">
70
- <p className="text-text-3 text-[13px]">No checkpoints found for this session.</p>
71
- <p className="text-[11px] text-text-3/50 mt-1">Only LangGraph-orchestrated sessions support time travel.</p>
70
+ <p className="text-text-3 text-[13px]">No checkpoints found for this chat.</p>
71
+ <p className="text-[11px] text-text-3/50 mt-1">Checkpoint restore is only available when a backend created checkpoints for this chat.</p>
72
72
  </div>
73
73
  )
74
74
  }
@@ -243,7 +243,10 @@ export const MessageBubble = memo(function MessageBubble({ message, assistantNam
243
243
  const delegationSource = !isUser && message.kind === 'system' ? parseDelegationSource(message.text || '') : null
244
244
  // Detect task completion system messages (delegated or direct)
245
245
  const taskCompletion = !isUser && message.kind === 'system' ? parseTaskCompletion(message.text || '') : null
246
- const displayText = delegationSource ? delegationSource.rest : message.text
246
+ const rawDisplayText = delegationSource ? delegationSource.rest : message.text
247
+ const displayText = rawDisplayText
248
+ ? rawDisplayText.split('\n').filter((l) => !/\[(MAIN_LOOP_META|MAIN_LOOP_PLAN|MAIN_LOOP_REVIEW|AGENT_HEARTBEAT_META)\]/.test(l)).join('\n').trim()
249
+ : ''
247
250
 
248
251
  const handleCopy = useCallback(() => {
249
252
  void copyTextToClipboard(message.text).then((copiedText) => {
@@ -5,6 +5,7 @@ import type { Message } from '@/types'
5
5
  import { useChatStore } from '@/stores/use-chat-store'
6
6
  import { useAppStore } from '@/stores/use-app-store'
7
7
  import { api } from '@/lib/api-client'
8
+ import { shouldHidePersistedStreamingAssistantMessage } from '@/lib/chat-streaming-state'
8
9
  import { AgentAvatar } from '@/components/agents/agent-avatar'
9
10
  import { MessageBubble } from './message-bubble'
10
11
  import { StreamingBubble } from './streaming-bubble'
@@ -129,7 +130,7 @@ export function MessageList({ messages, streaming, connectorFilter = null }: Pro
129
130
  if (!msg) return
130
131
  const next = !msg.bookmarked
131
132
  try {
132
- await api('PUT', `/sessions/${sessionId}/messages`, { messageIndex: index, bookmarked: next })
133
+ await api('PUT', `/chats/${sessionId}/messages`, { messageIndex: index, bookmarked: next })
133
134
  const updated = [...messages]
134
135
  updated[index] = { ...updated[index], bookmarked: next }
135
136
  setMessages(updated)
@@ -164,6 +165,7 @@ export function MessageList({ messages, streaming, connectorFilter = null }: Pro
164
165
 
165
166
  const displayedMessages: Message[] = []
166
167
  for (const msg of messages) {
168
+ if (shouldHidePersistedStreamingAssistantMessage(msg, { localStreaming: streaming, displayText })) continue
167
169
  const isHeartbeat = isHeartbeatMessage(msg)
168
170
 
169
171
  // Visibility filtering based on settings
@@ -469,7 +471,7 @@ export function MessageList({ messages, streaming, connectorFilter = null }: Pro
469
471
  type="button"
470
472
  onClick={async () => {
471
473
  try {
472
- await api('DELETE', `/sessions/${sessionId}/messages`, { messageIndex: originalIndex })
474
+ await api('DELETE', `/chats/${sessionId}/messages`, { messageIndex: originalIndex })
473
475
  setMessages(messages.filter((_: Message, idx: number) => idx !== originalIndex))
474
476
  } catch { /* best-effort */ }
475
477
  }}
@@ -514,7 +516,7 @@ export function MessageList({ messages, streaming, connectorFilter = null }: Pro
514
516
 
515
517
  return (
516
518
  <div
517
- key={`${sessionId}-${msg.time}-${i}`}
519
+ key={`${sessionId}-${msg.role}-${originalIndex >= 0 ? originalIndex : i}`}
518
520
  data-message-index={i}
519
521
  style={{
520
522
  animation: `${msg.role === 'user' ? 'msg-in-right' : 'msg-in-left'} 0.4s var(--ease-spring) both`,
@@ -209,7 +209,7 @@ export function SessionDebugPanel({ messages, open, onClose }: Props) {
209
209
  {currentSessionId ? (
210
210
  <CheckpointTimeline sessionId={currentSessionId} />
211
211
  ) : (
212
- <div className="p-12 text-center text-text-3">No active session</div>
212
+ <div className="p-12 text-center text-text-3">No active chat</div>
213
213
  )}
214
214
  </div>
215
215
  )}
@@ -62,13 +62,13 @@ export function ToolRequestBanner({ text, toolOutputs = [] }: Props) {
62
62
 
63
63
  const handleGrant = async (toolId: string) => {
64
64
  if (!sid || !session) return
65
- const currentTools: string[] = session.tools || []
65
+ const currentTools: string[] = session.plugins || []
66
66
  if (currentTools.includes(toolId)) {
67
67
  setGranted((prev) => new Set(prev).add(toolId))
68
68
  return
69
69
  }
70
70
  const updated = [...currentTools, toolId]
71
- await api('PUT', `/sessions/${sid}`, { tools: updated })
71
+ await api('PUT', `/chats/${sid}`, { plugins: updated })
72
72
  await loadSessions()
73
73
  const newGranted = new Set(granted).add(toolId)
74
74
  setGranted(newGranted)
@@ -108,7 +108,7 @@ export function ToolRequestBanner({ text, toolOutputs = [] }: Props) {
108
108
  return (
109
109
  <div className="max-w-[85%] md:max-w-[72%] flex flex-col gap-2 mt-2">
110
110
  {pluginRequests.map(({ pluginId, reason }) => {
111
- const isGranted = granted.has(pluginId) || (session?.tools || []).includes(pluginId)
111
+ const isGranted = granted.has(pluginId) || (session?.plugins || []).includes(pluginId)
112
112
  const isDenied = denied.has(pluginId)
113
113
  const label = TOOL_LABELS[pluginId] || pluginId
114
114
  return (
@@ -19,7 +19,7 @@ const ALL_TOOL_IDS = [...AVAILABLE_TOOLS, ...PLATFORM_TOOLS].map((t) => t.id)
19
19
  export function AgentHoverCard({ agent, children, status }: Props) {
20
20
  const [showAll, setShowAll] = useState(false)
21
21
  const [busy, setBusy] = useState(false)
22
- const tools = agent.tools ?? []
22
+ const tools = agent.plugins ?? []
23
23
 
24
24
  const displayTools = showAll ? ALL_TOOL_IDS : tools
25
25
 
@@ -27,11 +27,11 @@ export function AgentHoverCard({ agent, children, status }: Props) {
27
27
  if (busy) return
28
28
  setBusy(true)
29
29
  try {
30
- const current = agent.tools || []
30
+ const current = agent.plugins || []
31
31
  const updated = current.includes(toolId)
32
32
  ? current.filter((t) => t !== toolId)
33
33
  : [...current, toolId]
34
- await api('PUT', `/agents/${agent.id}`, { tools: updated })
34
+ await api('PUT', `/agents/${agent.id}`, { plugins: updated })
35
35
  useAppStore.getState().loadAgents()
36
36
  } finally {
37
37
  setBusy(false)
@@ -44,7 +44,7 @@ export function ChatroomToolRequestBanner({ agentId, agentName, text, toolOutput
44
44
  if (toolRequests.length === 0) return null
45
45
 
46
46
  const agent = agents[agentId]
47
- const agentTools: string[] = agent?.tools || []
47
+ const agentTools: string[] = agent?.plugins || []
48
48
 
49
49
  const handleGrant = async (toolId: string) => {
50
50
  if (agentTools.includes(toolId)) {
@@ -52,7 +52,7 @@ export function ChatroomToolRequestBanner({ agentId, agentName, text, toolOutput
52
52
  return
53
53
  }
54
54
  const updated = [...agentTools, toolId]
55
- await api('PUT', `/agents/${agentId}`, { tools: updated })
55
+ await api('PUT', `/agents/${agentId}`, { plugins: updated })
56
56
  await loadAgents()
57
57
  const newGranted = new Set(granted).add(toolId)
58
58
  setGranted(newGranted)