@agent-relay/dashboard 2.0.81 → 2.0.82

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 (244) hide show
  1. package/out/404.html +1 -1
  2. package/out/_next/static/chunks/{118-4c8241b0218335de.js → 118-ae2b650136a5a5fc.js} +1 -1
  3. package/out/_next/static/chunks/407-0c82986cf79c8ecb.js +1 -0
  4. package/out/_next/static/chunks/app/app/[[...slug]]/{page-1e81c047cff17212.js → page-f7eca1b66fb4249b.js} +1 -1
  5. package/out/_next/static/chunks/app/{page-6892fe2dd07fb48b.js → page-0ee604f7070d14c0.js} +1 -1
  6. package/out/_next/static/css/8968d98ed4c4d33f.css +1 -0
  7. package/out/about.html +2 -2
  8. package/out/about.txt +1 -1
  9. package/out/app/onboarding.html +1 -1
  10. package/out/app/onboarding.txt +1 -1
  11. package/out/app.html +1 -1
  12. package/out/app.txt +2 -2
  13. package/out/blog/go-to-bed-wake-up-to-a-finished-product.html +2 -2
  14. package/out/blog/go-to-bed-wake-up-to-a-finished-product.txt +1 -1
  15. package/out/blog/let-them-cook-multi-agent-orchestration.html +2 -2
  16. package/out/blog/let-them-cook-multi-agent-orchestration.txt +2 -2
  17. package/out/blog.html +2 -2
  18. package/out/blog.txt +1 -1
  19. package/out/careers.html +2 -2
  20. package/out/careers.txt +1 -1
  21. package/out/changelog.html +2 -2
  22. package/out/changelog.txt +1 -1
  23. package/out/cloud/link.html +1 -1
  24. package/out/cloud/link.txt +2 -2
  25. package/out/complete-profile.html +2 -2
  26. package/out/complete-profile.txt +1 -1
  27. package/out/connect-repos.html +1 -1
  28. package/out/connect-repos.txt +1 -1
  29. package/out/contact.html +2 -2
  30. package/out/contact.txt +1 -1
  31. package/out/docs.html +2 -2
  32. package/out/docs.txt +1 -1
  33. package/out/history.html +1 -1
  34. package/out/history.txt +2 -2
  35. package/out/index.html +1 -1
  36. package/out/index.txt +2 -2
  37. package/out/login.html +2 -2
  38. package/out/login.txt +1 -1
  39. package/out/metrics.html +1 -1
  40. package/out/metrics.txt +2 -2
  41. package/out/pricing.html +2 -2
  42. package/out/pricing.txt +1 -1
  43. package/out/privacy.html +2 -2
  44. package/out/privacy.txt +1 -1
  45. package/out/providers/setup/claude.html +1 -1
  46. package/out/providers/setup/claude.txt +1 -1
  47. package/out/providers/setup/codex.html +1 -1
  48. package/out/providers/setup/codex.txt +1 -1
  49. package/out/providers/setup/cursor.html +1 -1
  50. package/out/providers/setup/cursor.txt +1 -1
  51. package/out/providers.html +1 -1
  52. package/out/providers.txt +1 -1
  53. package/out/security.html +2 -2
  54. package/out/security.txt +1 -1
  55. package/out/signup.html +2 -2
  56. package/out/signup.txt +1 -1
  57. package/out/terms.html +2 -2
  58. package/out/terms.txt +1 -1
  59. package/package.json +7 -1
  60. package/src/app/about/page.tsx +7 -0
  61. package/src/app/app/[[...slug]]/DashboardPageClient.tsx +853 -0
  62. package/src/app/app/[[...slug]]/page.tsx +23 -0
  63. package/src/app/app/onboarding/page.tsx +394 -0
  64. package/src/app/apple-icon.png +0 -0
  65. package/src/app/blog/go-to-bed-wake-up-to-a-finished-product/page.tsx +88 -0
  66. package/src/app/blog/let-them-cook-multi-agent-orchestration/page.tsx +93 -0
  67. package/src/app/blog/page.tsx +15 -0
  68. package/src/app/careers/page.tsx +7 -0
  69. package/src/app/changelog/page.tsx +7 -0
  70. package/src/app/cloud/link/page.tsx +464 -0
  71. package/src/app/complete-profile/page.tsx +204 -0
  72. package/src/app/connect-repos/page.tsx +410 -0
  73. package/src/app/contact/page.tsx +7 -0
  74. package/src/app/docs/page.tsx +7 -0
  75. package/src/app/favicon.png +0 -0
  76. package/src/app/globals.css +200 -0
  77. package/src/app/history/page.tsx +658 -0
  78. package/src/app/layout.tsx +25 -0
  79. package/src/app/login/page.tsx +424 -0
  80. package/src/app/metrics/page.tsx +781 -0
  81. package/src/app/page.tsx +59 -0
  82. package/src/app/pricing/page.tsx +7 -0
  83. package/src/app/privacy/page.tsx +7 -0
  84. package/src/app/providers/page.tsx +193 -0
  85. package/src/app/providers/setup/[provider]/ProviderSetupClient.tsx +197 -0
  86. package/src/app/providers/setup/[provider]/constants.ts +35 -0
  87. package/src/app/providers/setup/[provider]/page.tsx +42 -0
  88. package/src/app/security/page.tsx +7 -0
  89. package/src/app/signup/page.tsx +533 -0
  90. package/src/app/terms/page.tsx +7 -0
  91. package/src/components/ActivityFeed.tsx +216 -0
  92. package/src/components/AddWorkspaceModal.tsx +170 -0
  93. package/src/components/AgentCard.test.tsx +134 -0
  94. package/src/components/AgentCard.tsx +585 -0
  95. package/src/components/AgentList.test.tsx +147 -0
  96. package/src/components/AgentList.tsx +419 -0
  97. package/src/components/AgentLogPreview.tsx +173 -0
  98. package/src/components/AgentProfilePanel.tsx +569 -0
  99. package/src/components/App.tsx +3424 -0
  100. package/src/components/BillingPanel.tsx +922 -0
  101. package/src/components/BillingResult.tsx +447 -0
  102. package/src/components/BroadcastComposer.tsx +690 -0
  103. package/src/components/ChannelAdminPanel.tsx +773 -0
  104. package/src/components/ChannelBrowser.tsx +385 -0
  105. package/src/components/ChannelChat.tsx +261 -0
  106. package/src/components/ChannelSidebar.tsx +399 -0
  107. package/src/components/CloudSessionProvider.tsx +130 -0
  108. package/src/components/CommandPalette.tsx +815 -0
  109. package/src/components/ConfirmationDialog.tsx +133 -0
  110. package/src/components/ConversationHistory.tsx +518 -0
  111. package/src/components/CoordinatorPanel.tsx +956 -0
  112. package/src/components/DecisionQueue.tsx +717 -0
  113. package/src/components/DirectMessageView.tsx +164 -0
  114. package/src/components/FileAutocomplete.tsx +368 -0
  115. package/src/components/FleetOverview.tsx +278 -0
  116. package/src/components/LogViewer.tsx +310 -0
  117. package/src/components/LogViewerPanel.tsx +482 -0
  118. package/src/components/Logo.tsx +284 -0
  119. package/src/components/MentionAutocomplete.tsx +384 -0
  120. package/src/components/MessageComposer.tsx +473 -0
  121. package/src/components/MessageList.tsx +725 -0
  122. package/src/components/MessageSenderName.tsx +91 -0
  123. package/src/components/MessageStatusIndicator.tsx +142 -0
  124. package/src/components/NewConversationModal.tsx +400 -0
  125. package/src/components/NotificationToast.tsx +488 -0
  126. package/src/components/OnlineUsersIndicator.tsx +164 -0
  127. package/src/components/Pagination.tsx +124 -0
  128. package/src/components/PricingPlans.tsx +386 -0
  129. package/src/components/ProjectList.tsx +711 -0
  130. package/src/components/ProviderAuthFlow.tsx +343 -0
  131. package/src/components/ProviderConnectionList.tsx +375 -0
  132. package/src/components/ProvisioningProgress.tsx +730 -0
  133. package/src/components/ReactionChips.tsx +70 -0
  134. package/src/components/ReactionPicker.tsx +121 -0
  135. package/src/components/RepoAccessPanel.tsx +787 -0
  136. package/src/components/RepositoriesPanel.tsx +901 -0
  137. package/src/components/ServerCard.tsx +202 -0
  138. package/src/components/SessionExpiredModal.tsx +128 -0
  139. package/src/components/SpawnModal.test.tsx +190 -0
  140. package/src/components/SpawnModal.tsx +1001 -0
  141. package/src/components/TaskAssignmentUI.tsx +375 -0
  142. package/src/components/TerminalProviderSetup.tsx +517 -0
  143. package/src/components/ThemeProvider.tsx +159 -0
  144. package/src/components/ThinkingIndicator.tsx +231 -0
  145. package/src/components/ThreadList.tsx +198 -0
  146. package/src/components/ThreadPanel.tsx +405 -0
  147. package/src/components/TrajectoryViewer.tsx +698 -0
  148. package/src/components/TypingIndicator.tsx +69 -0
  149. package/src/components/UsageBanner.tsx +231 -0
  150. package/src/components/UserProfilePanel.tsx +233 -0
  151. package/src/components/WorkspaceContext.tsx +95 -0
  152. package/src/components/WorkspaceSelector.tsx +234 -0
  153. package/src/components/WorkspaceStatusIndicator.tsx +396 -0
  154. package/src/components/XTermInteractive.tsx +516 -0
  155. package/src/components/XTermLogViewer.tsx +719 -0
  156. package/src/components/channels/ChannelDialogs.tsx +1411 -0
  157. package/src/components/channels/ChannelHeader.tsx +317 -0
  158. package/src/components/channels/ChannelMessageList.tsx +463 -0
  159. package/src/components/channels/ChannelViewV1.tsx +146 -0
  160. package/src/components/channels/MessageInput.tsx +302 -0
  161. package/src/components/channels/SearchInput.tsx +172 -0
  162. package/src/components/channels/SearchResults.tsx +336 -0
  163. package/src/components/channels/api.test.ts +1527 -0
  164. package/src/components/channels/api.ts +703 -0
  165. package/src/components/channels/index.ts +76 -0
  166. package/src/components/channels/mockApi.ts +344 -0
  167. package/src/components/channels/types.ts +566 -0
  168. package/src/components/hooks/index.ts +58 -0
  169. package/src/components/hooks/useAgentLogs.ts +504 -0
  170. package/src/components/hooks/useAgents.ts +127 -0
  171. package/src/components/hooks/useBroadcastDedup.test.ts +371 -0
  172. package/src/components/hooks/useBroadcastDedup.ts +86 -0
  173. package/src/components/hooks/useChannelAdmin.ts +329 -0
  174. package/src/components/hooks/useChannelBrowser.ts +239 -0
  175. package/src/components/hooks/useChannelCommands.ts +138 -0
  176. package/src/components/hooks/useChannels.ts +367 -0
  177. package/src/components/hooks/useDebounce.ts +29 -0
  178. package/src/components/hooks/useDirectMessage.test.ts +952 -0
  179. package/src/components/hooks/useDirectMessage.ts +141 -0
  180. package/src/components/hooks/useMessages.ts +310 -0
  181. package/src/components/hooks/useOrchestrator.test.ts +165 -0
  182. package/src/components/hooks/useOrchestrator.ts +424 -0
  183. package/src/components/hooks/usePinnedAgents.test.ts +356 -0
  184. package/src/components/hooks/usePinnedAgents.ts +140 -0
  185. package/src/components/hooks/usePresence.test.ts +245 -0
  186. package/src/components/hooks/usePresence.ts +377 -0
  187. package/src/components/hooks/useRecentRepos.ts +130 -0
  188. package/src/components/hooks/useSession.ts +209 -0
  189. package/src/components/hooks/useThread.ts +138 -0
  190. package/src/components/hooks/useTrajectory.ts +265 -0
  191. package/src/components/hooks/useWebSocket.ts +290 -0
  192. package/src/components/hooks/useWorkspaceMembers.ts +132 -0
  193. package/src/components/hooks/useWorkspaceRepos.ts +73 -0
  194. package/src/components/hooks/useWorkspaceStatus.ts +237 -0
  195. package/src/components/index.ts +81 -0
  196. package/src/components/layout/Header.tsx +311 -0
  197. package/src/components/layout/RepoContextHeader.tsx +361 -0
  198. package/src/components/layout/Sidebar.archive.test.tsx +126 -0
  199. package/src/components/layout/Sidebar.test.tsx +691 -0
  200. package/src/components/layout/Sidebar.tsx +900 -0
  201. package/src/components/layout/index.ts +7 -0
  202. package/src/components/settings/BillingSettingsPanel.tsx +564 -0
  203. package/src/components/settings/SettingsPage.tsx +683 -0
  204. package/src/components/settings/TeamSettingsPanel.tsx +560 -0
  205. package/src/components/settings/WorkspaceSettingsPanel.tsx +1368 -0
  206. package/src/components/settings/index.ts +11 -0
  207. package/src/components/settings/types.ts +79 -0
  208. package/src/components/utils/messageFormatting.test.tsx +331 -0
  209. package/src/components/utils/messageFormatting.tsx +597 -0
  210. package/src/index.ts +63 -0
  211. package/src/landing/AboutPage.tsx +77 -0
  212. package/src/landing/BlogContent.tsx +187 -0
  213. package/src/landing/BlogPage.tsx +47 -0
  214. package/src/landing/CareersPage.tsx +53 -0
  215. package/src/landing/ChangelogPage.tsx +33 -0
  216. package/src/landing/ContactPage.tsx +41 -0
  217. package/src/landing/DocsPage.tsx +43 -0
  218. package/src/landing/LandingPage.tsx +702 -0
  219. package/src/landing/PricingPage.tsx +549 -0
  220. package/src/landing/PrivacyPage.tsx +117 -0
  221. package/src/landing/SecurityPage.tsx +42 -0
  222. package/src/landing/StaticPage.tsx +165 -0
  223. package/src/landing/TermsPage.tsx +125 -0
  224. package/src/landing/blogData.ts +312 -0
  225. package/src/landing/index.ts +18 -0
  226. package/src/landing/styles.css +3673 -0
  227. package/src/lib/agent-merge.test.ts +43 -0
  228. package/src/lib/agent-merge.ts +35 -0
  229. package/src/lib/api.ts +1294 -0
  230. package/src/lib/cloudApi.ts +893 -0
  231. package/src/lib/colors.test.ts +175 -0
  232. package/src/lib/colors.ts +218 -0
  233. package/src/lib/config.ts +109 -0
  234. package/src/lib/hierarchy.ts +242 -0
  235. package/src/lib/stuckDetection.ts +142 -0
  236. package/src/lib/useUrlRouting.ts +190 -0
  237. package/src/types/index.ts +317 -0
  238. package/src/types/threading.ts +7 -0
  239. package/out/_next/static/chunks/285-dc644487a8d6500d.js +0 -1
  240. package/out/_next/static/css/4c58d9cf493aa626.css +0 -1
  241. /package/out/_next/static/{dYlczDQI12PIQ3tqq3N4Y → IxfA6RZu4trcsEMYlkQra}/_buildManifest.js +0 -0
  242. /package/out/_next/static/{dYlczDQI12PIQ3tqq3N4Y → IxfA6RZu4trcsEMYlkQra}/_ssgManifest.js +0 -0
  243. /package/out/_next/static/chunks/{528-d375bc8b46912d2c.js → 528-f5f676996d613c25.js} +0 -0
  244. /package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/{page-a58308f43557b908.js → page-b194f207fbd91862.js} +0 -0
@@ -0,0 +1,717 @@
1
+ /**
2
+ * DecisionQueue Component
3
+ *
4
+ * Displays pending decisions from agents that require human input,
5
+ * with approve/reject actions and priority indicators.
6
+ */
7
+
8
+ import React, { useState, useMemo } from 'react';
9
+ import { getAgentColor, getAgentInitials } from '../lib/colors';
10
+
11
+ export interface Decision {
12
+ id: string;
13
+ agentName: string;
14
+ timestamp: string | number;
15
+ type: 'approval' | 'choice' | 'confirmation' | 'input';
16
+ title: string;
17
+ description: string;
18
+ options?: { id: string; label: string; description?: string }[];
19
+ priority: 'low' | 'medium' | 'high' | 'critical';
20
+ context?: Record<string, unknown>;
21
+ expiresAt?: string | number;
22
+ }
23
+
24
+ export interface DecisionQueueProps {
25
+ decisions: Decision[];
26
+ onApprove?: (decisionId: string, optionId?: string) => Promise<void>;
27
+ onReject?: (decisionId: string, reason?: string) => Promise<void>;
28
+ onDismiss?: (decisionId: string) => void;
29
+ isProcessing?: Record<string, boolean>;
30
+ }
31
+
32
+ export function DecisionQueue({
33
+ decisions,
34
+ onApprove,
35
+ onReject,
36
+ onDismiss,
37
+ isProcessing = {},
38
+ }: DecisionQueueProps) {
39
+ const [expandedDecision, setExpandedDecision] = useState<string | null>(null);
40
+ const [rejectReason, setRejectReason] = useState<Record<string, string>>({});
41
+
42
+ // Sort by priority and timestamp
43
+ const sortedDecisions = useMemo(() => {
44
+ const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
45
+ return [...decisions].sort((a, b) => {
46
+ const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
47
+ if (priorityDiff !== 0) return priorityDiff;
48
+ return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime();
49
+ });
50
+ }, [decisions]);
51
+
52
+ // Count by priority
53
+ const priorityCounts = useMemo(() => {
54
+ return decisions.reduce(
55
+ (acc, d) => {
56
+ acc[d.priority] = (acc[d.priority] || 0) + 1;
57
+ return acc;
58
+ },
59
+ {} as Record<string, number>
60
+ );
61
+ }, [decisions]);
62
+
63
+ const handleApprove = async (decision: Decision, optionId?: string) => {
64
+ await onApprove?.(decision.id, optionId);
65
+ };
66
+
67
+ const handleReject = async (decision: Decision) => {
68
+ await onReject?.(decision.id, rejectReason[decision.id]);
69
+ setRejectReason((prev) => {
70
+ const next = { ...prev };
71
+ delete next[decision.id];
72
+ return next;
73
+ });
74
+ };
75
+
76
+ if (decisions.length === 0) {
77
+ return (
78
+ <div className="decision-queue decision-queue-empty">
79
+ <CheckIcon />
80
+ <span>No pending decisions</span>
81
+ </div>
82
+ );
83
+ }
84
+
85
+ return (
86
+ <div className="decision-queue">
87
+ <div className="decision-queue-header">
88
+ <div className="decision-queue-title">
89
+ <AlertIcon />
90
+ <span>Pending Decisions</span>
91
+ <span className="decision-queue-count">{decisions.length}</span>
92
+ </div>
93
+ <div className="decision-queue-summary">
94
+ {priorityCounts.critical && (
95
+ <span className="decision-priority-badge critical">{priorityCounts.critical} critical</span>
96
+ )}
97
+ {priorityCounts.high && (
98
+ <span className="decision-priority-badge high">{priorityCounts.high} high</span>
99
+ )}
100
+ </div>
101
+ </div>
102
+
103
+ <div className="decision-queue-list">
104
+ {sortedDecisions.map((decision) => (
105
+ <DecisionCard
106
+ key={decision.id}
107
+ decision={decision}
108
+ isExpanded={expandedDecision === decision.id}
109
+ isProcessing={isProcessing[decision.id] || false}
110
+ rejectReason={rejectReason[decision.id] || ''}
111
+ onToggle={() =>
112
+ setExpandedDecision((prev) => (prev === decision.id ? null : decision.id))
113
+ }
114
+ onApprove={(optionId) => handleApprove(decision, optionId)}
115
+ onReject={() => handleReject(decision)}
116
+ onRejectReasonChange={(reason) =>
117
+ setRejectReason((prev) => ({ ...prev, [decision.id]: reason }))
118
+ }
119
+ onDismiss={() => onDismiss?.(decision.id)}
120
+ />
121
+ ))}
122
+ </div>
123
+ </div>
124
+ );
125
+ }
126
+
127
+ interface DecisionCardProps {
128
+ decision: Decision;
129
+ isExpanded: boolean;
130
+ isProcessing: boolean;
131
+ rejectReason: string;
132
+ onToggle: () => void;
133
+ onApprove: (optionId?: string) => void;
134
+ onReject: () => void;
135
+ onRejectReasonChange: (reason: string) => void;
136
+ onDismiss: () => void;
137
+ }
138
+
139
+ function DecisionCard({
140
+ decision,
141
+ isExpanded,
142
+ isProcessing,
143
+ rejectReason,
144
+ onToggle,
145
+ onApprove,
146
+ onReject,
147
+ onRejectReasonChange,
148
+ onDismiss,
149
+ }: DecisionCardProps) {
150
+ const colors = getAgentColor(decision.agentName);
151
+ const timestamp = formatTimestamp(decision.timestamp);
152
+ const timeRemaining = decision.expiresAt ? getTimeRemaining(decision.expiresAt) : null;
153
+
154
+ return (
155
+ <div className={`decision-card ${decision.priority}`}>
156
+ <div className="decision-card-header" onClick={onToggle}>
157
+ <div
158
+ className="decision-card-avatar"
159
+ style={{ backgroundColor: colors.primary, color: colors.text }}
160
+ >
161
+ {getAgentInitials(decision.agentName)}
162
+ </div>
163
+
164
+ <div className="decision-card-info">
165
+ <div className="decision-card-title">
166
+ <span className="decision-card-agent">{decision.agentName}</span>
167
+ <span className="decision-card-type">{formatType(decision.type)}</span>
168
+ <PriorityBadge priority={decision.priority} />
169
+ </div>
170
+ <div className="decision-card-subtitle">{decision.title}</div>
171
+ </div>
172
+
173
+ <div className="decision-card-meta">
174
+ {timeRemaining && (
175
+ <span className={`decision-card-expires ${timeRemaining.urgent ? 'urgent' : ''}`}>
176
+ {timeRemaining.text}
177
+ </span>
178
+ )}
179
+ <span className="decision-card-time">{timestamp}</span>
180
+ <ChevronIcon isExpanded={isExpanded} />
181
+ </div>
182
+ </div>
183
+
184
+ {isExpanded && (
185
+ <div className="decision-card-body">
186
+ <p className="decision-card-desc">{decision.description}</p>
187
+
188
+ {decision.context && Object.keys(decision.context).length > 0 && (
189
+ <div className="decision-card-context">
190
+ <span className="decision-card-context-label">Context</span>
191
+ <pre>{JSON.stringify(decision.context, null, 2)}</pre>
192
+ </div>
193
+ )}
194
+
195
+ {decision.type === 'choice' && decision.options && (
196
+ <div className="decision-card-options">
197
+ {decision.options.map((option) => (
198
+ <button
199
+ key={option.id}
200
+ className="decision-card-option"
201
+ onClick={() => onApprove(option.id)}
202
+ disabled={isProcessing}
203
+ >
204
+ <span className="decision-option-label">{option.label}</span>
205
+ {option.description && (
206
+ <span className="decision-option-desc">{option.description}</span>
207
+ )}
208
+ </button>
209
+ ))}
210
+ </div>
211
+ )}
212
+
213
+ {decision.type !== 'choice' && (
214
+ <div className="decision-card-actions">
215
+ <button
216
+ className="decision-btn decision-btn-approve"
217
+ onClick={() => onApprove()}
218
+ disabled={isProcessing}
219
+ >
220
+ {isProcessing ? <Spinner /> : <CheckIcon />}
221
+ {decision.type === 'confirmation' ? 'Confirm' : 'Approve'}
222
+ </button>
223
+
224
+ <div className="decision-reject-group">
225
+ <input
226
+ type="text"
227
+ className="decision-reject-input"
228
+ placeholder="Reason (optional)"
229
+ value={rejectReason}
230
+ onChange={(e) => onRejectReasonChange(e.target.value)}
231
+ disabled={isProcessing}
232
+ />
233
+ <button
234
+ className="decision-btn decision-btn-reject"
235
+ onClick={onReject}
236
+ disabled={isProcessing}
237
+ >
238
+ <XIcon />
239
+ Reject
240
+ </button>
241
+ </div>
242
+ </div>
243
+ )}
244
+
245
+ <button
246
+ className="decision-card-dismiss"
247
+ onClick={onDismiss}
248
+ disabled={isProcessing}
249
+ >
250
+ Dismiss without action
251
+ </button>
252
+ </div>
253
+ )}
254
+ </div>
255
+ );
256
+ }
257
+
258
+ function PriorityBadge({ priority }: { priority: Decision['priority'] }) {
259
+ return <span className={`decision-priority-badge ${priority}`}>{priority}</span>;
260
+ }
261
+
262
+ // Helper functions
263
+ function formatTimestamp(ts: string | number): string {
264
+ const date = new Date(ts);
265
+ const now = new Date();
266
+ const diffMs = now.getTime() - date.getTime();
267
+ const diffMins = Math.floor(diffMs / 60000);
268
+
269
+ if (diffMins < 1) return 'just now';
270
+ if (diffMins < 60) return `${diffMins}m ago`;
271
+ if (diffMins < 1440) return `${Math.floor(diffMins / 60)}h ago`;
272
+ return date.toLocaleDateString();
273
+ }
274
+
275
+ function formatType(type: Decision['type']): string {
276
+ const labels: Record<Decision['type'], string> = {
277
+ approval: 'Approval',
278
+ choice: 'Choice',
279
+ confirmation: 'Confirm',
280
+ input: 'Input',
281
+ };
282
+ return labels[type];
283
+ }
284
+
285
+ function getTimeRemaining(expiresAt: string | number): { text: string; urgent: boolean } | null {
286
+ const expiry = new Date(expiresAt);
287
+ const now = new Date();
288
+ const diffMs = expiry.getTime() - now.getTime();
289
+
290
+ if (diffMs <= 0) return { text: 'Expired', urgent: true };
291
+
292
+ const diffMins = Math.floor(diffMs / 60000);
293
+ if (diffMins < 5) return { text: `${diffMins}m left`, urgent: true };
294
+ if (diffMins < 60) return { text: `${diffMins}m left`, urgent: false };
295
+ return { text: `${Math.floor(diffMins / 60)}h left`, urgent: false };
296
+ }
297
+
298
+ // Icon components
299
+ function AlertIcon() {
300
+ return (
301
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
302
+ <circle cx="12" cy="12" r="10" />
303
+ <line x1="12" y1="8" x2="12" y2="12" />
304
+ <line x1="12" y1="16" x2="12.01" y2="16" />
305
+ </svg>
306
+ );
307
+ }
308
+
309
+ function CheckIcon() {
310
+ return (
311
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
312
+ <polyline points="20 6 9 17 4 12" />
313
+ </svg>
314
+ );
315
+ }
316
+
317
+ function XIcon() {
318
+ return (
319
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
320
+ <line x1="18" y1="6" x2="6" y2="18" />
321
+ <line x1="6" y1="6" x2="18" y2="18" />
322
+ </svg>
323
+ );
324
+ }
325
+
326
+ function ChevronIcon({ isExpanded }: { isExpanded: boolean }) {
327
+ return (
328
+ <svg
329
+ width="14"
330
+ height="14"
331
+ viewBox="0 0 24 24"
332
+ fill="none"
333
+ stroke="currentColor"
334
+ strokeWidth="2"
335
+ style={{
336
+ transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',
337
+ transition: 'transform 0.2s',
338
+ }}
339
+ >
340
+ <polyline points="6 9 12 15 18 9" />
341
+ </svg>
342
+ );
343
+ }
344
+
345
+ function Spinner() {
346
+ return (
347
+ <svg className="decision-spinner" width="14" height="14" viewBox="0 0 24 24">
348
+ <circle
349
+ cx="12"
350
+ cy="12"
351
+ r="10"
352
+ stroke="currentColor"
353
+ strokeWidth="2"
354
+ fill="none"
355
+ strokeDasharray="32"
356
+ strokeLinecap="round"
357
+ />
358
+ </svg>
359
+ );
360
+ }
361
+
362
+ /**
363
+ * CSS styles for the decision queue
364
+ */
365
+ export const decisionQueueStyles = `
366
+ .decision-queue {
367
+ background: #ffffff;
368
+ border-radius: 8px;
369
+ border: 1px solid #e8e8e8;
370
+ overflow: hidden;
371
+ }
372
+
373
+ .decision-queue-empty {
374
+ display: flex;
375
+ align-items: center;
376
+ justify-content: center;
377
+ gap: 8px;
378
+ padding: 24px;
379
+ color: #10b981;
380
+ font-size: 13px;
381
+ }
382
+
383
+ .decision-queue-header {
384
+ display: flex;
385
+ align-items: center;
386
+ justify-content: space-between;
387
+ padding: 12px 16px;
388
+ border-bottom: 1px solid #e8e8e8;
389
+ background: #fafafa;
390
+ }
391
+
392
+ .decision-queue-title {
393
+ display: flex;
394
+ align-items: center;
395
+ gap: 8px;
396
+ font-weight: 600;
397
+ font-size: 14px;
398
+ color: #333;
399
+ }
400
+
401
+ .decision-queue-title svg {
402
+ color: #f59e0b;
403
+ }
404
+
405
+ .decision-queue-count {
406
+ font-weight: 600;
407
+ font-size: 12px;
408
+ color: #ffffff;
409
+ background: #f59e0b;
410
+ padding: 2px 8px;
411
+ border-radius: 10px;
412
+ }
413
+
414
+ .decision-queue-summary {
415
+ display: flex;
416
+ gap: 8px;
417
+ }
418
+
419
+ .decision-queue-list {
420
+ max-height: 500px;
421
+ overflow-y: auto;
422
+ }
423
+
424
+ .decision-card {
425
+ border-bottom: 1px solid #e8e8e8;
426
+ }
427
+
428
+ .decision-card:last-child {
429
+ border-bottom: none;
430
+ }
431
+
432
+ .decision-card.critical {
433
+ background: linear-gradient(to right, #fef2f2 0%, #ffffff 20%);
434
+ }
435
+
436
+ .decision-card.high {
437
+ background: linear-gradient(to right, #fffbeb 0%, #ffffff 20%);
438
+ }
439
+
440
+ .decision-card-header {
441
+ display: flex;
442
+ align-items: center;
443
+ gap: 12px;
444
+ padding: 12px 16px;
445
+ cursor: pointer;
446
+ transition: background 0.15s;
447
+ }
448
+
449
+ .decision-card-header:hover {
450
+ background: rgba(0, 0, 0, 0.02);
451
+ }
452
+
453
+ .decision-card-avatar {
454
+ width: 36px;
455
+ height: 36px;
456
+ border-radius: 8px;
457
+ display: flex;
458
+ align-items: center;
459
+ justify-content: center;
460
+ font-size: 12px;
461
+ font-weight: 600;
462
+ flex-shrink: 0;
463
+ }
464
+
465
+ .decision-card-info {
466
+ flex: 1;
467
+ min-width: 0;
468
+ }
469
+
470
+ .decision-card-title {
471
+ display: flex;
472
+ align-items: center;
473
+ gap: 8px;
474
+ }
475
+
476
+ .decision-card-agent {
477
+ font-weight: 600;
478
+ font-size: 13px;
479
+ color: #333;
480
+ }
481
+
482
+ .decision-card-type {
483
+ font-size: 11px;
484
+ color: #888;
485
+ background: #f0f0f0;
486
+ padding: 2px 6px;
487
+ border-radius: 3px;
488
+ }
489
+
490
+ .decision-card-subtitle {
491
+ font-size: 13px;
492
+ color: #555;
493
+ margin-top: 2px;
494
+ white-space: nowrap;
495
+ overflow: hidden;
496
+ text-overflow: ellipsis;
497
+ }
498
+
499
+ .decision-card-meta {
500
+ display: flex;
501
+ align-items: center;
502
+ gap: 12px;
503
+ flex-shrink: 0;
504
+ }
505
+
506
+ .decision-card-expires {
507
+ font-size: 11px;
508
+ color: #888;
509
+ }
510
+
511
+ .decision-card-expires.urgent {
512
+ color: #ef4444;
513
+ font-weight: 500;
514
+ }
515
+
516
+ .decision-card-time {
517
+ font-size: 11px;
518
+ color: #888;
519
+ }
520
+
521
+ .decision-card-body {
522
+ padding: 0 16px 16px 64px;
523
+ }
524
+
525
+ .decision-card-desc {
526
+ margin: 0 0 16px;
527
+ font-size: 13px;
528
+ color: #555;
529
+ line-height: 1.5;
530
+ }
531
+
532
+ .decision-card-context {
533
+ background: #f5f5f5;
534
+ border-radius: 6px;
535
+ padding: 12px;
536
+ margin-bottom: 16px;
537
+ }
538
+
539
+ .decision-card-context-label {
540
+ display: block;
541
+ font-size: 11px;
542
+ font-weight: 600;
543
+ color: #888;
544
+ text-transform: uppercase;
545
+ margin-bottom: 8px;
546
+ }
547
+
548
+ .decision-card-context pre {
549
+ margin: 0;
550
+ font-size: 12px;
551
+ font-family: 'SF Mono', monospace;
552
+ color: #333;
553
+ white-space: pre-wrap;
554
+ word-break: break-word;
555
+ }
556
+
557
+ .decision-card-options {
558
+ display: flex;
559
+ flex-direction: column;
560
+ gap: 8px;
561
+ margin-bottom: 16px;
562
+ }
563
+
564
+ .decision-card-option {
565
+ display: flex;
566
+ flex-direction: column;
567
+ align-items: flex-start;
568
+ padding: 12px 16px;
569
+ background: #fafafa;
570
+ border: 1px solid #e8e8e8;
571
+ border-radius: 6px;
572
+ cursor: pointer;
573
+ font-family: inherit;
574
+ text-align: left;
575
+ transition: all 0.15s;
576
+ }
577
+
578
+ .decision-card-option:hover:not(:disabled) {
579
+ background: #f0f0f0;
580
+ border-color: #d0d0d0;
581
+ }
582
+
583
+ .decision-card-option:disabled {
584
+ opacity: 0.5;
585
+ cursor: not-allowed;
586
+ }
587
+
588
+ .decision-option-label {
589
+ font-size: 13px;
590
+ font-weight: 500;
591
+ color: #333;
592
+ }
593
+
594
+ .decision-option-desc {
595
+ font-size: 12px;
596
+ color: #888;
597
+ margin-top: 2px;
598
+ }
599
+
600
+ .decision-card-actions {
601
+ display: flex;
602
+ flex-direction: column;
603
+ gap: 12px;
604
+ }
605
+
606
+ .decision-btn {
607
+ display: flex;
608
+ align-items: center;
609
+ justify-content: center;
610
+ gap: 6px;
611
+ padding: 10px 16px;
612
+ border: none;
613
+ border-radius: 6px;
614
+ font-size: 13px;
615
+ font-weight: 500;
616
+ cursor: pointer;
617
+ font-family: inherit;
618
+ transition: all 0.15s;
619
+ }
620
+
621
+ .decision-btn:disabled {
622
+ opacity: 0.5;
623
+ cursor: not-allowed;
624
+ }
625
+
626
+ .decision-btn-approve {
627
+ background: #10b981;
628
+ color: #ffffff;
629
+ }
630
+
631
+ .decision-btn-approve:hover:not(:disabled) {
632
+ background: #059669;
633
+ }
634
+
635
+ .decision-reject-group {
636
+ display: flex;
637
+ gap: 8px;
638
+ }
639
+
640
+ .decision-reject-input {
641
+ flex: 1;
642
+ padding: 10px 12px;
643
+ border: 1px solid #e8e8e8;
644
+ border-radius: 6px;
645
+ font-size: 13px;
646
+ font-family: inherit;
647
+ outline: none;
648
+ }
649
+
650
+ .decision-reject-input:focus {
651
+ border-color: #1264a3;
652
+ }
653
+
654
+ .decision-btn-reject {
655
+ background: #ffffff;
656
+ color: #ef4444;
657
+ border: 1px solid #fecaca;
658
+ }
659
+
660
+ .decision-btn-reject:hover:not(:disabled) {
661
+ background: #fef2f2;
662
+ border-color: #ef4444;
663
+ }
664
+
665
+ .decision-card-dismiss {
666
+ margin-top: 12px;
667
+ padding: 8px;
668
+ background: transparent;
669
+ border: none;
670
+ color: #888;
671
+ font-size: 12px;
672
+ cursor: pointer;
673
+ font-family: inherit;
674
+ transition: color 0.15s;
675
+ }
676
+
677
+ .decision-card-dismiss:hover:not(:disabled) {
678
+ color: #333;
679
+ }
680
+
681
+ .decision-priority-badge {
682
+ font-size: 10px;
683
+ padding: 2px 6px;
684
+ border-radius: 3px;
685
+ text-transform: uppercase;
686
+ font-weight: 600;
687
+ }
688
+
689
+ .decision-priority-badge.critical {
690
+ background: #fecaca;
691
+ color: #dc2626;
692
+ }
693
+
694
+ .decision-priority-badge.high {
695
+ background: #fed7aa;
696
+ color: #ea580c;
697
+ }
698
+
699
+ .decision-priority-badge.medium {
700
+ background: #fef08a;
701
+ color: #ca8a04;
702
+ }
703
+
704
+ .decision-priority-badge.low {
705
+ background: #e0e7ff;
706
+ color: #4f46e5;
707
+ }
708
+
709
+ .decision-spinner {
710
+ animation: spin 1s linear infinite;
711
+ }
712
+
713
+ @keyframes spin {
714
+ from { transform: rotate(0deg); }
715
+ to { transform: rotate(360deg); }
716
+ }
717
+ `;