@agent-relay/dashboard 2.0.82 → 2.0.84

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 (228) hide show
  1. package/out/404.html +1 -1
  2. package/out/_next/static/chunks/1028-da5d75e35d1420f1.js +1 -0
  3. package/out/_next/static/chunks/1528-78b17000a7e10bc6.js +2 -0
  4. package/out/_next/static/chunks/1695-4a5d33ba715e09b4.js +1 -0
  5. package/out/_next/static/chunks/1705-36c2180d00a4a569.js +1 -0
  6. package/out/_next/static/chunks/1dd3208c-e1f87c7b3dc1a820.js +1 -0
  7. package/out/_next/static/chunks/3663-47290254b8f6f5dd.js +1 -0
  8. package/out/_next/static/chunks/3677-4b225baf4801d9b9.js +73 -0
  9. package/out/_next/static/chunks/5118-7e8ada2df38eef07.js +1 -0
  10. package/out/_next/static/chunks/5888-15cbe97c90ed5fae.js +1 -0
  11. package/out/_next/static/chunks/6773-a45343a98df3abb5.js +1 -0
  12. package/out/_next/static/chunks/6940-b824612b605e79b3.js +9 -0
  13. package/out/_next/static/chunks/7894-f4a15249082a680d.js +1 -0
  14. package/out/_next/static/chunks/9175-b3617c1e5cbfed0e.js +1 -0
  15. package/out/_next/static/chunks/9372-1a804b8d08c7a236.js +1 -0
  16. package/out/_next/static/chunks/{ab6c8a12-0a58072fbb505134.js → ab6c8a12-91438a812d94ecf0.js} +1 -1
  17. package/out/_next/static/chunks/app/_not-found/page-8e8842f82d204726.js +1 -0
  18. package/out/_next/static/chunks/app/about/page-b78577a7da8fa459.js +1 -0
  19. package/out/_next/static/chunks/app/app/[[...slug]]/page-3dffd65b6344f53e.js +1 -0
  20. package/out/_next/static/chunks/app/app/onboarding/page-b89be9aa6264a5e1.js +1 -0
  21. package/out/_next/static/chunks/app/blog/go-to-bed-wake-up-to-a-finished-product/page-fbd00893ef69e499.js +1 -0
  22. package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/page-de2ea13649d0b6d3.js +1 -0
  23. package/out/_next/static/chunks/app/blog/page-a08e263c57a156fa.js +1 -0
  24. package/out/_next/static/chunks/app/careers/page-02228e1d6969b232.js +1 -0
  25. package/out/_next/static/chunks/app/changelog/page-1b5c1d79efc6e53a.js +1 -0
  26. package/out/_next/static/chunks/app/cloud/link/page-99654edffffb3af2.js +1 -0
  27. package/out/_next/static/chunks/app/complete-profile/page-59d146e5ddeafc5c.js +1 -0
  28. package/out/_next/static/chunks/app/connect-repos/page-995e16a976a6632c.js +1 -0
  29. package/out/_next/static/chunks/app/contact/page-273396a5ad57bcee.js +1 -0
  30. package/out/_next/static/chunks/app/dev/cli-tools/page-a71b80dcb2d5fc8d.js +1 -0
  31. package/out/_next/static/chunks/app/dev/log-viewer/page-46a6151ae1be0796.js +1 -0
  32. package/out/_next/static/chunks/app/docs/page-7c7cb603b24b7c40.js +1 -0
  33. package/out/_next/static/chunks/app/history/page-0c5cab1dab4e8886.js +1 -0
  34. package/out/_next/static/chunks/app/layout-96d72ba8ef8a43a0.js +1 -0
  35. package/out/_next/static/chunks/app/login/page-0ccbab34213df842.js +1 -0
  36. package/out/_next/static/chunks/app/metrics/page-8616272aeab9c8b0.js +1 -0
  37. package/out/_next/static/chunks/app/page-09ce10603ad9a251.js +1 -0
  38. package/out/_next/static/chunks/app/pricing/page-91c975079120c941.js +1 -0
  39. package/out/_next/static/chunks/app/privacy/{page-c21d51ac2dee3a88.js → page-a49ab271cc686644.js} +1 -1
  40. package/out/_next/static/chunks/app/providers/{page-59114505f4353512.js → page-d775d6eb5bc29e96.js} +1 -1
  41. package/out/_next/static/chunks/app/providers/setup/[provider]/page-ec4ef3cd80de807e.js +1 -0
  42. package/out/_next/static/chunks/app/security/page-d9da9bd9191e8f95.js +1 -0
  43. package/out/_next/static/chunks/app/signup/page-930eca0bf5fd299d.js +1 -0
  44. package/out/_next/static/chunks/app/terms/page-3e4827620b98613c.js +1 -0
  45. package/out/_next/static/chunks/framework-648e1ae7da590300.js +1 -0
  46. package/out/_next/static/chunks/{main-acb1b24265295d6a.js → main-2b1990080c292d92.js} +1 -1
  47. package/out/_next/static/chunks/main-app-9f6b7ff9e754a8f5.js +1 -0
  48. package/out/_next/static/chunks/pages/_app-a077b72e02273ab1.js +1 -0
  49. package/out/_next/static/chunks/pages/_error-84001666436a04e4.js +1 -0
  50. package/out/_next/static/chunks/{webpack-dd93b81e2659669c.js → webpack-7586035f1585f2db.js} +1 -1
  51. package/out/_next/static/css/eb9fc69d1e3d2bed.css +1 -0
  52. package/out/_next/static/{IxfA6RZu4trcsEMYlkQra → g3G0LMdB7lxcrU5mdM54m}/_buildManifest.js +1 -1
  53. package/out/about.html +2 -2
  54. package/out/about.txt +2 -2
  55. package/out/app/onboarding.html +1 -1
  56. package/out/app/onboarding.txt +2 -2
  57. package/out/app.html +1 -1
  58. package/out/app.txt +2 -2
  59. package/out/blog/go-to-bed-wake-up-to-a-finished-product.html +3 -3
  60. package/out/blog/go-to-bed-wake-up-to-a-finished-product.txt +1 -1
  61. package/out/blog/let-them-cook-multi-agent-orchestration.html +2 -2
  62. package/out/blog/let-them-cook-multi-agent-orchestration.txt +2 -2
  63. package/out/blog.html +2 -2
  64. package/out/blog.txt +1 -1
  65. package/out/careers.html +2 -2
  66. package/out/careers.txt +2 -2
  67. package/out/changelog.html +2 -2
  68. package/out/changelog.txt +2 -2
  69. package/out/cloud/link.html +1 -1
  70. package/out/cloud/link.txt +2 -2
  71. package/out/complete-profile.html +2 -2
  72. package/out/complete-profile.txt +2 -2
  73. package/out/connect-repos.html +1 -1
  74. package/out/connect-repos.txt +2 -2
  75. package/out/contact.html +2 -2
  76. package/out/contact.txt +2 -2
  77. package/out/dev/cli-tools.html +1 -0
  78. package/out/dev/cli-tools.txt +7 -0
  79. package/out/dev/log-viewer.html +23 -0
  80. package/out/dev/log-viewer.txt +7 -0
  81. package/out/docs.html +2 -2
  82. package/out/docs.txt +2 -2
  83. package/out/history.html +1 -1
  84. package/out/history.txt +2 -2
  85. package/out/index.html +1 -1
  86. package/out/index.txt +2 -2
  87. package/out/login.html +2 -2
  88. package/out/login.txt +2 -2
  89. package/out/metrics.html +1 -1
  90. package/out/metrics.txt +2 -2
  91. package/out/pricing.html +2 -2
  92. package/out/pricing.txt +2 -2
  93. package/out/privacy.html +2 -2
  94. package/out/privacy.txt +2 -2
  95. package/out/providers/setup/claude.html +1 -1
  96. package/out/providers/setup/claude.txt +2 -2
  97. package/out/providers/setup/codex.html +1 -1
  98. package/out/providers/setup/codex.txt +2 -2
  99. package/out/providers/setup/cursor.html +1 -1
  100. package/out/providers/setup/cursor.txt +2 -2
  101. package/out/providers.html +1 -1
  102. package/out/providers.txt +2 -2
  103. package/out/security.html +2 -2
  104. package/out/security.txt +2 -2
  105. package/out/signup.html +2 -2
  106. package/out/signup.txt +2 -2
  107. package/out/terms.html +2 -2
  108. package/out/terms.txt +2 -2
  109. package/package.json +5 -1
  110. package/src/adapters/DashboardConfigProvider.tsx +56 -0
  111. package/src/adapters/cloudFetchAdapter.ts +278 -0
  112. package/src/adapters/index.ts +3 -0
  113. package/src/adapters/types.ts +508 -0
  114. package/src/app/app/[[...slug]]/DashboardPageClient.tsx +67 -18
  115. package/src/app/app/onboarding/page.tsx +870 -170
  116. package/src/app/cloud/link/page.tsx +14 -6
  117. package/src/app/connect-repos/page.tsx +9 -3
  118. package/src/app/dev/cli-tools/page.tsx +130 -0
  119. package/src/app/dev/log-viewer/MockLogViewer.tsx +132 -0
  120. package/src/app/dev/log-viewer/fixtures.ts +110 -0
  121. package/src/app/dev/log-viewer/page.tsx +288 -0
  122. package/src/app/history/page.tsx +28 -12
  123. package/src/app/page.tsx +1 -1
  124. package/src/app/providers/setup/[provider]/ProviderSetupClient.tsx +209 -59
  125. package/src/components/AgentCard.tsx +4 -4
  126. package/src/components/AgentLogPreview.tsx +2 -38
  127. package/src/components/App.tsx +441 -2624
  128. package/src/components/CliToolHarness.test.tsx +83 -0
  129. package/src/components/CliToolHarness.tsx +292 -0
  130. package/src/components/CoordinatorPanel.tsx +13 -6
  131. package/src/components/LogViewer.tsx +2 -42
  132. package/src/components/ProviderAuthFlow.tsx +201 -81
  133. package/src/components/ProvisioningProgress.tsx +1 -1
  134. package/src/components/ReactionChips.tsx +2 -1
  135. package/src/components/SpawnModal.test.tsx +51 -18
  136. package/src/components/SpawnModal.tsx +175 -207
  137. package/src/components/TerminalProviderSetup.tsx +1 -1
  138. package/src/components/ThreadPanel.tsx +2 -0
  139. package/src/components/WorkspaceContext.tsx +7 -19
  140. package/src/components/XTermLogViewer.tsx +190 -27
  141. package/src/components/channels/ChannelMessageList.tsx +94 -4
  142. package/src/components/channels/ChannelViewV1.tsx +35 -11
  143. package/src/components/channels/api.ts +21 -20
  144. package/src/components/channels/types.ts +16 -0
  145. package/src/components/hooks/index.ts +0 -19
  146. package/src/components/hooks/useMessages.test.ts +80 -0
  147. package/src/components/hooks/useMessages.ts +13 -4
  148. package/src/components/hooks/useOrchestrator.ts +1 -1
  149. package/src/components/hooks/usePresence.ts +45 -6
  150. package/src/components/hooks/useThread.ts +83 -46
  151. package/src/components/hooks/useTrajectory.ts +62 -5
  152. package/src/components/hooks/useWebSocket.test.ts +358 -0
  153. package/src/components/hooks/useWebSocket.ts +243 -5
  154. package/src/components/index.ts +2 -14
  155. package/src/components/layout/Header.tsx +9 -15
  156. package/src/components/layout/Sidebar.tsx +1 -8
  157. package/src/components/settings/SettingsPage.tsx +108 -47
  158. package/src/components/settings/index.ts +0 -3
  159. package/src/landing/blogData.ts +1 -1
  160. package/src/lib/agent-merge.test.ts +2 -2
  161. package/src/lib/api.ts +8 -38
  162. package/src/lib/identity.test.ts +139 -0
  163. package/src/lib/identity.ts +48 -0
  164. package/src/lib/relaycastMessageAdapters.test.ts +182 -0
  165. package/src/lib/relaycastMessageAdapters.ts +105 -0
  166. package/src/lib/sanitize-logs.test.ts +227 -0
  167. package/src/lib/sanitize-logs.ts +202 -0
  168. package/src/providers/AgentProvider.tsx +799 -0
  169. package/src/providers/ChannelProvider.tsx +528 -0
  170. package/src/providers/CloudWorkspaceProvider.tsx +402 -0
  171. package/src/providers/MessageProvider.tsx +875 -0
  172. package/src/providers/RelayConfigProvider.tsx +94 -0
  173. package/src/providers/SendProvider.tsx +497 -0
  174. package/src/providers/SettingsProvider.tsx +247 -0
  175. package/src/providers/index.ts +26 -0
  176. package/src/types/index.ts +10 -10
  177. package/out/_next/static/chunks/11-9a2993a37266dcb3.js +0 -9
  178. package/out/_next/static/chunks/118-ae2b650136a5a5fc.js +0 -1
  179. package/out/_next/static/chunks/1dd3208c-40ab0fc0f60392b8.js +0 -1
  180. package/out/_next/static/chunks/202-fc0763dd7488e58f.js +0 -1
  181. package/out/_next/static/chunks/259-83b77fa1b91ba5aa.js +0 -1
  182. package/out/_next/static/chunks/407-0c82986cf79c8ecb.js +0 -1
  183. package/out/_next/static/chunks/528-f5f676996d613c25.js +0 -2
  184. package/out/_next/static/chunks/663-ddb04081febc3678.js +0 -1
  185. package/out/_next/static/chunks/687-88b6b139a6bb0e2e.js +0 -1
  186. package/out/_next/static/chunks/695-51d25b1988644374.js +0 -1
  187. package/out/_next/static/chunks/773-54a2641043c81e55.js +0 -1
  188. package/out/_next/static/chunks/app/_not-found/page-6da9b72091e5b511.js +0 -1
  189. package/out/_next/static/chunks/app/about/page-fff7c6457683f243.js +0 -1
  190. package/out/_next/static/chunks/app/app/[[...slug]]/page-f7eca1b66fb4249b.js +0 -1
  191. package/out/_next/static/chunks/app/app/onboarding/page-129abc5da2e67971.js +0 -1
  192. package/out/_next/static/chunks/app/blog/go-to-bed-wake-up-to-a-finished-product/page-5d5f28fd126b692f.js +0 -1
  193. package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/page-b194f207fbd91862.js +0 -1
  194. package/out/_next/static/chunks/app/blog/page-b9bd9d8703fca76a.js +0 -1
  195. package/out/_next/static/chunks/app/careers/page-a4bd8d5f4de8f4eb.js +0 -1
  196. package/out/_next/static/chunks/app/changelog/page-9a1f6ad1743d63c5.js +0 -1
  197. package/out/_next/static/chunks/app/cloud/link/page-0844c5699b027c3b.js +0 -1
  198. package/out/_next/static/chunks/app/complete-profile/page-39ed5a67916beb87.js +0 -1
  199. package/out/_next/static/chunks/app/connect-repos/page-297eddee0c39f2a3.js +0 -1
  200. package/out/_next/static/chunks/app/contact/page-3c1dd8690217fade.js +0 -1
  201. package/out/_next/static/chunks/app/docs/page-1875e981f2c3fd13.js +0 -1
  202. package/out/_next/static/chunks/app/history/page-2d5c5695c9e8b40c.js +0 -1
  203. package/out/_next/static/chunks/app/layout-0a4b99656da25511.js +0 -1
  204. package/out/_next/static/chunks/app/login/page-f69c076f5a6fc520.js +0 -1
  205. package/out/_next/static/chunks/app/metrics/page-bebbee055669a17e.js +0 -1
  206. package/out/_next/static/chunks/app/page-0ee604f7070d14c0.js +0 -1
  207. package/out/_next/static/chunks/app/pricing/page-eeae7d594af333b6.js +0 -1
  208. package/out/_next/static/chunks/app/providers/setup/[provider]/page-daf9b3e05e77ae19.js +0 -1
  209. package/out/_next/static/chunks/app/security/page-cd562730fe84a0a2.js +0 -1
  210. package/out/_next/static/chunks/app/signup/page-c242ca08101a84ff.js +0 -1
  211. package/out/_next/static/chunks/app/terms/page-c7001720e7941dc6.js +0 -1
  212. package/out/_next/static/chunks/framework-3664cab31236a9fa.js +0 -1
  213. package/out/_next/static/chunks/main-app-7f73a939a312a228.js +0 -1
  214. package/out/_next/static/chunks/pages/_app-10a93ab5b7c32eb3.js +0 -1
  215. package/out/_next/static/chunks/pages/_error-2d792b2a41857be4.js +0 -1
  216. package/out/_next/static/css/8968d98ed4c4d33f.css +0 -1
  217. package/src/components/BillingResult.tsx +0 -447
  218. package/src/components/CloudSessionProvider.tsx +0 -130
  219. package/src/components/SessionExpiredModal.tsx +0 -128
  220. package/src/components/WorkspaceStatusIndicator.tsx +0 -396
  221. package/src/components/hooks/useSession.ts +0 -209
  222. package/src/components/hooks/useWorkspaceMembers.ts +0 -132
  223. package/src/components/hooks/useWorkspaceStatus.ts +0 -237
  224. package/src/components/settings/BillingSettingsPanel.tsx +0 -564
  225. package/src/components/settings/TeamSettingsPanel.tsx +0 -560
  226. package/src/components/settings/WorkspaceSettingsPanel.tsx +0 -1368
  227. package/src/lib/cloudApi.ts +0 -893
  228. /package/out/_next/static/{IxfA6RZu4trcsEMYlkQra → g3G0LMdB7lxcrU5mdM54m}/_ssgManifest.js +0 -0
@@ -12,6 +12,29 @@ import { useState, useEffect, useCallback, useRef } from 'react';
12
12
  import type { Agent, Message, Session, AgentSummary, FleetData } from '../../types';
13
13
  import { getWebSocketUrl } from '../../lib/config';
14
14
 
15
+ /** Broker event payload forwarded by the dashboard server */
16
+ interface BrokerEvent {
17
+ kind: string;
18
+ name?: string;
19
+ from?: string;
20
+ target?: string;
21
+ body?: string;
22
+ event_id?: string;
23
+ thread_id?: string | null;
24
+ code?: number | null;
25
+ signal?: string | null;
26
+ cli?: string | null;
27
+ model?: string | null;
28
+ runtime?: string;
29
+ reason?: string;
30
+ idle_secs?: number;
31
+ restart_count?: number;
32
+ delivery_id?: string;
33
+ error?: unknown;
34
+ stream?: string;
35
+ chunk?: string;
36
+ }
37
+
15
38
  export interface DashboardData {
16
39
  agents: Agent[];
17
40
  users?: Agent[]; // Human users (cli === 'dashboard')
@@ -67,6 +90,214 @@ function getDefaultUrl(): string {
67
90
  return getWebSocketUrl('/ws');
68
91
  }
69
92
 
93
+ /**
94
+ * Apply an incremental broker event to the current dashboard state.
95
+ * Returns a new state object with the event applied, or the previous state
96
+ * if the event is not relevant for the UI.
97
+ */
98
+ export function applyBrokerEvent(prev: DashboardData | null, event: BrokerEvent): DashboardData | null {
99
+ if (!prev) {
100
+ // Bootstrap empty state so events arriving before the snapshot aren't lost
101
+ prev = { agents: [], messages: [] };
102
+ }
103
+
104
+ switch (event.kind) {
105
+ case 'relay_inbound': {
106
+ if (!event.from || !event.target || !event.body) return prev;
107
+ // Channel messages are handled by useChannels — skip here to avoid duplication
108
+ if (event.target.startsWith('#')) return prev;
109
+ const msgId = event.event_id || `broker_${Date.now()}_${Math.random().toString(36).slice(2)}`;
110
+ // Deduplicate by event_id — the same event can arrive via multiple paths
111
+ if (event.event_id && prev.messages.some((m) => m.id === event.event_id)) {
112
+ return prev;
113
+ }
114
+ const newMessage: Message = {
115
+ id: msgId,
116
+ from: event.from,
117
+ to: event.target,
118
+ content: event.body,
119
+ timestamp: new Date().toISOString(),
120
+ thread: event.thread_id ?? undefined,
121
+ isBroadcast: event.target === '*',
122
+ };
123
+ // Clear thinking/processing state only when a known processing agent sends a response
124
+ const senderIsProcessingAgent = prev.agents.some(
125
+ (a) => a.name === event.from && a.isProcessing,
126
+ );
127
+ // If this is a thread reply, increment replyCount on the parent message
128
+ const updatedMessages = [...prev.messages, newMessage];
129
+ if (event.thread_id) {
130
+ const parentIdx = updatedMessages.findIndex((m) => m.id === event.thread_id);
131
+ if (parentIdx !== -1) {
132
+ updatedMessages[parentIdx] = {
133
+ ...updatedMessages[parentIdx],
134
+ replyCount: (updatedMessages[parentIdx].replyCount ?? 0) + 1,
135
+ };
136
+ }
137
+ }
138
+ return {
139
+ ...prev,
140
+ messages: updatedMessages,
141
+ agents: senderIsProcessingAgent
142
+ ? prev.agents.map((a) =>
143
+ a.name === event.from
144
+ ? { ...a, isProcessing: false, processingStartedAt: undefined, lastLogLine: undefined }
145
+ : a,
146
+ )
147
+ : prev.agents,
148
+ };
149
+ }
150
+
151
+ case 'agent_spawned': {
152
+ if (!event.name) return prev;
153
+ // Avoid duplicates
154
+ const exists = prev.agents.some((a) => a.name === event.name);
155
+ if (exists) {
156
+ return {
157
+ ...prev,
158
+ agents: prev.agents.map((a) =>
159
+ a.name === event.name ? { ...a, status: 'online' as const, cli: event.cli ?? a.cli, model: event.model ?? a.model } : a,
160
+ ),
161
+ };
162
+ }
163
+ return {
164
+ ...prev,
165
+ agents: [
166
+ ...prev.agents,
167
+ {
168
+ name: event.name,
169
+ status: 'online' as const,
170
+ cli: event.cli ?? undefined,
171
+ model: event.model ?? undefined,
172
+ isSpawned: true,
173
+ },
174
+ ],
175
+ };
176
+ }
177
+
178
+ case 'agent_exited': {
179
+ if (!event.name) return prev;
180
+ return {
181
+ ...prev,
182
+ agents: prev.agents.filter((a) => a.name !== event.name),
183
+ };
184
+ }
185
+
186
+ case 'agent_released': {
187
+ if (!event.name) return prev;
188
+ return {
189
+ ...prev,
190
+ agents: prev.agents.filter((a) => a.name !== event.name),
191
+ };
192
+ }
193
+
194
+ case 'worker_ready': {
195
+ if (!event.name) return prev;
196
+ return {
197
+ ...prev,
198
+ agents: prev.agents.map((a) =>
199
+ a.name === event.name
200
+ ? { ...a, status: 'online' as const, cli: event.cli ?? a.cli, model: event.model ?? a.model }
201
+ : a,
202
+ ),
203
+ };
204
+ }
205
+
206
+ case 'agent_idle': {
207
+ if (!event.name) return prev;
208
+ return {
209
+ ...prev,
210
+ agents: prev.agents.map((a) =>
211
+ a.name === event.name
212
+ ? { ...a, isProcessing: false, processingStartedAt: undefined, lastLogLine: undefined }
213
+ : a,
214
+ ),
215
+ };
216
+ }
217
+
218
+ case 'agent_restarting': {
219
+ if (!event.name) return prev;
220
+ return {
221
+ ...prev,
222
+ agents: prev.agents.map((a) =>
223
+ a.name === event.name ? { ...a, status: 'busy' as const } : a,
224
+ ),
225
+ };
226
+ }
227
+
228
+ case 'agent_restarted': {
229
+ if (!event.name) return prev;
230
+ return {
231
+ ...prev,
232
+ agents: prev.agents.map((a) =>
233
+ a.name === event.name ? { ...a, status: 'online' as const } : a,
234
+ ),
235
+ };
236
+ }
237
+
238
+ case 'agent_permanently_dead': {
239
+ if (!event.name) return prev;
240
+ return {
241
+ ...prev,
242
+ agents: prev.agents.map((a) =>
243
+ a.name === event.name ? { ...a, status: 'offline' as const } : a,
244
+ ),
245
+ };
246
+ }
247
+
248
+ case 'delivery_verified': {
249
+ if (!event.event_id) return prev;
250
+ return {
251
+ ...prev,
252
+ messages: prev.messages.map((m) =>
253
+ m.id === event.event_id ? { ...m, status: 'acked' as const } : m,
254
+ ),
255
+ };
256
+ }
257
+
258
+ case 'delivery_failed': {
259
+ if (!event.event_id) return prev;
260
+ return {
261
+ ...prev,
262
+ messages: prev.messages.map((m) =>
263
+ m.id === event.event_id ? { ...m, status: 'failed' as const } : m,
264
+ ),
265
+ };
266
+ }
267
+
268
+ case 'delivery_ack':
269
+ case 'delivery_active': {
270
+ if (!event.name) return prev;
271
+ return {
272
+ ...prev,
273
+ agents: prev.agents.map((a) =>
274
+ a.name === event.name
275
+ ? { ...a, isProcessing: true, processingStartedAt: Date.now() }
276
+ : a,
277
+ ),
278
+ };
279
+ }
280
+
281
+ case 'worker_stream': {
282
+ if (!event.name) return prev;
283
+ return {
284
+ ...prev,
285
+ agents: prev.agents.map((a) =>
286
+ a.name === event.name ? { ...a, lastLogLine: event.chunk } : a,
287
+ ),
288
+ };
289
+ }
290
+
291
+ case 'worker_error': {
292
+ // Worker error — could show in agent details
293
+ return prev;
294
+ }
295
+
296
+ default:
297
+ return prev;
298
+ }
299
+ }
300
+
70
301
  export function useWebSocket(options: UseWebSocketOptions = {}): UseWebSocketReturn {
71
302
  const opts = { ...DEFAULT_OPTIONS, ...options };
72
303
 
@@ -122,13 +353,20 @@ export function useWebSocket(options: UseWebSocketOptions = {}): UseWebSocketRet
122
353
  // Strip seq from the payload before routing (it's only for tracking, not data)
123
354
  const { seq: _seq, ...payload } = parsed;
124
355
 
125
- // Check if this is an event message (has a 'type' field like direct_message, channel_message)
126
- // vs dashboard data (has agents array)
356
+ // Check message type and route accordingly
127
357
  if (payload && typeof payload === 'object' && 'type' in payload && typeof payload.type === 'string') {
128
- // This is an event message - route to callback
129
- onEventRef.current?.(payload as WebSocketEvent);
358
+ // Incremental broker event apply as state patch
359
+ if (payload.type === 'broker_event' && payload.payload && typeof payload.payload === 'object' && 'kind' in payload.payload) {
360
+ setData((prev) => applyBrokerEvent(prev, payload.payload as BrokerEvent));
361
+ } else {
362
+ // Other event messages (direct_message, channel_message, presence, etc.)
363
+ onEventRef.current?.(payload as WebSocketEvent);
364
+ }
365
+ } else if (payload && typeof payload === 'object' && 'kind' in payload && typeof payload.kind === 'string') {
366
+ // Raw (unwrapped) broker event — apply as incremental patch
367
+ setData((prev) => applyBrokerEvent(prev, payload as BrokerEvent));
130
368
  } else {
131
- // This is dashboard data - update state
369
+ // Full dashboard snapshot replace state
132
370
  setData(payload as DashboardData);
133
371
  }
134
372
 
@@ -14,7 +14,7 @@ export { MessageStatusIndicator, type MessageStatusIndicatorProps } from './Mess
14
14
  export { MessageList, type MessageListProps } from './MessageList';
15
15
  export { ThreadPanel, type ThreadPanelProps } from './ThreadPanel';
16
16
  export { CommandPalette, type CommandPaletteProps, type Command } from './CommandPalette';
17
- export { SpawnModal, type SpawnModalProps, type SpawnConfig, type SpeakOnTrigger } from './SpawnModal';
17
+ export { SpawnModal, type SpawnModalProps, type SpawnConfig, type SpeakOnTrigger, type ModelOption } from './SpawnModal';
18
18
  export { NewConversationModal, type NewConversationModalProps } from './NewConversationModal';
19
19
  export { TrajectoryViewer, type TrajectoryViewerProps, type TrajectoryStep } from './TrajectoryViewer';
20
20
  export { DecisionQueue, type DecisionQueueProps, type Decision } from './DecisionQueue';
@@ -28,18 +28,10 @@ export { App, appStyles, type AppProps } from './App';
28
28
  export { MentionAutocomplete, useMentionAutocomplete, getMentionQuery, completeMentionInValue, type MentionAutocompleteProps } from './MentionAutocomplete';
29
29
  export { ProjectList, type ProjectListProps } from './ProjectList';
30
30
  export { WorkspaceSelector, type WorkspaceSelectorProps, type Workspace } from './WorkspaceSelector';
31
- export { WorkspaceSettingsPanel, type WorkspaceSettingsPanelProps } from './settings/WorkspaceSettingsPanel';
32
31
  export { AddWorkspaceModal, type AddWorkspaceModalProps } from './AddWorkspaceModal';
33
32
  export { PricingPlans, type PricingPlansProps, type Plan } from './PricingPlans';
34
33
  export { BillingPanel, type BillingPanelProps, type Subscription, type Invoice, type PaymentMethod } from './BillingPanel';
35
- export { SessionExpiredModal, type SessionExpiredModalProps } from './SessionExpiredModal';
36
34
  export { ProvisioningProgress, type ProvisioningProgressProps } from './ProvisioningProgress';
37
- export {
38
- CloudSessionProvider,
39
- useCloudSession,
40
- useCloudSessionOptional,
41
- type CloudSessionProviderProps,
42
- } from './CloudSessionProvider';
43
35
  export {
44
36
  WorkspaceProvider,
45
37
  useWorkspace,
@@ -50,6 +42,7 @@ export {
50
42
  // Terminal Components
51
43
  export { XTermLogViewer, type XTermLogViewerProps } from './XTermLogViewer';
52
44
  export { XTermInteractive, type XTermInteractiveProps } from './XTermInteractive';
45
+ export { CliToolHarness, type CliToolHarnessConfig, type CliToolHarnessProps } from './CliToolHarness';
53
46
 
54
47
  // Layout Components
55
48
  export { Sidebar, type SidebarProps } from './layout/Sidebar';
@@ -61,7 +54,6 @@ export {
61
54
  useAgents,
62
55
  useMessages,
63
56
  useOrchestrator,
64
- useSession,
65
57
  type UseWebSocketOptions,
66
58
  type UseWebSocketReturn,
67
59
  type UseAgentsOptions,
@@ -70,12 +62,8 @@ export {
70
62
  type UseMessagesReturn,
71
63
  type UseOrchestratorOptions,
72
64
  type UseOrchestratorResult,
73
- type UseSessionOptions,
74
- type UseSessionReturn,
75
65
  type DashboardData,
76
66
  type AgentWithColor,
77
67
  type OrchestratorAgent,
78
68
  type OrchestratorEvent,
79
- type SessionError,
80
- type CloudUser,
81
69
  } from './hooks';
@@ -10,7 +10,6 @@ import type { Agent, Project } from '../../types';
10
10
  import { getAgentColor, getAgentInitials } from '../../lib/colors';
11
11
  import { getAgentBreadcrumb } from '../../lib/hierarchy';
12
12
  import { RepoContextHeader } from './RepoContextHeader';
13
- import { WorkspaceStatusIndicator } from '../WorkspaceStatusIndicator';
14
13
 
15
14
  export interface HeaderProps {
16
15
  currentChannel: string;
@@ -97,12 +96,6 @@ export function Header({
97
96
  <div className="w-px h-6 bg-border-subtle mr-3 max-md:hidden" />
98
97
  )}
99
98
 
100
- {/* Workspace Status Indicator */}
101
- <WorkspaceStatusIndicator className="max-md:hidden mr-3" />
102
-
103
- {/* Divider after workspace status */}
104
- <div className="w-px h-6 bg-border-subtle mr-3 max-md:hidden" />
105
-
106
99
  <div className="flex items-center gap-2 sm:gap-3 flex-1 min-w-0">
107
100
  {selectedAgent && !selectedAgent.isHuman && selectedAgent.cli !== 'dashboard' ? (
108
101
  <>
@@ -222,13 +215,15 @@ export function Header({
222
215
  <MetricsIcon />
223
216
  </a>
224
217
 
225
- <button
226
- className="flex items-center justify-center p-1.5 sm:p-2 bg-bg-tertiary border border-border-subtle rounded-lg text-text-secondary cursor-pointer transition-all duration-150 hover:bg-bg-elevated hover:border-border-medium hover:text-accent-purple"
227
- onClick={onSettingsClick}
228
- title="Settings"
229
- >
230
- <SettingsIcon />
231
- </button>
218
+ {onSettingsClick && (
219
+ <button
220
+ className="flex items-center justify-center p-1.5 sm:p-2 bg-bg-tertiary border border-border-subtle rounded-lg text-text-secondary cursor-pointer transition-all duration-150 hover:bg-bg-elevated hover:border-border-medium hover:text-accent-purple"
221
+ onClick={onSettingsClick}
222
+ title="Settings"
223
+ >
224
+ <SettingsIcon />
225
+ </button>
226
+ )}
232
227
  </div>
233
228
  </header>
234
229
  );
@@ -308,4 +303,3 @@ function MenuIcon() {
308
303
  </svg>
309
304
  );
310
305
  }
311
-
@@ -9,7 +9,6 @@ import React, { useState, useEffect } from 'react';
9
9
  import type { Agent, Project } from '../../types';
10
10
  import type { ThreadInfo } from '../hooks/useMessages';
11
11
  import { usePinnedAgents } from '../hooks/usePinnedAgents';
12
- import { useWorkspaceStatus } from '../hooks/useWorkspaceStatus';
13
12
  import { AgentList } from '../AgentList';
14
13
  import { ProjectList } from '../ProjectList';
15
14
  import { ThreadList } from '../ThreadList';
@@ -225,10 +224,6 @@ export function Sidebar({
225
224
  // Pinned agents for quick access
226
225
  const { pinnedAgents, togglePin, isMaxPinned } = usePinnedAgents();
227
226
 
228
- // Check if workspace is stopped - hide channels when stopped
229
- const { workspace } = useWorkspaceStatus();
230
- const isWorkspaceStopped = workspace?.isStopped ?? false;
231
-
232
227
  // Determine if we should show unified project view
233
228
  const hasProjects = projects.length > 0;
234
229
 
@@ -346,8 +341,7 @@ export function Sidebar({
346
341
  </button>
347
342
  </div>
348
343
 
349
- {/* Channels Section - Collapsible (hidden when workspace stopped) */}
350
- {!isWorkspaceStopped && (
344
+ {/* Channels Section - Collapsible */}
351
345
  <div className="border-b border-border-subtle">
352
346
  <button
353
347
  className="w-full flex items-center justify-between px-3 py-2 text-xs font-semibold text-text-muted uppercase tracking-wide hover:bg-bg-hover transition-colors"
@@ -494,7 +488,6 @@ export function Sidebar({
494
488
  </div>
495
489
  )}
496
490
  </div>
497
- )}
498
491
 
499
492
  {/* Agent/Project List */}
500
493
  <div className="flex-1 overflow-y-auto px-2">