@datalayer/agent-runtimes 1.0.3 → 1.0.5

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 (275) hide show
  1. package/README.md +35 -119
  2. package/lib/App.js +1 -1
  3. package/lib/agents/AgentDetails.d.ts +22 -1
  4. package/lib/agents/AgentDetails.js +34 -47
  5. package/lib/api/index.d.ts +0 -1
  6. package/lib/api/index.js +4 -2
  7. package/lib/chat/Chat.d.ts +5 -104
  8. package/lib/chat/Chat.js +4 -4
  9. package/lib/chat/ChatFloating.d.ts +7 -140
  10. package/lib/chat/ChatFloating.js +2 -2
  11. package/lib/chat/ChatPopupStandalone.d.ts +8 -47
  12. package/lib/chat/ChatPopupStandalone.js +3 -3
  13. package/lib/chat/ChatSidebar.d.ts +4 -69
  14. package/lib/chat/ChatSidebar.js +2 -2
  15. package/lib/chat/ChatStandalone.d.ts +4 -54
  16. package/lib/chat/ChatStandalone.js +3 -3
  17. package/lib/chat/base/ChatBase.js +1118 -141
  18. package/lib/chat/header/ChatHeaderBase.d.ts +11 -6
  19. package/lib/chat/header/ChatHeaderBase.js +18 -16
  20. package/lib/chat/indicators/McpStatusIndicator.d.ts +7 -4
  21. package/lib/chat/indicators/McpStatusIndicator.js +7 -32
  22. package/lib/chat/indicators/SandboxStatusIndicator.d.ts +4 -1
  23. package/lib/chat/indicators/SandboxStatusIndicator.js +9 -9
  24. package/lib/chat/indicators/SkillsStatusIndicator.d.ts +7 -0
  25. package/lib/chat/indicators/SkillsStatusIndicator.js +88 -0
  26. package/lib/chat/indicators/index.d.ts +1 -0
  27. package/lib/chat/indicators/index.js +1 -0
  28. package/lib/chat/messages/ChatMessageList.d.ts +1 -1
  29. package/lib/chat/messages/ChatMessageList.js +110 -102
  30. package/lib/chat/prompt/InputFooter.d.ts +19 -6
  31. package/lib/chat/prompt/InputFooter.js +71 -18
  32. package/lib/chat/prompt/InputPrompt.d.ts +3 -1
  33. package/lib/chat/prompt/InputPrompt.js +4 -4
  34. package/lib/chat/prompt/InputPromptFooter.js +1 -1
  35. package/lib/chat/prompt/InputPromptLexical.d.ts +3 -1
  36. package/lib/chat/prompt/InputPromptLexical.js +12 -5
  37. package/lib/chat/prompt/InputPromptText.d.ts +3 -1
  38. package/lib/chat/prompt/InputPromptText.js +2 -2
  39. package/lib/chat/tools/ToolApprovalBanner.js +1 -1
  40. package/lib/chat/tools/ToolCallDisplay.d.ts +3 -1
  41. package/lib/chat/tools/ToolCallDisplay.js +2 -2
  42. package/lib/chat/usage/TokenUsageBar.js +20 -2
  43. package/lib/client/AgentRuntimesClientContext.d.ts +53 -0
  44. package/lib/client/AgentRuntimesClientContext.js +55 -0
  45. package/lib/client/AgentsMixin.d.ts +48 -19
  46. package/lib/client/AgentsMixin.js +115 -30
  47. package/lib/client/IAgentRuntimesClient.d.ts +215 -0
  48. package/lib/client/IAgentRuntimesClient.js +5 -0
  49. package/lib/client/SdkAgentRuntimesClient.d.ts +151 -0
  50. package/lib/client/SdkAgentRuntimesClient.js +134 -0
  51. package/lib/client/index.d.ts +4 -1
  52. package/lib/client/index.js +3 -1
  53. package/lib/components/NotificationEventCard.js +55 -26
  54. package/lib/components/OutputCard.js +21 -7
  55. package/lib/components/ToolApprovalCard.js +20 -2
  56. package/lib/config/AgentConfiguration.js +3 -3
  57. package/lib/context/ContextDistribution.d.ts +3 -1
  58. package/lib/context/ContextDistribution.js +8 -27
  59. package/lib/context/ContextInspector.d.ts +3 -1
  60. package/lib/context/ContextInspector.js +19 -67
  61. package/lib/context/ContextPanel.d.ts +3 -1
  62. package/lib/context/ContextPanel.js +104 -64
  63. package/lib/context/ContextUsage.d.ts +3 -1
  64. package/lib/context/ContextUsage.js +3 -3
  65. package/lib/context/CostTracker.d.ts +9 -3
  66. package/lib/context/CostTracker.js +26 -47
  67. package/lib/context/CostUsageChart.d.ts +12 -0
  68. package/lib/context/CostUsageChart.js +378 -0
  69. package/lib/context/GraphFlowChart.d.ts +16 -0
  70. package/lib/context/GraphFlowChart.js +182 -0
  71. package/lib/context/TokenUsageChart.d.ts +8 -1
  72. package/lib/context/TokenUsageChart.js +349 -211
  73. package/lib/context/TurnGraphChart.d.ts +39 -0
  74. package/lib/context/TurnGraphChart.js +538 -0
  75. package/lib/context/otelWsPool.d.ts +20 -0
  76. package/lib/context/otelWsPool.js +69 -0
  77. package/lib/examples/A2UiComponentGalleryExample.d.ts +0 -17
  78. package/lib/examples/A2UiComponentGalleryExample.js +315 -522
  79. package/lib/examples/A2UiContactCardExample.d.ts +0 -18
  80. package/lib/examples/A2UiContactCardExample.js +154 -411
  81. package/lib/examples/A2UiRestaurantExample.d.ts +0 -30
  82. package/lib/examples/A2UiRestaurantExample.js +114 -212
  83. package/lib/examples/A2UiViewerExample.d.ts +0 -18
  84. package/lib/examples/A2UiViewerExample.js +283 -532
  85. package/lib/examples/AgUiBackendToolRenderingExample.js +1 -1
  86. package/lib/examples/AgUiHaikuGenUiExample.d.ts +1 -1
  87. package/lib/examples/AgUiHaikuGenUiExample.js +1 -1
  88. package/lib/examples/AgentCheckpointsExample.js +14 -34
  89. package/lib/examples/AgentCodemodeExample.d.ts +4 -6
  90. package/lib/examples/AgentCodemodeExample.js +591 -175
  91. package/lib/examples/AgentEvalsExample.js +13 -23
  92. package/lib/examples/AgentGuardrailsExample.js +371 -71
  93. package/lib/examples/AgentHooksExample.d.ts +3 -0
  94. package/lib/examples/AgentHooksExample.js +104 -0
  95. package/lib/examples/AgentMCPExample.d.ts +3 -0
  96. package/lib/examples/AgentMCPExample.js +480 -0
  97. package/lib/examples/AgentMemoryExample.js +14 -24
  98. package/lib/examples/AgentMonitoringExample.js +261 -206
  99. package/lib/examples/AgentNotificationsExample.js +50 -24
  100. package/lib/examples/AgentOtelExample.js +2 -3
  101. package/lib/examples/AgentOutputsExample.d.ts +11 -6
  102. package/lib/examples/AgentOutputsExample.js +383 -88
  103. package/lib/examples/AgentParametersExample.d.ts +3 -0
  104. package/lib/examples/AgentParametersExample.js +246 -0
  105. package/lib/examples/AgentSandboxExample.d.ts +2 -2
  106. package/lib/examples/AgentSandboxExample.js +69 -47
  107. package/lib/examples/AgentSkillsExample.js +92 -106
  108. package/lib/examples/{AgentspecExample.js → AgentSpecsExample.js} +10 -21
  109. package/lib/examples/AgentSubagentsExample.d.ts +14 -0
  110. package/lib/examples/AgentSubagentsExample.js +228 -0
  111. package/lib/examples/AgentToolApprovalsExample.js +30 -493
  112. package/lib/examples/AgentTriggersExample.js +1067 -246
  113. package/lib/examples/ChatCustomExample.js +11 -24
  114. package/lib/examples/ChatExample.js +9 -34
  115. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  116. package/lib/examples/CopilotKitNotebookExample.js +2 -1
  117. package/lib/examples/HomeExample.d.ts +15 -0
  118. package/lib/examples/HomeExample.js +77 -0
  119. package/lib/examples/Lexical2Example.js +4 -2
  120. package/lib/examples/{LexicalExample.d.ts → LexicalAgentExample.d.ts} +4 -4
  121. package/lib/examples/{LexicalExample.js → LexicalAgentExample.js} +65 -16
  122. package/lib/examples/{LexicalSidebarExample.d.ts → LexicalAgentSidebarExample.d.ts} +5 -5
  123. package/lib/examples/LexicalAgentSidebarExample.js +261 -0
  124. package/lib/examples/NotebookAgentExample.d.ts +9 -0
  125. package/lib/examples/NotebookAgentExample.js +192 -0
  126. package/lib/examples/{NotebookSidebarExample.d.ts → NotebookAgentSidebarExample.d.ts} +2 -2
  127. package/lib/examples/NotebookAgentSidebarExample.js +221 -0
  128. package/lib/examples/{DatalayerNotebookExample.d.ts → NotebookCollaborationExample.d.ts} +4 -4
  129. package/lib/examples/{DatalayerNotebookExample.js → NotebookCollaborationExample.js} +3 -3
  130. package/lib/examples/NotebookExample.d.ts +4 -7
  131. package/lib/examples/NotebookExample.js +14 -146
  132. package/lib/examples/components/AuthRequiredView.d.ts +6 -0
  133. package/lib/examples/components/AuthRequiredView.js +33 -0
  134. package/lib/examples/components/ErrorView.d.ts +14 -0
  135. package/lib/examples/components/ErrorView.js +20 -0
  136. package/lib/examples/components/ExampleWrapper.d.ts +7 -0
  137. package/lib/examples/components/ExampleWrapper.js +25 -6
  138. package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.js +1 -1
  139. package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.js +1 -1
  140. package/lib/examples/{ag-ui → components}/haiku/index.d.ts +1 -1
  141. package/lib/examples/{ag-ui → components}/haiku/index.js +1 -1
  142. package/lib/examples/components/index.d.ts +5 -0
  143. package/lib/examples/components/index.js +5 -0
  144. package/lib/examples/{ag-ui → components}/weather/index.d.ts +1 -1
  145. package/lib/examples/{ag-ui → components}/weather/index.js +1 -1
  146. package/lib/examples/example-selector.d.ts +17 -4
  147. package/lib/examples/example-selector.js +107 -41
  148. package/lib/examples/index.d.ts +9 -6
  149. package/lib/examples/index.js +9 -6
  150. package/lib/examples/main.d.ts +1 -0
  151. package/lib/examples/main.js +218 -27
  152. package/lib/examples/utils/a2ui.d.ts +18 -0
  153. package/lib/examples/utils/a2ui.js +69 -0
  154. package/lib/examples/utils/a2uiMarkdownProvider.d.ts +7 -0
  155. package/lib/examples/utils/a2uiMarkdownProvider.js +9 -0
  156. package/lib/examples/utils/agentId.d.ts +18 -0
  157. package/lib/examples/utils/agentId.js +54 -0
  158. package/lib/examples/utils/agents/earthquake-detector.json +11 -11
  159. package/lib/examples/utils/agents/sales-forecaster.json +11 -11
  160. package/lib/examples/utils/agents/social-post-generator.json +11 -11
  161. package/lib/examples/utils/agents/stock-market.json +11 -11
  162. package/lib/examples/utils/examplesStore.js +82 -27
  163. package/lib/hooks/index.d.ts +8 -8
  164. package/lib/hooks/index.js +7 -7
  165. package/lib/hooks/useA2A.d.ts +2 -3
  166. package/lib/hooks/useAIAgentsWebSocket.d.ts +43 -4
  167. package/lib/hooks/useAIAgentsWebSocket.js +118 -12
  168. package/lib/hooks/useAcp.d.ts +1 -2
  169. package/lib/hooks/useAgUi.d.ts +1 -1
  170. package/lib/hooks/{useAgents.d.ts → useAgentRuntimes.d.ts} +39 -2
  171. package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +125 -15
  172. package/lib/hooks/useAgentsCatalog.js +1 -1
  173. package/lib/hooks/useAgentsService.d.ts +2 -2
  174. package/lib/hooks/useAgentsService.js +7 -7
  175. package/lib/hooks/useCheckpoints.js +1 -1
  176. package/lib/hooks/useConfig.d.ts +4 -1
  177. package/lib/hooks/useConfig.js +10 -3
  178. package/lib/hooks/useContextSnapshot.d.ts +9 -4
  179. package/lib/hooks/useContextSnapshot.js +9 -37
  180. package/lib/hooks/useMonitoring.js +3 -0
  181. package/lib/hooks/useSandbox.d.ts +20 -8
  182. package/lib/hooks/useSandbox.js +105 -40
  183. package/lib/hooks/useSkills.d.ts +23 -5
  184. package/lib/hooks/useSkills.js +94 -39
  185. package/lib/hooks/useToolApprovals.d.ts +60 -36
  186. package/lib/hooks/useToolApprovals.js +318 -69
  187. package/lib/hooks/useVercelAI.d.ts +1 -1
  188. package/lib/index.d.ts +2 -1
  189. package/lib/index.js +1 -0
  190. package/lib/inference/index.d.ts +0 -1
  191. package/lib/middleware/index.d.ts +0 -1
  192. package/lib/protocols/AGUIAdapter.js +6 -0
  193. package/lib/protocols/VercelAIAdapter.d.ts +9 -0
  194. package/lib/protocols/VercelAIAdapter.js +144 -26
  195. package/lib/shims/json5.d.ts +4 -0
  196. package/lib/shims/json5.js +8 -0
  197. package/lib/specs/agents/agents.d.ts +10 -0
  198. package/lib/specs/agents/agents.js +752 -24
  199. package/lib/specs/envvars.d.ts +1 -0
  200. package/lib/specs/envvars.js +11 -0
  201. package/lib/specs/events.d.ts +1 -0
  202. package/lib/specs/events.js +1 -0
  203. package/lib/specs/index.d.ts +1 -0
  204. package/lib/specs/index.js +1 -0
  205. package/lib/specs/personas.d.ts +41 -0
  206. package/lib/specs/personas.js +168 -0
  207. package/lib/specs/skills.d.ts +2 -1
  208. package/lib/specs/skills.js +23 -5
  209. package/lib/specs/tools.js +3 -0
  210. package/lib/stores/agentRuntimeStore.d.ts +204 -0
  211. package/lib/stores/agentRuntimeStore.js +636 -0
  212. package/lib/stores/index.d.ts +1 -1
  213. package/lib/stores/index.js +1 -1
  214. package/lib/tools/adapters/copilotkit/lexicalHooks.d.ts +1 -2
  215. package/lib/tools/adapters/copilotkit/lexicalHooks.js +1 -3
  216. package/lib/tools/adapters/copilotkit/notebookHooks.d.ts +1 -2
  217. package/lib/tools/adapters/copilotkit/notebookHooks.js +1 -3
  218. package/lib/tools/index.d.ts +0 -2
  219. package/lib/tools/index.js +0 -1
  220. package/lib/types/agentspecs.d.ts +50 -1
  221. package/lib/types/chat.d.ts +309 -8
  222. package/lib/types/context.d.ts +27 -0
  223. package/lib/types/cost.d.ts +2 -2
  224. package/lib/types/index.d.ts +2 -0
  225. package/lib/types/index.js +2 -0
  226. package/lib/types/mcp.d.ts +8 -0
  227. package/lib/types/models.d.ts +2 -2
  228. package/lib/types/personas.d.ts +25 -0
  229. package/lib/types/personas.js +5 -0
  230. package/lib/types/skills.d.ts +43 -1
  231. package/lib/types/stream.d.ts +110 -0
  232. package/lib/types/stream.js +36 -0
  233. package/lib/types/tools.d.ts +2 -0
  234. package/lib/utils/utils.d.ts +9 -5
  235. package/lib/utils/utils.js +9 -5
  236. package/package.json +13 -9
  237. package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
  238. package/scripts/codegen/__pycache__/generate_events.cpython-313.pyc +0 -0
  239. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
  240. package/scripts/codegen/generate_agents.py +106 -7
  241. package/scripts/codegen/generate_events.py +47 -17
  242. package/scripts/codegen/generate_personas.py +319 -0
  243. package/scripts/codegen/generate_skills.py +9 -9
  244. package/scripts/codegen/generate_tools.py +20 -0
  245. package/scripts/sync-jupyter.sh +26 -7
  246. package/style/primer-primitives.css +1 -6
  247. package/lib/api/tool-approvals.d.ts +0 -62
  248. package/lib/api/tool-approvals.js +0 -145
  249. package/lib/examples/LexicalSidebarExample.js +0 -163
  250. package/lib/examples/NotebookSidebarExample.js +0 -119
  251. package/lib/examples/NotebookSimpleExample.d.ts +0 -6
  252. package/lib/examples/NotebookSimpleExample.js +0 -22
  253. package/lib/examples/ag-ui/index.d.ts +0 -10
  254. package/lib/examples/ag-ui/index.js +0 -16
  255. package/lib/hooks/useAgentsRegistry.d.ts +0 -10
  256. package/lib/hooks/useAgentsRegistry.js +0 -20
  257. package/lib/stores/agentsStore.d.ts +0 -123
  258. package/lib/stores/agentsStore.js +0 -270
  259. package/scripts/codegen/__pycache__/generate_envvars.cpython-313.pyc +0 -0
  260. package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
  261. package/scripts/codegen/__pycache__/generate_guardrails.cpython-313.pyc +0 -0
  262. package/scripts/codegen/__pycache__/generate_mcp_servers.cpython-313.pyc +0 -0
  263. package/scripts/codegen/__pycache__/generate_memory.cpython-313.pyc +0 -0
  264. package/scripts/codegen/__pycache__/generate_models.cpython-313.pyc +0 -0
  265. package/scripts/codegen/__pycache__/generate_notifications.cpython-313.pyc +0 -0
  266. package/scripts/codegen/__pycache__/generate_outputs.cpython-313.pyc +0 -0
  267. package/scripts/codegen/__pycache__/generate_skills.cpython-313.pyc +0 -0
  268. package/scripts/codegen/__pycache__/generate_teams.cpython-313.pyc +0 -0
  269. package/scripts/codegen/__pycache__/generate_tools.cpython-313.pyc +0 -0
  270. package/scripts/codegen/__pycache__/generate_triggers.cpython-313.pyc +0 -0
  271. /package/lib/examples/{AgentspecExample.d.ts → AgentSpecsExample.d.ts} +0 -0
  272. /package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.d.ts +0 -0
  273. /package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.d.ts +0 -0
  274. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.d.ts +0 -0
  275. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.js +0 -0
@@ -5,29 +5,15 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
5
5
  * ContextInspector component - Shows detailed context snapshot with full tool schemas,
6
6
  * message history with in_context flags, and model configuration.
7
7
  */
8
- import { Text, Spinner, Button, Label, ProgressBar } from '@primer/react';
8
+ import { Text, Button, Label, ProgressBar } from '@primer/react';
9
9
  import { Box } from '@datalayer/primer-addons';
10
10
  import { AiModelIcon, TerminalIcon, CommentDiscussionIcon, DatabaseIcon, KeyIcon, CodeIcon, CheckCircleIcon, XCircleIcon, ChevronDownIcon, ChevronRightIcon, InfoIcon, } from '@primer/octicons-react';
11
- import { useQuery } from '@tanstack/react-query';
12
- import React, { useState, useMemo, useEffect } from 'react';
13
- const RETRY_INTERVAL_SECONDS = 5;
11
+ import React, { useState, useMemo } from 'react';
14
12
  /**
15
13
  * Get the API base URL for fetching context data.
16
14
  * If apiBase prop is provided, use it.
17
15
  * Otherwise, fall back to localhost for local development.
18
16
  */
19
- function getApiBase(apiBase) {
20
- if (apiBase) {
21
- return apiBase;
22
- }
23
- if (typeof window === 'undefined') {
24
- return '';
25
- }
26
- const host = window.location.hostname;
27
- return host === 'localhost' || host === '127.0.0.1'
28
- ? 'http://127.0.0.1:8765'
29
- : '';
30
- }
31
17
  /**
32
18
  * Format token count for display
33
19
  */
@@ -143,34 +129,10 @@ function MessageDetailView({ message }) {
143
129
  /**
144
130
  * ContextInspector component displays full detailed context snapshot.
145
131
  */
146
- export function ContextInspector({ agentId, apiBase }) {
147
- const [retryCountdown, setRetryCountdown] = useState(RETRY_INTERVAL_SECONDS);
148
- const { data: contextData, isLoading, error, refetch, } = useQuery({
149
- queryKey: ['full-context', agentId, apiBase],
150
- queryFn: async () => {
151
- const base = getApiBase(apiBase);
152
- const response = await fetch(`${base}/api/v1/configure/agents/${encodeURIComponent(agentId)}/full-context`);
153
- if (!response.ok) {
154
- throw new Error('Failed to fetch full context');
155
- }
156
- return response.json();
157
- },
158
- refetchInterval: RETRY_INTERVAL_SECONDS * 1000,
159
- refetchOnMount: 'always',
160
- staleTime: 0,
161
- });
162
- const hasRetryError = Boolean(error) || Boolean(contextData?.error);
163
- useEffect(() => {
164
- if (!hasRetryError) {
165
- setRetryCountdown(RETRY_INTERVAL_SECONDS);
166
- return;
167
- }
168
- setRetryCountdown(RETRY_INTERVAL_SECONDS);
169
- const timer = window.setInterval(() => {
170
- setRetryCountdown(prev => prev <= 1 ? RETRY_INTERVAL_SECONDS : prev - 1);
171
- }, 1000);
172
- return () => window.clearInterval(timer);
173
- }, [hasRetryError]);
132
+ export function ContextInspector({ agentId, apiBase, liveData, }) {
133
+ const hasLiveData = liveData !== undefined;
134
+ // REST polling removed data comes exclusively via WS `agent.snapshot`.
135
+ const contextData = liveData;
174
136
  // Separate messages by in_context status
175
137
  const { inContextMessages, outOfContextMessages } = useMemo(() => {
176
138
  if (!contextData?.messages) {
@@ -181,30 +143,28 @@ export function ContextInspector({ agentId, apiBase }) {
181
143
  outOfContextMessages: contextData.messages.filter(m => !m.inContext),
182
144
  };
183
145
  }, [contextData?.messages]);
184
- if (isLoading) {
185
- return (_jsxs(Box, { sx: {
146
+ const messageHistoryTokens = useMemo(() => {
147
+ if (!contextData?.messages) {
148
+ return 0;
149
+ }
150
+ return contextData.messages.reduce((sum, message) => sum + (message.estimatedTokens || 0), 0);
151
+ }, [contextData?.messages]);
152
+ if (!hasLiveData) {
153
+ return (_jsx(Box, { sx: {
186
154
  p: 3,
187
155
  display: 'flex',
188
156
  alignItems: 'center',
189
157
  justifyContent: 'center',
190
- }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { ml: 2, color: 'fg.muted' }, children: "Loading full context snapshot..." })] }));
158
+ }, children: _jsx(Text, { sx: { color: 'fg.muted' }, children: "Waiting for context data from WebSocket stream..." }) }));
191
159
  }
192
- if (error || !contextData) {
160
+ if (!contextData) {
193
161
  return (_jsx(Box, { sx: {
194
162
  p: 3,
195
163
  bg: 'attention.subtle',
196
164
  borderRadius: 2,
197
165
  border: '1px solid',
198
166
  borderColor: 'attention.muted',
199
- }, children: _jsxs(Box, { sx: {
200
- display: 'flex',
201
- alignItems: 'center',
202
- justifyContent: 'space-between',
203
- gap: 2,
204
- }, children: [_jsxs(Text, { sx: { color: 'attention.fg' }, children: ["Service not available for context snapshot. Retrying in", ' ', retryCountdown, " second", retryCountdown === 1 ? '' : 's', "..."] }), _jsx(Button, { size: "small", variant: "invisible", onClick: () => {
205
- setRetryCountdown(RETRY_INTERVAL_SECONDS);
206
- void refetch();
207
- }, children: "Retry now" })] }) }));
167
+ }, children: _jsx(Text, { sx: { color: 'attention.fg' }, children: "No context data available." }) }));
208
168
  }
209
169
  if (contextData.error) {
210
170
  return (_jsx(Box, { sx: {
@@ -213,15 +173,7 @@ export function ContextInspector({ agentId, apiBase }) {
213
173
  borderRadius: 2,
214
174
  border: '1px solid',
215
175
  borderColor: 'attention.muted',
216
- }, children: _jsxs(Box, { sx: {
217
- display: 'flex',
218
- alignItems: 'center',
219
- justifyContent: 'space-between',
220
- gap: 2,
221
- }, children: [_jsx(Text, { sx: { color: 'attention.fg' }, children: `${contextData.error} Retrying in ${retryCountdown} second${retryCountdown === 1 ? '' : 's'}...` }), _jsx(Button, { size: "small", variant: "invisible", onClick: () => {
222
- setRetryCountdown(RETRY_INTERVAL_SECONDS);
223
- void refetch();
224
- }, children: "Retry now" })] }) }));
176
+ }, children: _jsx(Text, { sx: { color: 'attention.fg' }, children: contextData.error }) }));
225
177
  }
226
178
  const { tokenSummary, modelConfiguration } = contextData;
227
179
  return (_jsxs(Box, { children: [_jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: ["Total usage: ", formatTokens(tokenSummary.total), " /", ' ', formatTokens(tokenSummary.contextWindow), " (", Math.round(tokenSummary.usagePercent), "%)"] }), _jsx(ProgressBar, { progress: Math.min(tokenSummary.usagePercent, 100), sx: { mt: 2, height: 8 }, bg: tokenSummary.usagePercent > 90
@@ -258,7 +210,7 @@ export function ContextInspector({ agentId, apiBase }) {
258
210
  wordBreak: 'break-word',
259
211
  maxHeight: 200,
260
212
  overflow: 'auto',
261
- }, children: prompt.content })] }, idx)))) }), _jsx(CollapsibleSection, { title: "Tools", icon: TerminalIcon, count: contextData.tools.length, tokens: contextData.toolTokens, children: contextData.tools.length === 0 ? (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "No tools configured" })) : (contextData.tools.map((tool, idx) => (_jsx(ToolDetailView, { tool: tool }, idx)))) }), _jsx(CollapsibleSection, { title: "Message History", icon: CommentDiscussionIcon, count: contextData.messages.length, children: contextData.messages.length === 0 ? (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "No messages yet" })) : (_jsxs(_Fragment, { children: [outOfContextMessages.length > 0 && (_jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Text, { sx: {
213
+ }, children: prompt.content })] }, idx)))) }), _jsx(CollapsibleSection, { title: "Tool Definitions", icon: TerminalIcon, count: contextData.tools.length, tokens: contextData.toolTokens, children: contextData.tools.length === 0 ? (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "No tools configured" })) : (contextData.tools.map((tool, idx) => (_jsx(ToolDetailView, { tool: tool }, idx)))) }), _jsx(CollapsibleSection, { title: "Message History", icon: CommentDiscussionIcon, count: contextData.messages.length, tokens: messageHistoryTokens, children: contextData.messages.length === 0 ? (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "No messages yet" })) : (_jsxs(_Fragment, { children: [outOfContextMessages.length > 0 && (_jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Text, { sx: {
262
214
  fontWeight: 'semibold',
263
215
  fontSize: 1,
264
216
  color: 'fg.muted',
@@ -98,6 +98,8 @@ export interface ContextPanelProps {
98
98
  agentId: string;
99
99
  /** API base URL for fetching context data */
100
100
  apiBase?: string;
101
+ /** Live context snapshot pushed by websocket; skips internal polling when provided */
102
+ liveData?: ContextSnapshotResponse | null;
101
103
  /** Number of messages in conversation (from chat store) */
102
104
  messageCount?: number;
103
105
  /** Default view mode */
@@ -108,5 +110,5 @@ export interface ContextPanelProps {
108
110
  /**
109
111
  * ContextPanel component - unified context usage display.
110
112
  */
111
- export declare function ContextPanel({ agentId, apiBase, messageCount, defaultView, chartHeight, }: ContextPanelProps): import("react/jsx-runtime").JSX.Element;
113
+ export declare function ContextPanel({ agentId, apiBase, liveData, messageCount, defaultView, chartHeight, }: ContextPanelProps): import("react/jsx-runtime").JSX.Element;
112
114
  export default ContextPanel;
@@ -7,12 +7,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  * - Token distribution treemap
8
8
  * - Historic usage time-series chart
9
9
  */
10
- import { CommentDiscussionIcon, DatabaseIcon, FileIcon, ToolsIcon, ClockIcon, GraphIcon, ListUnorderedIcon, } from '@primer/octicons-react';
10
+ import { CommentDiscussionIcon, DatabaseIcon, FileIcon, ToolsIcon, ClockIcon, GraphIcon, AppsIcon, ListUnorderedIcon, DownloadIcon, } from '@primer/octicons-react';
11
11
  import { Heading, Text, ProgressBar, Spinner, Button, SegmentedControl, } from '@primer/react';
12
12
  import { Box } from '@datalayer/primer-addons';
13
- import { useQuery } from '@tanstack/react-query';
14
13
  import ReactECharts from 'echarts-for-react';
15
- import { useState, useMemo, useEffect } from 'react';
14
+ import { useState, useMemo } from 'react';
16
15
  /**
17
16
  * Format token count for display
18
17
  */
@@ -29,8 +28,11 @@ function formatTokens(tokens) {
29
28
  * Format duration
30
29
  */
31
30
  function formatDuration(seconds) {
31
+ if (!Number.isFinite(seconds) || Number.isNaN(seconds) || seconds < 0) {
32
+ return '0ms';
33
+ }
32
34
  if (seconds < 1) {
33
- return `${Math.round(seconds * 1000)}ms`;
35
+ return `${Math.max(0, Math.round(seconds * 1000))}ms`;
34
36
  }
35
37
  if (seconds < 60) {
36
38
  return `${seconds.toFixed(1)}s`;
@@ -39,6 +41,89 @@ function formatDuration(seconds) {
39
41
  const secs = Math.round(seconds % 60);
40
42
  return `${minutes}m ${secs}s`;
41
43
  }
44
+ function csvCell(value) {
45
+ const text = String(value ?? '');
46
+ if (/[",\n]/.test(text)) {
47
+ return `"${text.replace(/"/g, '""')}"`;
48
+ }
49
+ return text;
50
+ }
51
+ function downloadContextUsageAsCSV(data) {
52
+ const rows = [];
53
+ rows.push(['Context Usage Snapshot for Agent', data.agentId]);
54
+ rows.push(['Generated At', new Date().toISOString()]);
55
+ rows.push([]);
56
+ rows.push(['Summary']);
57
+ rows.push(['Total Tokens', data.totalTokens]);
58
+ rows.push(['Context Window', data.contextWindow]);
59
+ rows.push(['System Prompt Tokens', data.systemPromptTokens]);
60
+ rows.push(['Tool Tokens', data.toolTokens]);
61
+ rows.push(['History Tokens', data.historyTokens]);
62
+ rows.push(['Current Message Tokens', data.currentMessageTokens]);
63
+ rows.push(['User Message Tokens', data.userMessageTokens]);
64
+ rows.push(['Assistant Message Tokens', data.assistantMessageTokens]);
65
+ rows.push([]);
66
+ if (data.sessionUsage) {
67
+ rows.push(['Session Usage']);
68
+ rows.push(['Input Tokens', data.sessionUsage.inputTokens]);
69
+ rows.push(['Output Tokens', data.sessionUsage.outputTokens]);
70
+ rows.push(['Requests', data.sessionUsage.requests]);
71
+ rows.push(['Tool Calls', data.sessionUsage.toolCalls]);
72
+ rows.push(['Turns', data.sessionUsage.turns]);
73
+ rows.push(['Duration Seconds', data.sessionUsage.durationSeconds]);
74
+ rows.push([]);
75
+ }
76
+ if (data.turnUsage) {
77
+ rows.push(['Last Turn Usage']);
78
+ rows.push(['Input Tokens', data.turnUsage.inputTokens]);
79
+ rows.push(['Output Tokens', data.turnUsage.outputTokens]);
80
+ rows.push(['Requests', data.turnUsage.requests]);
81
+ rows.push(['Tool Calls', data.turnUsage.toolCalls]);
82
+ rows.push(['Duration Seconds', data.turnUsage.durationSeconds]);
83
+ rows.push([]);
84
+ }
85
+ rows.push(['Distribution']);
86
+ rows.push(['Category', 'Tokens']);
87
+ for (const category of data.distribution?.children ?? []) {
88
+ rows.push([category.name, category.value]);
89
+ }
90
+ rows.push([]);
91
+ if (data.perRequestUsage.length > 0) {
92
+ rows.push(['Per Request Usage']);
93
+ rows.push([
94
+ 'Request #',
95
+ 'Input Tokens',
96
+ 'Output Tokens',
97
+ 'Duration Ms',
98
+ 'Tool Names',
99
+ 'Timestamp',
100
+ 'Turn ID',
101
+ ]);
102
+ for (const request of data.perRequestUsage) {
103
+ rows.push([
104
+ request.requestNum,
105
+ request.inputTokens,
106
+ request.outputTokens,
107
+ request.durationMs,
108
+ request.toolNames.join('; '),
109
+ request.timestamp ?? '',
110
+ request.turnId ?? '',
111
+ ]);
112
+ }
113
+ }
114
+ const csv = rows.map(row => row.map(csvCell).join(',')).join('\n');
115
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
116
+ const url = URL.createObjectURL(blob);
117
+ const link = document.createElement('a');
118
+ link.href = url;
119
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
120
+ link.download = `context-usage-${data.agentId}-${ts}.csv`;
121
+ link.style.display = 'none';
122
+ document.body.appendChild(link);
123
+ link.click();
124
+ document.body.removeChild(link);
125
+ URL.revokeObjectURL(url);
126
+ }
42
127
  /**
43
128
  * Get icon for context category
44
129
  */
@@ -59,56 +144,17 @@ function getCategoryIcon(name) {
59
144
  return ClockIcon;
60
145
  }
61
146
  }
62
- const RETRY_INTERVAL_SECONDS = 5;
63
- /**
64
- * Get the API base URL for fetching context data.
65
- * If apiBase prop is provided, use it.
66
- * Otherwise, fall back to localhost for local development.
67
- */
68
- function getApiBase(apiBase) {
69
- if (apiBase) {
70
- return apiBase;
71
- }
72
- if (typeof window === 'undefined') {
73
- return '';
74
- }
75
- const host = window.location.hostname;
76
- return host === 'localhost' || host === '127.0.0.1'
77
- ? 'http://127.0.0.1:8765'
78
- : '';
79
- }
80
147
  /**
81
148
  * ContextPanel component - unified context usage display.
82
149
  */
83
- export function ContextPanel({ agentId, apiBase, messageCount = 0, defaultView = 'overview', chartHeight = '200px', }) {
150
+ export function ContextPanel({ agentId, apiBase, liveData, messageCount = 0, defaultView = 'overview', chartHeight = '200px', }) {
84
151
  const [viewMode, setViewMode] = useState(defaultView);
85
152
  const [showDetails, setShowDetails] = useState(false);
86
- const [retryCountdown, setRetryCountdown] = useState(RETRY_INTERVAL_SECONDS);
87
- const { data: snapshotData, isLoading, error, refetch, } = useQuery({
88
- queryKey: ['context-snapshot', agentId, apiBase],
89
- queryFn: async () => {
90
- const base = getApiBase(apiBase);
91
- const response = await fetch(`${base}/api/v1/configure/agents/${encodeURIComponent(agentId)}/context-snapshot`);
92
- if (!response.ok) {
93
- throw new Error('Failed to fetch context snapshot');
94
- }
95
- return response.json();
96
- },
97
- refetchInterval: RETRY_INTERVAL_SECONDS * 1000,
98
- refetchOnMount: 'always',
99
- staleTime: 0,
100
- });
101
- useEffect(() => {
102
- if (!error) {
103
- setRetryCountdown(RETRY_INTERVAL_SECONDS);
104
- return;
105
- }
106
- setRetryCountdown(RETRY_INTERVAL_SECONDS);
107
- const timer = window.setInterval(() => {
108
- setRetryCountdown(prev => prev <= 1 ? RETRY_INTERVAL_SECONDS : prev - 1);
109
- }, 1000);
110
- return () => window.clearInterval(timer);
111
- }, [error]);
153
+ const hasLiveData = liveData !== undefined;
154
+ // REST polling removed data comes exclusively via WS `agent.snapshot`.
155
+ const snapshotData = liveData;
156
+ const showLoading = !hasLiveData;
157
+ const hasError = false;
112
158
  // Build historic usage chart data from perRequestUsage
113
159
  const historyChartOption = useMemo(() => {
114
160
  if (!snapshotData?.perRequestUsage ||
@@ -251,7 +297,7 @@ export function ContextPanel({ agentId, apiBase, messageCount = 0, defaultView =
251
297
  };
252
298
  // eslint-disable-next-line react-hooks/exhaustive-deps
253
299
  }, [snapshotData?.distribution]);
254
- if (isLoading) {
300
+ if (showLoading) {
255
301
  return (_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: {
256
302
  fontSize: 1,
257
303
  fontWeight: 'semibold',
@@ -268,7 +314,7 @@ export function ContextPanel({ agentId, apiBase, messageCount = 0, defaultView =
268
314
  gap: 2,
269
315
  }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Loading context details..." })] })] }));
270
316
  }
271
- if (error || !snapshotData) {
317
+ if (hasError || !snapshotData) {
272
318
  return (_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: {
273
319
  fontSize: 1,
274
320
  fontWeight: 'semibold',
@@ -280,19 +326,13 @@ export function ContextPanel({ agentId, apiBase, messageCount = 0, defaultView =
280
326
  borderRadius: 2,
281
327
  border: '1px solid',
282
328
  borderColor: 'attention.muted',
283
- }, children: _jsxs(Box, { sx: {
284
- display: 'flex',
285
- alignItems: 'center',
286
- justifyContent: 'space-between',
287
- gap: 2,
288
- }, children: [_jsxs(Text, { sx: { fontSize: 1, color: 'attention.fg' }, children: ["Service not available for context usage. Retrying in", ' ', retryCountdown, " second", retryCountdown === 1 ? '' : 's', "..."] }), _jsx(Button, { size: "small", variant: "invisible", onClick: () => {
289
- setRetryCountdown(RETRY_INTERVAL_SECONDS);
290
- void refetch();
291
- }, children: "Retry now" })] }) })] }));
329
+ }, children: _jsx(Text, { sx: { fontSize: 1, color: 'attention.fg' }, children: "Waiting for context data from WebSocket stream..." }) })] }));
292
330
  }
293
331
  const { totalTokens, contextWindow, sessionUsage, turnUsage, distribution } = snapshotData;
332
+ const sentMessageCount = messageCount > 0
333
+ ? messageCount
334
+ : Math.max(sessionUsage?.turns ?? 0, snapshotData.perRequestUsage?.length > 0 ? 1 : 0);
294
335
  const hasDistributionData = distribution?.children && distribution.children.length > 0;
295
- const hasHistoryData = snapshotData.perRequestUsage && snapshotData.perRequestUsage.length > 0;
296
336
  return (_jsxs(Box, { children: [_jsxs(Box, { sx: {
297
337
  display: 'flex',
298
338
  justifyContent: 'space-between',
@@ -302,7 +342,7 @@ export function ContextPanel({ agentId, apiBase, messageCount = 0, defaultView =
302
342
  fontSize: 1,
303
343
  fontWeight: 'semibold',
304
344
  color: 'fg.muted',
305
- }, children: "Context Usage" }), messageCount > 0 && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [messageCount, " ", messageCount === 1 ? 'message' : 'messages'] }))] }), _jsxs(Box, { sx: {
345
+ }, children: "Context Usage" }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [sentMessageCount > 0 && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [sentMessageCount, ' ', sentMessageCount === 1 ? 'message sent' : 'messages sent'] })), _jsx(Button, { size: "small", variant: "invisible", leadingVisual: DownloadIcon, onClick: () => downloadContextUsageAsCSV(snapshotData), children: "Download" })] })] }), _jsxs(Box, { sx: {
306
346
  p: 3,
307
347
  bg: 'canvas.subtle',
308
348
  borderRadius: 2,
@@ -337,7 +377,7 @@ export function ContextPanel({ agentId, apiBase, messageCount = 0, defaultView =
337
377
  }, children: [_jsxs(SegmentedControl, { "aria-label": "View mode", size: "small", onChange: index => {
338
378
  const modes = ['overview', 'distribution', 'history'];
339
379
  setViewMode(modes[index]);
340
- }, children: [_jsx(SegmentedControl.Button, { selected: viewMode === 'overview', leadingIcon: ListUnorderedIcon, children: "Overview" }), _jsx(SegmentedControl.Button, { selected: viewMode === 'distribution', leadingIcon: GraphIcon, disabled: !hasDistributionData, children: "Distribution" }), _jsx(SegmentedControl.Button, { selected: viewMode === 'history', leadingIcon: ClockIcon, disabled: !hasHistoryData, children: "History" })] }), viewMode === 'overview' && (_jsx(Button, { size: "small", variant: "invisible", onClick: () => setShowDetails(!showDetails), children: showDetails ? 'Less' : 'More' }))] }), viewMode === 'overview' && (_jsxs(Box, { children: [_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [hasDistributionData &&
380
+ }, children: [_jsx(SegmentedControl.IconButton, { "aria-label": "Overview", selected: viewMode === 'overview', icon: ListUnorderedIcon }), _jsx(SegmentedControl.IconButton, { "aria-label": "Distribution", selected: viewMode === 'distribution', icon: AppsIcon, disabled: !hasDistributionData }), _jsx(SegmentedControl.IconButton, { "aria-label": "History", selected: viewMode === 'history', icon: GraphIcon })] }), viewMode === 'overview' && (_jsx(Button, { size: "small", variant: "invisible", onClick: () => setShowDetails(!showDetails), children: showDetails ? 'Less' : 'More' }))] }), viewMode === 'overview' && (_jsxs(Box, { children: [_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [hasDistributionData &&
341
381
  distribution.children.map(category => {
342
382
  const CategoryIcon = getCategoryIcon(category.name);
343
383
  const categoryPercent = (category.value / contextWindow) * 100;
@@ -384,6 +424,6 @@ export function ContextPanel({ agentId, apiBase, messageCount = 0, defaultView =
384
424
  py: 1,
385
425
  borderBottom: idx < 9 ? '1px solid' : 'none',
386
426
  borderColor: 'border.muted',
387
- }, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', minWidth: 30 }, children: ["#", req.requestNum] }), _jsxs(Text, { sx: { color: '#3B82F6' }, children: ["\u2193", formatTokens(req.inputTokens)] }), _jsxs(Text, { sx: { color: '#10B981' }, children: ["\u2191", formatTokens(req.outputTokens)] }), req.toolNames.length > 0 && (_jsxs(Text, { sx: { color: 'fg.muted' }, children: ["\uD83D\uDD27 ", req.toolNames.join(', ')] }))] }, req.requestNum))) }))] }))] })] }));
427
+ }, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', minWidth: 30 }, children: ["#", req.requestNum] }), _jsxs(Text, { sx: { color: '#3B82F6' }, children: ["\u2193", formatTokens(req.inputTokens)] }), _jsxs(Text, { sx: { color: '#10B981' }, children: ["\u2191", formatTokens(req.outputTokens)] }), req.toolNames.length > 0 && (_jsxs(Text, { sx: { color: 'fg.muted' }, children: ["\uD83D\uDD27 ", req.toolNames.join(', ')] }))] }, req.requestNum))) }))] })), viewMode === 'history' && !historyChartOption && (_jsx(Box, { sx: { py: 3 }, children: _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "No history" }) }))] })] }));
388
428
  }
389
429
  export default ContextPanel;
@@ -25,9 +25,11 @@ export interface ContextDetailsResponse {
25
25
  export interface ContextUsageProps {
26
26
  /** Agent ID for fetching context details (required) */
27
27
  agentId: string;
28
+ /** Optional base URL override (defaults to local 8765 or same-origin) */
29
+ baseUrl?: string;
28
30
  }
29
31
  /**
30
32
  * ContextUsage component displays token usage breakdown by category.
31
33
  */
32
- export declare function ContextUsage({ agentId }: ContextUsageProps): import("react/jsx-runtime").JSX.Element;
34
+ export declare function ContextUsage({ agentId, baseUrl }: ContextUsageProps): import("react/jsx-runtime").JSX.Element;
33
35
  export default ContextUsage;
@@ -52,11 +52,11 @@ function getCategoryIcon(name) {
52
52
  /**
53
53
  * ContextUsage component displays token usage breakdown by category.
54
54
  */
55
- export function ContextUsage({ agentId }) {
55
+ export function ContextUsage({ agentId, baseUrl }) {
56
56
  const { data: contextData, isLoading, error, } = useQuery({
57
- queryKey: ['context-details', agentId],
57
+ queryKey: ['context-details', agentId, baseUrl],
58
58
  queryFn: async () => {
59
- const apiBase = getLocalApiBase();
59
+ const apiBase = baseUrl || getLocalApiBase();
60
60
  const response = await fetch(`${apiBase}/api/v1/configure/agents/${encodeURIComponent(agentId)}/context-details`);
61
61
  if (!response.ok) {
62
62
  throw new Error('Failed to fetch context details');
@@ -3,8 +3,8 @@
3
3
  */
4
4
  export interface CostUsageResponse {
5
5
  agentId: string;
6
- /** Cost for the current run in USD */
7
- currentRunCostUsd: number;
6
+ /** Cost for the last completed turn in USD */
7
+ lastTurnCostUsd: number;
8
8
  /** Total cumulative cost in USD */
9
9
  cumulativeCostUsd: number;
10
10
  /** Per-run budget limit (from guardrails) */
@@ -23,15 +23,21 @@ export interface CostUsageResponse {
23
23
  costUsd: number;
24
24
  requests: number;
25
25
  }>;
26
+ /** Optional run traces with pricing resolution info */
27
+ runs?: Array<{
28
+ pricingResolved: boolean;
29
+ }>;
26
30
  }
27
31
  export interface CostTrackerProps {
28
32
  /** Agent ID for fetching cost data */
29
33
  agentId: string;
30
34
  /** Compact mode — show only the summary bar */
31
35
  compact?: boolean;
36
+ /** Live cost data pushed by websocket (single source of truth). */
37
+ liveData?: CostUsageResponse | null;
32
38
  }
33
39
  /**
34
40
  * Displays running cost and budget utilization for an agent.
35
41
  */
36
- export declare function CostTracker({ agentId, compact }: CostTrackerProps): import("react/jsx-runtime").JSX.Element;
42
+ export declare function CostTracker({ agentId: _agentId, compact, liveData, }: CostTrackerProps): import("react/jsx-runtime").JSX.Element;
37
43
  export default CostTracker;
@@ -11,17 +11,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  *
12
12
  * @module components/context/CostTracker
13
13
  */
14
- import { Box, Heading, Text, ProgressBar, Spinner, Flash, Label, } from '@primer/react';
14
+ import { Box, Heading, Text, ProgressBar, Flash, Label } from '@primer/react';
15
15
  import { CreditCardIcon, AlertIcon } from '@primer/octicons-react';
16
- import { useQuery } from '@tanstack/react-query';
17
- function getLocalApiBase() {
18
- if (typeof window === 'undefined')
19
- return '';
20
- const host = window.location.hostname;
21
- return host === 'localhost' || host === '127.0.0.1'
22
- ? 'http://127.0.0.1:8765'
23
- : '';
24
- }
25
16
  function formatUsd(amount) {
26
17
  if (amount < 0.01)
27
18
  return `$${amount.toFixed(4)}`;
@@ -39,38 +30,23 @@ function formatTokens(tokens) {
39
30
  /**
40
31
  * Displays running cost and budget utilization for an agent.
41
32
  */
42
- export function CostTracker({ agentId, compact = false }) {
43
- const { data: costData, isLoading, error, } = useQuery({
44
- queryKey: ['cost-usage', agentId],
45
- queryFn: async () => {
46
- const apiBase = getLocalApiBase();
47
- const response = await fetch(`${apiBase}/api/v1/configure/agents/${encodeURIComponent(agentId)}/cost-usage`);
48
- if (!response.ok)
49
- throw new Error('Failed to fetch cost data');
50
- return response.json();
51
- },
52
- refetchInterval: 5000,
53
- staleTime: 0,
54
- });
55
- if (isLoading) {
56
- return (_jsxs(Box, { sx: {
57
- p: 2,
58
- display: 'flex',
59
- alignItems: 'center',
60
- gap: 2,
61
- }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Loading cost\u2026" })] }));
62
- }
63
- if (error || !costData) {
64
- return (_jsx(Box, { sx: { p: 2 }, children: _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Cost data unavailable" }) }));
33
+ export function CostTracker({ agentId: _agentId, compact = false, liveData, }) {
34
+ const costData = liveData;
35
+ if (!costData) {
36
+ return (_jsx(Box, { sx: { p: 2 }, children: _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Waiting for websocket snapshot..." }) }));
65
37
  }
66
38
  const cumulativePercent = costData.cumulativeBudgetUsd != null && costData.cumulativeBudgetUsd > 0
67
39
  ? (costData.cumulativeCostUsd / costData.cumulativeBudgetUsd) * 100
68
40
  : null;
69
- const runPercent = costData.perRunBudgetUsd != null && costData.perRunBudgetUsd > 0
70
- ? (costData.currentRunCostUsd / costData.perRunBudgetUsd) * 100
41
+ const lastTurnCostUsd = costData.lastTurnCostUsd;
42
+ const lastTurnPercent = costData.perRunBudgetUsd != null && costData.perRunBudgetUsd > 0
43
+ ? (lastTurnCostUsd / costData.perRunBudgetUsd) * 100
71
44
  : null;
72
45
  const isOverBudget = cumulativePercent != null && cumulativePercent > 100;
73
46
  const isNearBudget = cumulativePercent != null && cumulativePercent > 80;
47
+ const hasUnresolvedPricing = Array.isArray(costData.runs) &&
48
+ costData.runs.some(run => run.pricingResolved === false);
49
+ const displayUsd = (amount) => hasUnresolvedPricing && amount === 0 ? '…' : formatUsd(amount);
74
50
  // Compact: single row summary
75
51
  if (compact) {
76
52
  return (_jsxs(Box, { sx: {
@@ -79,11 +55,12 @@ export function CostTracker({ agentId, compact = false }) {
79
55
  gap: 2,
80
56
  px: 2,
81
57
  py: 1,
82
- }, children: [_jsx(CreditCardIcon, { size: 14 }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Run: ", formatUsd(costData.currentRunCostUsd), " | Total:", ' ', formatUsd(costData.cumulativeCostUsd), costData.cumulativeBudgetUsd != null &&
83
- ` / ${formatUsd(costData.cumulativeBudgetUsd)}`] }), isOverBudget && (_jsx(Label, { variant: "danger", size: "small", children: "Over budget" })), !isOverBudget && isNearBudget && (_jsx(Label, { variant: "attention", size: "small", children: "Near limit" }))] }));
58
+ }, children: [_jsx(CreditCardIcon, { size: 14 }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Cumulative: ", displayUsd(costData.cumulativeCostUsd), costData.cumulativeBudgetUsd != null &&
59
+ ` (budget ${formatUsd(costData.cumulativeBudgetUsd)})`, ' | ', "Last turn: ", displayUsd(lastTurnCostUsd), costData.perRunBudgetUsd != null &&
60
+ ` (budget ${formatUsd(costData.perRunBudgetUsd)})`] }), isOverBudget && (_jsx(Label, { variant: "danger", size: "small", children: "Over budget" })), !isOverBudget && isNearBudget && (_jsx(Label, { variant: "attention", size: "small", children: "Near limit" }))] }));
84
61
  }
85
62
  // Full display
86
- return (_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, fontWeight: 'semibold', mb: 2, color: 'fg.muted' }, children: "Cost Tracker" }), isOverBudget && (_jsxs(Flash, { variant: "danger", sx: { mb: 2 }, children: [_jsx(AlertIcon, { size: 16 }), "Cumulative cost (", formatUsd(costData.cumulativeCostUsd), ") exceeds budget (", formatUsd(costData.cumulativeBudgetUsd), ")."] })), _jsxs(Box, { sx: {
63
+ return (_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, fontWeight: 'semibold', mb: 2, color: 'fg.muted' }, children: "Cost Tracker" }), isOverBudget && (_jsxs(Flash, { variant: "danger", sx: { mb: 2 }, children: [_jsx(AlertIcon, { size: 16 }), "Cumulative cost (", formatUsd(costData.cumulativeCostUsd), ") exceeds budget (", formatUsd(costData.cumulativeBudgetUsd), ")."] })), hasUnresolvedPricing && costData.requestCount > 0 && (_jsxs(Flash, { variant: "warning", sx: { mb: 2 }, children: [_jsx(AlertIcon, { size: 16 }), "Some model pricing could not be resolved yet, so cost may remain at \u2026 even when requests/tokens are increasing."] })), _jsxs(Box, { sx: {
87
64
  p: 3,
88
65
  bg: 'canvas.subtle',
89
66
  borderRadius: 2,
@@ -93,17 +70,17 @@ export function CostTracker({ agentId, compact = false }) {
93
70
  display: 'flex',
94
71
  justifyContent: 'space-between',
95
72
  mb: 1,
96
- }, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: "Current run" }), _jsxs(Text, { sx: { fontSize: 1 }, children: [formatUsd(costData.currentRunCostUsd), costData.perRunBudgetUsd != null &&
97
- ` / ${formatUsd(costData.perRunBudgetUsd)}`] })] }), runPercent != null && (_jsx(ProgressBar, { progress: Math.min(runPercent, 100), sx: { height: 6 }, bg: runPercent > 80 ? 'danger.emphasis' : 'accent.emphasis' }))] }), _jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Box, { sx: {
98
- display: 'flex',
99
- justifyContent: 'space-between',
100
- mb: 1,
101
- }, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: "Cumulative" }), _jsxs(Text, { sx: { fontSize: 1 }, children: [formatUsd(costData.cumulativeCostUsd), costData.cumulativeBudgetUsd != null &&
102
- ` / ${formatUsd(costData.cumulativeBudgetUsd)}`] })] }), cumulativePercent != null && (_jsx(ProgressBar, { progress: Math.min(cumulativePercent, 100), sx: { height: 6 }, bg: cumulativePercent > 100
73
+ }, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: "Cumulative" }), _jsxs(Text, { sx: { fontSize: 1 }, children: [displayUsd(costData.cumulativeCostUsd), costData.cumulativeBudgetUsd != null &&
74
+ ` (budget ${formatUsd(costData.cumulativeBudgetUsd)})`] })] }), cumulativePercent != null && (_jsx(ProgressBar, { progress: Math.min(cumulativePercent, 100), sx: { height: 6 }, bg: cumulativePercent > 100
103
75
  ? 'danger.emphasis'
104
76
  : cumulativePercent > 80
105
77
  ? 'attention.emphasis'
106
- : 'accent.emphasis' }))] }), _jsxs(Box, { sx: {
78
+ : 'accent.emphasis' }))] }), _jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Box, { sx: {
79
+ display: 'flex',
80
+ justifyContent: 'space-between',
81
+ mb: 1,
82
+ }, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: "Last turn" }), _jsxs(Text, { sx: { fontSize: 1 }, children: [displayUsd(lastTurnCostUsd), costData.perRunBudgetUsd != null &&
83
+ ` (budget ${formatUsd(costData.perRunBudgetUsd)})`] })] }), lastTurnPercent != null && (_jsx(ProgressBar, { progress: Math.min(lastTurnPercent, 100), sx: { height: 6 }, bg: lastTurnPercent > 80 ? 'danger.emphasis' : 'accent.emphasis' }))] }), _jsxs(Box, { sx: {
107
84
  display: 'flex',
108
85
  gap: 3,
109
86
  borderTop: '1px solid',
@@ -119,6 +96,8 @@ export function CostTracker({ agentId, compact = false }) {
119
96
  justifyContent: 'space-between',
120
97
  alignItems: 'center',
121
98
  py: '2px',
122
- }, children: [_jsx(Text, { sx: { fontSize: 0 }, children: m.model }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [formatUsd(m.costUsd), " (", m.requests, " req)"] })] }, m.model)))] }))] })] }));
99
+ }, children: [_jsx(Text, { sx: { fontSize: 0 }, children: m.model }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [displayUsd(m.costUsd), " (", m.requests, " req)", hasUnresolvedPricing && m.costUsd === 0
100
+ ? ' - pricing pending'
101
+ : ''] })] }, m.model)))] }))] })] }));
123
102
  }
124
103
  export default CostTracker;
@@ -0,0 +1,12 @@
1
+ export interface CostUsageChartProps {
2
+ serviceName?: string;
3
+ agentId?: string;
4
+ apiKey?: string;
5
+ runUrl?: string;
6
+ wsRunUrl?: string;
7
+ liveCumulativeUsd?: number;
8
+ liveTimestampMs?: number | null;
9
+ height?: number;
10
+ }
11
+ export declare function CostUsageChart({ serviceName, agentId, apiKey, runUrl, wsRunUrl, liveCumulativeUsd, liveTimestampMs, height, }: CostUsageChartProps): import("react/jsx-runtime").JSX.Element;
12
+ export default CostUsageChart;