@datalayer/agent-runtimes 1.0.4 → 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 (267) hide show
  1. package/README.md +34 -0
  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 -106
  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 +1083 -157
  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 +108 -113
  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 +0 -18
  46. package/lib/client/AgentsMixin.js +6 -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 +5 -1
  54. package/lib/config/AgentConfiguration.js +3 -3
  55. package/lib/context/ContextDistribution.d.ts +3 -1
  56. package/lib/context/ContextDistribution.js +8 -27
  57. package/lib/context/ContextInspector.d.ts +3 -1
  58. package/lib/context/ContextInspector.js +19 -67
  59. package/lib/context/ContextPanel.d.ts +3 -1
  60. package/lib/context/ContextPanel.js +104 -64
  61. package/lib/context/ContextUsage.d.ts +3 -1
  62. package/lib/context/ContextUsage.js +3 -3
  63. package/lib/context/CostTracker.d.ts +9 -3
  64. package/lib/context/CostTracker.js +26 -47
  65. package/lib/context/CostUsageChart.d.ts +12 -0
  66. package/lib/context/CostUsageChart.js +378 -0
  67. package/lib/context/GraphFlowChart.d.ts +16 -0
  68. package/lib/context/GraphFlowChart.js +182 -0
  69. package/lib/context/TokenUsageChart.d.ts +8 -1
  70. package/lib/context/TokenUsageChart.js +349 -211
  71. package/lib/context/TurnGraphChart.d.ts +39 -0
  72. package/lib/context/TurnGraphChart.js +538 -0
  73. package/lib/context/otelWsPool.d.ts +20 -0
  74. package/lib/context/otelWsPool.js +69 -0
  75. package/lib/examples/A2UiComponentGalleryExample.d.ts +0 -17
  76. package/lib/examples/A2UiComponentGalleryExample.js +315 -522
  77. package/lib/examples/A2UiContactCardExample.d.ts +0 -18
  78. package/lib/examples/A2UiContactCardExample.js +154 -411
  79. package/lib/examples/A2UiRestaurantExample.d.ts +0 -30
  80. package/lib/examples/A2UiRestaurantExample.js +114 -212
  81. package/lib/examples/A2UiViewerExample.d.ts +0 -18
  82. package/lib/examples/A2UiViewerExample.js +283 -532
  83. package/lib/examples/AgUiBackendToolRenderingExample.js +1 -1
  84. package/lib/examples/AgUiHaikuGenUiExample.d.ts +1 -1
  85. package/lib/examples/AgUiHaikuGenUiExample.js +1 -1
  86. package/lib/examples/AgentCheckpointsExample.js +13 -27
  87. package/lib/examples/AgentCodemodeExample.d.ts +4 -6
  88. package/lib/examples/AgentCodemodeExample.js +591 -169
  89. package/lib/examples/AgentEvalsExample.js +12 -16
  90. package/lib/examples/AgentGuardrailsExample.js +370 -64
  91. package/lib/examples/AgentHooksExample.d.ts +3 -0
  92. package/lib/examples/AgentHooksExample.js +104 -0
  93. package/lib/examples/AgentMCPExample.d.ts +3 -0
  94. package/lib/examples/AgentMCPExample.js +480 -0
  95. package/lib/examples/AgentMemoryExample.js +13 -17
  96. package/lib/examples/AgentMonitoringExample.js +260 -199
  97. package/lib/examples/AgentNotificationsExample.js +49 -17
  98. package/lib/examples/AgentOtelExample.js +2 -3
  99. package/lib/examples/AgentOutputsExample.d.ts +11 -6
  100. package/lib/examples/AgentOutputsExample.js +382 -81
  101. package/lib/examples/AgentParametersExample.d.ts +3 -0
  102. package/lib/examples/AgentParametersExample.js +246 -0
  103. package/lib/examples/AgentSandboxExample.d.ts +2 -2
  104. package/lib/examples/AgentSandboxExample.js +68 -40
  105. package/lib/examples/AgentSkillsExample.js +91 -99
  106. package/lib/examples/{AgentspecExample.js → AgentSpecsExample.js} +10 -21
  107. package/lib/examples/AgentSubagentsExample.d.ts +14 -0
  108. package/lib/examples/AgentSubagentsExample.js +228 -0
  109. package/lib/examples/AgentToolApprovalsExample.js +29 -557
  110. package/lib/examples/AgentTriggersExample.js +819 -565
  111. package/lib/examples/ChatCustomExample.js +11 -24
  112. package/lib/examples/ChatExample.js +7 -24
  113. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  114. package/lib/examples/CopilotKitNotebookExample.js +2 -1
  115. package/lib/examples/HomeExample.d.ts +15 -0
  116. package/lib/examples/HomeExample.js +77 -0
  117. package/lib/examples/Lexical2Example.js +4 -2
  118. package/lib/examples/{LexicalExample.d.ts → LexicalAgentExample.d.ts} +4 -4
  119. package/lib/examples/{LexicalExample.js → LexicalAgentExample.js} +65 -16
  120. package/lib/examples/{LexicalSidebarExample.d.ts → LexicalAgentSidebarExample.d.ts} +5 -5
  121. package/lib/examples/LexicalAgentSidebarExample.js +261 -0
  122. package/lib/examples/NotebookAgentExample.d.ts +9 -0
  123. package/lib/examples/NotebookAgentExample.js +192 -0
  124. package/lib/examples/{NotebookSidebarExample.d.ts → NotebookAgentSidebarExample.d.ts} +2 -2
  125. package/lib/examples/NotebookAgentSidebarExample.js +221 -0
  126. package/lib/examples/{DatalayerNotebookExample.d.ts → NotebookCollaborationExample.d.ts} +4 -4
  127. package/lib/examples/{DatalayerNotebookExample.js → NotebookCollaborationExample.js} +3 -3
  128. package/lib/examples/NotebookExample.d.ts +4 -7
  129. package/lib/examples/NotebookExample.js +14 -146
  130. package/lib/examples/components/AuthRequiredView.d.ts +6 -0
  131. package/lib/examples/components/AuthRequiredView.js +33 -0
  132. package/lib/examples/components/ExampleWrapper.d.ts +7 -0
  133. package/lib/examples/components/ExampleWrapper.js +25 -6
  134. package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.js +1 -1
  135. package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.js +1 -1
  136. package/lib/examples/{ag-ui → components}/haiku/index.d.ts +1 -1
  137. package/lib/examples/{ag-ui → components}/haiku/index.js +1 -1
  138. package/lib/examples/components/index.d.ts +3 -0
  139. package/lib/examples/components/index.js +4 -0
  140. package/lib/examples/{ag-ui → components}/weather/index.d.ts +1 -1
  141. package/lib/examples/{ag-ui → components}/weather/index.js +1 -1
  142. package/lib/examples/example-selector.d.ts +17 -4
  143. package/lib/examples/example-selector.js +107 -41
  144. package/lib/examples/index.d.ts +9 -6
  145. package/lib/examples/index.js +9 -6
  146. package/lib/examples/main.js +217 -27
  147. package/lib/examples/utils/a2ui.d.ts +18 -0
  148. package/lib/examples/utils/a2ui.js +69 -0
  149. package/lib/examples/utils/a2uiMarkdownProvider.d.ts +7 -0
  150. package/lib/examples/utils/a2uiMarkdownProvider.js +9 -0
  151. package/lib/examples/utils/agentId.d.ts +18 -0
  152. package/lib/examples/utils/agentId.js +54 -0
  153. package/lib/examples/utils/agents/earthquake-detector.json +11 -11
  154. package/lib/examples/utils/agents/sales-forecaster.json +11 -11
  155. package/lib/examples/utils/agents/social-post-generator.json +11 -11
  156. package/lib/examples/utils/agents/stock-market.json +11 -11
  157. package/lib/examples/utils/examplesStore.js +82 -27
  158. package/lib/hooks/index.d.ts +8 -8
  159. package/lib/hooks/index.js +7 -7
  160. package/lib/hooks/useA2A.d.ts +2 -3
  161. package/lib/hooks/useAIAgentsWebSocket.d.ts +43 -4
  162. package/lib/hooks/useAIAgentsWebSocket.js +118 -12
  163. package/lib/hooks/useAcp.d.ts +1 -2
  164. package/lib/hooks/useAgUi.d.ts +1 -1
  165. package/lib/hooks/{useAgents.d.ts → useAgentRuntimes.d.ts} +39 -2
  166. package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +125 -15
  167. package/lib/hooks/useAgentsCatalog.js +1 -1
  168. package/lib/hooks/useAgentsService.d.ts +2 -2
  169. package/lib/hooks/useAgentsService.js +7 -7
  170. package/lib/hooks/useCheckpoints.js +1 -1
  171. package/lib/hooks/useConfig.d.ts +4 -1
  172. package/lib/hooks/useConfig.js +10 -3
  173. package/lib/hooks/useContextSnapshot.d.ts +9 -4
  174. package/lib/hooks/useContextSnapshot.js +9 -37
  175. package/lib/hooks/useMonitoring.js +3 -0
  176. package/lib/hooks/useSandbox.d.ts +20 -8
  177. package/lib/hooks/useSandbox.js +105 -40
  178. package/lib/hooks/useSkills.d.ts +23 -5
  179. package/lib/hooks/useSkills.js +94 -39
  180. package/lib/hooks/useToolApprovals.d.ts +60 -36
  181. package/lib/hooks/useToolApprovals.js +318 -69
  182. package/lib/hooks/useVercelAI.d.ts +1 -1
  183. package/lib/index.d.ts +2 -1
  184. package/lib/index.js +1 -0
  185. package/lib/inference/index.d.ts +0 -1
  186. package/lib/middleware/index.d.ts +0 -1
  187. package/lib/protocols/AGUIAdapter.js +6 -0
  188. package/lib/protocols/VercelAIAdapter.d.ts +7 -0
  189. package/lib/protocols/VercelAIAdapter.js +59 -7
  190. package/lib/specs/agents/agents.d.ts +10 -0
  191. package/lib/specs/agents/agents.js +2139 -262
  192. package/lib/specs/agents/index.js +3 -1
  193. package/lib/specs/envvars.d.ts +1 -0
  194. package/lib/specs/envvars.js +38 -20
  195. package/lib/specs/evals.js +6 -6
  196. package/lib/specs/events.d.ts +3 -10
  197. package/lib/specs/events.js +127 -84
  198. package/lib/specs/frontendTools.js +2 -2
  199. package/lib/specs/guardrails.d.ts +0 -7
  200. package/lib/specs/guardrails.js +240 -159
  201. package/lib/specs/index.d.ts +1 -0
  202. package/lib/specs/index.js +1 -0
  203. package/lib/specs/mcpServers.js +35 -6
  204. package/lib/specs/memory.d.ts +0 -2
  205. package/lib/specs/memory.js +4 -17
  206. package/lib/specs/models.js +25 -5
  207. package/lib/specs/notifications.js +102 -18
  208. package/lib/specs/outputs.js +15 -9
  209. package/lib/specs/personas.d.ts +41 -0
  210. package/lib/specs/personas.js +168 -0
  211. package/lib/specs/skills.d.ts +2 -1
  212. package/lib/specs/skills.js +41 -23
  213. package/lib/specs/teams/index.js +3 -1
  214. package/lib/specs/teams/teams.js +468 -348
  215. package/lib/specs/tools.js +4 -4
  216. package/lib/specs/triggers.js +61 -11
  217. package/lib/stores/agentRuntimeStore.d.ts +204 -0
  218. package/lib/stores/agentRuntimeStore.js +636 -0
  219. package/lib/stores/index.d.ts +1 -1
  220. package/lib/stores/index.js +1 -1
  221. package/lib/tools/adapters/copilotkit/lexicalHooks.d.ts +1 -2
  222. package/lib/tools/adapters/copilotkit/lexicalHooks.js +1 -3
  223. package/lib/tools/adapters/copilotkit/notebookHooks.d.ts +1 -2
  224. package/lib/tools/adapters/copilotkit/notebookHooks.js +1 -3
  225. package/lib/tools/index.d.ts +0 -2
  226. package/lib/tools/index.js +0 -1
  227. package/lib/types/agentspecs.d.ts +50 -1
  228. package/lib/types/chat.d.ts +309 -8
  229. package/lib/types/context.d.ts +27 -0
  230. package/lib/types/cost.d.ts +2 -2
  231. package/lib/types/index.d.ts +2 -0
  232. package/lib/types/index.js +2 -0
  233. package/lib/types/mcp.d.ts +8 -0
  234. package/lib/types/models.d.ts +2 -2
  235. package/lib/types/personas.d.ts +25 -0
  236. package/lib/types/personas.js +5 -0
  237. package/lib/types/skills.d.ts +43 -1
  238. package/lib/types/stream.d.ts +110 -0
  239. package/lib/types/stream.js +36 -0
  240. package/lib/utils/utils.d.ts +9 -5
  241. package/lib/utils/utils.js +9 -5
  242. package/package.json +13 -9
  243. package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
  244. package/scripts/codegen/__pycache__/generate_events.cpython-313.pyc +0 -0
  245. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
  246. package/scripts/codegen/generate_agents.py +102 -6
  247. package/scripts/codegen/generate_events.py +35 -13
  248. package/scripts/codegen/generate_personas.py +319 -0
  249. package/scripts/codegen/generate_skills.py +9 -9
  250. package/scripts/sync-jupyter.sh +26 -7
  251. package/lib/api/tool-approvals.d.ts +0 -62
  252. package/lib/api/tool-approvals.js +0 -145
  253. package/lib/examples/LexicalSidebarExample.js +0 -163
  254. package/lib/examples/NotebookSidebarExample.js +0 -119
  255. package/lib/examples/NotebookSimpleExample.d.ts +0 -6
  256. package/lib/examples/NotebookSimpleExample.js +0 -22
  257. package/lib/examples/ag-ui/index.d.ts +0 -10
  258. package/lib/examples/ag-ui/index.js +0 -16
  259. package/lib/hooks/useAgentsRegistry.d.ts +0 -10
  260. package/lib/hooks/useAgentsRegistry.js +0 -20
  261. package/lib/stores/agentsStore.d.ts +0 -123
  262. package/lib/stores/agentsStore.js +0 -270
  263. /package/lib/examples/{AgentspecExample.d.ts → AgentSpecsExample.d.ts} +0 -0
  264. /package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.d.ts +0 -0
  265. /package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.d.ts +0 -0
  266. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.d.ts +0 -0
  267. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.js +0 -0
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /*
3
3
  * Copyright (c) 2025-2026 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
@@ -6,51 +6,77 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
6
6
  /**
7
7
  * AgentCodemodeExample
8
8
  *
9
- * Demonstrates Codemode: tools that return structured outputs with schemas
10
- * rendered inline as executable code blocks, diffs, or file previews.
9
+ * Compares two Tavily-based agents side-by-side:
10
+ * - Tavily MCP without codemode conversion
11
+ * - Tavily MCP with codemode conversion
11
12
  *
12
- * - Creates a cloud agent runtime (environment: 'ai-agents-env') via the Datalayer
13
- * Runtimes API and deploys an agent on its sidecar
14
- * - Shows a code panel alongside the chat displaying tool outputs with
15
- * syntax highlighting, diff views, and the ability to accept/reject changes
13
+ * A sidebar gauge tracks consumed tokens for each agent in real time.
16
14
  */
17
15
  /// <reference types="vite/client" />
18
- import { useEffect, useState, useCallback, useRef } from 'react';
19
- import { Text, Button, Spinner, Heading, Label, Flash } from '@primer/react';
20
- import { CodeIcon, DiffIcon, FileCodeIcon, CheckIcon, XIcon, SignOutIcon, } from '@primer/octicons-react';
16
+ import { useEffect, useState, useCallback, useRef, useMemo, } from 'react';
17
+ import { Text, Spinner, Heading, Label, Flash } from '@primer/react';
18
+ import { CodeIcon } from '@primer/octicons-react';
21
19
  import { Box } from '@datalayer/primer-addons';
22
- import { ErrorView } from './components';
20
+ import ReactECharts from 'echarts-for-react';
23
21
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
24
22
  import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel';
25
- import { SignInSimple } from '@datalayer/core/lib/views/iam';
26
- import { UserBadge } from '@datalayer/core/lib/views/profile';
27
23
  import { ThemedProvider } from './utils/themedProvider';
28
- import { useAgents } from '../hooks/useAgents';
24
+ import { AuthRequiredView } from './components';
25
+ import { uniqueAgentId } from './utils/agentId';
26
+ import { useAIAgentsWebSocket } from '../hooks';
29
27
  import { Chat } from '../chat';
28
+ import { ContextPanel, } from '../context/ContextPanel';
29
+ import { parseAgentStreamMessage, } from '../types/stream';
30
+ import { MCP_SERVER_LIBRARY } from '../specs/mcpServers';
30
31
  const queryClient = new QueryClient();
31
- // ─── Constants ─────────────────────────────────────────────────────────────
32
- const AGENT_NAME = 'codemode-demo-agent';
33
- const AGENT_SPEC_ID = 'monitor-sales-kpis';
34
- // ─── Inner component (rendered after auth) ─────────────────────────────────
35
- const AgentCodemodeInner = ({ onLogout, }) => {
36
- const { token } = useSimpleAuthStore();
37
- const { runtime, status: runtimeStatus, isReady, error: hookError, } = useAgents({
38
- agentSpecId: AGENT_SPEC_ID,
39
- autoStart: true,
40
- agentConfig: {
41
- name: AGENT_NAME,
42
- protocol: 'ag-ui',
43
- description: 'Agent with Codemode structured tool outputs',
44
- },
45
- });
46
- const [codeView, setCodeView] = useState('output');
47
- const [artifacts, setArtifacts] = useState([]);
48
- const [selectedId, setSelectedId] = useState(null);
49
- const [flash, setFlash] = useState(null);
50
- const agentBaseUrl = runtime?.agentBaseUrl || '';
51
- const agentId = runtime?.agentId || AGENT_NAME;
52
- const podName = runtime?.podName || '(launching…)';
53
- // Authenticated fetch helper
32
+ // Each pane talks to its own agent-runtimes process so the two agents are
33
+ // fully isolated (no shared global codemode/MCP state). Override via env:
34
+ // VITE_BASE_URL_NO_CODEMODE=http://localhost:8765
35
+ // VITE_BASE_URL_CODEMODE=http://localhost:8766
36
+ const NO_CODEMODE_BASE_URL = import.meta.env.VITE_BASE_URL_NO_CODEMODE ||
37
+ import.meta.env.VITE_BASE_URL ||
38
+ 'http://localhost:8765';
39
+ const CODEMODE_BASE_URL = import.meta.env.VITE_BASE_URL_CODEMODE || 'http://localhost:8766';
40
+ const NO_CODEMODE_SUGGESTION_MESSAGE = 'Use the Tavily Extract tool to extract information from https://datalayer.ai, then use your sandbox to persist that information in a variable named "about_datalayer".';
41
+ const CODEMODE_SUGGESTION_MESSAGE = 'Extract information from the https://datalayer.ai website and assign it to the variable "about_datalayer", all in one step using the sandbox';
42
+ const DEMO_AGENT_CONFIGS = [
43
+ {
44
+ key: 'no-codemode',
45
+ title: 'Tavily MCP (No Codemode)',
46
+ subtitle: 'Raw MCP tools without codemode conversion',
47
+ suggestionMessage: NO_CODEMODE_SUGGESTION_MESSAGE,
48
+ specId: 'demo-tavily-no-codemode',
49
+ color: '#0969DA',
50
+ baseUrl: NO_CODEMODE_BASE_URL,
51
+ },
52
+ {
53
+ key: 'codemode',
54
+ title: 'Tavily MCP (Codemode)',
55
+ subtitle: 'MCP tools converted into programmatic tools',
56
+ suggestionMessage: CODEMODE_SUGGESTION_MESSAGE,
57
+ specId: 'demo-tavily-codemode',
58
+ color: '#8250DF',
59
+ baseUrl: CODEMODE_BASE_URL,
60
+ },
61
+ ];
62
+ function extractConsumedTokens(payload) {
63
+ const snapshotCost = payload.contextSnapshot?.costUsage ?? payload.costUsage;
64
+ const totalFromCost = Number(snapshotCost?.totalTokensUsed ?? 0);
65
+ if (totalFromCost > 0) {
66
+ return totalFromCost;
67
+ }
68
+ return Number(payload.contextSnapshot?.totalTokens ?? 0);
69
+ }
70
+ const AgentRuntimePane = ({ config, token, onTokenConsumed, onAgentIdChange, onContextSnapshot, }) => {
71
+ const runtimeName = useRef(uniqueAgentId(`codemode-${config.key}`)).current;
72
+ const [runtimeStatus, setRuntimeStatus] = useState('launching');
73
+ const [hookError, setHookError] = useState(null);
74
+ const [agentId, setAgentId] = useState(runtimeName);
75
+ const [isReconnectedAgent, setIsReconnectedAgent] = useState(false);
76
+ const [selectedServerIds, setSelectedServerIds] = useState([]);
77
+ const [agentTools, setAgentTools] = useState([]);
78
+ const [liveMcpStatus, setLiveMcpStatus] = useState(undefined);
79
+ const [codemodeEnabled, setCodemodeEnabled] = useState(config.key === 'codemode');
54
80
  const authFetch = useCallback((url, opts = {}) => fetch(url, {
55
81
  ...opts,
56
82
  headers: {
@@ -59,63 +85,437 @@ const AgentCodemodeInner = ({ onLogout, }) => {
59
85
  ...(opts.headers ?? {}),
60
86
  },
61
87
  }), [token]);
62
- // ── Poll code artifacts ───────────────────────────────────────────────
63
88
  useEffect(() => {
64
- if (!isReady || !agentBaseUrl)
89
+ let cancelled = false;
90
+ const createLocalAgent = async () => {
91
+ setRuntimeStatus('launching');
92
+ setHookError(null);
93
+ setIsReconnectedAgent(false);
94
+ try {
95
+ const response = await authFetch(`${config.baseUrl}/api/v1/agents`, {
96
+ method: 'POST',
97
+ body: JSON.stringify({
98
+ name: runtimeName,
99
+ description: config.subtitle,
100
+ agent_library: 'pydantic-ai',
101
+ transport: 'vercel-ai',
102
+ agent_spec_id: config.specId,
103
+ enable_skills: true,
104
+ tools: [],
105
+ }),
106
+ });
107
+ let resolvedAgentId = runtimeName;
108
+ let alreadyRunning = false;
109
+ if (response.ok) {
110
+ const data = await response.json();
111
+ resolvedAgentId = data?.id || runtimeName;
112
+ }
113
+ else {
114
+ const contentType = response.headers.get('content-type') || '';
115
+ let detail = '';
116
+ if (contentType.includes('application/json')) {
117
+ const data = await response.json().catch(() => null);
118
+ detail =
119
+ (typeof data?.detail === 'string' && data.detail) ||
120
+ (typeof data?.message === 'string' && data.message) ||
121
+ '';
122
+ }
123
+ else {
124
+ detail = await response.text();
125
+ }
126
+ if (response.status === 409 || /already exists/i.test(detail || '')) {
127
+ alreadyRunning = true;
128
+ }
129
+ else {
130
+ throw new Error(detail || `Failed to create local agent: ${response.status}`);
131
+ }
132
+ }
133
+ if (!cancelled) {
134
+ setAgentId(resolvedAgentId);
135
+ onAgentIdChange?.(config.key, resolvedAgentId);
136
+ setIsReconnectedAgent(alreadyRunning);
137
+ setRuntimeStatus('ready');
138
+ }
139
+ }
140
+ catch (error) {
141
+ if (!cancelled) {
142
+ setHookError(error instanceof Error ? error.message : 'Agent failed to start');
143
+ setRuntimeStatus('error');
144
+ }
145
+ }
146
+ };
147
+ void createLocalAgent();
148
+ return () => {
149
+ cancelled = true;
150
+ };
151
+ }, [authFetch, config.specId, config.subtitle, config.baseUrl, runtimeName]);
152
+ const handleStreamMessage = useCallback((message) => {
153
+ try {
154
+ const stream = parseAgentStreamMessage(message?.raw ?? message);
155
+ if (!stream || stream.type !== 'agent.snapshot') {
156
+ return;
157
+ }
158
+ const payload = stream.payload;
159
+ const consumed = extractConsumedTokens(payload);
160
+ if (consumed >= 0) {
161
+ onTokenConsumed(config.key, consumed);
162
+ }
163
+ if (payload.mcpStatus !== undefined) {
164
+ setLiveMcpStatus(payload.mcpStatus ?? undefined);
165
+ }
166
+ if (payload.contextSnapshot) {
167
+ onContextSnapshot?.(config.key, payload.contextSnapshot);
168
+ }
169
+ const fc = payload.fullContext;
170
+ if (fc && Array.isArray(fc.tools) && fc.tools.length > 0) {
171
+ setAgentTools(fc.tools);
172
+ }
173
+ if (payload.codemodeStatus &&
174
+ typeof payload.codemodeStatus.enabled === 'boolean') {
175
+ setCodemodeEnabled(payload.codemodeStatus.enabled);
176
+ }
177
+ }
178
+ catch {
179
+ // Ignore malformed stream payloads.
180
+ }
181
+ }, [config.key, onTokenConsumed, onContextSnapshot]);
182
+ useAIAgentsWebSocket({
183
+ enabled: runtimeStatus === 'ready',
184
+ baseUrl: config.baseUrl,
185
+ path: '/api/v1/tool-approvals/ws',
186
+ queryParams: { agent_id: agentId },
187
+ onMessage: handleStreamMessage,
188
+ reconnectDelayMs: attempt => Math.min(1000 * 2 ** Math.max(0, attempt - 1), 10000),
189
+ });
190
+ // Fetch creation spec to get selected MCP server IDs (for the MCP indicator).
191
+ useEffect(() => {
192
+ if (runtimeStatus !== 'ready')
65
193
  return;
66
- const poll = async () => {
194
+ let cancelled = false;
195
+ const fetchSpec = async () => {
67
196
  try {
68
- const res = await authFetch(`${agentBaseUrl}/api/v1/agents/${agentId}/codemode/artifacts`);
69
- if (res.ok) {
70
- const d = await res.json();
71
- setArtifacts(Array.isArray(d) ? d : (d.artifacts ?? []));
197
+ const res = await authFetch(`${config.baseUrl}/api/v1/configure/agents/${agentId}/spec`);
198
+ if (!res.ok)
199
+ return;
200
+ const spec = await res.json();
201
+ const servers = (spec?.selected_mcp_servers ?? []);
202
+ if (!cancelled) {
203
+ setSelectedServerIds(servers.map(s => s.id));
72
204
  }
73
205
  }
74
206
  catch {
75
- /* ok */
207
+ // Non-fatal.
76
208
  }
77
209
  };
78
- poll();
79
- const interval = setInterval(poll, 5_000);
80
- return () => clearInterval(interval);
81
- }, [isReady, agentBaseUrl, agentId, authFetch]);
82
- // ── Accept / Reject ───────────────────────────────────────────────────
83
- const handleDecision = useCallback(async (artifactId, decision) => {
84
- if (!agentBaseUrl)
85
- return;
86
- setFlash(null);
210
+ void fetchSpec();
211
+ return () => {
212
+ cancelled = true;
213
+ };
214
+ }, [runtimeStatus, agentId, authFetch, config.baseUrl]);
215
+ // Build McpServerInfo[] from selected servers + catalog + WS tools.
216
+ const mcpServers = useMemo(() => {
217
+ if (selectedServerIds.length === 0)
218
+ return [];
219
+ return selectedServerIds.map(serverId => {
220
+ const catalogServer = MCP_SERVER_LIBRARY[serverId];
221
+ const serverName = catalogServer?.name ?? serverId;
222
+ const liveServer = liveMcpStatus?.servers?.find(s => s.id === serverId);
223
+ const serverTools = agentTools
224
+ .filter(t => t.name.startsWith(`${serverId}__`))
225
+ .map(t => ({
226
+ name: t.name,
227
+ description: t.description,
228
+ serverId,
229
+ serverName,
230
+ inputSchema: t.parametersSchema,
231
+ }));
232
+ const toolsFromLive = (liveServer?.tools ?? []).map(t => ({
233
+ name: t.name,
234
+ description: t.description ?? '',
235
+ serverId,
236
+ serverName,
237
+ inputSchema: undefined,
238
+ }));
239
+ const tools = toolsFromLive.length > 0 ? toolsFromLive : serverTools;
240
+ const status = liveServer?.status ?? (serverTools.length > 0 ? 'started' : 'starting');
241
+ const toolsCount = liveServer?.tools_count ?? tools.length;
242
+ return {
243
+ id: serverId,
244
+ name: serverName,
245
+ description: catalogServer?.description,
246
+ status,
247
+ toolsCount,
248
+ tools,
249
+ emoji: catalogServer?.emoji,
250
+ icon: catalogServer?.icon,
251
+ };
252
+ });
253
+ }, [selectedServerIds, agentTools, liveMcpStatus]);
254
+ // Build a synthetic McpToolsetsStatusResponse for the Chat MCP indicator.
255
+ const mcpStatusData = useMemo(() => {
256
+ const derivedEnabledToolsByServer = {};
257
+ const derivedApprovedToolsByServer = {};
258
+ for (const s of mcpServers) {
259
+ if (s.tools.length > 0) {
260
+ derivedEnabledToolsByServer[s.id] = s.tools.map(t => t.name);
261
+ derivedApprovedToolsByServer[s.id] = [];
262
+ }
263
+ }
264
+ if (liveMcpStatus && liveMcpStatus.servers.length > 0) {
265
+ const live = liveMcpStatus;
266
+ const enabledToolsByServer = { ...(live.enabled_tools_by_server ?? {}) };
267
+ for (const [serverId, tools] of Object.entries(derivedEnabledToolsByServer)) {
268
+ if (!enabledToolsByServer[serverId] ||
269
+ enabledToolsByServer[serverId].length === 0) {
270
+ enabledToolsByServer[serverId] = tools;
271
+ }
272
+ }
273
+ const approvedToolsByServer = {
274
+ ...(live.approved_tools_by_server ?? {}),
275
+ };
276
+ for (const [serverId, tools] of Object.entries(derivedApprovedToolsByServer)) {
277
+ if (!approvedToolsByServer[serverId]) {
278
+ approvedToolsByServer[serverId] = tools;
279
+ }
280
+ }
281
+ return {
282
+ ...live,
283
+ enabled_tools_by_server: enabledToolsByServer,
284
+ approved_tools_by_server: approvedToolsByServer,
285
+ };
286
+ }
287
+ if (mcpServers.length === 0)
288
+ return undefined;
289
+ const servers = mcpServers.map(s => ({
290
+ id: s.id,
291
+ status: s.status,
292
+ tools_count: s.toolsCount,
293
+ tools: s.tools.map(t => ({
294
+ name: t.name,
295
+ description: t.description,
296
+ enabled: true,
297
+ })),
298
+ }));
299
+ const readyServers = servers
300
+ .filter(s => s.status === 'started')
301
+ .map(s => s.id);
302
+ return {
303
+ initialized: true,
304
+ ready_count: readyServers.length,
305
+ failed_count: servers.filter(s => s.status === 'failed').length,
306
+ ready_servers: readyServers,
307
+ failed_servers: {},
308
+ servers,
309
+ enabled_tools_by_server: derivedEnabledToolsByServer,
310
+ approved_tools_by_server: derivedApprovedToolsByServer,
311
+ enabled_tools_count: mcpServers.reduce((sum, s) => sum + s.tools.length, 0),
312
+ };
313
+ }, [liveMcpStatus, mcpServers]);
314
+ // Synthetic codemode status so the Info panel never shows
315
+ // "Waiting for Codemode status from WebSocket stream...".
316
+ // This is configuration, so it's known locally from the spec/toggle state.
317
+ const codemodeStatusData = useMemo(() => ({
318
+ enabled: codemodeEnabled,
319
+ skills: [],
320
+ available_skills: [],
321
+ sandbox: null,
322
+ }), [codemodeEnabled]);
323
+ const handleToggleCodemode = useCallback(async (enabled) => {
324
+ // Optimistic update; WS snapshot will reconcile.
325
+ setCodemodeEnabled(enabled);
87
326
  try {
88
- const res = await authFetch(`${agentBaseUrl}/api/v1/agents/${agentId}/codemode/artifacts/${artifactId}`, {
89
- method: 'PATCH',
90
- body: JSON.stringify({ status: decision }),
327
+ const res = await authFetch(`${config.baseUrl}/api/v1/configure/codemode/toggle`, {
328
+ method: 'POST',
329
+ body: JSON.stringify({ enabled, agent_id: agentId }),
91
330
  });
92
- if (res.ok) {
93
- setArtifacts(prev => prev.map(a => a.id === artifactId ? { ...a, status: decision } : a));
94
- setFlash(`Change ${decision}`);
331
+ if (!res.ok) {
332
+ throw new Error(`Toggle failed: ${res.status}`);
95
333
  }
96
334
  }
97
335
  catch {
98
- setFlash('Network error');
336
+ // Rollback optimistic update on failure.
337
+ setCodemodeEnabled(prev => !prev);
99
338
  }
100
- }, [agentBaseUrl, agentId, authFetch]);
101
- // ── Loading / Error ───────────────────────────────────────────────────
102
- if (!isReady && runtimeStatus !== 'error') {
339
+ }, [authFetch, agentId, config.baseUrl]);
340
+ if (runtimeStatus === 'launching') {
103
341
  return (_jsxs(Box, { sx: {
342
+ border: '1px solid',
343
+ borderColor: 'border.default',
344
+ borderRadius: 2,
345
+ p: 3,
346
+ minHeight: 220,
104
347
  display: 'flex',
105
- flexDirection: 'column',
106
348
  alignItems: 'center',
107
349
  justifyContent: 'center',
108
- height: '100vh',
109
- gap: 3,
110
- }, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: runtimeStatus === 'launching'
111
- ? 'Launching runtime for codemode agent…'
112
- : 'Creating codemode demo agent…' })] }));
350
+ flexDirection: 'column',
351
+ gap: 2,
352
+ }, children: [_jsx(Spinner, { size: "small" }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Launching ", config.title, "..."] })] }));
113
353
  }
114
354
  if (runtimeStatus === 'error' || hookError) {
115
- return _jsx(ErrorView, { error: hookError, onLogout: onLogout });
355
+ return (_jsxs(Flash, { variant: "danger", sx: { borderRadius: 2 }, children: [config.title, ": ", hookError || 'Failed to start'] }));
356
+ }
357
+ return (_jsx(Box, { sx: {
358
+ border: '1px solid',
359
+ borderColor: 'border.default',
360
+ borderRadius: 2,
361
+ overflow: 'hidden',
362
+ minHeight: 560,
363
+ display: 'flex',
364
+ flexDirection: 'column',
365
+ }, children: _jsx(Box, { sx: { flex: 1, minHeight: 0 }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: config.baseUrl, agentId: agentId, authToken: token, title: config.title, subtitle: config.subtitle, placeholder: "Ask both agents the same request to compare behavior...", description: config.subtitle, showHeader: true, headerActions: isReconnectedAgent ? (_jsx(Label, { size: "small", variant: "attention", children: "Reconnected" })) : undefined, autoFocus: false, height: "100%", runtimeId: agentId, historyEndpoint: `${config.baseUrl}/api/v1/history`, mcpStatusData: mcpStatusData, codemodeStatusData: codemodeStatusData, codemodeEnabled: codemodeEnabled, onToggleCodemode: handleToggleCodemode, suggestions: [
366
+ {
367
+ title: 'Datalayer extraction',
368
+ message: config.suggestionMessage,
369
+ },
370
+ ], submitOnSuggestionClick: true }) }) }));
371
+ };
372
+ const AgentCodemodeInner = ({ onLogout, }) => {
373
+ const { token } = useSimpleAuthStore();
374
+ const [consumedByAgent, setConsumedByAgent] = useState({
375
+ 'no-codemode': 0,
376
+ codemode: 0,
377
+ });
378
+ const [agentIdByKey, setAgentIdByKey] = useState({});
379
+ const [contextSnapshotByKey, setContextSnapshotByKey] = useState({});
380
+ const handleAgentIdChange = useCallback((agentKey, agentId) => {
381
+ setAgentIdByKey(prev => prev[agentKey] === agentId ? prev : { ...prev, [agentKey]: agentId });
382
+ }, []);
383
+ const handleContextSnapshot = useCallback((agentKey, snapshot) => {
384
+ setContextSnapshotByKey(prev => ({ ...prev, [agentKey]: snapshot }));
385
+ }, []);
386
+ const handleTokenConsumed = useCallback((agentKey, tokens) => {
387
+ setConsumedByAgent(prev => ({
388
+ ...prev,
389
+ [agentKey]: Math.max(prev[agentKey] ?? 0, tokens),
390
+ }));
391
+ }, []);
392
+ const maxGaugeValue = useMemo(() => {
393
+ const values = Object.values(consumedByAgent);
394
+ const currentMax = values.length > 0 ? Math.max(...values) : 0;
395
+ if (currentMax <= 2000) {
396
+ return 2000;
397
+ }
398
+ const magnitude = 10 ** Math.floor(Math.log10(currentMax));
399
+ return Math.ceil((currentMax * 1.2) / magnitude) * magnitude;
400
+ }, [consumedByAgent]);
401
+ const tokenComparison = useMemo(() => {
402
+ const noCodemodeTokens = consumedByAgent['no-codemode'] ?? 0;
403
+ const codemodeTokens = consumedByAgent.codemode ?? 0;
404
+ if (noCodemodeTokens === codemodeTokens) {
405
+ return {
406
+ tie: true,
407
+ winnerKey: null,
408
+ loserKey: null,
409
+ };
410
+ }
411
+ const winnerKey = noCodemodeTokens < codemodeTokens ? 'no-codemode' : 'codemode';
412
+ return {
413
+ tie: false,
414
+ winnerKey,
415
+ loserKey: winnerKey === 'codemode'
416
+ ? 'no-codemode'
417
+ : 'codemode',
418
+ };
419
+ }, [consumedByAgent]);
420
+ const displayNameFor = useCallback((agentKey) => agentKey === 'codemode' ? 'Codemode' : 'No codemode', []);
421
+ const outcomeFor = useCallback((agentKey) => {
422
+ if (tokenComparison.tie) {
423
+ return {
424
+ emoji: '🤝',
425
+ title: 'Tie',
426
+ message: 'Both agents are tied on token usage.',
427
+ bg: 'canvas.subtle',
428
+ borderColor: 'border.default',
429
+ color: 'fg.default',
430
+ };
431
+ }
432
+ if (tokenComparison.winnerKey === agentKey) {
433
+ return {
434
+ emoji: '🏆',
435
+ title: 'Winner',
436
+ message: `${displayNameFor(agentKey)} is the winner.`,
437
+ bg: 'success.subtle',
438
+ borderColor: 'success.muted',
439
+ color: 'success.fg',
440
+ };
441
+ }
442
+ return {
443
+ emoji: '😓',
444
+ title: 'Loser',
445
+ message: `${displayNameFor(agentKey)} is the loser.`,
446
+ bg: 'attention.subtle',
447
+ borderColor: 'attention.muted',
448
+ color: 'attention.fg',
449
+ };
450
+ }, [displayNameFor, tokenComparison]);
451
+ const gaugeOptionFor = useCallback((config) => ({
452
+ series: [
453
+ {
454
+ type: 'gauge',
455
+ center: ['50%', '55%'],
456
+ radius: '80%',
457
+ min: 0,
458
+ max: maxGaugeValue,
459
+ splitNumber: 5,
460
+ progress: {
461
+ show: true,
462
+ width: 14,
463
+ itemStyle: {
464
+ color: config.color,
465
+ },
466
+ },
467
+ axisLine: {
468
+ lineStyle: {
469
+ width: 14,
470
+ color: [[1, '#d1d9e0']],
471
+ },
472
+ },
473
+ axisTick: { show: false },
474
+ splitLine: {
475
+ length: 10,
476
+ lineStyle: { color: '#8c959f', width: 1 },
477
+ },
478
+ axisLabel: {
479
+ distance: 14,
480
+ color: '#57606a',
481
+ fontSize: 10,
482
+ },
483
+ pointer: {
484
+ width: 3,
485
+ length: '60%',
486
+ },
487
+ anchor: {
488
+ show: true,
489
+ size: 8,
490
+ itemStyle: {
491
+ color: config.color,
492
+ },
493
+ },
494
+ title: { show: false },
495
+ detail: {
496
+ valueAnimation: true,
497
+ offsetCenter: [0, '70%'],
498
+ color: '#24292f',
499
+ fontSize: 14,
500
+ fontWeight: 'bold',
501
+ formatter: (value) => `${Math.round(value).toLocaleString()} tok`,
502
+ },
503
+ data: [
504
+ {
505
+ value: consumedByAgent[config.key] ?? 0,
506
+ name: config.title,
507
+ },
508
+ ],
509
+ },
510
+ ],
511
+ tooltip: {
512
+ trigger: 'item',
513
+ formatter: (params) => `${params.seriesName || 'Agent'}<br/>${Math.round(params.value || 0).toLocaleString()} tokens`,
514
+ },
515
+ }), [consumedByAgent, maxGaugeValue]);
516
+ if (!token) {
517
+ return null;
116
518
  }
117
- const selected = artifacts.find(a => a.id === selectedId) ?? artifacts[0] ?? null;
118
- const pendingCount = artifacts.filter(a => a.status === 'pending').length;
119
519
  return (_jsxs(Box, { sx: {
120
520
  height: 'calc(100vh - 60px)',
121
521
  display: 'flex',
@@ -129,106 +529,133 @@ const AgentCodemodeInner = ({ onLogout, }) => {
129
529
  borderBottom: '1px solid',
130
530
  borderColor: 'border.default',
131
531
  flexShrink: 0,
132
- }, children: [_jsx(CodeIcon, { size: 16 }), _jsxs(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: ["Codemode \u2014 ", podName] }), pendingCount > 0 && (_jsxs(Label, { variant: "attention", size: "small", children: [pendingCount, " pending"] })), token && _jsx(UserBadge, { token: token, variant: "small" }), _jsx(Button, { size: "small", variant: "invisible", onClick: onLogout, leadingVisual: SignOutIcon, sx: { color: 'fg.muted' }, children: "Sign out" })] }), _jsxs(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "ag-ui", baseUrl: agentBaseUrl, agentId: agentId, title: "Codemode Agent", placeholder: "Ask the agent to generate or modify code\u2026", description: `${artifacts.length} code artifact${artifacts.length !== 1 ? 's' : ''}`, showHeader: true, autoFocus: true, height: "100%", runtimeId: podName, historyEndpoint: `${agentBaseUrl}/api/v1/history`, suggestions: [
133
- {
134
- title: 'Generate script',
135
- message: 'Write a Python script to analyze the KPI data',
136
- },
137
- {
138
- title: 'Refactor',
139
- message: 'Refactor the last code block for readability',
140
- },
141
- ], submitOnSuggestionClick: true }) }), _jsxs(Box, { sx: {
142
- width: 480,
143
- borderLeft: '1px solid',
144
- borderColor: 'border.default',
145
- display: 'flex',
146
- flexDirection: 'column',
147
- }, children: [_jsx(Box, { sx: {
148
- display: 'flex',
149
- borderBottom: '1px solid',
150
- borderColor: 'border.default',
151
- flexShrink: 0,
152
- }, children: [
153
- {
154
- key: 'output',
155
- icon: FileCodeIcon,
156
- label: 'Output',
157
- },
158
- { key: 'diff', icon: DiffIcon, label: 'Diff' },
159
- ].map(t => (_jsx(Button, { size: "small", variant: "invisible", leadingVisual: t.icon, onClick: () => setCodeView(t.key), sx: {
160
- flex: 1,
161
- borderRadius: 0,
162
- borderBottom: codeView === t.key ? '2px solid' : '2px solid transparent',
163
- borderColor: codeView === t.key ? 'accent.fg' : 'transparent',
164
- fontWeight: codeView === t.key ? 'bold' : 'normal',
165
- }, children: t.label }, t.key))) }), _jsxs(Box, { sx: { display: 'flex', flex: 1, minHeight: 0 }, children: [_jsx(Box, { sx: {
166
- width: 140,
167
- borderRight: '1px solid',
168
- borderColor: 'border.default',
169
- overflow: 'auto',
170
- }, children: artifacts.length === 0 ? (_jsx(Text, { sx: {
171
- p: 2,
532
+ }, children: [_jsx(CodeIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: "Codemode \u2014 Tavily MCP vs Tavily Codemode" })] }), _jsxs(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: [DEMO_AGENT_CONFIGS.filter(c => c.key === 'no-codemode').map(config => (() => {
533
+ const outcome = outcomeFor(config.key);
534
+ return (_jsxs(Box, { sx: {
535
+ width: 320,
536
+ borderRight: '1px solid',
537
+ borderColor: 'border.default',
538
+ p: 3,
539
+ display: 'flex',
540
+ flexDirection: 'column',
541
+ gap: 3,
542
+ flexShrink: 0,
543
+ overflow: 'auto',
544
+ }, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mb: 1 }, children: config.title }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0, display: 'block' }, children: "Tokens consumed (no codemode)." }), _jsx(Text, { sx: {
172
545
  color: 'fg.muted',
173
546
  fontSize: 0,
547
+ fontFamily: 'mono',
548
+ display: 'block',
549
+ mt: 1,
550
+ }, children: config.baseUrl }), _jsx(Text, { sx: {
551
+ color: 'fg.muted',
552
+ fontSize: 0,
553
+ fontFamily: 'mono',
554
+ display: 'block',
555
+ mt: 1,
556
+ wordBreak: 'break-all',
557
+ }, children: agentIdByKey[config.key] || 'launching…' })] }), _jsxs(Box, { sx: {
558
+ p: 2,
559
+ borderRadius: 2,
560
+ border: '1px solid',
561
+ bg: outcome.bg,
562
+ borderColor: outcome.borderColor,
563
+ textAlign: 'center',
564
+ display: 'flex',
565
+ flexDirection: 'column',
566
+ alignItems: 'center',
567
+ }, children: [_jsx(Text, { sx: { fontSize: 4, lineHeight: 1, mb: 1 }, children: outcome.emoji }), _jsx(Text, { sx: {
568
+ fontSize: 0,
569
+ color: outcome.color,
570
+ fontWeight: 'bold',
571
+ }, children: outcome.title }), _jsx(Text, { sx: {
572
+ fontSize: 0,
573
+ color: 'fg.muted',
574
+ display: 'block',
575
+ mt: 1,
576
+ }, children: outcome.message })] }), _jsx(ReactECharts, { option: gaugeOptionFor(config), style: {
577
+ height: 240,
578
+ minHeight: 240,
579
+ maxHeight: 240,
580
+ width: '100%',
581
+ flexShrink: 0,
582
+ } }), _jsxs(Box, { sx: {
583
+ display: 'flex',
584
+ justifyContent: 'space-between',
585
+ gap: 2,
586
+ }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Consumed" }), _jsxs(Text, { sx: { fontSize: 0, fontWeight: 'bold' }, children: [(consumedByAgent[config.key] ?? 0).toLocaleString(), " tokens"] })] }), agentIdByKey[config.key] && (_jsx(ContextPanel, { agentId: agentIdByKey[config.key], apiBase: config.baseUrl, liveData: contextSnapshotByKey[config.key] ?? null, chartHeight: "180px" }))] }, config.key));
587
+ })()), _jsx(Box, { sx: {
588
+ flex: 1,
589
+ minWidth: 0,
590
+ p: 3,
591
+ display: 'grid',
592
+ gridTemplateColumns: ['1fr', null, '1fr 1fr'],
593
+ gap: 3,
594
+ overflow: 'auto',
595
+ }, children: DEMO_AGENT_CONFIGS.map(config => (_jsx(AgentRuntimePane, { config: config, token: token, onTokenConsumed: handleTokenConsumed, onAgentIdChange: handleAgentIdChange, onContextSnapshot: handleContextSnapshot }, config.key))) }), DEMO_AGENT_CONFIGS.filter(c => c.key === 'codemode').map(config => (() => {
596
+ const outcome = outcomeFor(config.key);
597
+ return (_jsxs(Box, { sx: {
598
+ width: 320,
599
+ borderLeft: '1px solid',
600
+ borderColor: 'border.default',
601
+ p: 3,
602
+ display: 'flex',
603
+ flexDirection: 'column',
604
+ gap: 3,
605
+ flexShrink: 0,
606
+ overflow: 'auto',
607
+ }, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mb: 1 }, children: config.title }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0, display: 'block' }, children: "Tokens consumed (codemode)." }), _jsx(Text, { sx: {
608
+ color: 'fg.muted',
609
+ fontSize: 0,
610
+ fontFamily: 'mono',
611
+ display: 'block',
612
+ mt: 1,
613
+ }, children: config.baseUrl }), _jsx(Text, { sx: {
614
+ color: 'fg.muted',
615
+ fontSize: 0,
616
+ fontFamily: 'mono',
617
+ display: 'block',
618
+ mt: 1,
619
+ wordBreak: 'break-all',
620
+ }, children: agentIdByKey[config.key] || 'launching…' })] }), _jsxs(Box, { sx: {
621
+ p: 2,
622
+ borderRadius: 2,
623
+ border: '1px solid',
624
+ bg: outcome.bg,
625
+ borderColor: outcome.borderColor,
626
+ textAlign: 'center',
627
+ display: 'flex',
628
+ flexDirection: 'column',
629
+ alignItems: 'center',
630
+ }, children: [_jsx(Text, { sx: { fontSize: 4, lineHeight: 1, mb: 1 }, children: outcome.emoji }), _jsx(Text, { sx: {
631
+ fontSize: 0,
632
+ color: outcome.color,
633
+ fontWeight: 'bold',
634
+ }, children: outcome.title }), _jsx(Text, { sx: {
635
+ fontSize: 0,
636
+ color: 'fg.muted',
174
637
  display: 'block',
175
- }, children: "No artifacts yet." })) : (artifacts.map(a => (_jsxs(Box, { onClick: () => setSelectedId(a.id), sx: {
176
- p: 2,
177
- borderBottom: '1px solid',
178
- borderColor: 'border.muted',
179
- bg: selected?.id === a.id ? 'accent.subtle' : 'transparent',
180
- cursor: 'pointer',
181
- ':hover': { bg: 'canvas.subtle' },
182
- }, children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', display: 'block' }, children: a.tool_name }), _jsx(Label, { size: "small", variant: a.status === 'accepted'
183
- ? 'success'
184
- : a.status === 'rejected'
185
- ? 'danger'
186
- : 'attention', children: a.status })] }, a.id)))) }), _jsx(Box, { sx: {
187
- flex: 1,
188
- display: 'flex',
189
- flexDirection: 'column',
190
- overflow: 'auto',
191
- }, children: selected ? (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
192
- p: 2,
193
- borderBottom: '1px solid',
194
- borderColor: 'border.default',
195
- display: 'flex',
196
- justifyContent: 'space-between',
197
- alignItems: 'center',
198
- flexShrink: 0,
199
- }, children: [_jsxs(Box, { children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: selected.tool_name }), _jsx(Label, { size: "small", variant: "secondary", sx: { ml: 1 }, children: selected.language })] }), selected.status === 'pending' && (_jsxs(Box, { sx: { display: 'flex', gap: 1 }, children: [_jsx(Button, { size: "small", variant: "primary", leadingVisual: CheckIcon, onClick: () => handleDecision(selected.id, 'accepted'), children: "Accept" }), _jsx(Button, { size: "small", variant: "danger", leadingVisual: XIcon, onClick: () => handleDecision(selected.id, 'rejected'), children: "Reject" })] }))] }), _jsx(Box, { sx: {
200
- flex: 1,
201
- overflow: 'auto',
202
- bg: 'canvas.subtle',
203
- p: 3,
204
- }, children: _jsx(Box, { as: "pre", sx: {
205
- fontFamily: 'mono',
206
- fontSize: 0,
207
- m: 0,
208
- whiteSpace: 'pre-wrap',
209
- wordBreak: 'break-word',
210
- }, children: codeView === 'diff' && selected.diff
211
- ? selected.diff
212
- : selected.content }) })] })) : (_jsx(Box, { sx: {
213
- flex: 1,
214
- display: 'flex',
215
- alignItems: 'center',
216
- justifyContent: 'center',
217
- }, children: _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Ask the agent to generate code to see it here." }) })) })] }), flash && (_jsx(Flash, { variant: flash.includes('accepted')
218
- ? 'success'
219
- : flash.includes('rejected')
220
- ? 'warning'
221
- : 'danger', sx: { m: 2, fontSize: 0 }, children: flash }))] })] })] }));
638
+ mt: 1,
639
+ }, children: outcome.message })] }), _jsx(ReactECharts, { option: gaugeOptionFor(config), style: {
640
+ height: 240,
641
+ minHeight: 240,
642
+ maxHeight: 240,
643
+ width: '100%',
644
+ flexShrink: 0,
645
+ } }), _jsxs(Box, { sx: {
646
+ display: 'flex',
647
+ justifyContent: 'space-between',
648
+ gap: 2,
649
+ }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Consumed" }), _jsxs(Text, { sx: { fontSize: 0, fontWeight: 'bold' }, children: [(consumedByAgent[config.key] ?? 0).toLocaleString(), " tokens"] })] }), agentIdByKey[config.key] && (_jsx(ContextPanel, { agentId: agentIdByKey[config.key], apiBase: config.baseUrl, liveData: contextSnapshotByKey[config.key] ?? null, chartHeight: "180px" }))] }, config.key));
650
+ })())] })] }));
222
651
  };
223
- // ─── Sync token to core IAM store ──────────────────────────────────────────
224
652
  const syncTokenToIamStore = (token) => {
225
653
  import('@datalayer/core/lib/state').then(({ iamStore }) => {
226
654
  iamStore.setState({ token });
227
655
  });
228
656
  };
229
- // ─── Main component with auth gate ─────────────────────────────────────────
230
657
  const AgentCodemodeExample = () => {
231
- const { token, setAuth, clearAuth } = useSimpleAuthStore();
658
+ const { token, clearAuth } = useSimpleAuthStore();
232
659
  const hasSynced = useRef(false);
233
660
  useEffect(() => {
234
661
  if (token && !hasSynced.current) {
@@ -236,11 +663,6 @@ const AgentCodemodeExample = () => {
236
663
  syncTokenToIamStore(token);
237
664
  }
238
665
  }, [token]);
239
- const handleSignIn = useCallback((newToken, handle) => {
240
- setAuth(newToken, handle);
241
- hasSynced.current = true;
242
- syncTokenToIamStore(newToken);
243
- }, [setAuth]);
244
666
  const handleLogout = useCallback(() => {
245
667
  clearAuth();
246
668
  hasSynced.current = false;
@@ -249,7 +671,7 @@ const AgentCodemodeExample = () => {
249
671
  });
250
672
  }, [clearAuth]);
251
673
  if (!token) {
252
- return (_jsx(ThemedProvider, { children: _jsx(SignInSimple, { onSignIn: handleSignIn, onApiKeySignIn: apiKey => handleSignIn(apiKey, 'api-key-user'), title: "Agent Codemode", description: "Sign in to use Codemode with structured tool outputs.", leadingIcon: _jsx(CodeIcon, { size: 24 }) }) }));
674
+ return (_jsx(ThemedProvider, { children: _jsx(AuthRequiredView, {}) }));
253
675
  }
254
676
  return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(ThemedProvider, { children: _jsx(AgentCodemodeInner, { onLogout: handleLogout }) }) }));
255
677
  };