@roj-ai/debug 0.0.2

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 (77) hide show
  1. package/dist/components/debug/DebugContext.d.ts +10 -0
  2. package/dist/components/debug/DebugNavigation.d.ts +29 -0
  3. package/dist/components/debug/DebugShell.d.ts +18 -0
  4. package/dist/components/debug/LLMCallDetail.d.ts +7 -0
  5. package/dist/components/debug/TimelineDetailInspector.d.ts +6 -0
  6. package/dist/components/debug/communication/CommunicationDiagram.d.ts +9 -0
  7. package/dist/components/debug/communication/DiagramHeader.d.ts +7 -0
  8. package/dist/components/debug/communication/ParticipantLane.d.ts +7 -0
  9. package/dist/components/debug/communication/TimeAxis.d.ts +9 -0
  10. package/dist/components/debug/communication/elements/IdleGap.d.ts +9 -0
  11. package/dist/components/debug/communication/elements/LLMBlock.d.ts +9 -0
  12. package/dist/components/debug/communication/elements/MessageArrow.d.ts +10 -0
  13. package/dist/components/debug/communication/elements/ToolBlock.d.ts +9 -0
  14. package/dist/components/debug/communication/hooks/useDiagramData.d.ts +12 -0
  15. package/dist/components/debug/communication/hooks/useTimeCompression.d.ts +7 -0
  16. package/dist/components/debug/communication/hooks/useZoomPan.d.ts +11 -0
  17. package/dist/components/debug/communication/popovers/ElementPopover.d.ts +8 -0
  18. package/dist/components/debug/communication/types.d.ts +136 -0
  19. package/dist/components/debug/index.d.ts +11 -0
  20. package/dist/components/debug/pages/AgentDetailPage.d.ts +3 -0
  21. package/dist/components/debug/pages/AgentsPage.d.ts +1 -0
  22. package/dist/components/debug/pages/CommunicationPage.d.ts +1 -0
  23. package/dist/components/debug/pages/DashboardPage.d.ts +1 -0
  24. package/dist/components/debug/pages/EventsPage.d.ts +1 -0
  25. package/dist/components/debug/pages/FilesPage.d.ts +1 -0
  26. package/dist/components/debug/pages/LLMCallPage.d.ts +1 -0
  27. package/dist/components/debug/pages/LLMCallsPage.d.ts +1 -0
  28. package/dist/components/debug/pages/LogsPage.d.ts +1 -0
  29. package/dist/components/debug/pages/MailboxPage.d.ts +1 -0
  30. package/dist/components/debug/pages/ServicesPage.d.ts +1 -0
  31. package/dist/components/debug/pages/TimelinePage.d.ts +1 -0
  32. package/dist/components/debug/pages/UserChatPage.d.ts +1 -0
  33. package/dist/components/debug/pages/index.d.ts +13 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/lib/domain-utils.d.ts +7 -0
  36. package/dist/providers/EventPollingProvider.d.ts +27 -0
  37. package/dist/stores/event-store.d.ts +93 -0
  38. package/dist/utils/format.d.ts +1 -0
  39. package/package.json +43 -0
  40. package/src/components/debug/DebugContext.tsx +18 -0
  41. package/src/components/debug/DebugNavigation.tsx +55 -0
  42. package/src/components/debug/DebugShell.tsx +321 -0
  43. package/src/components/debug/LLMCallDetail.tsx +740 -0
  44. package/src/components/debug/TimelineDetailInspector.tsx +204 -0
  45. package/src/components/debug/communication/CommunicationDiagram.tsx +260 -0
  46. package/src/components/debug/communication/DiagramHeader.tsx +113 -0
  47. package/src/components/debug/communication/ParticipantLane.tsx +60 -0
  48. package/src/components/debug/communication/TimeAxis.tsx +106 -0
  49. package/src/components/debug/communication/elements/IdleGap.tsx +90 -0
  50. package/src/components/debug/communication/elements/LLMBlock.tsx +107 -0
  51. package/src/components/debug/communication/elements/MessageArrow.tsx +119 -0
  52. package/src/components/debug/communication/elements/ToolBlock.tsx +99 -0
  53. package/src/components/debug/communication/hooks/useDiagramData.ts +294 -0
  54. package/src/components/debug/communication/hooks/useTimeCompression.ts +140 -0
  55. package/src/components/debug/communication/hooks/useZoomPan.ts +87 -0
  56. package/src/components/debug/communication/popovers/ElementPopover.tsx +158 -0
  57. package/src/components/debug/communication/types.ts +180 -0
  58. package/src/components/debug/index.ts +37 -0
  59. package/src/components/debug/pages/AgentDetailPage.tsx +1295 -0
  60. package/src/components/debug/pages/AgentsPage.tsx +297 -0
  61. package/src/components/debug/pages/CommunicationPage.tsx +89 -0
  62. package/src/components/debug/pages/DashboardPage.tsx +1504 -0
  63. package/src/components/debug/pages/EventsPage.tsx +276 -0
  64. package/src/components/debug/pages/FilesPage.tsx +366 -0
  65. package/src/components/debug/pages/LLMCallPage.tsx +32 -0
  66. package/src/components/debug/pages/LLMCallsPage.tsx +473 -0
  67. package/src/components/debug/pages/LogsPage.tsx +199 -0
  68. package/src/components/debug/pages/MailboxPage.tsx +232 -0
  69. package/src/components/debug/pages/ServicesPage.tsx +193 -0
  70. package/src/components/debug/pages/TimelinePage.tsx +569 -0
  71. package/src/components/debug/pages/UserChatPage.tsx +250 -0
  72. package/src/components/debug/pages/index.ts +13 -0
  73. package/src/index.ts +55 -0
  74. package/src/lib/domain-utils.ts +12 -0
  75. package/src/providers/EventPollingProvider.tsx +60 -0
  76. package/src/stores/event-store.ts +497 -0
  77. package/src/utils/format.ts +8 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Unified navigation utilities for debug UI.
3
+ *
4
+ * All navigation goes through DebugContext, which is provided by the host app
5
+ * (admin via buzola, worker SPA via react-router, etc.).
6
+ */
7
+
8
+ import type { SessionId } from '@roj-ai/shared'
9
+ import type { ReactNode } from 'react'
10
+ import { useCallback } from 'react'
11
+ import { useDebugContext } from './DebugContext'
12
+
13
+ /**
14
+ * Get the session ID from the debug context.
15
+ */
16
+ export function useDebugSessionId(): SessionId {
17
+ return useDebugContext().sessionId
18
+ }
19
+
20
+ /**
21
+ * Get route params from the debug context.
22
+ */
23
+ export function useDebugParams<T extends Record<string, string | undefined>>(): T {
24
+ return useDebugContext().params as T
25
+ }
26
+
27
+ /**
28
+ * Navigate to a debug subpath (e.g., "agents/abc123", "llm-calls/xyz").
29
+ */
30
+ export function useDebugNavigate(): (subpath: string) => void {
31
+ const { navigate } = useDebugContext()
32
+ return useCallback((subpath: string) => navigate(subpath), [navigate])
33
+ }
34
+
35
+ /**
36
+ * Link component for debug navigation.
37
+ * Renders an <a> tag with proper href for right-click/open-in-new-tab support.
38
+ */
39
+ export function DebugLink({ to, className, children }: { to: string; className?: string; children: ReactNode }) {
40
+ const { navigate, createHref } = useDebugContext()
41
+
42
+ return (
43
+ <a
44
+ href={createHref(to)}
45
+ className={className}
46
+ onClick={(e) => {
47
+ if (e.metaKey || e.ctrlKey || e.shiftKey) return
48
+ e.preventDefault()
49
+ navigate(to)
50
+ }}
51
+ >
52
+ {children}
53
+ </a>
54
+ )
55
+ }
@@ -0,0 +1,321 @@
1
+ import { type ReactNode, useState } from 'react'
2
+ import { api, unwrap } from '@roj-ai/client'
3
+ import { useEventPolling } from '../../providers/EventPollingProvider'
4
+ import { useEventStore, useMetrics } from '../../stores/event-store'
5
+
6
+ export interface NavItem {
7
+ to: string
8
+ label: string
9
+ icon: React.FC
10
+ }
11
+
12
+ export const navItems: NavItem[] = [
13
+ { to: 'dashboard', label: 'Dashboard', icon: DashboardIcon },
14
+ { to: 'agents', label: 'Agents', icon: AgentsIcon },
15
+ { to: 'communication', label: 'Communication', icon: CommunicationIcon },
16
+ { to: 'user-chat', label: 'User Chat', icon: UserChatIcon },
17
+ { to: 'timeline', label: 'Timeline', icon: TimelineIcon },
18
+ { to: 'llm-calls', label: 'LLM Calls', icon: LLMIcon },
19
+ { to: 'events', label: 'Events', icon: EventsIcon },
20
+ { to: 'mailbox', label: 'Mailbox', icon: MailboxIcon },
21
+ { to: 'files', label: 'Files', icon: FilesIcon },
22
+ { to: 'services', label: 'Services', icon: ServicesIcon },
23
+ { to: 'logs', label: 'Logs', icon: LogsIcon },
24
+ ]
25
+
26
+ interface DebugShellProps {
27
+ sessionId: string
28
+ children: ReactNode
29
+ className?: string
30
+ renderNavItem: (item: NavItem) => ReactNode
31
+ sidebarFooter?: ReactNode
32
+ }
33
+
34
+ export function DebugShell({ sessionId, children, className = 'fixed inset-0 flex flex-col', renderNavItem, sidebarFooter }: DebugShellProps) {
35
+ const { isLoading } = useEventPolling(sessionId)
36
+ const metrics = useMetrics()
37
+ const hasEvents = useEventStore((s) => s.events.length > 0)
38
+
39
+ return (
40
+ <div className={`${className} bg-surface`}>
41
+ {/* Header */}
42
+ <header className="h-14 bg-white shadow-card flex items-center px-5 shrink-0 z-10">
43
+ <div className="flex items-center gap-3">
44
+ <span className="bg-accent-lime rounded-full px-4 py-1.5 text-sm font-bold text-gray-900">
45
+ Roj
46
+ </span>
47
+ <span className="text-gray-300">/</span>
48
+ <span className="text-sm text-gray-400 font-mono">{sessionId.slice(0, 8)}</span>
49
+ <span className="text-gray-300">/</span>
50
+ <span className="text-sm font-semibold text-gray-700">Debug</span>
51
+ </div>
52
+
53
+ {/* Metrics in header */}
54
+ <div className="ml-auto flex items-center gap-5 text-sm">
55
+ {hasEvents && (
56
+ <>
57
+ {isLoading && <span className="text-gray-400 text-xs animate-pulse">Loading...</span>}
58
+ <MetricBadge label="Tokens" value={metrics.totalTokens.toLocaleString()} />
59
+ <MetricBadge label="LLM" value={metrics.llmCalls.toString()} />
60
+ <MetricBadge label="Tools" value={metrics.toolCalls.toString()} />
61
+ <MetricBadge label="Agents" value={metrics.agentCount.toString()} />
62
+ {metrics.totalCost !== undefined && metrics.totalCost > 0 && (
63
+ <MetricBadge
64
+ label="Cost"
65
+ value={`$${metrics.totalCost.toFixed(4)}`}
66
+ className="text-green-600"
67
+ />
68
+ )}
69
+ </>
70
+ )}
71
+ <SessionActionButton sessionId={sessionId} />
72
+ </div>
73
+ </header>
74
+
75
+ <div className="flex flex-1 overflow-hidden">
76
+ {/* Sidebar */}
77
+ <nav className="w-52 bg-white shadow-card flex flex-col shrink-0 z-[5]">
78
+ <div className="flex-1 py-3 px-3 space-y-0.5">
79
+ {navItems.map((item) => renderNavItem(item))}
80
+ </div>
81
+
82
+ {sidebarFooter && (
83
+ <div className="p-3 border-t border-gray-100">
84
+ {sidebarFooter}
85
+ </div>
86
+ )}
87
+ </nav>
88
+
89
+ {/* Main content */}
90
+ <main className="flex-1 overflow-auto p-5">
91
+ {children}
92
+ </main>
93
+ </div>
94
+ </div>
95
+ )
96
+ }
97
+
98
+ export function getNavItemClassName(isActive: boolean) {
99
+ return `flex items-center gap-3 px-4 py-2.5 text-sm rounded-2xl transition-colors ${
100
+ isActive
101
+ ? 'bg-accent-lime text-gray-900 font-semibold'
102
+ : 'text-gray-500 hover:bg-gray-100 hover:text-gray-800'
103
+ }`
104
+ }
105
+
106
+ function SessionActionButton({ sessionId }: { sessionId: string }) {
107
+ const [loading, setLoading] = useState(false)
108
+ const status = useEventStore((s) => s.sessionInfoState.id ? s.sessionInfoState.status : null)
109
+
110
+ if (!status) return null
111
+
112
+ const handleAction = async () => {
113
+ if (loading) return
114
+ setLoading(true)
115
+ try {
116
+ if (status === 'active') {
117
+ unwrap(await api.call('sessions.close', { sessionId }))
118
+ } else {
119
+ unwrap(await api.call('sessions.reopen', { sessionId }))
120
+ }
121
+ } catch (e) {
122
+ console.error(`Failed to ${status === 'active' ? 'stop' : 'reopen'} session:`, e)
123
+ } finally {
124
+ setLoading(false)
125
+ }
126
+ }
127
+
128
+ if (status === 'active') {
129
+ return (
130
+ <button
131
+ onClick={handleAction}
132
+ disabled={loading}
133
+ className={`px-4 py-1.5 text-xs font-medium rounded-full transition-colors ${
134
+ loading
135
+ ? 'bg-red-500 text-white cursor-wait opacity-70'
136
+ : 'bg-gray-900 text-white hover:bg-gray-800'
137
+ }`}
138
+ >
139
+ {loading ? 'Stopping...' : 'Stop Session'}
140
+ </button>
141
+ )
142
+ }
143
+
144
+ return (
145
+ <button
146
+ onClick={handleAction}
147
+ disabled={loading}
148
+ className={`px-4 py-1.5 text-xs font-medium rounded-full transition-colors ${
149
+ loading
150
+ ? 'bg-green-500 text-white cursor-wait opacity-70'
151
+ : 'bg-green-600 text-white hover:bg-green-500'
152
+ }`}
153
+ >
154
+ {loading ? 'Reopening...' : 'Reopen Session'}
155
+ </button>
156
+ )
157
+ }
158
+
159
+ function MetricBadge({
160
+ label,
161
+ value,
162
+ className,
163
+ }: {
164
+ label: string
165
+ value: string
166
+ className?: string
167
+ }) {
168
+ return (
169
+ <div className="flex items-center gap-1.5">
170
+ <span className="text-gray-400 text-xs">{label}</span>
171
+ <span className={`font-semibold text-sm ${className || 'text-gray-700'}`}>{value}</span>
172
+ </div>
173
+ )
174
+ }
175
+
176
+ // Icons — thin-line, rounded endpoints, 1.5px stroke
177
+ function DashboardIcon() {
178
+ return (
179
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
180
+ <path
181
+ strokeLinecap="round"
182
+ strokeLinejoin="round"
183
+ strokeWidth={1.5}
184
+ d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"
185
+ />
186
+ </svg>
187
+ )
188
+ }
189
+
190
+ function AgentsIcon() {
191
+ return (
192
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
193
+ <path
194
+ strokeLinecap="round"
195
+ strokeLinejoin="round"
196
+ strokeWidth={1.5}
197
+ d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
198
+ />
199
+ </svg>
200
+ )
201
+ }
202
+
203
+ function TimelineIcon() {
204
+ return (
205
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
206
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M13 10V3L4 14h7v7l9-11h-7z" />
207
+ </svg>
208
+ )
209
+ }
210
+
211
+ function LLMIcon() {
212
+ return (
213
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
214
+ <path
215
+ strokeLinecap="round"
216
+ strokeLinejoin="round"
217
+ strokeWidth={1.5}
218
+ d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
219
+ />
220
+ </svg>
221
+ )
222
+ }
223
+
224
+ function EventsIcon() {
225
+ return (
226
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
227
+ <path
228
+ strokeLinecap="round"
229
+ strokeLinejoin="round"
230
+ strokeWidth={1.5}
231
+ d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"
232
+ />
233
+ </svg>
234
+ )
235
+ }
236
+
237
+ function MailboxIcon() {
238
+ return (
239
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
240
+ <path
241
+ strokeLinecap="round"
242
+ strokeLinejoin="round"
243
+ strokeWidth={1.5}
244
+ d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
245
+ />
246
+ </svg>
247
+ )
248
+ }
249
+
250
+ function FilesIcon() {
251
+ return (
252
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
253
+ <path
254
+ strokeLinecap="round"
255
+ strokeLinejoin="round"
256
+ strokeWidth={1.5}
257
+ d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"
258
+ />
259
+ </svg>
260
+ )
261
+ }
262
+
263
+ function ServicesIcon() {
264
+ return (
265
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
266
+ <path
267
+ strokeLinecap="round"
268
+ strokeLinejoin="round"
269
+ strokeWidth={1.5}
270
+ d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"
271
+ />
272
+ </svg>
273
+ )
274
+ }
275
+
276
+ function LogsIcon() {
277
+ return (
278
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
279
+ <path
280
+ strokeLinecap="round"
281
+ strokeLinejoin="round"
282
+ strokeWidth={1.5}
283
+ d="M4 6h16M4 10h16M4 14h10M4 18h7"
284
+ />
285
+ </svg>
286
+ )
287
+ }
288
+
289
+ export function BackIcon() {
290
+ return (
291
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
292
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
293
+ </svg>
294
+ )
295
+ }
296
+
297
+ function CommunicationIcon() {
298
+ return (
299
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
300
+ <path
301
+ strokeLinecap="round"
302
+ strokeLinejoin="round"
303
+ strokeWidth={1.5}
304
+ d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
305
+ />
306
+ </svg>
307
+ )
308
+ }
309
+
310
+ function UserChatIcon() {
311
+ return (
312
+ <svg className="w-[18px] h-[18px]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
313
+ <path
314
+ strokeLinecap="round"
315
+ strokeLinejoin="round"
316
+ strokeWidth={1.5}
317
+ d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z"
318
+ />
319
+ </svg>
320
+ )
321
+ }