@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,297 @@
1
+ import type { ProtocolAgentStatus, SessionId } from '@roj-ai/sdk'
2
+ import type { AgentTreeNode } from '@roj-ai/shared'
3
+ import { AgentId } from '@roj-ai/shared'
4
+ import type { RpcOutput } from '@roj-ai/shared/rpc'
5
+ import { useCallback, useEffect, useState } from 'react'
6
+ import { api, unwrap } from '@roj-ai/client'
7
+ import { useAgentTree, useEventStore } from '../../../stores/event-store'
8
+ import { useDebugContext } from '../DebugContext'
9
+ import { DebugLink, useDebugSessionId } from '../DebugNavigation'
10
+ import { AgentDetailPage } from './AgentDetailPage'
11
+
12
+ export function AgentsPage() {
13
+ const sessionId = useDebugSessionId()
14
+ const { params } = useDebugContext()
15
+
16
+ // Get agent tree from event store (already loaded by DebugLayout)
17
+ const agents = useAgentTree()
18
+ const isLoading = useEventStore((s) => s.isLoading)
19
+ const error = useEventStore((s) => s.error)
20
+
21
+ const selectedAgentId = params.agentId ?? null
22
+
23
+ return (
24
+ <div className="h-full flex gap-4">
25
+ {/* Agent Tree - Left Panel */}
26
+ <div className="w-80 shrink-0 bg-white rounded-2xl shadow-card flex flex-col">
27
+ <div className="p-3 border-b border-gray-100">
28
+ <h2 className="font-semibold text-gray-900 text-sm">Agent Tree</h2>
29
+ </div>
30
+ <div className="flex-1 overflow-auto p-3">
31
+ {isLoading && agents.length === 0
32
+ ? <div className="text-gray-400 text-sm">Loading...</div>
33
+ : error
34
+ ? <div className="text-red-500 text-sm">{error}</div>
35
+ : agents.length === 0
36
+ ? <div className="text-gray-400 text-sm">No agents yet</div>
37
+ : (
38
+ <div>
39
+ {agents.map((agent, i) => (
40
+ <AgentNode
41
+ key={agent.id}
42
+ agent={agent}
43
+ selectedId={selectedAgentId}
44
+ isLast={i === agents.length - 1}
45
+ guides={[]}
46
+ />
47
+ ))}
48
+ </div>
49
+ )}
50
+ </div>
51
+ {selectedAgentId && (
52
+ <SpawnAgentSection
53
+ sessionId={sessionId}
54
+ parentId={selectedAgentId}
55
+ parentDefinitionName={findAgentDefinitionName(agents, selectedAgentId)}
56
+ />
57
+ )}
58
+ </div>
59
+
60
+ {/* Agent Detail - Right Panel */}
61
+ <div className="flex-1 bg-gray-50/50 rounded-2xl flex flex-col min-w-0">
62
+ <div className="p-3 border-b border-gray-100">
63
+ <h2 className="font-semibold text-gray-900 text-sm">Agent Detail</h2>
64
+ </div>
65
+ <div className="flex-1 overflow-auto p-4">
66
+ {selectedAgentId
67
+ ? <AgentDetailPage agentId={selectedAgentId} />
68
+ : (
69
+ <div className="text-gray-400 text-sm">
70
+ Select an agent from the tree to view details
71
+ </div>
72
+ )}
73
+ </div>
74
+ </div>
75
+ </div>
76
+ )
77
+ }
78
+
79
+ /**
80
+ * Find the definitionName of an agent by its ID in the tree.
81
+ */
82
+ function findAgentDefinitionName(agents: AgentTreeNode[], agentId: string): string | null {
83
+ for (const agent of agents) {
84
+ if (agent.id === agentId) return agent.definitionName
85
+ const found = findAgentDefinitionName(agent.children, agentId)
86
+ if (found) return found
87
+ }
88
+ return null
89
+ }
90
+
91
+ type PresetAgent = RpcOutput<'presets.getAgents'>['agents'][number]
92
+
93
+ function SpawnAgentSection({
94
+ sessionId,
95
+ parentId,
96
+ parentDefinitionName,
97
+ }: {
98
+ sessionId: SessionId
99
+ parentId: string
100
+ parentDefinitionName: string | null
101
+ }) {
102
+ const [presetAgents, setPresetAgents] = useState<PresetAgent[]>([])
103
+ const [selectedAgent, setSelectedAgent] = useState('')
104
+ const [message, setMessage] = useState('')
105
+ const [spawning, setSpawning] = useState(false)
106
+ const [error, setError] = useState<string | null>(null)
107
+
108
+ useEffect(() => {
109
+ api.call('presets.getAgents', { sessionId }).then((result) => {
110
+ setPresetAgents(unwrap(result).agents)
111
+ }).catch(() => {
112
+ setPresetAgents([])
113
+ })
114
+ }, [sessionId])
115
+
116
+ // Filter agents spawnable by the selected parent
117
+ const spawnableAgents = presetAgents.filter((a) => parentDefinitionName && a.spawnableBy.includes(parentDefinitionName))
118
+
119
+ const handleSpawn = useCallback(async () => {
120
+ if (!selectedAgent) return
121
+
122
+ setSpawning(true)
123
+ setError(null)
124
+
125
+ try {
126
+ unwrap(
127
+ await api.call('agents.spawn', {
128
+ sessionId,
129
+ definitionName: selectedAgent,
130
+ parentId: AgentId(parentId),
131
+ message: message.trim() || undefined,
132
+ }),
133
+ )
134
+ setSelectedAgent('')
135
+ setMessage('')
136
+ } catch (e) {
137
+ setError(e instanceof Error ? e.message : 'Failed to spawn agent')
138
+ } finally {
139
+ setSpawning(false)
140
+ }
141
+ }, [selectedAgent, sessionId, parentId, message])
142
+
143
+ if (spawnableAgents.length === 0) return null
144
+
145
+ return (
146
+ <div className="p-3 border-t border-gray-100">
147
+ <h3 className="text-xs font-medium text-gray-400 mb-2">Spawn Agent</h3>
148
+ <div className="space-y-2">
149
+ <select
150
+ value={selectedAgent}
151
+ onChange={(e) => setSelectedAgent(e.target.value)}
152
+ className="w-full text-xs border border-gray-200 rounded px-2 py-1"
153
+ >
154
+ <option value="">Select agent...</option>
155
+ {spawnableAgents.map((a) => (
156
+ <option key={a.name} value={a.name}>
157
+ {a.name}
158
+ {a.hasInputSchema ? ' (typed)' : ''}
159
+ </option>
160
+ ))}
161
+ </select>
162
+ <input
163
+ type="text"
164
+ value={message}
165
+ onChange={(e) => setMessage(e.target.value)}
166
+ placeholder="Initial message (optional)"
167
+ className="w-full text-xs border border-gray-200 rounded px-2 py-1"
168
+ />
169
+ {error && <div className="text-xs text-red-600">{error}</div>}
170
+ <button
171
+ onClick={handleSpawn}
172
+ disabled={spawning || !selectedAgent}
173
+ className="w-full px-2 py-1 text-xs font-medium text-white bg-green-600 rounded hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed"
174
+ >
175
+ {spawning ? 'Spawning...' : 'Spawn'}
176
+ </button>
177
+ </div>
178
+ </div>
179
+ )
180
+ }
181
+
182
+ const statusDotColors: Record<ProtocolAgentStatus, string> = {
183
+ idle: 'bg-gray-400',
184
+ thinking: 'bg-accent-lime animate-pulse',
185
+ responding: 'bg-accent-peri',
186
+ waiting_for_user: 'bg-purple-400',
187
+ error: 'bg-red-400',
188
+ paused: 'bg-amber-400',
189
+ }
190
+
191
+ const statusTextColors: Record<ProtocolAgentStatus, string> = {
192
+ idle: 'text-gray-500',
193
+ thinking: 'text-lime-600',
194
+ responding: 'text-indigo-500',
195
+ waiting_for_user: 'text-purple-500',
196
+ error: 'text-red-500',
197
+ paused: 'text-amber-500',
198
+ }
199
+
200
+ const statusLabels: Record<ProtocolAgentStatus, string> = {
201
+ idle: 'idle',
202
+ thinking: 'thinking',
203
+ responding: 'responding',
204
+ waiting_for_user: 'waiting',
205
+ error: 'error',
206
+ paused: 'paused',
207
+ }
208
+
209
+ function AgentNode({
210
+ agent,
211
+ selectedId,
212
+ isLast,
213
+ guides,
214
+ }: {
215
+ agent: AgentTreeNode
216
+ selectedId: string | null
217
+ isLast: boolean
218
+ guides: boolean[]
219
+ }) {
220
+ const isSelected = selectedId === agent.id
221
+
222
+ return (
223
+ <>
224
+ <div className="flex">
225
+ {/* Ancestor vertical guide lines */}
226
+ {guides.map((active, i) => (
227
+ <div key={i} className="w-5 shrink-0 relative">
228
+ {active && <div className="absolute left-2 top-0 bottom-0 w-px bg-gray-200" />}
229
+ </div>
230
+ ))}
231
+
232
+ {/* Branch connector (vertical + horizontal) */}
233
+ <div className="w-5 shrink-0 relative">
234
+ <div className={`absolute left-2 top-0 w-px bg-gray-200 ${isLast ? 'h-5' : 'h-full'}`} />
235
+ <div className="absolute left-2 top-5 h-px w-2.5 bg-gray-200" />
236
+ </div>
237
+
238
+ {/* Node card */}
239
+ <div className="flex-1 min-w-0 py-0.5">
240
+ <DebugLink
241
+ to={`agents/${agent.id}`}
242
+ className={`block w-full text-left px-2.5 py-2 rounded-lg border transition-all ${
243
+ isSelected
244
+ ? 'bg-accent-peri/15 border-accent-peri/40 border-l-[3px] border-l-accent-peri text-gray-900 shadow-sm'
245
+ : 'bg-white border-gray-100 text-gray-700 hover:border-gray-200 hover:shadow-sm'
246
+ }`}
247
+ >
248
+ {/* Row 1: status dot + name + right-aligned status/cost */}
249
+ <div className="flex items-center gap-2">
250
+ <span className={`w-2 h-2 rounded-full shrink-0 ${statusDotColors[agent.status]}`} />
251
+ <span className="font-mono text-sm truncate font-medium">{agent.definitionName}</span>
252
+ <span className="ml-auto flex items-center gap-1.5 shrink-0">
253
+ <span className={`text-[10px] font-medium ${statusTextColors[agent.status]}`}>
254
+ {statusLabels[agent.status]}
255
+ </span>
256
+ {agent.cost > 0 && (
257
+ <span className="text-[10px] text-emerald-600 font-medium tabular-nums">${agent.cost.toFixed(4)}</span>
258
+ )}
259
+ </span>
260
+ </div>
261
+
262
+ {/* Row 2: badges + id */}
263
+ <div className="flex items-center gap-1.5 ml-4 mt-1">
264
+ {agent.isExecuting && (
265
+ <span className="text-[10px] bg-amber-100 text-amber-700 px-1.5 py-0.5 rounded-full font-semibold animate-pulse">
266
+ exec
267
+ </span>
268
+ )}
269
+ {agent.mailboxCount > 0 && (
270
+ <span className="text-[10px] bg-orange-100 text-orange-600 px-1.5 py-0.5 rounded-full font-medium">
271
+ {agent.mailboxCount} msgs
272
+ </span>
273
+ )}
274
+ {agent.pendingToolCalls > 0 && (
275
+ <span className="text-[10px] bg-accent-peri/15 text-gray-600 px-1.5 py-0.5 rounded-full font-medium">
276
+ {agent.pendingToolCalls} tools
277
+ </span>
278
+ )}
279
+ <span className="text-[10px] text-gray-400 font-mono ml-auto">{agent.id.slice(0, 8)}</span>
280
+ </div>
281
+ </DebugLink>
282
+ </div>
283
+ </div>
284
+
285
+ {/* Children */}
286
+ {agent.children.map((child, i) => (
287
+ <AgentNode
288
+ key={child.id}
289
+ agent={child}
290
+ selectedId={selectedId}
291
+ isLast={i === agent.children.length - 1}
292
+ guides={[...guides, !isLast]}
293
+ />
294
+ ))}
295
+ </>
296
+ )
297
+ }
@@ -0,0 +1,89 @@
1
+ import { useAgentTree, useEvents, useEventStore } from '../../../stores/event-store'
2
+ import { CommunicationDiagram } from '../communication/CommunicationDiagram'
3
+ import { useDiagramData } from '../communication/hooks/useDiagramData'
4
+
5
+ export function CommunicationPage() {
6
+ // Get events and agents from event store (already loaded by DebugLayout)
7
+ const events = useEvents()
8
+ const agents = useAgentTree()
9
+ const isLoading = useEventStore((s) => s.isLoading)
10
+ const error = useEventStore((s) => s.error)
11
+
12
+ const diagramData = useDiagramData({ events, agents })
13
+
14
+ // Calculate stats
15
+ const messageCount = diagramData.messages.length
16
+ const llmCount = diagramData.llmBlocks.length
17
+ const toolCount = diagramData.toolBlocks.length
18
+ const participantCount = diagramData.participants.length - 1 // Exclude user
19
+
20
+ return (
21
+ <div className="h-full flex flex-col gap-3">
22
+ {/* Summary bar */}
23
+ <div className="flex items-center gap-5 text-sm shrink-0">
24
+ <div className="flex items-center gap-1.5">
25
+ <span className="text-slate-400">Agents:</span>
26
+ <span className="font-semibold text-slate-700">{participantCount}</span>
27
+ </div>
28
+ <div className="flex items-center gap-1.5">
29
+ <span className="w-1.5 h-1.5 rounded-full bg-blue-400" />
30
+ <span className="text-slate-400">Messages:</span>
31
+ <span className="font-semibold text-slate-700">{messageCount}</span>
32
+ </div>
33
+ <div className="flex items-center gap-1.5">
34
+ <span className="w-1.5 h-1.5 rounded-full bg-violet-400" />
35
+ <span className="text-slate-400">LLM:</span>
36
+ <span className="font-semibold text-slate-700">{llmCount}</span>
37
+ </div>
38
+ <div className="flex items-center gap-1.5">
39
+ <span className="w-1.5 h-1.5 rounded-full bg-teal-400" />
40
+ <span className="text-slate-400">Tools:</span>
41
+ <span className="font-semibold text-slate-700">{toolCount}</span>
42
+ </div>
43
+ {isLoading && events.length > 0 && <span className="text-slate-300 text-xs ml-auto">Updating...</span>}
44
+ </div>
45
+
46
+ {/* Error */}
47
+ {error && (
48
+ <div className="text-red-600 text-sm shrink-0 bg-red-50 border border-red-100 rounded-md px-3 py-2">
49
+ {error}
50
+ </div>
51
+ )}
52
+
53
+ {/* Loading state */}
54
+ {isLoading && events.length === 0 && (
55
+ <div className="flex-1 flex items-center justify-center">
56
+ <div className="text-center">
57
+ <div className="w-6 h-6 border-2 border-slate-200 border-t-violet-500 rounded-full animate-spin mx-auto mb-2" />
58
+ <div className="text-sm text-slate-400">Loading diagram...</div>
59
+ </div>
60
+ </div>
61
+ )}
62
+
63
+ {/* Empty state */}
64
+ {!isLoading && events.length === 0 && (
65
+ <div className="flex-1 flex items-center justify-center">
66
+ <div className="text-center">
67
+ <svg className="w-10 h-10 text-slate-200 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
68
+ <path
69
+ strokeLinecap="round"
70
+ strokeLinejoin="round"
71
+ strokeWidth={1.5}
72
+ 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"
73
+ />
74
+ </svg>
75
+ <div className="text-sm text-slate-500 font-medium">No activity yet</div>
76
+ <div className="text-xs text-slate-400 mt-0.5">Send a message to start</div>
77
+ </div>
78
+ </div>
79
+ )}
80
+
81
+ {/* Diagram */}
82
+ {events.length > 0 && (
83
+ <div className="flex-1 bg-white rounded-md border border-slate-200 overflow-hidden min-h-0">
84
+ <CommunicationDiagram data={diagramData} />
85
+ </div>
86
+ )}
87
+ </div>
88
+ )
89
+ }