@datalayer/agent-runtimes 1.0.4 → 1.0.6

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 (299) hide show
  1. package/README.md +182 -1
  2. package/lib/AgentNode.d.ts +3 -0
  3. package/lib/AgentNode.js +676 -0
  4. package/lib/App.js +1 -1
  5. package/lib/agent-node/themeStore.d.ts +3 -0
  6. package/lib/agent-node/themeStore.js +156 -0
  7. package/lib/agent-node-main.d.ts +1 -0
  8. package/lib/agent-node-main.js +14 -0
  9. package/lib/agents/AgentDetails.d.ts +22 -1
  10. package/lib/agents/AgentDetails.js +34 -47
  11. package/lib/api/index.d.ts +0 -1
  12. package/lib/api/index.js +4 -2
  13. package/lib/chat/Chat.d.ts +5 -106
  14. package/lib/chat/Chat.js +20 -14
  15. package/lib/chat/ChatFloating.d.ts +7 -140
  16. package/lib/chat/ChatFloating.js +3 -3
  17. package/lib/chat/ChatPopupStandalone.d.ts +8 -47
  18. package/lib/chat/ChatPopupStandalone.js +3 -3
  19. package/lib/chat/ChatSidebar.d.ts +4 -69
  20. package/lib/chat/ChatSidebar.js +83 -51
  21. package/lib/chat/ChatStandalone.d.ts +4 -54
  22. package/lib/chat/ChatStandalone.js +3 -3
  23. package/lib/chat/base/ChatBase.js +1414 -174
  24. package/lib/chat/display/FloatingBrandButton.js +8 -1
  25. package/lib/chat/header/ChatHeader.d.ts +3 -1
  26. package/lib/chat/header/ChatHeader.js +15 -12
  27. package/lib/chat/header/ChatHeaderBase.d.ts +30 -5
  28. package/lib/chat/header/ChatHeaderBase.js +41 -16
  29. package/lib/chat/indicators/McpStatusIndicator.d.ts +7 -4
  30. package/lib/chat/indicators/McpStatusIndicator.js +7 -32
  31. package/lib/chat/indicators/SandboxStatusIndicator.d.ts +4 -1
  32. package/lib/chat/indicators/SandboxStatusIndicator.js +91 -56
  33. package/lib/chat/indicators/SkillsStatusIndicator.d.ts +7 -0
  34. package/lib/chat/indicators/SkillsStatusIndicator.js +88 -0
  35. package/lib/chat/indicators/index.d.ts +1 -0
  36. package/lib/chat/indicators/index.js +1 -0
  37. package/lib/chat/messages/ChatMessageList.d.ts +1 -1
  38. package/lib/chat/messages/ChatMessageList.js +154 -114
  39. package/lib/chat/messages/ChatMessages.js +6 -2
  40. package/lib/chat/prompt/InputFooter.d.ts +21 -6
  41. package/lib/chat/prompt/InputFooter.js +76 -20
  42. package/lib/chat/prompt/InputPrompt.d.ts +5 -1
  43. package/lib/chat/prompt/InputPrompt.js +4 -4
  44. package/lib/chat/prompt/InputPromptFooter.d.ts +3 -1
  45. package/lib/chat/prompt/InputPromptFooter.js +3 -3
  46. package/lib/chat/prompt/InputPromptLexical.d.ts +3 -1
  47. package/lib/chat/prompt/InputPromptLexical.js +12 -5
  48. package/lib/chat/prompt/InputPromptText.d.ts +3 -1
  49. package/lib/chat/prompt/InputPromptText.js +2 -2
  50. package/lib/chat/tools/ToolApprovalBanner.js +1 -1
  51. package/lib/chat/tools/ToolCallDisplay.d.ts +3 -1
  52. package/lib/chat/tools/ToolCallDisplay.js +2 -2
  53. package/lib/chat/usage/TokenUsageBar.js +20 -2
  54. package/lib/client/AgentRuntimesClientContext.d.ts +53 -0
  55. package/lib/client/AgentRuntimesClientContext.js +55 -0
  56. package/lib/client/AgentsMixin.d.ts +0 -18
  57. package/lib/client/AgentsMixin.js +20 -30
  58. package/lib/client/IAgentRuntimesClient.d.ts +215 -0
  59. package/lib/client/IAgentRuntimesClient.js +5 -0
  60. package/lib/client/SdkAgentRuntimesClient.d.ts +151 -0
  61. package/lib/client/SdkAgentRuntimesClient.js +134 -0
  62. package/lib/client/index.d.ts +4 -1
  63. package/lib/client/index.js +3 -1
  64. package/lib/components/NotificationEventCard.js +5 -1
  65. package/lib/config/AgentConfiguration.d.ts +22 -0
  66. package/lib/config/AgentConfiguration.js +319 -64
  67. package/lib/context/ContextDistribution.d.ts +3 -1
  68. package/lib/context/ContextDistribution.js +8 -27
  69. package/lib/context/ContextInspector.d.ts +3 -1
  70. package/lib/context/ContextInspector.js +19 -67
  71. package/lib/context/ContextPanel.d.ts +3 -1
  72. package/lib/context/ContextPanel.js +104 -64
  73. package/lib/context/ContextUsage.d.ts +3 -1
  74. package/lib/context/ContextUsage.js +3 -3
  75. package/lib/context/CostTracker.d.ts +9 -3
  76. package/lib/context/CostTracker.js +26 -47
  77. package/lib/context/CostUsageChart.d.ts +12 -0
  78. package/lib/context/CostUsageChart.js +378 -0
  79. package/lib/context/GraphFlowChart.d.ts +16 -0
  80. package/lib/context/GraphFlowChart.js +182 -0
  81. package/lib/context/TokenUsageChart.d.ts +8 -1
  82. package/lib/context/TokenUsageChart.js +349 -211
  83. package/lib/context/TurnGraphChart.d.ts +39 -0
  84. package/lib/context/TurnGraphChart.js +538 -0
  85. package/lib/context/otelWsPool.d.ts +20 -0
  86. package/lib/context/otelWsPool.js +69 -0
  87. package/lib/examples/A2UiComponentGalleryExample.d.ts +0 -17
  88. package/lib/examples/A2UiComponentGalleryExample.js +315 -522
  89. package/lib/examples/A2UiContactCardExample.d.ts +0 -18
  90. package/lib/examples/A2UiContactCardExample.js +154 -411
  91. package/lib/examples/A2UiRestaurantExample.d.ts +0 -30
  92. package/lib/examples/A2UiRestaurantExample.js +114 -212
  93. package/lib/examples/A2UiViewerExample.d.ts +0 -18
  94. package/lib/examples/A2UiViewerExample.js +283 -532
  95. package/lib/examples/AgUiBackendToolRenderingExample.js +1 -1
  96. package/lib/examples/AgUiHaikuGenUiExample.d.ts +1 -1
  97. package/lib/examples/AgUiHaikuGenUiExample.js +1 -1
  98. package/lib/examples/AgUiSharedStateExample.js +2 -1
  99. package/lib/examples/AgentCheckpointsExample.js +14 -28
  100. package/lib/examples/AgentCodemodeExample.d.ts +4 -6
  101. package/lib/examples/AgentCodemodeExample.js +603 -169
  102. package/lib/examples/AgentEvalsExample.js +339 -53
  103. package/lib/examples/AgentGuardrailsExample.js +383 -66
  104. package/lib/examples/AgentHooksExample.d.ts +3 -0
  105. package/lib/examples/AgentHooksExample.js +122 -0
  106. package/lib/examples/AgentInferenceProviderExample.d.ts +3 -0
  107. package/lib/examples/AgentInferenceProviderExample.js +329 -0
  108. package/lib/examples/AgentMCPExample.d.ts +3 -0
  109. package/lib/examples/AgentMCPExample.js +481 -0
  110. package/lib/examples/AgentMemoryExample.d.ts +1 -2
  111. package/lib/examples/AgentMemoryExample.js +78 -33
  112. package/lib/examples/AgentMonitoringExample.js +261 -200
  113. package/lib/examples/AgentNotificationsExample.d.ts +1 -2
  114. package/lib/examples/AgentNotificationsExample.js +114 -33
  115. package/lib/examples/AgentOtelExample.js +32 -42
  116. package/lib/examples/AgentOutputsExample.d.ts +11 -6
  117. package/lib/examples/AgentOutputsExample.js +433 -81
  118. package/lib/examples/AgentParametersExample.d.ts +3 -0
  119. package/lib/examples/AgentParametersExample.js +248 -0
  120. package/lib/examples/AgentSandboxExample.d.ts +3 -3
  121. package/lib/examples/AgentSandboxExample.js +74 -45
  122. package/lib/examples/AgentSkillsExample.js +95 -103
  123. package/lib/examples/AgentSubagentsExample.d.ts +14 -0
  124. package/lib/examples/AgentSubagentsExample.js +228 -0
  125. package/lib/examples/AgentToolApprovalsExample.js +49 -561
  126. package/lib/examples/AgentTriggersExample.js +823 -569
  127. package/lib/examples/{AgentspecExample.d.ts → AgentspecsExample.d.ts} +2 -2
  128. package/lib/examples/AgentspecsExample.js +1096 -0
  129. package/lib/examples/ChatCustomExample.js +16 -28
  130. package/lib/examples/ChatExample.js +13 -29
  131. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  132. package/lib/examples/CopilotKitNotebookExample.js +2 -1
  133. package/lib/examples/HomeExample.d.ts +15 -0
  134. package/lib/examples/HomeExample.js +77 -0
  135. package/lib/examples/Lexical2Example.js +4 -2
  136. package/lib/examples/{LexicalExample.d.ts → LexicalAgentExample.d.ts} +4 -4
  137. package/lib/examples/{LexicalExample.js → LexicalAgentExample.js} +66 -17
  138. package/lib/examples/{LexicalSidebarExample.d.ts → LexicalAgentSidebarExample.d.ts} +5 -5
  139. package/lib/examples/LexicalAgentSidebarExample.js +261 -0
  140. package/lib/examples/NotebookAgentExample.d.ts +9 -0
  141. package/lib/examples/NotebookAgentExample.js +192 -0
  142. package/lib/examples/{NotebookSidebarExample.d.ts → NotebookAgentSidebarExample.d.ts} +2 -2
  143. package/lib/examples/NotebookAgentSidebarExample.js +221 -0
  144. package/lib/examples/{DatalayerNotebookExample.d.ts → NotebookCollaborationExample.d.ts} +4 -4
  145. package/lib/examples/{DatalayerNotebookExample.js → NotebookCollaborationExample.js} +3 -3
  146. package/lib/examples/NotebookExample.d.ts +4 -7
  147. package/lib/examples/NotebookExample.js +14 -146
  148. package/lib/examples/components/AuthRequiredView.d.ts +6 -0
  149. package/lib/examples/components/AuthRequiredView.js +33 -0
  150. package/lib/examples/components/ExampleWrapper.d.ts +9 -3
  151. package/lib/examples/components/ExampleWrapper.js +45 -9
  152. package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.js +1 -1
  153. package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.js +1 -1
  154. package/lib/examples/{ag-ui → components}/haiku/index.d.ts +1 -1
  155. package/lib/examples/{ag-ui → components}/haiku/index.js +1 -1
  156. package/lib/examples/components/index.d.ts +3 -0
  157. package/lib/examples/components/index.js +4 -0
  158. package/lib/examples/{ag-ui → components}/weather/index.d.ts +1 -1
  159. package/lib/examples/{ag-ui → components}/weather/index.js +1 -1
  160. package/lib/examples/example-selector.d.ts +17 -4
  161. package/lib/examples/example-selector.js +108 -41
  162. package/lib/examples/index.d.ts +10 -6
  163. package/lib/examples/index.js +10 -6
  164. package/lib/examples/lexical/initial-content.json +6 -6
  165. package/lib/examples/main.js +257 -27
  166. package/lib/examples/utils/a2ui.d.ts +18 -0
  167. package/lib/examples/utils/a2ui.js +69 -0
  168. package/lib/examples/utils/a2uiMarkdownProvider.d.ts +7 -0
  169. package/lib/examples/utils/a2uiMarkdownProvider.js +9 -0
  170. package/lib/examples/utils/agentId.d.ts +18 -0
  171. package/lib/examples/utils/agentId.js +54 -0
  172. package/lib/examples/utils/agents/earthquake-detector.json +11 -11
  173. package/lib/examples/utils/agents/sales-forecaster.json +11 -11
  174. package/lib/examples/utils/agents/social-post-generator.json +11 -11
  175. package/lib/examples/utils/agents/stock-market.json +11 -11
  176. package/lib/examples/utils/examplesStore.js +82 -27
  177. package/lib/examples/utils/useExampleAgentRuntimesUrl.d.ts +5 -0
  178. package/lib/examples/utils/useExampleAgentRuntimesUrl.js +19 -0
  179. package/lib/hooks/index.d.ts +8 -8
  180. package/lib/hooks/index.js +7 -7
  181. package/lib/hooks/useA2A.d.ts +2 -3
  182. package/lib/hooks/useAIAgentsWebSocket.d.ts +43 -4
  183. package/lib/hooks/useAIAgentsWebSocket.js +153 -12
  184. package/lib/hooks/useAcp.d.ts +1 -2
  185. package/lib/hooks/useAgUi.d.ts +1 -1
  186. package/lib/hooks/{useAgents.d.ts → useAgentRuntimes.d.ts} +70 -4
  187. package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +237 -32
  188. package/lib/hooks/useAgentsCatalog.js +1 -1
  189. package/lib/hooks/useAgentsService.d.ts +2 -2
  190. package/lib/hooks/useAgentsService.js +7 -7
  191. package/lib/hooks/useCheckpoints.js +1 -1
  192. package/lib/hooks/useConfig.d.ts +4 -1
  193. package/lib/hooks/useConfig.js +10 -3
  194. package/lib/hooks/useContextSnapshot.d.ts +9 -4
  195. package/lib/hooks/useContextSnapshot.js +9 -37
  196. package/lib/hooks/useMonitoring.js +3 -0
  197. package/lib/hooks/useSandbox.d.ts +20 -8
  198. package/lib/hooks/useSandbox.js +105 -40
  199. package/lib/hooks/useSkills.d.ts +23 -5
  200. package/lib/hooks/useSkills.js +94 -39
  201. package/lib/hooks/useToolApprovals.d.ts +60 -36
  202. package/lib/hooks/useToolApprovals.js +318 -69
  203. package/lib/hooks/useVercelAI.d.ts +1 -1
  204. package/lib/index.d.ts +2 -1
  205. package/lib/index.js +1 -0
  206. package/lib/inference/index.d.ts +0 -1
  207. package/lib/middleware/index.d.ts +0 -1
  208. package/lib/protocols/AGUIAdapter.js +6 -0
  209. package/lib/protocols/VercelAIAdapter.d.ts +7 -0
  210. package/lib/protocols/VercelAIAdapter.js +59 -7
  211. package/lib/specs/agents/agents.d.ts +21 -4
  212. package/lib/specs/agents/agents.js +2879 -316
  213. package/lib/specs/agents/index.js +3 -1
  214. package/lib/specs/benchmarks.d.ts +20 -0
  215. package/lib/specs/benchmarks.js +205 -0
  216. package/lib/specs/envvars.js +27 -20
  217. package/lib/specs/evals.d.ts +10 -9
  218. package/lib/specs/evals.js +128 -88
  219. package/lib/specs/events.d.ts +3 -10
  220. package/lib/specs/events.js +127 -84
  221. package/lib/specs/frontendTools.js +2 -2
  222. package/lib/specs/guardrails.d.ts +0 -7
  223. package/lib/specs/guardrails.js +240 -159
  224. package/lib/specs/mcpServers.js +35 -6
  225. package/lib/specs/memory.d.ts +0 -2
  226. package/lib/specs/memory.js +4 -17
  227. package/lib/specs/models.d.ts +0 -2
  228. package/lib/specs/models.js +20 -15
  229. package/lib/specs/notifications.js +102 -18
  230. package/lib/specs/outputs.js +15 -9
  231. package/lib/specs/personas.d.ts +41 -0
  232. package/lib/specs/personas.js +168 -0
  233. package/lib/specs/skills.d.ts +1 -1
  234. package/lib/specs/skills.js +23 -23
  235. package/lib/specs/teams/index.js +3 -1
  236. package/lib/specs/teams/teams.js +468 -348
  237. package/lib/specs/tools.js +4 -4
  238. package/lib/specs/triggers.js +61 -11
  239. package/lib/stores/agentRuntimeStore.d.ts +208 -0
  240. package/lib/stores/agentRuntimeStore.js +650 -0
  241. package/lib/stores/conversationStore.js +2 -2
  242. package/lib/stores/index.d.ts +1 -1
  243. package/lib/stores/index.js +1 -1
  244. package/lib/tools/adapters/copilotkit/lexicalHooks.d.ts +1 -2
  245. package/lib/tools/adapters/copilotkit/lexicalHooks.js +1 -3
  246. package/lib/tools/adapters/copilotkit/notebookHooks.d.ts +1 -2
  247. package/lib/tools/adapters/copilotkit/notebookHooks.js +1 -3
  248. package/lib/tools/index.d.ts +0 -2
  249. package/lib/tools/index.js +0 -1
  250. package/lib/types/agents-lifecycle.d.ts +18 -0
  251. package/lib/types/agents.d.ts +6 -0
  252. package/lib/types/agentspecs.d.ts +54 -1
  253. package/lib/types/benchmarks.d.ts +43 -0
  254. package/lib/types/benchmarks.js +5 -0
  255. package/lib/types/chat.d.ts +325 -8
  256. package/lib/types/context.d.ts +27 -0
  257. package/lib/types/cost.d.ts +2 -2
  258. package/lib/types/evals.d.ts +26 -17
  259. package/lib/types/index.d.ts +3 -0
  260. package/lib/types/index.js +3 -0
  261. package/lib/types/mcp.d.ts +8 -0
  262. package/lib/types/models.d.ts +2 -2
  263. package/lib/types/personas.d.ts +25 -0
  264. package/lib/types/personas.js +5 -0
  265. package/lib/types/skills.d.ts +43 -1
  266. package/lib/types/stream.d.ts +110 -0
  267. package/lib/types/stream.js +36 -0
  268. package/lib/utils/utils.d.ts +9 -5
  269. package/lib/utils/utils.js +9 -5
  270. package/package.json +19 -11
  271. package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
  272. package/scripts/codegen/__pycache__/generate_benchmarks.cpython-313.pyc +0 -0
  273. package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
  274. package/scripts/codegen/__pycache__/generate_events.cpython-313.pyc +0 -0
  275. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
  276. package/scripts/codegen/generate_agents.py +187 -45
  277. package/scripts/codegen/generate_benchmarks.py +441 -0
  278. package/scripts/codegen/generate_evals.py +94 -16
  279. package/scripts/codegen/generate_events.py +35 -14
  280. package/scripts/codegen/generate_personas.py +319 -0
  281. package/scripts/codegen/generate_skills.py +9 -9
  282. package/scripts/sync-jupyter.sh +26 -7
  283. package/lib/api/tool-approvals.d.ts +0 -62
  284. package/lib/api/tool-approvals.js +0 -145
  285. package/lib/examples/AgentspecExample.js +0 -705
  286. package/lib/examples/LexicalSidebarExample.js +0 -163
  287. package/lib/examples/NotebookSidebarExample.js +0 -119
  288. package/lib/examples/NotebookSimpleExample.d.ts +0 -6
  289. package/lib/examples/NotebookSimpleExample.js +0 -22
  290. package/lib/examples/ag-ui/index.d.ts +0 -10
  291. package/lib/examples/ag-ui/index.js +0 -16
  292. package/lib/hooks/useAgentsRegistry.d.ts +0 -10
  293. package/lib/hooks/useAgentsRegistry.js +0 -20
  294. package/lib/stores/agentsStore.d.ts +0 -123
  295. package/lib/stores/agentsStore.js +0 -270
  296. /package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.d.ts +0 -0
  297. /package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.d.ts +0 -0
  298. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.d.ts +0 -0
  299. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.js +0 -0
@@ -0,0 +1,538 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * TurnGraphChart — ECharts force-directed graph built from OTEL traces.
8
+ *
9
+ * Each graph run emitted by ``run_graph_with_telemetry`` /
10
+ * ``run_beta_graph_with_telemetry`` produces one OTEL trace whose spans map
11
+ * directly to pydantic-graph nodes. This component:
12
+ *
13
+ * 1. Fetches recent traces from the Datalayer OTEL service using
14
+ * ``createOtelClient`` from ``@datalayer/core/lib/otel`` (TypeScript-side
15
+ * fetch, Python-side emission).
16
+ * 2. Filters and groups spans by ``agent.id`` attribute and ``trace_id``.
17
+ * 3. Renders the latest run as an ECharts ``graph`` type (force-directed),
18
+ * with:
19
+ * - Node size ∝ span duration
20
+ * - Node colour by type (start / step / end / error)
21
+ * - Edges following the parent→child span hierarchy
22
+ * - Rich hover tooltips (duration, type, error)
23
+ * 4. Shows a small "run history" selector so the user can browse past runs.
24
+ */
25
+ import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
26
+ import ReactECharts from 'echarts-for-react';
27
+ import { createOtelClient, useOtelWebSocket } from '@datalayer/core/lib/otel';
28
+ import { Box } from '@datalayer/primer-addons';
29
+ import { Text } from '@primer/react';
30
+ // ── Colour palette ─────────────────────────────────────────────────────────
31
+ //
32
+ // Colours and symbols mirror the pydantic-graph beta node vocabulary
33
+ // (https://pydantic.dev/docs/ai/graph/beta):
34
+ //
35
+ // - step → executes an async function
36
+ // - decision → conditional branching
37
+ // - spread → parallel processing of iterables
38
+ // - broadcast → send the same data to multiple parallel paths
39
+ // - join → aggregate results from parallel execution (reducer)
40
+ // - reducer → alias for the reducer half of a join
41
+ // - start/end → graph entry / exit markers
42
+ const NODE_COLORS = {
43
+ root: '#58a6ff',
44
+ start: '#58a6ff',
45
+ step: '#3fb950',
46
+ end: '#f85149',
47
+ end_or_continue: '#d29922',
48
+ join: '#bc8cff',
49
+ reducer: '#a371f7',
50
+ decision: '#f0883e',
51
+ broadcast: '#2dd4bf',
52
+ spread: '#79c0ff',
53
+ error: '#da3633',
54
+ parallel: '#79c0ff',
55
+ default: '#8b949e',
56
+ };
57
+ /** Mapping of node type → ECharts symbol shape, for at-a-glance recognition. */
58
+ const NODE_SYMBOLS = {
59
+ root: 'circle',
60
+ start: 'circle',
61
+ step: 'roundRect',
62
+ decision: 'diamond',
63
+ broadcast: 'triangle',
64
+ spread: 'arrow',
65
+ join: 'pin',
66
+ reducer: 'pin',
67
+ end: 'circle',
68
+ end_or_continue: 'diamond',
69
+ parallel: 'arrow',
70
+ error: 'circle',
71
+ default: 'roundRect',
72
+ };
73
+ /** Human-readable legend entries used by the UI. */
74
+ const LEGEND_ENTRIES = [
75
+ { type: 'start', label: 'Start', description: 'Graph entry point' },
76
+ { type: 'step', label: 'Step', description: 'Async function execution' },
77
+ { type: 'decision', label: 'Decision', description: 'Conditional branching' },
78
+ {
79
+ type: 'broadcast',
80
+ label: 'Broadcast',
81
+ description: 'Send data to multiple parallel paths',
82
+ },
83
+ {
84
+ type: 'spread',
85
+ label: 'Spread',
86
+ description: 'Parallel processing of iterables',
87
+ },
88
+ {
89
+ type: 'join',
90
+ label: 'Join',
91
+ description: 'Aggregate parallel results (reducer)',
92
+ },
93
+ { type: 'end', label: 'End', description: 'Graph exit point' },
94
+ ];
95
+ function spanColor(span) {
96
+ if (span.status_code === 'ERROR')
97
+ return NODE_COLORS.error;
98
+ const rawType = span.attributes?.['graph.node.type'] ?? '';
99
+ const nodeType = rawType.toLowerCase();
100
+ if (nodeType in NODE_COLORS) {
101
+ return (NODE_COLORS[nodeType] ?? NODE_COLORS.step);
102
+ }
103
+ if (span.span_name === 'agent.graph.run')
104
+ return NODE_COLORS.root;
105
+ return NODE_COLORS.step;
106
+ }
107
+ function formatDur(ms) {
108
+ if (ms < 1)
109
+ return '<1ms';
110
+ if (ms < 1000)
111
+ return `${ms.toFixed(1)}ms`;
112
+ return `${(ms / 1000).toFixed(2)}s`;
113
+ }
114
+ function nanoToIso(val) {
115
+ const n = Number(val);
116
+ if (!Number.isFinite(n) || n <= 0)
117
+ return '';
118
+ if (n > 1e15)
119
+ return new Date(n / 1e6).toISOString();
120
+ if (n > 1e12)
121
+ return new Date(n / 1e3).toISOString();
122
+ return new Date(n).toISOString();
123
+ }
124
+ function parseAttrs(raw) {
125
+ if (typeof raw === 'object' && raw !== null) {
126
+ return raw;
127
+ }
128
+ if (typeof raw === 'string' && raw.trim().length > 0) {
129
+ try {
130
+ const parsed = JSON.parse(raw);
131
+ if (typeof parsed === 'object' && parsed !== null) {
132
+ return parsed;
133
+ }
134
+ }
135
+ catch {
136
+ return undefined;
137
+ }
138
+ }
139
+ return undefined;
140
+ }
141
+ function normalizeSpan(raw) {
142
+ const source = raw;
143
+ const durationMs = source.duration_ms != null
144
+ ? Number(source.duration_ms)
145
+ : source.duration_ns != null
146
+ ? Number(source.duration_ns) / 1e6
147
+ : 0;
148
+ return {
149
+ trace_id: String(source.trace_id ?? ''),
150
+ span_id: String(source.span_id ?? ''),
151
+ parent_span_id: source.parent_span_id != null && String(source.parent_span_id).length > 0
152
+ ? String(source.parent_span_id)
153
+ : undefined,
154
+ span_name: String(source.span_name ?? source.operation_name ?? source.name ?? ''),
155
+ service_name: String(source.service_name ?? ''),
156
+ kind: String(source.kind ?? source.span_kind ?? 'INTERNAL'),
157
+ start_time: typeof source.start_time === 'string' && source.start_time.length > 0
158
+ ? source.start_time
159
+ : nanoToIso(source.start_time_unix_nano),
160
+ end_time: typeof source.end_time === 'string' && source.end_time.length > 0
161
+ ? source.end_time
162
+ : nanoToIso(source.end_time_unix_nano),
163
+ duration_ms: Number.isFinite(durationMs) ? durationMs : 0,
164
+ status_code: source.status_code != null ? String(source.status_code) : undefined,
165
+ status_message: source.status_message != null ? String(source.status_message) : undefined,
166
+ attributes: parseAttrs(source.attributes),
167
+ };
168
+ }
169
+ function isGraphSpan(span) {
170
+ return (span.span_name === 'agent.graph.run' ||
171
+ span.span_name.startsWith('graph.node.'));
172
+ }
173
+ // ── ECharts option builder ─────────────────────────────────────────────────
174
+ /**
175
+ * Compute a left-to-right layered DAG layout for the spans.
176
+ *
177
+ * Execution traces are strictly hierarchical (parent → child spans), so a
178
+ * fixed layered layout produces a far cleaner visual than force-directed
179
+ * relaxation — matching the "Layout: none + manual positions" pattern used
180
+ * by several ECharts graph examples for DAGs.
181
+ *
182
+ * Each span is placed at:
183
+ * x = depth * columnWidth
184
+ * y = indexWithinLayer * rowHeight - verticalCentre
185
+ *
186
+ * Root spans (no parent or parent missing from trace) anchor depth 0.
187
+ */
188
+ function computeLayout(spans) {
189
+ const spanById = new Map(spans.map(s => [s.span_id, s]));
190
+ const childrenByParent = new Map();
191
+ const roots = [];
192
+ for (const s of spans) {
193
+ if (s.parent_span_id && spanById.has(s.parent_span_id)) {
194
+ const list = childrenByParent.get(s.parent_span_id) ?? [];
195
+ list.push(s.span_id);
196
+ childrenByParent.set(s.parent_span_id, list);
197
+ }
198
+ else {
199
+ roots.push(s.span_id);
200
+ }
201
+ }
202
+ // Preserve execution order within a layer by sorting children by start time.
203
+ for (const [parent, kids] of childrenByParent) {
204
+ kids.sort((a, b) => {
205
+ const sa = spanById.get(a)?.start_time ?? '';
206
+ const sb = spanById.get(b)?.start_time ?? '';
207
+ return sa.localeCompare(sb);
208
+ });
209
+ childrenByParent.set(parent, kids);
210
+ }
211
+ // BFS depth assignment from every root.
212
+ const depthById = new Map();
213
+ const queue = roots.map(r => [r, 0]);
214
+ while (queue.length > 0) {
215
+ const next = queue.shift();
216
+ if (!next)
217
+ break;
218
+ const [spanId, depth] = next;
219
+ if (depthById.has(spanId))
220
+ continue;
221
+ depthById.set(spanId, depth);
222
+ for (const child of childrenByParent.get(spanId) ?? []) {
223
+ queue.push([child, depth + 1]);
224
+ }
225
+ }
226
+ // Defensive: any span we never reached (orphan) → depth 0.
227
+ for (const s of spans) {
228
+ if (!depthById.has(s.span_id))
229
+ depthById.set(s.span_id, 0);
230
+ }
231
+ // Bucket spans by depth.
232
+ const byDepth = new Map();
233
+ for (const [spanId, depth] of depthById) {
234
+ const list = byDepth.get(depth) ?? [];
235
+ list.push(spanId);
236
+ byDepth.set(depth, list);
237
+ }
238
+ // Preserve start-time order within each depth layer.
239
+ for (const [depth, ids] of byDepth) {
240
+ ids.sort((a, b) => {
241
+ const sa = spanById.get(a)?.start_time ?? '';
242
+ const sb = spanById.get(b)?.start_time ?? '';
243
+ return sa.localeCompare(sb);
244
+ });
245
+ byDepth.set(depth, ids);
246
+ }
247
+ const columnWidth = 180;
248
+ const rowHeight = 90;
249
+ const positions = new Map();
250
+ for (const [depth, ids] of byDepth) {
251
+ const count = ids.length;
252
+ const totalHeight = (count - 1) * rowHeight;
253
+ const yStart = -totalHeight / 2;
254
+ ids.forEach((spanId, idx) => {
255
+ positions.set(spanId, {
256
+ x: depth * columnWidth,
257
+ y: yStart + idx * rowHeight,
258
+ });
259
+ });
260
+ }
261
+ return positions;
262
+ }
263
+ function buildOption(run) {
264
+ const { spans } = run;
265
+ const spanById = new Map(spans.map(s => [s.span_id, s]));
266
+ const maxDur = Math.max(...spans.map(s => s.duration_ms ?? 1), 1);
267
+ const positions = computeLayout(spans);
268
+ const nodes = spans.map(s => {
269
+ const isRoot = s.span_name === 'agent.graph.run';
270
+ const dur = s.duration_ms ?? 1;
271
+ const nodeId = s.attributes?.['graph.node.id'] ??
272
+ s.span_id.slice(0, 8);
273
+ const nodeType = s.attributes?.['graph.node.type'] ??
274
+ (isRoot ? 'root' : 'step');
275
+ const status = s.attributes?.['graph.node.status'] ??
276
+ 'completed';
277
+ const label = isRoot ? '▶ Run' : nodeId.replace('__', '');
278
+ const hasError = s.status_code === 'ERROR' ||
279
+ status === 'error' ||
280
+ !!s.attributes?.['error.message'];
281
+ const symbol = NODE_SYMBOLS[nodeType.toLowerCase()] ??
282
+ (isRoot ? 'circle' : NODE_SYMBOLS.default);
283
+ const pos = positions.get(s.span_id) ?? { x: 0, y: 0 };
284
+ return {
285
+ id: s.span_id,
286
+ name: label,
287
+ x: pos.x,
288
+ y: pos.y,
289
+ symbol,
290
+ symbolSize: isRoot ? 44 : 22 + Math.round((dur / maxDur) * 26),
291
+ itemStyle: {
292
+ color: hasError ? NODE_COLORS.error : spanColor(s),
293
+ borderWidth: isRoot ? 3 : dur > maxDur * 0.5 ? 2 : 1,
294
+ borderColor: hasError ? NODE_COLORS.error : 'rgba(240,246,252,0.25)',
295
+ shadowBlur: isRoot ? 12 : 4,
296
+ shadowColor: 'rgba(0,0,0,0.35)',
297
+ },
298
+ label: {
299
+ show: true,
300
+ position: 'bottom',
301
+ distance: 8,
302
+ fontSize: isRoot ? 12 : 11,
303
+ color: '#c9d1d9',
304
+ formatter: label,
305
+ backgroundColor: 'rgba(13,17,23,0.7)',
306
+ padding: [2, 6],
307
+ borderRadius: 3,
308
+ },
309
+ value: dur,
310
+ // Custom tooltip via series-level formatter below.
311
+ _meta: {
312
+ nodeId,
313
+ nodeType,
314
+ status,
315
+ dur,
316
+ isRoot,
317
+ error: s.attributes?.['error.message'],
318
+ },
319
+ };
320
+ });
321
+ const links = [];
322
+ for (const s of spans) {
323
+ if (s.parent_span_id && spanById.has(s.parent_span_id)) {
324
+ const isError = s.status_code === 'ERROR';
325
+ links.push({
326
+ source: s.parent_span_id,
327
+ target: s.span_id,
328
+ lineStyle: {
329
+ color: isError ? NODE_COLORS.error : '#6e7681',
330
+ width: isError ? 2 : 1.4,
331
+ curveness: 0.15,
332
+ opacity: 0.9,
333
+ },
334
+ });
335
+ }
336
+ }
337
+ return {
338
+ backgroundColor: 'transparent',
339
+ tooltip: {
340
+ trigger: 'item',
341
+ backgroundColor: '#161b22',
342
+ borderColor: '#30363d',
343
+ textStyle: { color: '#c9d1d9', fontSize: 12 },
344
+ formatter: (params) => {
345
+ const meta = params?.data?._meta;
346
+ if (!meta)
347
+ return '';
348
+ const lines = [
349
+ `<b>${meta.nodeId}</b>`,
350
+ `Type: ${meta.nodeType}`,
351
+ `Status: ${meta.status}`,
352
+ `Duration: ${formatDur(Number(meta.dur))}`,
353
+ ];
354
+ if (meta.error) {
355
+ lines.push(`<span style="color:${NODE_COLORS.error}">Error: ${meta.error}</span>`);
356
+ }
357
+ return lines.join('<br/>');
358
+ },
359
+ },
360
+ series: [
361
+ {
362
+ type: 'graph',
363
+ // Layered DAG: positions are precomputed per-node, so 'none' gives
364
+ // us a stable, non-jittery layout (mirrors the static DAG samples
365
+ // in https://echarts.apache.org/examples/en/index.html#chart-type-graph).
366
+ layout: 'none',
367
+ roam: true,
368
+ draggable: true,
369
+ data: nodes,
370
+ links,
371
+ edgeSymbol: ['none', 'arrow'],
372
+ edgeSymbolSize: [0, 9],
373
+ lineStyle: { opacity: 0.9, curveness: 0.15 },
374
+ emphasis: {
375
+ focus: 'adjacency',
376
+ lineStyle: { width: 3, opacity: 1 },
377
+ label: { show: true },
378
+ },
379
+ labelLayout: { hideOverlap: true },
380
+ autoCurveness: true,
381
+ zoom: 1,
382
+ animation: true,
383
+ animationDuration: 400,
384
+ animationEasingUpdate: 'cubicOut',
385
+ },
386
+ ],
387
+ };
388
+ }
389
+ export const TurnGraphChart = ({ serviceName, agentId, runUrl, apiKey, autoRefreshMs = 10_000, height = 320, style, }) => {
390
+ const [runs, setRuns] = useState([]);
391
+ const [selectedIdx, setSelectedIdx] = useState(0);
392
+ const [loading, setLoading] = useState(false);
393
+ const [error, setError] = useState(null);
394
+ const mountedRef = useRef(true);
395
+ const upsertRunsFromSpans = useCallback((incoming, mode = 'merge') => {
396
+ const normalized = incoming.map(normalizeSpan);
397
+ const spansByTrace = new Map();
398
+ for (const span of normalized) {
399
+ if (!isGraphSpan(span))
400
+ continue;
401
+ if (agentId &&
402
+ span.attributes?.['agent.id'] &&
403
+ span.attributes['agent.id'] !== agentId) {
404
+ continue;
405
+ }
406
+ const list = spansByTrace.get(span.trace_id) ?? [];
407
+ list.push(span);
408
+ spansByTrace.set(span.trace_id, list);
409
+ }
410
+ const incomingRuns = [];
411
+ for (const [traceId, spans] of spansByTrace.entries()) {
412
+ const root = spans.find(s => s.span_name === 'agent.graph.run') ?? spans[0];
413
+ if (!root)
414
+ continue;
415
+ incomingRuns.push({
416
+ traceId,
417
+ rootSpan: root,
418
+ spans,
419
+ startTime: new Date(root.start_time),
420
+ durationMs: root.duration_ms ?? 0,
421
+ });
422
+ }
423
+ setRuns(prev => {
424
+ const merged = new Map();
425
+ if (mode === 'merge') {
426
+ for (const run of prev)
427
+ merged.set(run.traceId, run);
428
+ }
429
+ for (const run of incomingRuns)
430
+ merged.set(run.traceId, run);
431
+ const sorted = Array.from(merged.values()).sort((a, b) => b.startTime.getTime() - a.startTime.getTime());
432
+ return sorted.slice(0, 20);
433
+ });
434
+ setSelectedIdx(prev => Math.max(0, prev));
435
+ }, [agentId]);
436
+ useEffect(() => {
437
+ mountedRef.current = true;
438
+ return () => {
439
+ mountedRef.current = false;
440
+ };
441
+ }, []);
442
+ const fetchData = useCallback(async () => {
443
+ if (!runUrl || !apiKey)
444
+ return;
445
+ setLoading(true);
446
+ setError(null);
447
+ try {
448
+ const client = createOtelClient({ baseUrl: runUrl, token: apiKey });
449
+ const result = await client.fetchTraces({ serviceName, limit: 200 });
450
+ if (mountedRef.current)
451
+ upsertRunsFromSpans(result.data, 'replace');
452
+ }
453
+ catch (err) {
454
+ if (mountedRef.current) {
455
+ setError(err instanceof Error ? err.message : String(err));
456
+ }
457
+ }
458
+ finally {
459
+ if (mountedRef.current)
460
+ setLoading(false);
461
+ }
462
+ }, [runUrl, apiKey, serviceName, upsertRunsFromSpans]);
463
+ const { connected: wsConnected, error: wsError } = useOtelWebSocket({
464
+ baseUrl: runUrl,
465
+ token: apiKey,
466
+ callbacks: {
467
+ onTraces: spans => {
468
+ upsertRunsFromSpans(spans, 'merge');
469
+ },
470
+ },
471
+ });
472
+ useEffect(() => {
473
+ void fetchData();
474
+ // WebSocket is the primary live feed; keep HTTP polling only as fallback.
475
+ if (autoRefreshMs > 0 && !wsConnected) {
476
+ const id = setInterval(() => void fetchData(), autoRefreshMs);
477
+ return () => clearInterval(id);
478
+ }
479
+ }, [fetchData, autoRefreshMs, wsConnected]);
480
+ const selectedRun = runs[selectedIdx];
481
+ const option = useMemo(() => (selectedRun ? buildOption(selectedRun) : null), [selectedRun]);
482
+ // Not configured — caller didn't pass auth.
483
+ if (!runUrl || !apiKey)
484
+ return null;
485
+ if (loading && runs.length === 0) {
486
+ return (_jsx(Box, { sx: { color: 'fg.muted', fontSize: 1, py: 2 }, children: "Loading OTEL traces\u2026" }));
487
+ }
488
+ if (error && runs.length === 0) {
489
+ return (_jsxs(Box, { sx: { color: 'danger.fg', fontSize: 0, py: 1 }, children: ["OTEL trace fetch failed: ", error] }));
490
+ }
491
+ if (!option) {
492
+ return (_jsx(Box, { sx: { color: 'fg.muted', fontSize: 1, py: 2 }, children: "No graph trace data yet \u2014 run a pydantic-graph agent to see execution turns here." }));
493
+ }
494
+ return (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [runs.length > 1 && (_jsx(Box, { sx: {
495
+ display: 'flex',
496
+ gap: 1,
497
+ flexWrap: 'wrap',
498
+ mb: 1,
499
+ }, children: runs.slice(0, 8).map((run, idx) => (_jsxs(Box, { onClick: () => setSelectedIdx(idx), sx: {
500
+ px: 2,
501
+ py: '2px',
502
+ borderRadius: 2,
503
+ fontSize: 0,
504
+ cursor: 'pointer',
505
+ border: '1px solid',
506
+ borderColor: idx === selectedIdx ? 'accent.emphasis' : 'border.default',
507
+ bg: idx === selectedIdx ? 'accent.subtle' : 'canvas.subtle',
508
+ color: idx === selectedIdx ? 'accent.fg' : 'fg.muted',
509
+ userSelect: 'none',
510
+ whiteSpace: 'nowrap',
511
+ }, children: ["#", runs.length - idx, " \u00A0", run.startTime.toLocaleTimeString([], {
512
+ hour: '2-digit',
513
+ minute: '2-digit',
514
+ second: '2-digit',
515
+ }), run.durationMs ? ` (${formatDur(run.durationMs)})` : ''] }, run.traceId))) })), _jsx(Box, { sx: {
516
+ display: 'flex',
517
+ gap: 2,
518
+ flexWrap: 'wrap',
519
+ alignItems: 'center',
520
+ fontSize: 0,
521
+ color: 'fg.muted',
522
+ px: 1,
523
+ py: '2px',
524
+ }, "aria-label": "Graph node legend", children: LEGEND_ENTRIES.map(entry => (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, title: entry.description, children: [_jsx(Box, { "aria-hidden": true, sx: {
525
+ width: 10,
526
+ height: 10,
527
+ borderRadius: entry.type === 'decision'
528
+ ? 0
529
+ : entry.type === 'broadcast' || entry.type === 'spread'
530
+ ? '2px'
531
+ : '50%',
532
+ transform: entry.type === 'decision' ? 'rotate(45deg)' : 'none',
533
+ bg: NODE_COLORS[entry.type] ?? NODE_COLORS.default,
534
+ } }), _jsx(Text, { sx: { fontSize: 0 }, children: entry.label })] }, entry.type))) }), _jsx(ReactECharts, { option: option, style: { height, width: '100%', ...style }, opts: { renderer: 'svg' }, notMerge: false }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [selectedRun.spans.length - 1, " node(s)", selectedRun.durationMs
535
+ ? ` · ${formatDur(selectedRun.durationMs)} total`
536
+ : '', wsConnected ? ' · ws live' : ' · ws disconnected', wsError ? ` · ws error: ${wsError}` : '', loading ? ' · refreshing…' : '', error ? ` · fetch error: ${error}` : ''] })] }));
537
+ };
538
+ export default TurnGraphChart;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Shared WebSocket connection pool for the OTEL service.
3
+ *
4
+ * Multiple chart components that subscribe to the same OTEL WS URL will
5
+ * share a single underlying WebSocket connection via reference counting.
6
+ */
7
+ type OtelWsMessage = {
8
+ signal?: string;
9
+ data?: Array<Record<string, unknown>>;
10
+ count?: number;
11
+ };
12
+ type MessageListener = (msg: OtelWsMessage) => void;
13
+ /**
14
+ * Subscribe to a shared OTEL WebSocket connection.
15
+ *
16
+ * Returns an unsubscribe function. When the last subscriber unsubscribes,
17
+ * the underlying WebSocket is closed and removed from the pool.
18
+ */
19
+ export declare function subscribeOtelWs(wsUrl: string, listener: MessageListener): () => void;
20
+ export {};
@@ -0,0 +1,69 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ const pool = new Map();
6
+ function connectEntry(entry) {
7
+ if (entry.disposed || entry.listeners.size === 0)
8
+ return;
9
+ const ws = new WebSocket(entry.url);
10
+ ws.onmessage = (event) => {
11
+ try {
12
+ const msg = JSON.parse(event.data);
13
+ for (const listener of entry.listeners) {
14
+ listener(msg);
15
+ }
16
+ }
17
+ catch {
18
+ // Ignore unparseable messages.
19
+ }
20
+ };
21
+ ws.onerror = () => { };
22
+ ws.onclose = () => {
23
+ entry.ws = null;
24
+ if (entry.disposed || entry.listeners.size === 0)
25
+ return;
26
+ entry.reconnectTimer = setTimeout(() => connectEntry(entry), 2000);
27
+ };
28
+ entry.ws = ws;
29
+ }
30
+ /**
31
+ * Subscribe to a shared OTEL WebSocket connection.
32
+ *
33
+ * Returns an unsubscribe function. When the last subscriber unsubscribes,
34
+ * the underlying WebSocket is closed and removed from the pool.
35
+ */
36
+ export function subscribeOtelWs(wsUrl, listener) {
37
+ let entry = pool.get(wsUrl);
38
+ if (!entry) {
39
+ entry = {
40
+ ws: null,
41
+ listeners: new Set(),
42
+ reconnectTimer: null,
43
+ disposed: false,
44
+ url: wsUrl,
45
+ };
46
+ pool.set(wsUrl, entry);
47
+ }
48
+ entry.listeners.add(listener);
49
+ // If the WS isn't connected yet (first subscriber or after full teardown),
50
+ // start the connection.
51
+ if (!entry.ws && !entry.disposed) {
52
+ connectEntry(entry);
53
+ }
54
+ return () => {
55
+ if (!entry)
56
+ return;
57
+ entry.listeners.delete(listener);
58
+ if (entry.listeners.size === 0) {
59
+ entry.disposed = true;
60
+ if (entry.reconnectTimer) {
61
+ clearTimeout(entry.reconnectTimer);
62
+ entry.reconnectTimer = null;
63
+ }
64
+ entry.ws?.close();
65
+ entry.ws = null;
66
+ pool.delete(wsUrl);
67
+ }
68
+ };
69
+ }
@@ -1,20 +1,3 @@
1
- /**
2
- * A2UiComponentGalleryExample
3
- *
4
- * Showcases the full catalog of A2UI components rendered via @a2ui/react.
5
- * Uses static A2UI JSON messages (no backend required) to demonstrate:
6
- * - Text, Button, CheckBox, Slider, TextField, DateTimeInput
7
- * - MultipleChoice (default, chips, filterable)
8
- * - Row, Column, Card, List, Tabs, Divider, Modal
9
- * - Icon, Image, Video, AudioPlayer
10
- *
11
- * All messages follow the A2UI three-step pattern:
12
- * beginRendering → surfaceUpdate → dataModelUpdate
13
- */
14
1
  import React from 'react';
15
- /**
16
- * A2UiComponentGalleryExample — Kitchen sink of all A2UI components.
17
- * No backend required — uses static A2UI JSON messages rendered client-side.
18
- */
19
2
  declare const A2UiComponentGalleryExample: React.FC;
20
3
  export default A2UiComponentGalleryExample;