@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
package/src/proxy.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import type { NextRequest } from 'next/server'
3
+ import { AUTH_COOKIE_NAME } from '@/lib/auth'
3
4
 
4
5
  /* ------------------------------------------------------------------ */
5
6
  /* Rate-limit state — HMR-safe via globalThis */
@@ -44,7 +45,7 @@ function getClientIp(request: NextRequest): string {
44
45
  /* ------------------------------------------------------------------ */
45
46
 
46
47
  /** Access key auth proxy with brute-force rate limiting.
47
- * Checks X-Access-Key header or ?key= param on all /api/ routes except /api/auth.
48
+ * Checks X-Access-Key header or auth cookie on all /api/ routes except /api/auth.
48
49
  * The key is validated against the ACCESS_KEY env var.
49
50
  * After 5 failed attempts from a single IP the client is locked out for 15 minutes.
50
51
  */
@@ -55,11 +56,10 @@ export function proxy(request: NextRequest) {
55
56
  const isConnectorWebhook = request.method === 'POST'
56
57
  && /^\/api\/connectors\/[^/]+\/webhook\/?$/.test(pathname)
57
58
 
58
- // Only protect API routes (not auth, uploads served as static assets, or inbound webhooks)
59
+ // Only protect API routes (not auth or inbound webhooks)
59
60
  if (
60
61
  !pathname.startsWith('/api/')
61
62
  || pathname === '/api/auth'
62
- || pathname.startsWith('/api/uploads/')
63
63
  || isWebhookTrigger
64
64
  || isConnectorWebhook
65
65
  ) {
@@ -88,8 +88,8 @@ export function proxy(request: NextRequest) {
88
88
  }
89
89
 
90
90
  const providedKey =
91
- request.headers.get('x-access-key')
92
- || request.nextUrl.searchParams.get('key')
91
+ request.headers.get('x-access-key')?.trim()
92
+ || request.cookies.get(AUTH_COOKIE_NAME)?.value?.trim()
93
93
  || ''
94
94
 
95
95
  if (providedKey !== accessKey) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { create } from 'zustand'
4
4
  import type { Sessions, Session, NetworkInfo, Directory, ProviderInfo, Credentials, Agent, Schedule, AppView, BoardTask, AppSettings, OrchestratorSecret, ProviderConfig, Skill, Connector, Webhook, McpServerConfig, PluginMeta, Project, FleetFilter, ActivityEntry, AppNotification, ApprovalRequest } from '../types'
5
- import { fetchSessions, fetchDirs, fetchProviders, fetchCredentials } from '../lib/sessions'
5
+ import { fetchChats, fetchDirs, fetchProviders, fetchCredentials } from '../lib/chats'
6
6
  import { fetchAgents } from '../lib/agents'
7
7
  import { fetchSchedules } from '../lib/schedules'
8
8
  import { fetchTasks } from '../lib/tasks'
@@ -43,9 +43,6 @@ interface AppState {
43
43
  settingsOpen: boolean
44
44
  setSettingsOpen: (open: boolean) => void
45
45
 
46
- newSessionOpen: boolean
47
- setNewSessionOpen: (open: boolean) => void
48
-
49
46
  activeView: AppView
50
47
  setActiveView: (view: AppView) => void
51
48
 
@@ -235,7 +232,7 @@ export const useAppStore = create<AppState>((set, get) => ({
235
232
  currentSessionId: null,
236
233
  loadSessions: async () => {
237
234
  try {
238
- const sessions = await fetchSessions()
235
+ const sessions = await fetchChats()
239
236
  set({ sessions })
240
237
  } catch {
241
238
  // ignore
@@ -249,7 +246,7 @@ export const useAppStore = create<AppState>((set, get) => ({
249
246
  },
250
247
  clearSessions: async (ids) => {
251
248
  if (!ids.length) return
252
- await api('DELETE', '/sessions', { ids })
249
+ await api('DELETE', '/chats', { ids })
253
250
  const sessions = { ...get().sessions }
254
251
  for (const id of ids) delete sessions[id]
255
252
  set({ sessions, currentSessionId: ids.includes(get().currentSessionId!) ? null : get().currentSessionId })
@@ -260,7 +257,7 @@ export const useAppStore = create<AppState>((set, get) => ({
260
257
  sessions[id] = { ...sessions[id], pinned: !sessions[id].pinned }
261
258
  set({ sessions })
262
259
  // Persist to server
263
- void api('PUT', `/sessions/${id}`, { pinned: sessions[id].pinned })
260
+ void api('PUT', `/chats/${id}`, { pinned: sessions[id].pinned })
264
261
  }
265
262
  },
266
263
  updateSessionInStore: (session) => {
@@ -268,7 +265,7 @@ export const useAppStore = create<AppState>((set, get) => ({
268
265
  },
269
266
  forkSession: async (sessionId, messageIndex) => {
270
267
  try {
271
- const forked = await api<Session>('POST', `/sessions/${sessionId}/fork`, { messageIndex })
268
+ const forked = await api<Session>('POST', `/chats/${sessionId}/fork`, { messageIndex })
272
269
  if (!forked?.id) return null
273
270
  await get().loadSessions()
274
271
  set({ currentSessionId: forked.id })
@@ -325,9 +322,6 @@ export const useAppStore = create<AppState>((set, get) => ({
325
322
  settingsOpen: false,
326
323
  setSettingsOpen: (open) => set({ settingsOpen: open }),
327
324
 
328
- newSessionOpen: false,
329
- setNewSessionOpen: (open) => set({ newSessionOpen: open }),
330
-
331
325
  activeView: 'home',
332
326
  setActiveView: (view) => set({ activeView: view }),
333
327
 
@@ -3,6 +3,7 @@
3
3
  import { create } from 'zustand'
4
4
  import type { Message, DevServerStatus, SSEEvent, ChatTraceBlock } from '../types'
5
5
  import { streamChat } from '../lib/chat'
6
+ import { mergeCompletedAssistantMessage } from '../lib/chat-streaming-state'
6
7
  import { speak } from '../lib/tts'
7
8
  import { getStoredAccessKey } from '../lib/api-client'
8
9
  import { useAppStore } from './use-app-store'
@@ -306,6 +307,11 @@ export const useChatStore = create<ChatState>((set, get) => ({
306
307
  } else if (event.t === 'tool_call') {
307
308
  const id = `tc-${++toolCallCounter}`
308
309
  set((s) => ({
310
+ ...(s.toolEvents[s.toolEvents.length - 1]?.name === (event.toolName || 'unknown')
311
+ && s.toolEvents[s.toolEvents.length - 1]?.input === (event.toolInput || '')
312
+ && s.toolEvents[s.toolEvents.length - 1]?.status === 'running'
313
+ ? {}
314
+ : {
309
315
  streamPhase: 'tool' as const,
310
316
  streamToolName: event.toolName || 'unknown',
311
317
  toolEvents: [...s.toolEvents, {
@@ -314,6 +320,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
314
320
  input: event.toolInput || '',
315
321
  status: 'running',
316
322
  }],
323
+ }),
317
324
  }))
318
325
  } else if (event.t === 'tool_result') {
319
326
  const soundOn = get().soundEnabled
@@ -322,6 +329,22 @@ export const useChatStore = create<ChatState>((set, get) => ({
322
329
  const idx = events.findLastIndex(
323
330
  (e) => e.name === event.toolName && e.status === 'running',
324
331
  )
332
+ if (idx === -1) {
333
+ const last = events[events.length - 1]
334
+ const output = event.toolOutput || ''
335
+ const isError = /^(Error:|error:|ECONNREFUSED|ETIMEDOUT|timeout|failed)/i.test(output.trim())
336
+ || output.includes('ECONNREFUSED')
337
+ || output.includes('ETIMEDOUT')
338
+ || output.includes('Error:')
339
+ if (
340
+ last
341
+ && last.name === event.toolName
342
+ && last.output === output
343
+ && last.status === (isError ? 'error' : 'done')
344
+ ) {
345
+ return { toolEvents: events }
346
+ }
347
+ }
325
348
  if (idx !== -1) {
326
349
  const output = event.toolOutput || ''
327
350
  const isError = /^(Error:|error:|ECONNREFUSED|ETIMEDOUT|timeout|failed)/i.test(output.trim())
@@ -348,7 +371,13 @@ export const useChatStore = create<ChatState>((set, get) => ({
348
371
  } else if (event.t === 'status') {
349
372
  try {
350
373
  const parsed = JSON.parse(event.text || '{}')
351
- set({ agentStatus: parsed })
374
+ if (
375
+ parsed
376
+ && typeof parsed === 'object'
377
+ && ['goal', 'status', 'summary', 'nextAction'].some((key) => key in parsed)
378
+ ) {
379
+ set({ agentStatus: parsed })
380
+ }
352
381
  } catch {
353
382
  // ignore malformed status
354
383
  }
@@ -377,7 +406,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
377
406
  suggestions: suggestions || undefined,
378
407
  }
379
408
  set((s) => ({
380
- messages: [...s.messages, assistantMsg],
409
+ messages: mergeCompletedAssistantMessage(s.messages, assistantMsg),
381
410
  streaming: false,
382
411
  streamingSessionId: null,
383
412
  streamText: '',
@@ -407,7 +436,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
407
436
  if (!sessionId) return
408
437
  try {
409
438
  const key = getStoredAccessKey()
410
- const res = await fetch(`/api/sessions/${sessionId}/edit-resend`, {
439
+ const res = await fetch(`/api/chats/${sessionId}/edit-resend`, {
411
440
  method: 'POST',
412
441
  headers: {
413
442
  'Content-Type': 'application/json',
@@ -417,7 +446,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
417
446
  })
418
447
  if (!res.ok) return
419
448
  // Reload messages from server (truncated)
420
- const msgsRes = await fetch(`/api/sessions/${sessionId}/messages`, {
449
+ const msgsRes = await fetch(`/api/chats/${sessionId}/messages`, {
421
450
  headers: key ? { 'X-Access-Key': key } : undefined,
422
451
  })
423
452
  if (msgsRes.ok) {
@@ -437,7 +466,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
437
466
  if (!sessionId) return
438
467
  try {
439
468
  const key = getStoredAccessKey()
440
- const res = await fetch(`/api/sessions/${sessionId}/retry`, {
469
+ const res = await fetch(`/api/chats/${sessionId}/retry`, {
441
470
  method: 'POST',
442
471
  headers: key ? { 'X-Access-Key': key } : undefined,
443
472
  })
@@ -445,7 +474,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
445
474
  const { message, imagePath } = await res.json()
446
475
  if (!message) return
447
476
  // Reload messages from server (without the popped ones)
448
- const msgsRes = await fetch(`/api/sessions/${sessionId}/messages`, {
477
+ const msgsRes = await fetch(`/api/chats/${sessionId}/messages`, {
449
478
  headers: key ? { 'X-Access-Key': key } : undefined,
450
479
  })
451
480
  if (msgsRes.ok) {
@@ -546,7 +575,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
546
575
  set((s) => ({ messages: [...s.messages, marker] }))
547
576
  try {
548
577
  const key = getStoredAccessKey()
549
- await fetch(`/api/sessions/${sessionId}/messages`, {
578
+ await fetch(`/api/chats/${sessionId}/messages`, {
550
579
  method: 'POST',
551
580
  headers: { 'Content-Type': 'application/json', ...(key ? { 'X-Access-Key': key } : {}) },
552
581
  body: JSON.stringify({ kind: 'context-clear' }),
@@ -569,7 +598,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
569
598
  const key = getStoredAccessKey()
570
599
  // Find the earliest message's original index (startIndex tracked on initial load)
571
600
  const currentStartIndex = totalMessages - messages.length
572
- const res = await fetch(`/api/sessions/${sessionId}/messages?limit=100&before=${currentStartIndex}`, {
601
+ const res = await fetch(`/api/chats/${sessionId}/messages?limit=100&before=${currentStartIndex}`, {
573
602
  headers: key ? { 'X-Access-Key': key } : undefined,
574
603
  })
575
604
  if (res.ok) {
@@ -593,7 +622,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
593
622
  if (sessionId) {
594
623
  try {
595
624
  const key = getStoredAccessKey()
596
- await fetch(`/api/sessions/${sessionId}/stop`, {
625
+ await fetch(`/api/chats/${sessionId}/stop`, {
597
626
  method: 'POST',
598
627
  headers: key ? { 'X-Access-Key': key } : undefined,
599
628
  })