@swarmclawai/swarmclaw 1.1.6 → 1.1.7

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 (88) hide show
  1. package/README.md +28 -12
  2. package/package.json +1 -1
  3. package/src/app/activity/page.tsx +9 -1
  4. package/src/app/api/chats/[id]/route.ts +2 -0
  5. package/src/app/api/delegation-jobs/route.ts +19 -0
  6. package/src/app/api/files/open/route.ts +5 -5
  7. package/src/app/api/files/serve/route.ts +9 -6
  8. package/src/app/globals.css +14 -0
  9. package/src/app/missions/page.tsx +3 -2
  10. package/src/cli/index.js +3 -3
  11. package/src/cli/spec.js +3 -3
  12. package/src/components/agents/agent-chat-list.tsx +26 -11
  13. package/src/components/agents/agent-sheet.tsx +267 -161
  14. package/src/components/agents/inspector-panel.tsx +1116 -303
  15. package/src/components/chat/chat-area.tsx +27 -3
  16. package/src/components/chat/chat-header.tsx +23 -672
  17. package/src/components/chat/file-path-chip.tsx +3 -3
  18. package/src/components/chat/message-bubble.tsx +59 -15
  19. package/src/components/chat/message-list.tsx +15 -15
  20. package/src/components/chat/thinking-indicator.tsx +28 -20
  21. package/src/components/chat/tool-events-section.tsx +240 -110
  22. package/src/components/input/chat-input.tsx +13 -11
  23. package/src/components/knowledge/knowledge-list.tsx +5 -0
  24. package/src/components/org-chart/mini-chat-bubble.tsx +19 -12
  25. package/src/components/org-chart/org-chart-context-menu.tsx +11 -8
  26. package/src/components/org-chart/org-chart-detail-panel.tsx +37 -0
  27. package/src/components/org-chart/org-chart-edge-popover.tsx +145 -0
  28. package/src/components/org-chart/org-chart-edge.tsx +140 -19
  29. package/src/components/org-chart/org-chart-node.tsx +27 -5
  30. package/src/components/org-chart/org-chart-sidebar.tsx +3 -2
  31. package/src/components/org-chart/org-chart-view.tsx +61 -4
  32. package/src/components/projects/assign-agent-picker.tsx +67 -0
  33. package/src/components/projects/project-detail-header.tsx +117 -0
  34. package/src/components/projects/project-detail.tsx +32 -923
  35. package/src/components/projects/project-list.tsx +8 -12
  36. package/src/components/projects/project-utils.ts +24 -0
  37. package/src/components/projects/tabs/activity-tab.tsx +93 -0
  38. package/src/components/projects/tabs/operations-tab.tsx +281 -0
  39. package/src/components/projects/tabs/overview-tab.tsx +198 -0
  40. package/src/components/projects/tabs/work-tab.tsx +246 -0
  41. package/src/components/schedules/schedule-console.tsx +7 -1
  42. package/src/components/tasks/task-sheet.tsx +3 -0
  43. package/src/hooks/use-delegation-edge-state.ts +126 -0
  44. package/src/lib/credential-registry.ts +369 -0
  45. package/src/lib/provider-sets.ts +3 -0
  46. package/src/lib/server/agents/delegation-jobs-advanced.test.ts +1 -1
  47. package/src/lib/server/agents/delegation-jobs.test.ts +1 -1
  48. package/src/lib/server/agents/delegation-jobs.ts +11 -3
  49. package/src/lib/server/agents/main-agent-loop.ts +28 -4
  50. package/src/lib/server/autonomy/supervisor-reflection.ts +7 -1
  51. package/src/lib/server/build-llm.ts +2 -1
  52. package/src/lib/server/chat-execution/chat-execution-utils.test.ts +14 -3
  53. package/src/lib/server/chat-execution/chat-execution-utils.ts +3 -2
  54. package/src/lib/server/chat-execution/chat-turn-state.ts +4 -0
  55. package/src/lib/server/chat-execution/continuation-evaluator.ts +60 -15
  56. package/src/lib/server/chat-execution/continuation-limits.ts +8 -0
  57. package/src/lib/server/chat-execution/message-classifier.ts +7 -1
  58. package/src/lib/server/chat-execution/post-stream-finalization.ts +28 -0
  59. package/src/lib/server/chat-execution/prompt-sections.ts +54 -1
  60. package/src/lib/server/chat-execution/response-completeness.test.ts +123 -0
  61. package/src/lib/server/chat-execution/response-completeness.ts +206 -0
  62. package/src/lib/server/chat-execution/stream-agent-chat.ts +40 -0
  63. package/src/lib/server/chat-execution/stream-continuation.ts +65 -0
  64. package/src/lib/server/protocols/protocol-foreach.ts +4 -74
  65. package/src/lib/server/protocols/protocol-step-helpers.ts +90 -7
  66. package/src/lib/server/query-expansion.ts +9 -2
  67. package/src/lib/server/resolve-workspace-path.ts +46 -0
  68. package/src/lib/server/runtime/daemon-state.ts +1 -1
  69. package/src/lib/server/runtime/heartbeat-service.ts +27 -2
  70. package/src/lib/server/runtime/process-manager.ts +5 -1
  71. package/src/lib/server/runtime/session-run-manager.ts +16 -4
  72. package/src/lib/server/session-tools/context.ts +11 -0
  73. package/src/lib/server/session-tools/crud.ts +126 -1
  74. package/src/lib/server/session-tools/file.ts +1 -1
  75. package/src/lib/server/session-tools/index.ts +0 -1
  76. package/src/lib/server/session-tools/session-tools-wiring.test.ts +26 -0
  77. package/src/lib/server/session-tools/shell.ts +35 -2
  78. package/src/lib/server/storage-normalization.ts +7 -0
  79. package/src/lib/server/storage.ts +7 -0
  80. package/src/lib/server/tool-loop-detection.ts +11 -0
  81. package/src/lib/strip-internal-metadata.test.ts +160 -0
  82. package/src/lib/strip-internal-metadata.ts +88 -0
  83. package/src/stores/slices/ui-slice.ts +18 -6
  84. package/src/stores/use-chat-store.ts +84 -61
  85. package/src/views/settings/section-runtime-loop.tsx +3 -0
  86. package/tsconfig.json +2 -1
  87. package/src/app/api/dashboard/route.ts +0 -130
  88. package/src/lib/server/session-tools/http.ts +0 -126
package/README.md CHANGED
@@ -15,6 +15,19 @@ Docs: https://swarmclaw.ai/docs
15
15
  Website: https://swarmclaw.ai
16
16
  Extension tutorial: https://swarmclaw.ai/docs/extension-tutorial
17
17
 
18
+ ## Screenshots
19
+
20
+ <table>
21
+ <tr>
22
+ <td width="50%"><img src="doc/assets/screenshots/org-chart.png" alt="SwarmClaw org chart view showing CEO, Developer, and Researcher agents." /></td>
23
+ <td width="50%"><img src="doc/assets/screenshots/agent-chat.png" alt="SwarmClaw agent chat view showing a CEO conversation." /></td>
24
+ </tr>
25
+ <tr>
26
+ <td align="center"><sub>Org chart for visualizing agent teams, delegation, and live activity.</sub></td>
27
+ <td align="center"><sub>Agent chat with durable history, tools, and operator controls.</sub></td>
28
+ </tr>
29
+ </table>
30
+
18
31
  <div align="center">
19
32
  <table>
20
33
  <tr>
@@ -177,6 +190,21 @@ The building blocks are the same: **agents, tools, memory, delegation, schedules
177
190
 
178
191
  ## Release Notes
179
192
 
193
+ ### v1.1.7 Highlights
194
+
195
+ - **Projects page redesign**: tabbed navigation (Overview, Work, Operations, Activity) with health grid, sortable task list, and timeline feed.
196
+ - **Delegation visualization**: live org chart edges show active delegations with status, direction, and message popover on click.
197
+ - **Credential self-service**: agents can check whether a credential exists and request missing ones from humans with structured messages, signup URLs, and durable wait.
198
+ - **Main loop state persistence**: autonomous operation state now survives server restarts via on-disk persistence.
199
+ - **Internal metadata stripping**: classification JSON and loop detection messages no longer leak into streamed agent output.
200
+ - **Response completeness evaluator**: LLM-based detection of incomplete agent responses triggers continuation nudges.
201
+ - **Coordinator delegation nudging**: coordinators that make 3+ direct tool calls get prompted to delegate to workers.
202
+ - **Inspector panel overhaul**: new dashboard/config/files tabs absorb model switcher and workspace controls from chat header.
203
+ - **Streaming phase indicators**: agent chat list shows queued, tool-in-use, responding, and reconnecting states.
204
+ - **Shell safety**: agents can no longer kill SwarmClaw's own process or port.
205
+ - **Worker-only providers**: CLI-backed providers (claude-cli, codex-cli, etc.) properly restricted from coordinator/heartbeat roles.
206
+ - **HTTP tool removed**: the built-in HTTP session tool was removed from the standard toolkit.
207
+
180
208
  ### v1.1.6 Highlights
181
209
 
182
210
  - **Org chart view**: visual agent hierarchy with drag-and-drop reparenting, team grouping, and context-menu actions for managing agent relationships directly from the canvas.
@@ -203,18 +231,6 @@ The building blocks are the same: **agents, tools, memory, delegation, schedules
203
231
  - **Plugin-to-extension cleanup finished**: remaining rename residue in scripts and tests was cleaned up so packaging and release tooling stay aligned with the current extensions model.
204
232
  - **Safe body parsing utility**: shared `safeParseBody()` replaces scattered `await req.json()` try/catch blocks across API routes.
205
233
 
206
- ### v1.1.4 Highlights
207
-
208
- - **Orchestrator agents as a real agent mode**: eligible agents can now run scheduled orchestrator wake cycles with their own mission, governance policy, wake interval, and cycle cap.
209
- - **Runtime durability and recovery**: configurable parallel task execution, stuck-task idle timeout detection, orphaned running-task recovery on startup, and restart-safe swarm/provider-health persistence.
210
- - **Failover and safety improvements**: provider errors classified for smarter routing, agent budget limits block task execution before it starts.
211
-
212
- ### v1.1.3 Highlights
213
-
214
- - **`build:ci` repair**: fixed the langgraph checkpoint duplicate-column crash that blocked CI/build validation.
215
- - **Safer storage writes**: credentials and agents use upsert-only save behavior, and a collection safety guard blocks accidental bulk-delete paths.
216
- >>>>>>> Stashed changes
217
-
218
234
  ### v1.1.2 Highlights
219
235
 
220
236
  - **Structured Sessions expanded into richer orchestration**: ProtocolRun-based sessions now support dependency-aware step graphs, reusable step outputs, and a broader advanced execution model on the same durable runtime instead of bringing back a separate orchestrator.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmclawai/swarmclaw",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "Self-hosted AI runtime for OpenClaw, delegation, autonomy, runtime skills, crypto wallets, and chat platform connectors.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -5,6 +5,7 @@ import { useAppStore } from '@/stores/use-app-store'
5
5
  import { useNow } from '@/hooks/use-now'
6
6
  import { useWs } from '@/hooks/use-ws'
7
7
  import { MainContent } from '@/components/layout/main-content'
8
+ import { PageLoader } from '@/components/ui/page-loader'
8
9
  import { timeAgo } from '@/lib/time-format'
9
10
  import type { ActivityEntry } from '@/types'
10
11
 
@@ -34,10 +35,17 @@ export default function ActivityPage() {
34
35
  const entries = useAppStore((s) => s.activityEntries)
35
36
  const loadActivity = useAppStore((s) => s.loadActivity)
36
37
  const [filterType, setFilterType] = useState('')
38
+ const [loaded, setLoaded] = useState(false)
37
39
 
38
- useEffect(() => { loadActivity({ entityType: filterType || undefined, limit: 100 }) }, [filterType, loadActivity])
40
+ useEffect(() => {
41
+ void loadActivity({ entityType: filterType || undefined, limit: 100 }).then(() => setLoaded(true))
42
+ }, [filterType, loadActivity])
39
43
  useWs('activity', () => loadActivity({ entityType: filterType || undefined, limit: 100 }), 10_000)
40
44
 
45
+ if (!loaded) {
46
+ return <MainContent><PageLoader label="Loading activity..." /></MainContent>
47
+ }
48
+
41
49
  return (
42
50
  <MainContent>
43
51
  <div className="flex-1 flex flex-col h-full overflow-hidden">
@@ -4,6 +4,7 @@ import { notFound } from '@/lib/server/collection-helpers'
4
4
  import { normalizeProviderEndpoint } from '@/lib/openclaw/openclaw-endpoint'
5
5
  import { resolvePrimaryAgentRoute } from '@/lib/server/agents/agent-runtime-config'
6
6
  import { clearMainLoopStateForSession } from '@/lib/server/agents/main-agent-loop'
7
+ import { cleanupSessionProcesses } from '@/lib/server/runtime/process-manager'
7
8
  import { getSessionQueueSnapshot, getSessionRunState } from '@/lib/server/runtime/session-run-manager'
8
9
  import { normalizeCapabilitySelection } from '@/lib/capability-selection'
9
10
  import { enrichSessionWithMissionSummary } from '@/lib/server/missions/mission-service'
@@ -152,6 +153,7 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
152
153
  try { active.get(id)?.kill() } catch {}
153
154
  active.delete(id)
154
155
  }
156
+ cleanupSessionProcesses(id)
155
157
  deleteSession(id)
156
158
  return new NextResponse('OK')
157
159
  }
@@ -0,0 +1,19 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { listDelegationJobs } from '@/lib/server/agents/delegation-jobs'
3
+
4
+ export const dynamic = 'force-dynamic'
5
+
6
+ const RECENT_WINDOW_MS = 30_000
7
+
8
+ export async function GET() {
9
+ const cutoff = Date.now() - RECENT_WINDOW_MS
10
+ const jobs = listDelegationJobs().filter((job) => {
11
+ // Active jobs always included
12
+ if (job.status === 'queued' || job.status === 'running') return true
13
+ // Recently-completed jobs (within window)
14
+ if (job.completedAt && job.completedAt >= cutoff) return true
15
+ if (job.updatedAt >= cutoff) return true
16
+ return false
17
+ })
18
+ return NextResponse.json(jobs)
19
+ }
@@ -3,19 +3,19 @@ import { spawn } from 'child_process'
3
3
  import fs from 'fs'
4
4
  import path from 'path'
5
5
  import { safeParseBody } from '@/lib/server/safe-parse-body'
6
+ import { resolveWorkspacePath } from '@/lib/server/resolve-workspace-path'
6
7
 
7
8
  export async function POST(req: Request) {
8
- const { data: body, error } = await safeParseBody<{ path?: string }>(req)
9
+ const { data: body, error } = await safeParseBody<{ path?: string; cwd?: string }>(req)
9
10
  if (error) return error
10
- const { path: targetPath } = body
11
+ const { path: targetPath, cwd } = body
11
12
  if (!targetPath || typeof targetPath !== 'string') {
12
13
  return NextResponse.json({ error: 'path is required' }, { status: 400 })
13
14
  }
14
15
 
15
- const resolved = path.resolve(targetPath)
16
+ const resolved = resolveWorkspacePath(targetPath, cwd)
16
17
 
17
- // Verify the path exists
18
- if (!fs.existsSync(resolved)) {
18
+ if (!resolved) {
19
19
  return NextResponse.json({ error: 'Path does not exist' }, { status: 404 })
20
20
  }
21
21
 
@@ -1,6 +1,7 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import fs from 'fs'
3
3
  import path from 'path'
4
+ import { resolveWorkspacePath } from '@/lib/server/resolve-workspace-path'
4
5
 
5
6
  const MIME_MAP: Record<string, string> = {
6
7
  '.html': 'text/html',
@@ -41,8 +42,14 @@ export async function GET(req: Request) {
41
42
  return NextResponse.json({ error: 'Missing path parameter' }, { status: 400 })
42
43
  }
43
44
 
44
- // Resolve and normalize the path
45
- const resolved = path.resolve(filePath)
45
+ const cwd = url.searchParams.get('cwd')
46
+
47
+ // Resolve the path, trying workspace-relative fallbacks
48
+ const resolved = resolveWorkspacePath(filePath, cwd)
49
+
50
+ if (!resolved) {
51
+ return NextResponse.json({ error: 'File not found' }, { status: 404 })
52
+ }
46
53
 
47
54
  // Block access to sensitive paths
48
55
  const blocked = ['.env', 'credentials', '.ssh', '.gnupg', '.aws']
@@ -50,10 +57,6 @@ export async function GET(req: Request) {
50
57
  return NextResponse.json({ error: 'Access denied' }, { status: 403 })
51
58
  }
52
59
 
53
- if (!fs.existsSync(resolved)) {
54
- return NextResponse.json({ error: 'File not found' }, { status: 404 })
55
- }
56
-
57
60
  const stat = fs.statSync(resolved)
58
61
  if (!stat.isFile()) {
59
62
  return NextResponse.json({ error: 'Not a file' }, { status: 400 })
@@ -310,6 +310,20 @@ textarea:hover::-webkit-scrollbar { width: 6px; }
310
310
  to { opacity: 1; transform: translateY(0); }
311
311
  }
312
312
 
313
+ /* Org chart node delegation glow — shadow-only pulse, no size change */
314
+ @keyframes delegation-glow-pulse {
315
+ 0%, 100% { opacity: 1; }
316
+ 50% { opacity: 0.7; }
317
+ }
318
+
319
+ /* Org chart edge snippet: fade in, hold, then fade out */
320
+ @keyframes fadeInHold {
321
+ 0% { opacity: 0; transform: translateY(4px); }
322
+ 5% { opacity: 1; transform: translateY(0); }
323
+ 85% { opacity: 1; }
324
+ 100% { opacity: 0; }
325
+ }
326
+
313
327
  /* ===== SwarmClaw Loader Keyframes ===== */
314
328
  @keyframes sc-orbit {
315
329
  from { transform: rotate(0deg); }
@@ -6,6 +6,7 @@ import { api } from '@/lib/app/api-client'
6
6
  import { useWs } from '@/hooks/use-ws'
7
7
  import { useAppStore } from '@/stores/use-app-store'
8
8
  import { FilterPill } from '@/components/ui/filter-pill'
9
+ import { PageLoader } from '@/components/ui/page-loader'
9
10
  import { StatCard } from '@/components/ui/stat-card'
10
11
  import { StructuredSessionLauncher } from '@/components/protocols/structured-session-launcher'
11
12
  import { timeAgo } from '@/lib/time-format'
@@ -332,7 +333,7 @@ export default function MissionsPage() {
332
333
  </div>
333
334
  <div className="max-h-[70vh] min-h-0 space-y-2 overflow-y-auto pr-1">
334
335
  {loading ? (
335
- <div className="px-3 py-4 text-[13px] text-text-3/55">Loading missions…</div>
336
+ <PageLoader label="Loading missions..." />
336
337
  ) : filtered.length === 0 ? (
337
338
  <div className="px-3 py-4 text-[13px] text-text-3/55">No missions match the current filters.</div>
338
339
  ) : filtered.map((mission) => {
@@ -385,7 +386,7 @@ export default function MissionsPage() {
385
386
  Select a mission to inspect its detail, linked work, and operator actions.
386
387
  </div>
387
388
  ) : detailLoading && !selectedMission ? (
388
- <div className="px-2 py-4 text-[13px] text-text-3/55">Loading mission detail…</div>
389
+ <PageLoader label="Loading mission..." />
389
390
  ) : selectedMission ? (
390
391
  <div className="space-y-4">
391
392
  <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
package/src/cli/index.js CHANGED
@@ -170,10 +170,10 @@ const COMMAND_GROUPS = [
170
170
  ],
171
171
  },
172
172
  {
173
- name: 'dashboard',
174
- description: 'Dashboard summary data',
173
+ name: 'delegation-jobs',
174
+ description: 'Delegation job status',
175
175
  commands: [
176
- cmd('get', 'GET', '/dashboard', 'Get dashboard summary'),
176
+ cmd('list', 'GET', '/delegation-jobs', 'List active and recent delegation jobs'),
177
177
  ],
178
178
  },
179
179
  {
package/src/cli/spec.js CHANGED
@@ -124,10 +124,10 @@ const COMMAND_GROUPS = {
124
124
  'health-check': { description: 'Run daemon health checks immediately', method: 'POST', path: '/daemon/health-check' },
125
125
  },
126
126
  },
127
- dashboard: {
128
- description: 'Dashboard summary data',
127
+ 'delegation-jobs': {
128
+ description: 'Delegation job status',
129
129
  commands: {
130
- get: { description: 'Get dashboard summary', method: 'GET', path: '/dashboard' },
130
+ list: { description: 'List active and recent delegation jobs', method: 'GET', path: '/delegation-jobs' },
131
131
  },
132
132
  },
133
133
  dirs: {
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
4
+ import { useShallow } from 'zustand/react/shallow'
4
5
  import { useAppStore } from '@/stores/use-app-store'
5
6
  import { useChatStore } from '@/stores/use-chat-store'
6
7
  import { useChatroomStore } from '@/stores/use-chatroom-store'
@@ -35,7 +36,13 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
35
36
  const togglePinAgent = useAppStore((s) => s.togglePinAgent)
36
37
  const appSettings = useAppStore((s) => s.appSettings)
37
38
  const updateSettings = useAppStore((s) => s.updateSettings)
38
- const streamingSessionId = useChatStore((s) => s.streamingSessionId)
39
+ const { streamingSessionId, streamPhase, streamToolName } = useChatStore(
40
+ useShallow((s) => ({
41
+ streamingSessionId: s.streamingSessionId,
42
+ streamPhase: s.streamPhase,
43
+ streamToolName: s.streamToolName,
44
+ })),
45
+ )
39
46
  const chatFilter = useAppStore((s) => s.chatFilter ?? 'all')
40
47
  const setChatFilter = useAppStore((s) => s.setChatFilter)
41
48
  const chatrooms = useChatroomStore((s) => s.chatrooms)
@@ -352,13 +359,17 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
352
359
  </span>
353
360
  </div>
354
361
  {isTyping ? (
355
- <div className="text-[12px] text-accent-bright/80 mt-1 flex items-center gap-1.5">
362
+ <div className={`text-[12px] mt-1 flex items-center gap-1.5 ${streamPhase === 'queued' ? 'text-amber-300/80' : 'text-accent-bright/80'}`}>
356
363
  <span className="flex gap-0.5">
357
- <span className="w-1 h-1 rounded-full bg-accent-bright/70 animate-bounce [animation-delay:0ms]" />
358
- <span className="w-1 h-1 rounded-full bg-accent-bright/70 animate-bounce [animation-delay:150ms]" />
359
- <span className="w-1 h-1 rounded-full bg-accent-bright/70 animate-bounce [animation-delay:300ms]" />
364
+ <span className={`w-1 h-1 rounded-full animate-bounce [animation-delay:0ms] ${streamPhase === 'queued' ? 'bg-amber-400/70' : 'bg-accent-bright/70'}`} />
365
+ <span className={`w-1 h-1 rounded-full animate-bounce [animation-delay:150ms] ${streamPhase === 'queued' ? 'bg-amber-400/70' : 'bg-accent-bright/70'}`} />
366
+ <span className={`w-1 h-1 rounded-full animate-bounce [animation-delay:300ms] ${streamPhase === 'queued' ? 'bg-amber-400/70' : 'bg-accent-bright/70'}`} />
360
367
  </span>
361
- Typing...
368
+ {streamPhase === 'queued' ? 'Queued...'
369
+ : streamPhase === 'tool' && streamToolName ? `Using ${streamToolName}...`
370
+ : streamPhase === 'responding' ? 'Responding...'
371
+ : streamPhase === 'connecting' ? 'Reconnecting...'
372
+ : 'Thinking...'}
362
373
  </div>
363
374
  ) : (
364
375
  <div className="text-[12px] text-text-3/70 mt-1 truncate">
@@ -504,13 +515,17 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
504
515
  </button>
505
516
  </div>
506
517
  {isTyping ? (
507
- <div className="text-[12px] text-accent-bright/70 mt-0.5 flex items-center gap-1.5">
518
+ <div className={`text-[12px] mt-0.5 flex items-center gap-1.5 ${streamPhase === 'queued' ? 'text-amber-300/70' : 'text-accent-bright/70'}`}>
508
519
  <span className="flex gap-0.5">
509
- <span className="w-1 h-1 rounded-full bg-accent-bright/70 animate-bounce [animation-delay:0ms]" />
510
- <span className="w-1 h-1 rounded-full bg-accent-bright/70 animate-bounce [animation-delay:150ms]" />
511
- <span className="w-1 h-1 rounded-full bg-accent-bright/70 animate-bounce [animation-delay:300ms]" />
520
+ <span className={`w-1 h-1 rounded-full animate-bounce [animation-delay:0ms] ${streamPhase === 'queued' ? 'bg-amber-400/70' : 'bg-accent-bright/70'}`} />
521
+ <span className={`w-1 h-1 rounded-full animate-bounce [animation-delay:150ms] ${streamPhase === 'queued' ? 'bg-amber-400/70' : 'bg-accent-bright/70'}`} />
522
+ <span className={`w-1 h-1 rounded-full animate-bounce [animation-delay:300ms] ${streamPhase === 'queued' ? 'bg-amber-400/70' : 'bg-accent-bright/70'}`} />
512
523
  </span>
513
- Typing...
524
+ {streamPhase === 'queued' ? 'Queued...'
525
+ : streamPhase === 'tool' && streamToolName ? `Using ${streamToolName}...`
526
+ : streamPhase === 'responding' ? 'Responding...'
527
+ : streamPhase === 'connecting' ? 'Reconnecting...'
528
+ : 'Thinking...'}
514
529
  </div>
515
530
  ) : preview ? (
516
531
  <div className="text-[12px] text-text-3/70 mt-0.5 truncate">