@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,250 @@
1
+ import type { DebugChatMessage } from '@roj-ai/shared'
2
+ import { useState } from 'react'
3
+ import { DebugLink } from '../DebugNavigation'
4
+ import { useChatDebug, useEventStore } from '../../../stores/event-store'
5
+
6
+ export function UserChatPage() {
7
+
8
+ // Get chat debug messages from event store
9
+ const messages = useChatDebug()
10
+ const isLoading = useEventStore((s) => s.isLoading)
11
+ const error = useEventStore((s) => s.error)
12
+
13
+ const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set())
14
+
15
+ const toggleExpanded = (id: string) => {
16
+ setExpandedIds((prev) => {
17
+ const next = new Set(prev)
18
+ if (next.has(id)) {
19
+ next.delete(id)
20
+ } else {
21
+ next.add(id)
22
+ }
23
+ return next
24
+ })
25
+ }
26
+
27
+ // Calculate stats
28
+ const total = messages.length
29
+ const userCount = messages.filter((m) => m.type === 'user_message').length
30
+ const agentCount = messages.filter((m) => m.type === 'agent_message').length
31
+ const askUserCount = messages.filter((m) => m.type === 'ask_user').length
32
+ const answeredCount = messages.filter((m) => m.type === 'ask_user' && m.answered).length
33
+
34
+ return (
35
+ <div className="space-y-4">
36
+ {/* Summary */}
37
+ <div className="flex items-center gap-6 text-sm">
38
+ <span className="text-slate-600">
39
+ <span className="font-medium text-slate-900">{total}</span> messages
40
+ </span>
41
+ <span className="text-slate-600">
42
+ <span className="font-medium text-blue-600">{userCount}</span> user
43
+ </span>
44
+ <span className="text-slate-600">
45
+ <span className="font-medium text-green-600">{agentCount}</span> agent
46
+ </span>
47
+ {askUserCount > 0 && (
48
+ <span className="text-slate-600">
49
+ <span className="font-medium text-purple-600">{askUserCount}</span> questions
50
+ {answeredCount > 0 && <span className="text-slate-500">({answeredCount} answered)</span>}
51
+ </span>
52
+ )}
53
+ </div>
54
+
55
+ {/* Error */}
56
+ {error && <div className="text-red-500 text-sm">{error}</div>}
57
+
58
+ {/* Loading */}
59
+ {isLoading && messages.length === 0 && <div className="text-slate-500 text-sm">Loading chat messages...</div>}
60
+
61
+ {/* Table */}
62
+ {messages.length > 0 && (
63
+ <div className="bg-white rounded-md border border-slate-200 overflow-hidden">
64
+ <div className="overflow-x-auto">
65
+ <table className="w-full text-sm">
66
+ <thead className="bg-slate-50 border-b border-slate-200">
67
+ <tr>
68
+ <th className="px-3 py-2 text-left font-medium text-slate-600 w-20">Type</th>
69
+ <th className="px-3 py-2 text-left font-medium text-slate-600">Content</th>
70
+ <th className="px-3 py-2 text-left font-medium text-slate-600 w-24">Agent</th>
71
+ <th className="px-3 py-2 text-left font-medium text-slate-600 w-32">Links</th>
72
+ <th className="px-3 py-2 text-left font-medium text-slate-600 w-20">Time</th>
73
+ <th className="px-3 py-2 text-left font-medium text-slate-600 w-16">Event</th>
74
+ </tr>
75
+ </thead>
76
+ <tbody className="divide-y divide-slate-200">
77
+ {messages.map((msg) => (
78
+ <MessageRow
79
+ key={msg.messageId}
80
+ message={msg}
81
+ isExpanded={expandedIds.has(msg.messageId)}
82
+ onToggleExpand={() => toggleExpanded(msg.messageId)}
83
+ />
84
+ ))}
85
+ </tbody>
86
+ </table>
87
+ </div>
88
+ </div>
89
+ )}
90
+
91
+ {/* Empty state */}
92
+ {!isLoading && messages.length === 0 && <div className="text-slate-500 text-sm">No chat messages found</div>}
93
+ </div>
94
+ )
95
+ }
96
+
97
+ function MessageRow({
98
+ message,
99
+ isExpanded,
100
+ onToggleExpand,
101
+ }: {
102
+ message: DebugChatMessage
103
+ isExpanded: boolean
104
+ onToggleExpand: () => void
105
+ }) {
106
+ const isLongMessage = message.content.length > 100
107
+ const displayContent = isExpanded
108
+ ? message.content
109
+ : message.content.slice(0, 100) + (isLongMessage ? '...' : '')
110
+
111
+ return (
112
+ <tr className="hover:bg-slate-50">
113
+ {/* Type */}
114
+ <td className="px-3 py-2">
115
+ <TypeBadge type={message.type} answered={message.answered} />
116
+ </td>
117
+
118
+ {/* Content */}
119
+ <td className="px-3 py-2">
120
+ <div
121
+ className={`text-slate-700 ${isLongMessage ? 'cursor-pointer' : ''}`}
122
+ onClick={isLongMessage ? onToggleExpand : undefined}
123
+ >
124
+ <span className="whitespace-pre-wrap break-words">{displayContent}</span>
125
+ {isLongMessage && (
126
+ <button
127
+ onClick={(e) => {
128
+ e.stopPropagation()
129
+ onToggleExpand()
130
+ }}
131
+ className="ml-2 text-violet-600 hover:text-violet-800 text-xs"
132
+ >
133
+ {isExpanded ? 'less' : 'more'}
134
+ </button>
135
+ )}
136
+ </div>
137
+ {/* Show answer for ask_user messages */}
138
+ {message.type === 'ask_user' && message.answered && (
139
+ <div className="mt-1 text-xs text-slate-500">
140
+ Answer: <span className="font-mono">{JSON.stringify(message.answer)}</span>
141
+ </div>
142
+ )}
143
+ {/* Show input type for ask_user messages */}
144
+ {message.type === 'ask_user' && message.inputType && (
145
+ <div className="mt-1 text-xs text-slate-400">
146
+ Input: {message.inputType.type}
147
+ </div>
148
+ )}
149
+ </td>
150
+
151
+ {/* Agent */}
152
+ <td className="px-3 py-2">
153
+ {message.agentId && message.agentName && (
154
+ <AgentBadge
155
+ agentId={message.agentId}
156
+ agentName={message.agentName}
157
+ />
158
+ )}
159
+ </td>
160
+
161
+ {/* Links */}
162
+ <td className="px-3 py-2">
163
+ <div className="flex flex-wrap gap-1">
164
+ {message.llmCallId && (
165
+ <DebugLink
166
+ to={`llm-calls/${message.llmCallId}`}
167
+ className="inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-yellow-100 text-yellow-700 hover:bg-yellow-200"
168
+ >
169
+ LLM
170
+ </DebugLink>
171
+ )}
172
+ {message.toolCallId && (
173
+ <span
174
+ className="inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-slate-100 text-slate-600"
175
+ title={`Tool Call: ${message.toolCallId}`}
176
+ >
177
+ Tool
178
+ </span>
179
+ )}
180
+ {message.mailboxMessageId && (
181
+ <DebugLink
182
+ to="mailbox"
183
+ className="inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-indigo-100 text-indigo-700 hover:bg-indigo-200"
184
+ >
185
+ Mailbox
186
+ </DebugLink>
187
+ )}
188
+ </div>
189
+ </td>
190
+
191
+ {/* Time */}
192
+ <td className="px-3 py-2 font-mono text-xs text-slate-500 whitespace-nowrap">
193
+ {new Date(message.timestamp).toLocaleTimeString()}
194
+ </td>
195
+
196
+ {/* Event Index */}
197
+ <td className="px-3 py-2 font-mono text-xs text-slate-400">
198
+ #{message.eventIndex}
199
+ </td>
200
+ </tr>
201
+ )
202
+ }
203
+
204
+ function TypeBadge({ type, answered }: { type: DebugChatMessage['type']; answered?: boolean }) {
205
+ switch (type) {
206
+ case 'user_message':
207
+ return (
208
+ <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-700">
209
+ User
210
+ </span>
211
+ )
212
+ case 'agent_message':
213
+ return (
214
+ <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700">
215
+ Agent
216
+ </span>
217
+ )
218
+ case 'ask_user':
219
+ return (
220
+ <span
221
+ className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${
222
+ answered
223
+ ? 'bg-purple-100 text-purple-700'
224
+ : 'bg-purple-200 text-purple-800 animate-pulse'
225
+ }`}
226
+ >
227
+ Question{answered ? '' : ' (pending)'}
228
+ </span>
229
+ )
230
+ }
231
+ }
232
+
233
+ function AgentBadge({
234
+ agentId,
235
+ agentName,
236
+ }: {
237
+ agentId: string
238
+ agentName: string
239
+ }) {
240
+ return (
241
+ <DebugLink
242
+ to={`agents/${agentId}`}
243
+ className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium bg-slate-100 text-slate-700 hover:bg-slate-200"
244
+ >
245
+ <span className="truncate max-w-20" title={agentName}>
246
+ {agentName}
247
+ </span>
248
+ </DebugLink>
249
+ )
250
+ }
@@ -0,0 +1,13 @@
1
+ export { AgentDetailPage } from './AgentDetailPage'
2
+ export { DashboardPage } from './DashboardPage'
3
+ export { AgentsPage } from './AgentsPage'
4
+ export { CommunicationPage } from './CommunicationPage'
5
+ export { EventsPage } from './EventsPage'
6
+ export { LLMCallPage } from './LLMCallPage'
7
+ export { LLMCallsPage } from './LLMCallsPage'
8
+ export { MailboxPage } from './MailboxPage'
9
+ export { TimelinePage } from './TimelinePage'
10
+ export { UserChatPage } from './UserChatPage'
11
+ export { FilesPage } from './FilesPage'
12
+ export { LogsPage } from './LogsPage'
13
+ export { ServicesPage } from './ServicesPage'
package/src/index.ts ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @roj-ai/debug
3
+ *
4
+ * React debug UI for session event inspection — extracted from @roj-ai/client.
5
+ */
6
+
7
+ // Event store
8
+ export {
9
+ selectAgentTree,
10
+ selectGlobalMailbox,
11
+ selectMetrics,
12
+ selectTimeline,
13
+ useAgentDetail,
14
+ useAgentTree,
15
+ useChatDebug,
16
+ useEvents,
17
+ useEventStore,
18
+ useGlobalMailbox,
19
+ useMetrics,
20
+ useSessionInfo,
21
+ useTimeline,
22
+ } from './stores/event-store'
23
+
24
+ // Event polling provider
25
+ export { EventPollingProvider, useEventPolling } from './providers/EventPollingProvider'
26
+
27
+ // Debug components (context, navigation, shell, pages, diagrams)
28
+ export {
29
+ AgentDetailPage,
30
+ AgentsPage,
31
+ BackIcon,
32
+ CommunicationDiagram,
33
+ CommunicationPage,
34
+ DashboardPage,
35
+ DebugContext,
36
+ DebugLink,
37
+ DebugShell,
38
+ EventsPage,
39
+ FilesPage,
40
+ getNavItemClassName,
41
+ LLMCallDetail,
42
+ LLMCallPage,
43
+ LLMCallsPage,
44
+ LogsPage,
45
+ MailboxPage,
46
+ navItems,
47
+ ServicesPage,
48
+ TimelinePage,
49
+ useDebugContext,
50
+ useDebugNavigate,
51
+ useDebugParams,
52
+ useDebugSessionId,
53
+ UserChatPage,
54
+ } from './components/debug'
55
+ export type { DebugContextValue, NavItem } from './components/debug'
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Client-specific domain utilities.
3
+ */
4
+
5
+ import { estimateTokenCount } from 'tokenx'
6
+
7
+ /**
8
+ * Estimate token count for a text string.
9
+ */
10
+ export function estimateTokens(text: string): number {
11
+ return estimateTokenCount(text)
12
+ }
@@ -0,0 +1,60 @@
1
+ import { useEffect } from 'react'
2
+ import { useEventStore } from '../stores/event-store'
3
+
4
+ interface EventPollingProviderProps {
5
+ sessionId: string
6
+ children: React.ReactNode
7
+ }
8
+
9
+ /**
10
+ * Provider component that manages event polling for a session.
11
+ *
12
+ * When mounted, it loads the session's events and starts polling for new events.
13
+ * When unmounted (e.g., navigating away), it stops polling and resets the store.
14
+ *
15
+ * Usage:
16
+ * ```tsx
17
+ * <EventPollingProvider sessionId={sessionId}>
18
+ * <DebugViews />
19
+ * </EventPollingProvider>
20
+ * ```
21
+ */
22
+ export function EventPollingProvider({ sessionId, children }: EventPollingProviderProps) {
23
+ const loadSession = useEventStore((s) => s.loadSession)
24
+ const reset = useEventStore((s) => s.reset)
25
+
26
+ useEffect(() => {
27
+ loadSession(sessionId)
28
+
29
+ // Cleanup on unmount or sessionId change
30
+ return () => {
31
+ reset()
32
+ }
33
+ }, [sessionId, loadSession, reset])
34
+
35
+ return <>{children}</>
36
+ }
37
+
38
+ /**
39
+ * Hook to initialize event polling for a session.
40
+ * Use this instead of EventPollingProvider if you want more control.
41
+ */
42
+ export function useEventPolling(sessionId: string | undefined) {
43
+ const loadSession = useEventStore((s) => s.loadSession)
44
+ const reset = useEventStore((s) => s.reset)
45
+ const isLoading = useEventStore((s) => s.isLoading)
46
+ const error = useEventStore((s) => s.error)
47
+
48
+ useEffect(() => {
49
+ if (!sessionId) return
50
+
51
+ loadSession(sessionId)
52
+
53
+ // Cleanup on unmount or sessionId change
54
+ return () => {
55
+ reset()
56
+ }
57
+ }, [sessionId, loadSession, reset])
58
+
59
+ return { isLoading, error }
60
+ }