@datalayer/agent-runtimes 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/README.md +182 -1
  2. package/lib/AgentNode.d.ts +3 -0
  3. package/lib/AgentNode.js +676 -0
  4. package/lib/App.js +1 -1
  5. package/lib/agent-node/themeStore.d.ts +3 -0
  6. package/lib/agent-node/themeStore.js +156 -0
  7. package/lib/agent-node-main.d.ts +1 -0
  8. package/lib/agent-node-main.js +14 -0
  9. package/lib/agents/AgentDetails.d.ts +22 -1
  10. package/lib/agents/AgentDetails.js +34 -47
  11. package/lib/api/index.d.ts +0 -1
  12. package/lib/api/index.js +4 -2
  13. package/lib/chat/Chat.d.ts +5 -106
  14. package/lib/chat/Chat.js +20 -14
  15. package/lib/chat/ChatFloating.d.ts +7 -140
  16. package/lib/chat/ChatFloating.js +3 -3
  17. package/lib/chat/ChatPopupStandalone.d.ts +8 -47
  18. package/lib/chat/ChatPopupStandalone.js +3 -3
  19. package/lib/chat/ChatSidebar.d.ts +4 -69
  20. package/lib/chat/ChatSidebar.js +83 -51
  21. package/lib/chat/ChatStandalone.d.ts +4 -54
  22. package/lib/chat/ChatStandalone.js +3 -3
  23. package/lib/chat/base/ChatBase.js +1414 -174
  24. package/lib/chat/display/FloatingBrandButton.js +8 -1
  25. package/lib/chat/header/ChatHeader.d.ts +3 -1
  26. package/lib/chat/header/ChatHeader.js +15 -12
  27. package/lib/chat/header/ChatHeaderBase.d.ts +30 -5
  28. package/lib/chat/header/ChatHeaderBase.js +41 -16
  29. package/lib/chat/indicators/McpStatusIndicator.d.ts +7 -4
  30. package/lib/chat/indicators/McpStatusIndicator.js +7 -32
  31. package/lib/chat/indicators/SandboxStatusIndicator.d.ts +4 -1
  32. package/lib/chat/indicators/SandboxStatusIndicator.js +91 -56
  33. package/lib/chat/indicators/SkillsStatusIndicator.d.ts +7 -0
  34. package/lib/chat/indicators/SkillsStatusIndicator.js +88 -0
  35. package/lib/chat/indicators/index.d.ts +1 -0
  36. package/lib/chat/indicators/index.js +1 -0
  37. package/lib/chat/messages/ChatMessageList.d.ts +1 -1
  38. package/lib/chat/messages/ChatMessageList.js +154 -114
  39. package/lib/chat/messages/ChatMessages.js +6 -2
  40. package/lib/chat/prompt/InputFooter.d.ts +21 -6
  41. package/lib/chat/prompt/InputFooter.js +76 -20
  42. package/lib/chat/prompt/InputPrompt.d.ts +5 -1
  43. package/lib/chat/prompt/InputPrompt.js +4 -4
  44. package/lib/chat/prompt/InputPromptFooter.d.ts +3 -1
  45. package/lib/chat/prompt/InputPromptFooter.js +3 -3
  46. package/lib/chat/prompt/InputPromptLexical.d.ts +3 -1
  47. package/lib/chat/prompt/InputPromptLexical.js +12 -5
  48. package/lib/chat/prompt/InputPromptText.d.ts +3 -1
  49. package/lib/chat/prompt/InputPromptText.js +2 -2
  50. package/lib/chat/tools/ToolApprovalBanner.js +1 -1
  51. package/lib/chat/tools/ToolCallDisplay.d.ts +3 -1
  52. package/lib/chat/tools/ToolCallDisplay.js +2 -2
  53. package/lib/chat/usage/TokenUsageBar.js +20 -2
  54. package/lib/client/AgentRuntimesClientContext.d.ts +53 -0
  55. package/lib/client/AgentRuntimesClientContext.js +55 -0
  56. package/lib/client/AgentsMixin.d.ts +0 -18
  57. package/lib/client/AgentsMixin.js +20 -30
  58. package/lib/client/IAgentRuntimesClient.d.ts +215 -0
  59. package/lib/client/IAgentRuntimesClient.js +5 -0
  60. package/lib/client/SdkAgentRuntimesClient.d.ts +151 -0
  61. package/lib/client/SdkAgentRuntimesClient.js +134 -0
  62. package/lib/client/index.d.ts +4 -1
  63. package/lib/client/index.js +3 -1
  64. package/lib/components/NotificationEventCard.js +5 -1
  65. package/lib/config/AgentConfiguration.d.ts +22 -0
  66. package/lib/config/AgentConfiguration.js +319 -64
  67. package/lib/context/ContextDistribution.d.ts +3 -1
  68. package/lib/context/ContextDistribution.js +8 -27
  69. package/lib/context/ContextInspector.d.ts +3 -1
  70. package/lib/context/ContextInspector.js +19 -67
  71. package/lib/context/ContextPanel.d.ts +3 -1
  72. package/lib/context/ContextPanel.js +104 -64
  73. package/lib/context/ContextUsage.d.ts +3 -1
  74. package/lib/context/ContextUsage.js +3 -3
  75. package/lib/context/CostTracker.d.ts +9 -3
  76. package/lib/context/CostTracker.js +26 -47
  77. package/lib/context/CostUsageChart.d.ts +12 -0
  78. package/lib/context/CostUsageChart.js +378 -0
  79. package/lib/context/GraphFlowChart.d.ts +16 -0
  80. package/lib/context/GraphFlowChart.js +182 -0
  81. package/lib/context/TokenUsageChart.d.ts +8 -1
  82. package/lib/context/TokenUsageChart.js +349 -211
  83. package/lib/context/TurnGraphChart.d.ts +39 -0
  84. package/lib/context/TurnGraphChart.js +538 -0
  85. package/lib/context/otelWsPool.d.ts +20 -0
  86. package/lib/context/otelWsPool.js +69 -0
  87. package/lib/examples/A2UiComponentGalleryExample.d.ts +0 -17
  88. package/lib/examples/A2UiComponentGalleryExample.js +315 -522
  89. package/lib/examples/A2UiContactCardExample.d.ts +0 -18
  90. package/lib/examples/A2UiContactCardExample.js +154 -411
  91. package/lib/examples/A2UiRestaurantExample.d.ts +0 -30
  92. package/lib/examples/A2UiRestaurantExample.js +114 -212
  93. package/lib/examples/A2UiViewerExample.d.ts +0 -18
  94. package/lib/examples/A2UiViewerExample.js +283 -532
  95. package/lib/examples/AgUiBackendToolRenderingExample.js +1 -1
  96. package/lib/examples/AgUiHaikuGenUiExample.d.ts +1 -1
  97. package/lib/examples/AgUiHaikuGenUiExample.js +1 -1
  98. package/lib/examples/AgUiSharedStateExample.js +2 -1
  99. package/lib/examples/AgentCheckpointsExample.js +14 -28
  100. package/lib/examples/AgentCodemodeExample.d.ts +4 -6
  101. package/lib/examples/AgentCodemodeExample.js +603 -169
  102. package/lib/examples/AgentEvalsExample.js +339 -53
  103. package/lib/examples/AgentGuardrailsExample.js +383 -66
  104. package/lib/examples/AgentHooksExample.d.ts +3 -0
  105. package/lib/examples/AgentHooksExample.js +122 -0
  106. package/lib/examples/AgentInferenceProviderExample.d.ts +3 -0
  107. package/lib/examples/AgentInferenceProviderExample.js +329 -0
  108. package/lib/examples/AgentMCPExample.d.ts +3 -0
  109. package/lib/examples/AgentMCPExample.js +481 -0
  110. package/lib/examples/AgentMemoryExample.d.ts +1 -2
  111. package/lib/examples/AgentMemoryExample.js +78 -33
  112. package/lib/examples/AgentMonitoringExample.js +261 -200
  113. package/lib/examples/AgentNotificationsExample.d.ts +1 -2
  114. package/lib/examples/AgentNotificationsExample.js +114 -33
  115. package/lib/examples/AgentOtelExample.js +32 -42
  116. package/lib/examples/AgentOutputsExample.d.ts +11 -6
  117. package/lib/examples/AgentOutputsExample.js +433 -81
  118. package/lib/examples/AgentParametersExample.d.ts +3 -0
  119. package/lib/examples/AgentParametersExample.js +248 -0
  120. package/lib/examples/AgentSandboxExample.d.ts +3 -3
  121. package/lib/examples/AgentSandboxExample.js +74 -45
  122. package/lib/examples/AgentSkillsExample.js +95 -103
  123. package/lib/examples/AgentSubagentsExample.d.ts +14 -0
  124. package/lib/examples/AgentSubagentsExample.js +228 -0
  125. package/lib/examples/AgentToolApprovalsExample.js +49 -561
  126. package/lib/examples/AgentTriggersExample.js +823 -569
  127. package/lib/examples/{AgentspecExample.d.ts → AgentspecsExample.d.ts} +2 -2
  128. package/lib/examples/AgentspecsExample.js +1096 -0
  129. package/lib/examples/ChatCustomExample.js +16 -28
  130. package/lib/examples/ChatExample.js +13 -29
  131. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  132. package/lib/examples/CopilotKitNotebookExample.js +2 -1
  133. package/lib/examples/HomeExample.d.ts +15 -0
  134. package/lib/examples/HomeExample.js +77 -0
  135. package/lib/examples/Lexical2Example.js +4 -2
  136. package/lib/examples/{LexicalExample.d.ts → LexicalAgentExample.d.ts} +4 -4
  137. package/lib/examples/{LexicalExample.js → LexicalAgentExample.js} +66 -17
  138. package/lib/examples/{LexicalSidebarExample.d.ts → LexicalAgentSidebarExample.d.ts} +5 -5
  139. package/lib/examples/LexicalAgentSidebarExample.js +261 -0
  140. package/lib/examples/NotebookAgentExample.d.ts +9 -0
  141. package/lib/examples/NotebookAgentExample.js +192 -0
  142. package/lib/examples/{NotebookSidebarExample.d.ts → NotebookAgentSidebarExample.d.ts} +2 -2
  143. package/lib/examples/NotebookAgentSidebarExample.js +221 -0
  144. package/lib/examples/{DatalayerNotebookExample.d.ts → NotebookCollaborationExample.d.ts} +4 -4
  145. package/lib/examples/{DatalayerNotebookExample.js → NotebookCollaborationExample.js} +3 -3
  146. package/lib/examples/NotebookExample.d.ts +4 -7
  147. package/lib/examples/NotebookExample.js +14 -146
  148. package/lib/examples/components/AuthRequiredView.d.ts +6 -0
  149. package/lib/examples/components/AuthRequiredView.js +33 -0
  150. package/lib/examples/components/ExampleWrapper.d.ts +9 -3
  151. package/lib/examples/components/ExampleWrapper.js +45 -9
  152. package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.js +1 -1
  153. package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.js +1 -1
  154. package/lib/examples/{ag-ui → components}/haiku/index.d.ts +1 -1
  155. package/lib/examples/{ag-ui → components}/haiku/index.js +1 -1
  156. package/lib/examples/components/index.d.ts +3 -0
  157. package/lib/examples/components/index.js +4 -0
  158. package/lib/examples/{ag-ui → components}/weather/index.d.ts +1 -1
  159. package/lib/examples/{ag-ui → components}/weather/index.js +1 -1
  160. package/lib/examples/example-selector.d.ts +17 -4
  161. package/lib/examples/example-selector.js +108 -41
  162. package/lib/examples/index.d.ts +10 -6
  163. package/lib/examples/index.js +10 -6
  164. package/lib/examples/lexical/initial-content.json +6 -6
  165. package/lib/examples/main.js +257 -27
  166. package/lib/examples/utils/a2ui.d.ts +18 -0
  167. package/lib/examples/utils/a2ui.js +69 -0
  168. package/lib/examples/utils/a2uiMarkdownProvider.d.ts +7 -0
  169. package/lib/examples/utils/a2uiMarkdownProvider.js +9 -0
  170. package/lib/examples/utils/agentId.d.ts +18 -0
  171. package/lib/examples/utils/agentId.js +54 -0
  172. package/lib/examples/utils/agents/earthquake-detector.json +11 -11
  173. package/lib/examples/utils/agents/sales-forecaster.json +11 -11
  174. package/lib/examples/utils/agents/social-post-generator.json +11 -11
  175. package/lib/examples/utils/agents/stock-market.json +11 -11
  176. package/lib/examples/utils/examplesStore.js +82 -27
  177. package/lib/examples/utils/useExampleAgentRuntimesUrl.d.ts +5 -0
  178. package/lib/examples/utils/useExampleAgentRuntimesUrl.js +19 -0
  179. package/lib/hooks/index.d.ts +8 -8
  180. package/lib/hooks/index.js +7 -7
  181. package/lib/hooks/useA2A.d.ts +2 -3
  182. package/lib/hooks/useAIAgentsWebSocket.d.ts +43 -4
  183. package/lib/hooks/useAIAgentsWebSocket.js +153 -12
  184. package/lib/hooks/useAcp.d.ts +1 -2
  185. package/lib/hooks/useAgUi.d.ts +1 -1
  186. package/lib/hooks/{useAgents.d.ts → useAgentRuntimes.d.ts} +70 -4
  187. package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +237 -32
  188. package/lib/hooks/useAgentsCatalog.js +1 -1
  189. package/lib/hooks/useAgentsService.d.ts +2 -2
  190. package/lib/hooks/useAgentsService.js +7 -7
  191. package/lib/hooks/useCheckpoints.js +1 -1
  192. package/lib/hooks/useConfig.d.ts +4 -1
  193. package/lib/hooks/useConfig.js +10 -3
  194. package/lib/hooks/useContextSnapshot.d.ts +9 -4
  195. package/lib/hooks/useContextSnapshot.js +9 -37
  196. package/lib/hooks/useMonitoring.js +3 -0
  197. package/lib/hooks/useSandbox.d.ts +20 -8
  198. package/lib/hooks/useSandbox.js +105 -40
  199. package/lib/hooks/useSkills.d.ts +23 -5
  200. package/lib/hooks/useSkills.js +94 -39
  201. package/lib/hooks/useToolApprovals.d.ts +60 -36
  202. package/lib/hooks/useToolApprovals.js +318 -69
  203. package/lib/hooks/useVercelAI.d.ts +1 -1
  204. package/lib/index.d.ts +2 -1
  205. package/lib/index.js +1 -0
  206. package/lib/inference/index.d.ts +0 -1
  207. package/lib/middleware/index.d.ts +0 -1
  208. package/lib/protocols/AGUIAdapter.js +6 -0
  209. package/lib/protocols/VercelAIAdapter.d.ts +7 -0
  210. package/lib/protocols/VercelAIAdapter.js +59 -7
  211. package/lib/specs/agents/agents.d.ts +21 -4
  212. package/lib/specs/agents/agents.js +2879 -316
  213. package/lib/specs/agents/index.js +3 -1
  214. package/lib/specs/benchmarks.d.ts +20 -0
  215. package/lib/specs/benchmarks.js +205 -0
  216. package/lib/specs/envvars.js +27 -20
  217. package/lib/specs/evals.d.ts +10 -9
  218. package/lib/specs/evals.js +128 -88
  219. package/lib/specs/events.d.ts +3 -10
  220. package/lib/specs/events.js +127 -84
  221. package/lib/specs/frontendTools.js +2 -2
  222. package/lib/specs/guardrails.d.ts +0 -7
  223. package/lib/specs/guardrails.js +240 -159
  224. package/lib/specs/mcpServers.js +35 -6
  225. package/lib/specs/memory.d.ts +0 -2
  226. package/lib/specs/memory.js +4 -17
  227. package/lib/specs/models.d.ts +0 -2
  228. package/lib/specs/models.js +20 -15
  229. package/lib/specs/notifications.js +102 -18
  230. package/lib/specs/outputs.js +15 -9
  231. package/lib/specs/personas.d.ts +41 -0
  232. package/lib/specs/personas.js +168 -0
  233. package/lib/specs/skills.d.ts +1 -1
  234. package/lib/specs/skills.js +23 -23
  235. package/lib/specs/teams/index.js +3 -1
  236. package/lib/specs/teams/teams.js +468 -348
  237. package/lib/specs/tools.js +4 -4
  238. package/lib/specs/triggers.js +61 -11
  239. package/lib/stores/agentRuntimeStore.d.ts +208 -0
  240. package/lib/stores/agentRuntimeStore.js +650 -0
  241. package/lib/stores/conversationStore.js +2 -2
  242. package/lib/stores/index.d.ts +1 -1
  243. package/lib/stores/index.js +1 -1
  244. package/lib/tools/adapters/copilotkit/lexicalHooks.d.ts +1 -2
  245. package/lib/tools/adapters/copilotkit/lexicalHooks.js +1 -3
  246. package/lib/tools/adapters/copilotkit/notebookHooks.d.ts +1 -2
  247. package/lib/tools/adapters/copilotkit/notebookHooks.js +1 -3
  248. package/lib/tools/index.d.ts +0 -2
  249. package/lib/tools/index.js +0 -1
  250. package/lib/types/agents-lifecycle.d.ts +18 -0
  251. package/lib/types/agents.d.ts +6 -0
  252. package/lib/types/agentspecs.d.ts +54 -1
  253. package/lib/types/benchmarks.d.ts +43 -0
  254. package/lib/types/benchmarks.js +5 -0
  255. package/lib/types/chat.d.ts +325 -8
  256. package/lib/types/context.d.ts +27 -0
  257. package/lib/types/cost.d.ts +2 -2
  258. package/lib/types/evals.d.ts +26 -17
  259. package/lib/types/index.d.ts +3 -0
  260. package/lib/types/index.js +3 -0
  261. package/lib/types/mcp.d.ts +8 -0
  262. package/lib/types/models.d.ts +2 -2
  263. package/lib/types/personas.d.ts +25 -0
  264. package/lib/types/personas.js +5 -0
  265. package/lib/types/skills.d.ts +43 -1
  266. package/lib/types/stream.d.ts +110 -0
  267. package/lib/types/stream.js +36 -0
  268. package/lib/utils/utils.d.ts +9 -5
  269. package/lib/utils/utils.js +9 -5
  270. package/package.json +19 -11
  271. package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
  272. package/scripts/codegen/__pycache__/generate_benchmarks.cpython-313.pyc +0 -0
  273. package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
  274. package/scripts/codegen/__pycache__/generate_events.cpython-313.pyc +0 -0
  275. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
  276. package/scripts/codegen/generate_agents.py +187 -45
  277. package/scripts/codegen/generate_benchmarks.py +441 -0
  278. package/scripts/codegen/generate_evals.py +94 -16
  279. package/scripts/codegen/generate_events.py +35 -14
  280. package/scripts/codegen/generate_personas.py +319 -0
  281. package/scripts/codegen/generate_skills.py +9 -9
  282. package/scripts/sync-jupyter.sh +26 -7
  283. package/lib/api/tool-approvals.d.ts +0 -62
  284. package/lib/api/tool-approvals.js +0 -145
  285. package/lib/examples/AgentspecExample.js +0 -705
  286. package/lib/examples/LexicalSidebarExample.js +0 -163
  287. package/lib/examples/NotebookSidebarExample.js +0 -119
  288. package/lib/examples/NotebookSimpleExample.d.ts +0 -6
  289. package/lib/examples/NotebookSimpleExample.js +0 -22
  290. package/lib/examples/ag-ui/index.d.ts +0 -10
  291. package/lib/examples/ag-ui/index.js +0 -16
  292. package/lib/hooks/useAgentsRegistry.d.ts +0 -10
  293. package/lib/hooks/useAgentsRegistry.js +0 -20
  294. package/lib/stores/agentsStore.d.ts +0 -123
  295. package/lib/stores/agentsStore.js +0 -270
  296. /package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.d.ts +0 -0
  297. /package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.d.ts +0 -0
  298. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.d.ts +0 -0
  299. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.js +0 -0
@@ -10,6 +10,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  * @module chat/display/FloatingBrandButton
11
11
  */
12
12
  import { useState } from 'react';
13
+ import { createPortal } from 'react-dom';
13
14
  import { Box, IconButton, Tooltip, Text } from '@primer/react';
14
15
  import { CommentDiscussionIcon, XIcon } from '@primer/octicons-react';
15
16
  /**
@@ -26,7 +27,7 @@ export function FloatingBrandButton({ isOpen, onToggle, position = 'bottom-right
26
27
  'top-left': { top: 20, left: 20 },
27
28
  };
28
29
  const posStyle = positionStyles[position];
29
- return (_jsx(Box, { className: className, sx: {
30
+ const floatingButton = (_jsx(Box, { className: className, sx: {
30
31
  position: 'fixed',
31
32
  zIndex: 1000,
32
33
  ...posStyle,
@@ -84,5 +85,11 @@ export function FloatingBrandButton({ isOpen, onToggle, position = 'bottom-right
84
85
  },
85
86
  },
86
87
  } }))] }) }));
88
+ // Render in a body portal so fixed positioning stays anchored to the
89
+ // actual viewport edge even inside transformed containers.
90
+ if (typeof document !== 'undefined' && document.body) {
91
+ return createPortal(floatingButton, document.body);
92
+ }
93
+ return floatingButton;
87
94
  }
88
95
  export default FloatingBrandButton;
@@ -9,6 +9,8 @@ export interface ChatHeaderProps {
9
9
  description?: string;
10
10
  /** Current connection state */
11
11
  connectionState: ConnectionState;
12
+ /** Marks the runtime as actively executing for busy-state animation. */
13
+ runtimeBusy?: boolean;
12
14
  /** Callback when reconnect is clicked */
13
15
  onReconnect?: () => void;
14
16
  /** Callback when disconnect is clicked */
@@ -36,5 +38,5 @@ export interface ChatHeaderProps {
36
38
  * />
37
39
  * ```
38
40
  */
39
- export declare function ChatHeader({ title, description, connectionState, onReconnect, onDisconnect, onLogout, onCollapsePanel, }: ChatHeaderProps): import("react/jsx-runtime").JSX.Element;
41
+ export declare function ChatHeader({ title, description, connectionState, runtimeBusy, onReconnect, onDisconnect, onLogout, onCollapsePanel, }: ChatHeaderProps): import("react/jsx-runtime").JSX.Element;
40
42
  export default ChatHeader;
@@ -10,6 +10,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  */
11
11
  import { Text, IconButton, Button } from '@primer/react';
12
12
  import { Box } from '@datalayer/primer-addons';
13
+ import { KernelIndicator, KERNEL_STATE_VISUALS, } from '@datalayer/jupyter-react';
13
14
  import { SyncIcon, SignOutIcon, SidebarCollapseIcon, } from '@primer/octicons-react';
14
15
  /**
15
16
  * Chat header component with connection status indicator.
@@ -29,13 +30,17 @@ import { SyncIcon, SignOutIcon, SidebarCollapseIcon, } from '@primer/octicons-re
29
30
  * />
30
31
  * ```
31
32
  */
32
- export function ChatHeader({ title, description, connectionState, onReconnect, onDisconnect, onLogout, onCollapsePanel, }) {
33
- const colors = {
34
- connected: 'success.fg',
35
- connecting: 'attention.fg',
36
- disconnected: 'neutral.fg',
37
- error: 'danger.fg',
38
- };
33
+ export function ChatHeader({ title, description, connectionState, runtimeBusy = false, onReconnect, onDisconnect, onLogout, onCollapsePanel, }) {
34
+ const indicatorState = connectionState === 'connected'
35
+ ? runtimeBusy
36
+ ? 'connected-busy'
37
+ : 'connected-idle'
38
+ : connectionState === 'connecting'
39
+ ? 'connecting'
40
+ : connectionState === 'error'
41
+ ? 'connected-dead'
42
+ : 'disconnected';
43
+ const statusColor = KERNEL_STATE_VISUALS[indicatorState].color;
39
44
  const labels = {
40
45
  connected: 'Connected',
41
46
  connecting: 'Connecting...',
@@ -56,11 +61,9 @@ export function ChatHeader({ title, description, connectionState, onReconnect, o
56
61
  gap: 2,
57
62
  fontSize: 0,
58
63
  }, children: [_jsx(Box, { sx: {
59
- width: 8,
60
- height: 8,
61
- borderRadius: '50%',
62
- backgroundColor: colors[connectionState],
63
- } }), _jsx(Text, { sx: { color: colors[connectionState] }, children: labels[connectionState] }), (connectionState === 'disconnected' ||
64
+ display: 'inline-flex',
65
+ alignItems: 'center',
66
+ }, children: _jsx(KernelIndicator, { state: indicatorState }) }), _jsx(Text, { sx: { color: statusColor }, children: labels[connectionState] }), (connectionState === 'disconnected' ||
64
67
  connectionState === 'error') &&
65
68
  onReconnect && (_jsx(IconButton, { icon: SyncIcon, "aria-label": "Reconnect", size: "small", variant: "invisible", onClick: onReconnect }))] }), onDisconnect && connectionState === 'connected' && (_jsx(Button, { variant: "invisible", size: "small", onClick: onDisconnect, children: "Disconnect" })), onLogout && (_jsx(IconButton, { icon: SignOutIcon, "aria-label": "Logout", size: "small", variant: "invisible", onClick: onLogout })), onCollapsePanel && (_jsx(IconButton, { icon: SidebarCollapseIcon, "aria-label": "Collapse panel", size: "small", variant: "invisible", onClick: onCollapsePanel }))] }) }));
66
69
  }
@@ -8,20 +8,44 @@
8
8
  * @module chat/header/ChatHeaderBase
9
9
  */
10
10
  import { type ReactNode } from 'react';
11
+ import { type ExecutionState } from '@datalayer/jupyter-react';
12
+ import type { IKernelConnection } from '@jupyterlab/services/lib/kernel/kernel';
11
13
  import type { ChatViewMode, HeaderButtonsConfig } from '../../types/chat';
12
14
  import type { SandboxStatusData } from '../../types/context';
15
+ import type { SandboxWsStatus } from '../../types/sandbox';
16
+ type RuntimeStatus = SandboxStatusData | SandboxWsStatus;
17
+ export declare function toRuntimeExecutionState(runtimeStatus?: RuntimeStatus | null): ExecutionState | undefined;
13
18
  export interface ChatBaseHeaderProps {
14
19
  title?: string;
20
+ subtitle?: string;
15
21
  brandIcon?: ReactNode;
16
22
  headerContent?: ReactNode;
17
23
  headerActions?: ReactNode;
18
24
  showInformation?: boolean;
19
25
  onInformationClick?: () => void;
20
26
  padding: number;
21
- /** Sandbox status from the backend */
22
- sandboxStatus?: SandboxStatusData;
23
- /** Callback to interrupt sandbox code execution */
24
- onSandboxInterrupt: () => void;
27
+ /** Optional kernel indicator state override from notebook runtime. */
28
+ kernelIndicatorState?: ExecutionState;
29
+ /**
30
+ * Runtime status from agent-runtimes sandbox status stream.
31
+ * Uses the same execution-state model as KernelIndicator.
32
+ */
33
+ runtimeStatus?: RuntimeStatus | null;
34
+ /**
35
+ * Live kernel connection from the notebook runtime. When provided,
36
+ * the chat header renders the same `<KernelIndicator>` as the notebook
37
+ * toolbar — subscribing to the kernel's live signals so the colour and
38
+ * tooltip stay in sync with the notebook indicator.
39
+ */
40
+ kernel?: IKernelConnection | null;
41
+ /** Optional environment name shown in indicator details. */
42
+ kernelEnvironmentName?: string;
43
+ /** Optional CPU info shown in indicator details. */
44
+ kernelCpu?: string;
45
+ /** Optional memory info shown in indicator details. */
46
+ kernelMemory?: string;
47
+ /** Optional GPU info shown in indicator details. */
48
+ kernelGpu?: string;
25
49
  /** Header button configuration */
26
50
  headerButtons?: HeaderButtonsConfig;
27
51
  /** Current count of messages (used to conditionally show clear button) */
@@ -35,4 +59,5 @@ export interface ChatBaseHeaderProps {
35
59
  /** Callback when view mode changes */
36
60
  onChatViewModeChange?: (mode: ChatViewMode) => void;
37
61
  }
38
- export declare function ChatBaseHeader({ title, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, sandboxStatus, onSandboxInterrupt, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, }: ChatBaseHeaderProps): import("react/jsx-runtime").JSX.Element;
62
+ export declare function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, kernelIndicatorState, runtimeStatus, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, }: ChatBaseHeaderProps): import("react/jsx-runtime").JSX.Element;
63
+ export {};
@@ -1,12 +1,36 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Heading, IconButton, Truncate } from '@primer/react';
2
+ import { Heading, IconButton, Text, Truncate } from '@primer/react';
3
3
  import { Box } from '@datalayer/primer-addons';
4
- import { PlusIcon, TrashIcon, GearIcon, CircleIcon, SquareFillIcon, CommentDiscussionIcon, DeviceMobileIcon, SidebarExpandIcon, InfoIcon, } from '@primer/octicons-react';
4
+ import { KernelIndicator } from '@datalayer/jupyter-react';
5
+ import { PlusIcon, TrashIcon, GearIcon, CommentDiscussionIcon, DeviceMobileIcon, SidebarExpandIcon, InfoIcon, } from '@primer/octicons-react';
5
6
  import { AiAgentIcon } from '@datalayer/icons-react';
7
+ export function toRuntimeExecutionState(runtimeStatus) {
8
+ if (!runtimeStatus) {
9
+ return undefined;
10
+ }
11
+ if ('available' in runtimeStatus && runtimeStatus.available === false) {
12
+ return undefined;
13
+ }
14
+ if (runtimeStatus.variant === 'unavailable' ||
15
+ runtimeStatus.variant === 'error') {
16
+ return undefined;
17
+ }
18
+ if (runtimeStatus.sandbox_running === false) {
19
+ return 'disconnected';
20
+ }
21
+ if (runtimeStatus.is_executing === true) {
22
+ return 'connected-busy';
23
+ }
24
+ if (runtimeStatus.sandbox_running === true) {
25
+ return 'connected-idle';
26
+ }
27
+ return undefined;
28
+ }
6
29
  // ---------------------------------------------------------------------------
7
30
  // Component
8
31
  // ---------------------------------------------------------------------------
9
- export function ChatBaseHeader({ title, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, sandboxStatus, onSandboxInterrupt, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, }) {
32
+ export function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, kernelIndicatorState, runtimeStatus, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, }) {
33
+ const effectiveIndicatorState = kernelIndicatorState ?? toRuntimeExecutionState(runtimeStatus);
10
34
  return (_jsx(Box, { sx: {
11
35
  display: 'flex',
12
36
  flexDirection: 'column',
@@ -23,21 +47,22 @@ export function ChatBaseHeader({ title, brandIcon, headerContent, headerActions,
23
47
  gap: 2,
24
48
  minWidth: 0,
25
49
  flex: '1 1 auto',
26
- }, children: [brandIcon || _jsx(AiAgentIcon, { colored: true, size: 20 }), title && (_jsx(Heading, { as: "h3", sx: {
27
- fontSize: 2,
28
- fontWeight: 'semibold',
50
+ }, children: [brandIcon || _jsx(AiAgentIcon, { colored: true, size: 20 }), (title || subtitle) && (_jsxs(Box, { sx: {
51
+ display: 'flex',
52
+ flexDirection: 'column',
29
53
  minWidth: 0,
30
54
  maxWidth: '100%',
31
- }, children: _jsx(Truncate, { title: title, maxWidth: "28ch", children: title }) })), headerContent, showInformation && (_jsx(IconButton, { icon: InfoIcon, "aria-label": "Information", variant: "invisible", size: "small", onClick: onInformationClick }))] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, flexShrink: 0 }, children: [sandboxStatus?.available &&
32
- sandboxStatus?.sandbox_running &&
33
- (sandboxStatus.is_executing ? (_jsx(IconButton, { icon: SquareFillIcon, "aria-label": "Interrupt code execution", variant: "invisible", size: "small", sx: { color: 'danger.fg' }, onClick: onSandboxInterrupt })) : (_jsx(Box, { sx: {
34
- display: 'flex',
35
- alignItems: 'center',
36
- justifyContent: 'center',
37
- width: 28,
38
- height: 28,
39
- color: 'fg.subtle',
40
- }, title: "Code sandbox ready", children: _jsx(CircleIcon, { size: 12 }) }))), headerButtons?.showNewChat && (_jsx(IconButton, { icon: PlusIcon, "aria-label": "New chat", variant: "invisible", size: "small", onClick: onNewChat })), headerButtons?.showClear && messageCount > 0 && (_jsx(IconButton, { icon: TrashIcon, "aria-label": "Clear messages", variant: "invisible", size: "small", onClick: onClear })), headerButtons?.showSettings && (_jsx(IconButton, { icon: GearIcon, "aria-label": "Settings", variant: "invisible", size: "small", onClick: headerButtons.onSettings })), chatViewMode && onChatViewModeChange && (_jsx(Box, { sx: {
55
+ }, children: [title && (_jsx(Heading, { as: "h3", sx: {
56
+ fontSize: 2,
57
+ fontWeight: 'semibold',
58
+ minWidth: 0,
59
+ maxWidth: '100%',
60
+ }, children: _jsx(Truncate, { title: title, maxWidth: "28ch", children: title }) })), subtitle && (_jsx(Text, { sx: {
61
+ fontSize: 0,
62
+ color: 'fg.muted',
63
+ minWidth: 0,
64
+ maxWidth: '100%',
65
+ }, children: _jsx(Truncate, { title: subtitle, maxWidth: "40ch", children: subtitle }) }))] })), headerContent, showInformation && (_jsx(IconButton, { icon: InfoIcon, "aria-label": "Information", variant: "invisible", size: "small", onClick: onInformationClick }))] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, flexShrink: 0 }, children: [kernel ? (_jsx(KernelIndicator, { kernel: kernel, environmentName: kernelEnvironmentName, cpu: kernelCpu, memory: kernelMemory, gpu: kernelGpu, position: "sw", bordered: false })) : (_jsx(KernelIndicator, { state: effectiveIndicatorState ?? 'undefined', environmentName: kernelEnvironmentName, cpu: kernelCpu, memory: kernelMemory, gpu: kernelGpu, position: "sw", bordered: false })), headerButtons?.showNewChat && (_jsx(IconButton, { icon: PlusIcon, "aria-label": "New chat", variant: "invisible", size: "small", onClick: onNewChat })), headerButtons?.showClear && messageCount > 0 && (_jsx(IconButton, { icon: TrashIcon, "aria-label": "Clear messages", variant: "invisible", size: "small", onClick: onClear })), headerButtons?.showSettings && (_jsx(IconButton, { icon: GearIcon, "aria-label": "Settings", variant: "invisible", size: "small", onClick: headerButtons.onSettings })), chatViewMode && onChatViewModeChange && (_jsx(Box, { sx: {
41
66
  display: 'inline-flex',
42
67
  alignItems: 'center',
43
68
  bg: 'neutral.muted',
@@ -1,9 +1,12 @@
1
+ import type { McpToolsetsStatusResponse } from '../../types/mcp';
1
2
  export interface McpStatusIndicatorProps {
2
- /** API base URL (e.g. "http://127.0.0.1:8765"). Defaults to
3
- * the current host on non-localhost, or localhost:8765. */
3
+ /** Pre-fetched MCP status data pushed via WebSocket. When provided the
4
+ * component will NOT poll the REST endpoint. */
5
+ data?: McpToolsetsStatusResponse | null;
6
+ /** @deprecated — Only used when `data` is not provided. */
4
7
  apiBase?: string;
5
- /** Optional auth token for authenticated requests (e.g. K8s ingress). */
8
+ /** @deprecated Only used when `data` is not provided. */
6
9
  authToken?: string;
7
10
  }
8
- export declare function McpStatusIndicator({ apiBase, authToken, }: McpStatusIndicatorProps): import("react/jsx-runtime").JSX.Element | null;
11
+ export declare function McpStatusIndicator({ data: wsData, apiBase, authToken, }: McpStatusIndicatorProps): import("react/jsx-runtime").JSX.Element;
9
12
  export default McpStatusIndicator;
@@ -21,19 +21,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
21
21
  import { useEffect, useMemo } from 'react';
22
22
  import { Tooltip } from '@primer/react';
23
23
  import { Box } from '@datalayer/primer-addons';
24
- import { useQuery } from '@tanstack/react-query';
25
24
  import { MCP_STATUS_COLORS, MCP_STATUS_LABELS } from '../../types/mcp';
26
25
  /* ── Helpers ───────────────────────────────────────────── */
27
- function getApiBase(apiBase) {
28
- if (apiBase)
29
- return apiBase;
30
- if (typeof window === 'undefined')
31
- return '';
32
- const host = window.location.hostname;
33
- return host === 'localhost' || host === '127.0.0.1'
34
- ? 'http://127.0.0.1:8765'
35
- : '';
36
- }
37
26
  function deriveAggregate(servers) {
38
27
  if (!servers || servers.length === 0)
39
28
  return 'none';
@@ -47,7 +36,7 @@ function deriveAggregate(servers) {
47
36
  }
48
37
  function buildTooltipText(aggregate, servers) {
49
38
  if (aggregate === 'none')
50
- return MCP_STATUS_LABELS.none;
39
+ return 'No MCP Server defined';
51
40
  const lines = [MCP_STATUS_LABELS[aggregate]];
52
41
  for (const s of servers) {
53
42
  let detail = `• ${s.id}: ${s.status}`;
@@ -79,29 +68,15 @@ function useInjectKeyframes() {
79
68
  }, []);
80
69
  }
81
70
  /* ── Component ─────────────────────────────────────────── */
82
- export function McpStatusIndicator({ apiBase, authToken, }) {
71
+ export function McpStatusIndicator({ data: wsData, apiBase, authToken, }) {
83
72
  useInjectKeyframes();
84
- const { data } = useQuery({
85
- queryKey: ['mcp-toolsets-status', apiBase],
86
- queryFn: async () => {
87
- const base = getApiBase(apiBase);
88
- const headers = {};
89
- if (authToken) {
90
- headers['Authorization'] = `Bearer ${authToken}`;
91
- }
92
- const response = await fetch(`${base}/api/v1/configure/mcp-toolsets-status`, { headers });
93
- if (!response.ok)
94
- throw new Error('Failed to fetch MCP status');
95
- return response.json();
96
- },
97
- refetchInterval: 5000,
98
- });
99
- const servers = data?.servers ?? [];
73
+ // REST polling removed — data comes exclusively via WS `agent.snapshot`.
74
+ const effectiveData = wsData;
75
+ const servers = effectiveData?.servers ?? [];
100
76
  const aggregate = useMemo(() => deriveAggregate(servers), [servers]);
101
77
  const tooltipText = useMemo(() => buildTooltipText(aggregate, servers), [aggregate, servers]);
102
- // Hide when no servers are configured at all.
103
- if (aggregate === 'none')
104
- return null;
78
+ // Show a subtle gray dot when no MCP servers are configured.
79
+ // The tooltip tells the user none are defined.
105
80
  return (_jsx(Tooltip, { text: tooltipText, direction: "n", children: _jsx("button", { type: "button", "aria-label": tooltipText, style: {
106
81
  display: 'inline-flex',
107
82
  alignItems: 'center',
@@ -1,3 +1,4 @@
1
+ import type { SandboxWsStatus } from '../../types/sandbox';
1
2
  export interface SandboxStatusIndicatorProps {
2
3
  /** API base URL (e.g. "http://127.0.0.1:8765"). */
3
4
  apiBase?: string;
@@ -5,6 +6,8 @@ export interface SandboxStatusIndicatorProps {
5
6
  authToken?: string;
6
7
  /** Agent ID to scope sandbox status to a specific agent. */
7
8
  agentId?: string;
9
+ /** Optional status override to update indicator immediately from parent UI. */
10
+ statusOverride?: SandboxWsStatus | null;
8
11
  }
9
- export declare function SandboxStatusIndicator({ apiBase, authToken, agentId, }: SandboxStatusIndicatorProps): import("react/jsx-runtime").JSX.Element | null;
12
+ export declare function SandboxStatusIndicator({ apiBase, authToken, agentId, statusOverride, }: SandboxStatusIndicatorProps): import("react/jsx-runtime").JSX.Element;
10
13
  export default SandboxStatusIndicator;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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.
@@ -7,12 +7,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
7
7
  * SandboxStatusIndicator — Round coloured dot that shows the
8
8
  * real-time sandbox execution status via a WebSocket connection.
9
9
  *
10
- * Aggregate logic
10
+ * Aggregate logic (mapped to jupyter-react KernelIndicator states)
11
11
  * ───────────────
12
- * - variant === "unavailable" → hidden
13
- * - sandbox_running === false → "stopped" (gray)
14
- * - is_executing === false → "idle" (green)
15
- * - is_executing === true → "executing" (blue, pulsing)
12
+ * - variant === "unavailable" → connected-unknown
13
+ * - sandbox_running === false → disconnected
14
+ * - is_executing === false → connected-idle
15
+ * - is_executing === true → connected-busy (themed fade animation)
16
16
  *
17
17
  * The component connects to the `/configure/sandbox/ws` WebSocket
18
18
  * and receives status updates in real time. It can also send
@@ -21,9 +21,14 @@ import { jsx as _jsx } from "react/jsx-runtime";
21
21
  * @module chat/indicators/SandboxStatusIndicator
22
22
  */
23
23
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
24
- import { Tooltip } from '@primer/react';
25
24
  import { Box } from '@datalayer/primer-addons';
26
- import { SANDBOX_STATUS_COLORS, SANDBOX_STATUS_LABELS, } from '../../types/sandbox';
25
+ import { SANDBOX_STATUS_LABELS } from '../../types/sandbox';
26
+ const SANDBOX_INDICATOR_COLORS = {
27
+ unavailable: 'fg.muted',
28
+ stopped: 'fg.muted',
29
+ idle: 'success.fg',
30
+ executing: 'attention.fg',
31
+ };
27
32
  /* ── Helpers ───────────────────────────────────────────── */
28
33
  function getWsUrl(apiBase, authToken, agentId) {
29
34
  if (typeof window === 'undefined')
@@ -63,27 +68,39 @@ function deriveAggregate(status) {
63
68
  return 'executing';
64
69
  return 'idle';
65
70
  }
66
- const SANDBOX_PULSE_KEYFRAMES = `
67
- @keyframes sandbox-pulse {
68
- 0%, 100% { opacity: 1; }
69
- 50% { opacity: 0.4; }
70
- }
71
- `;
72
- function useInjectKeyframes() {
73
- useEffect(() => {
74
- const id = '__sandbox-pulse-keyframes__';
75
- if (document.getElementById(id))
76
- return;
77
- const style = document.createElement('style');
78
- style.id = id;
79
- style.textContent = SANDBOX_PULSE_KEYFRAMES;
80
- document.head.appendChild(style);
81
- }, []);
71
+ function renderSandboxGlyph(aggregate) {
72
+ return (_jsx(Box, { as: "span", sx: {
73
+ display: 'inline-block',
74
+ width: 10,
75
+ height: 10,
76
+ borderRadius: '50%',
77
+ bg: SANDBOX_INDICATOR_COLORS[aggregate],
78
+ ...(aggregate === 'executing' && {
79
+ animation: 'sandbox-busy-fade 1.2s ease-in-out infinite',
80
+ '@keyframes sandbox-busy-fade': {
81
+ '0%': {
82
+ opacity: 1,
83
+ transform: 'scale(1)',
84
+ filter: 'saturate(1)',
85
+ },
86
+ '50%': {
87
+ opacity: 0.45,
88
+ transform: 'scale(0.92)',
89
+ filter: 'saturate(0.75)',
90
+ },
91
+ '100%': {
92
+ opacity: 1,
93
+ transform: 'scale(1)',
94
+ filter: 'saturate(1)',
95
+ },
96
+ },
97
+ }),
98
+ } }));
82
99
  }
83
100
  /* ── Component ─────────────────────────────────────────── */
84
- export function SandboxStatusIndicator({ apiBase, authToken, agentId, }) {
85
- useInjectKeyframes();
101
+ export function SandboxStatusIndicator({ apiBase, authToken, agentId, statusOverride, }) {
86
102
  const [status, setStatus] = useState(null);
103
+ const [isOverlayOpen, setIsOverlayOpen] = useState(false);
87
104
  const wsRef = useRef(null);
88
105
  const reconnectTimerRef = useRef();
89
106
  const wsUrl = useMemo(() => getWsUrl(apiBase, authToken, agentId), [apiBase, authToken, agentId]);
@@ -138,38 +155,56 @@ export function SandboxStatusIndicator({ apiBase, authToken, agentId, }) {
138
155
  }
139
156
  }, []);
140
157
  // ---- Derived display values ----
141
- const aggregate = useMemo(() => deriveAggregate(status), [status]);
158
+ const effectiveStatus = statusOverride ?? status;
159
+ const aggregate = useMemo(() => deriveAggregate(effectiveStatus), [effectiveStatus]);
142
160
  const tooltipText = useMemo(() => {
143
- if (!status)
144
- return SANDBOX_STATUS_LABELS.unavailable;
161
+ if (!effectiveStatus)
162
+ return 'No Sandbox defined';
145
163
  const label = SANDBOX_STATUS_LABELS[aggregate];
146
- const variant = status.variant;
164
+ const variant = effectiveStatus.variant;
147
165
  return `${label} (${variant})`;
148
- }, [aggregate, status]);
149
- // Hide when sandbox is not available at all.
150
- if (aggregate === 'unavailable')
151
- return null;
152
- return (_jsx(Tooltip, { text: tooltipText, direction: "n", children: _jsx("button", { type: "button", "aria-label": tooltipText, onClick: aggregate === 'executing' ? sendInterrupt : undefined, style: {
153
- display: 'inline-flex',
154
- alignItems: 'center',
155
- justifyContent: 'center',
156
- width: 28,
157
- height: 28,
158
- padding: 0,
159
- border: 'none',
160
- background: 'none',
161
- cursor: aggregate === 'executing' ? 'pointer' : 'default',
162
- lineHeight: 0,
163
- }, children: _jsx(Box, { as: "span", sx: {
164
- display: 'inline-block',
165
- width: 12,
166
- height: 12,
167
- borderRadius: '50%',
168
- bg: SANDBOX_STATUS_COLORS[aggregate],
169
- flexShrink: 0,
170
- ...(aggregate === 'executing' && {
171
- animation: 'sandbox-pulse 1.5s ease-in-out infinite',
172
- }),
173
- } }) }) }));
166
+ }, [aggregate, effectiveStatus]);
167
+ // Show a subtle gray dot when sandbox is unavailable.
168
+ // The tooltip tells the user none is configured.
169
+ return (_jsxs(Box, { as: "span", sx: {
170
+ position: 'relative',
171
+ display: 'inline-flex',
172
+ alignItems: 'center',
173
+ justifyContent: 'center',
174
+ }, onMouseEnter: () => setIsOverlayOpen(true), onMouseLeave: () => setIsOverlayOpen(false), children: [_jsx("button", { type: "button", "aria-label": tooltipText, onClick: aggregate === 'executing' ? sendInterrupt : undefined, onFocus: () => setIsOverlayOpen(true), onBlur: () => setIsOverlayOpen(false), style: {
175
+ display: 'inline-flex',
176
+ alignItems: 'center',
177
+ justifyContent: 'center',
178
+ width: 28,
179
+ height: 28,
180
+ padding: 0,
181
+ border: 'none',
182
+ background: 'none',
183
+ outline: 'none',
184
+ boxShadow: 'none',
185
+ borderRadius: 0,
186
+ WebkitAppearance: 'none',
187
+ appearance: 'none',
188
+ cursor: aggregate === 'executing' ? 'pointer' : 'default',
189
+ lineHeight: 0,
190
+ }, children: _jsx(Box, { as: "span", sx: { display: 'inline-flex', alignItems: 'center', flexShrink: 0 }, children: renderSandboxGlyph(aggregate) }) }), isOverlayOpen && (_jsxs(Box, { role: "tooltip", sx: {
191
+ position: 'absolute',
192
+ right: 0,
193
+ top: 'calc(100% + 6px)',
194
+ minWidth: 220,
195
+ px: 2,
196
+ py: 2,
197
+ borderRadius: 2,
198
+ bg: 'canvas.overlay',
199
+ color: 'fg.default',
200
+ fontSize: 0,
201
+ lineHeight: 1.5,
202
+ boxShadow: 'shadow.medium',
203
+ border: '1px solid',
204
+ borderColor: 'border.default',
205
+ zIndex: 1000,
206
+ fontFamily: 'mono',
207
+ pointerEvents: 'none',
208
+ }, children: [_jsx(Box, { sx: { mb: 1, fontWeight: 600, fontFamily: 'normal' }, children: tooltipText }), effectiveStatus ? (_jsxs(_Fragment, { children: [_jsxs(Box, { children: [_jsxs(Box, { as: "span", sx: { fontWeight: 600 }, children: ["variant:", ' '] }), _jsx(Box, { as: "span", children: effectiveStatus.variant })] }), _jsxs(Box, { children: [_jsxs(Box, { as: "span", sx: { fontWeight: 600 }, children: ["sandbox_running:", ' '] }), _jsx(Box, { as: "span", children: String(effectiveStatus.sandbox_running) })] }), _jsxs(Box, { children: [_jsxs(Box, { as: "span", sx: { fontWeight: 600 }, children: ["is_executing:", ' '] }), _jsx(Box, { as: "span", children: String(effectiveStatus.is_executing) })] }), effectiveStatus.jupyter_url && (_jsxs(Box, { sx: { wordBreak: 'break-all' }, children: [_jsxs(Box, { as: "span", sx: { fontWeight: 600 }, children: ["jupyter_url:", ' '] }), _jsx(Box, { as: "span", children: effectiveStatus.jupyter_url })] })), effectiveStatus.error && (_jsx(Box, { sx: { mt: 1, color: 'danger.fg' }, children: effectiveStatus.error })), aggregate === 'executing' && (_jsx(Box, { sx: { mt: 1, fontFamily: 'normal', color: 'fg.muted' }, children: "Click to interrupt execution" }))] })) : (_jsx(Box, { sx: { fontFamily: 'normal', color: 'fg.muted' }, children: "No sandbox configured for this agent." }))] }))] }));
174
209
  }
175
210
  export default SandboxStatusIndicator;
@@ -0,0 +1,7 @@
1
+ export interface SkillsStatusIndicatorProps {
2
+ skillsCount: number;
3
+ enabledCount: number;
4
+ loading?: boolean;
5
+ }
6
+ export declare function SkillsStatusIndicator({ skillsCount, enabledCount, loading, }: SkillsStatusIndicatorProps): import("react/jsx-runtime").JSX.Element;
7
+ export default SkillsStatusIndicator;
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * SkillsStatusIndicator — Round status dot representing skill state.
8
+ *
9
+ * Aggregate logic:
10
+ * - no skills configured -> none (gray)
11
+ * - skills loading -> loading (amber, pulsing)
12
+ * - some enabled -> active (green)
13
+ * - none enabled -> inactive (gray)
14
+ */
15
+ import { useEffect, useMemo } from 'react';
16
+ import { Tooltip } from '@primer/react';
17
+ import { Box } from '@datalayer/primer-addons';
18
+ const SKILLS_STATUS_COLORS = {
19
+ none: '#8b949e',
20
+ loading: '#d29922',
21
+ inactive: '#8b949e',
22
+ active: '#3fb950',
23
+ };
24
+ const SKILLS_PULSE_KEYFRAMES = `
25
+ @keyframes skills-pulse {
26
+ 0%, 100% { opacity: 1; }
27
+ 50% { opacity: 0.4; }
28
+ }
29
+ `;
30
+ function useInjectKeyframes() {
31
+ useEffect(() => {
32
+ const id = '__skills-pulse-keyframes__';
33
+ if (document.getElementById(id))
34
+ return;
35
+ const style = document.createElement('style');
36
+ style.id = id;
37
+ style.textContent = SKILLS_PULSE_KEYFRAMES;
38
+ document.head.appendChild(style);
39
+ }, []);
40
+ }
41
+ function deriveAggregate(skillsCount, enabledCount, loading) {
42
+ if (skillsCount <= 0)
43
+ return 'none';
44
+ if (loading)
45
+ return 'loading';
46
+ if (enabledCount > 0)
47
+ return 'active';
48
+ return 'inactive';
49
+ }
50
+ function buildTooltip(aggregate, skillsCount, enabledCount) {
51
+ if (aggregate === 'none')
52
+ return 'No Skills defined';
53
+ if (aggregate === 'loading') {
54
+ return `Skills loading (${enabledCount}/${skillsCount} enabled)`;
55
+ }
56
+ if (aggregate === 'active') {
57
+ return `Skills active (${enabledCount}/${skillsCount} enabled)`;
58
+ }
59
+ return `Skills available (${enabledCount}/${skillsCount} enabled)`;
60
+ }
61
+ export function SkillsStatusIndicator({ skillsCount, enabledCount, loading = false, }) {
62
+ useInjectKeyframes();
63
+ const aggregate = useMemo(() => deriveAggregate(skillsCount, enabledCount, loading), [skillsCount, enabledCount, loading]);
64
+ const tooltipText = useMemo(() => buildTooltip(aggregate, skillsCount, enabledCount), [aggregate, skillsCount, enabledCount]);
65
+ return (_jsx(Tooltip, { text: tooltipText, direction: "n", children: _jsx("button", { type: "button", "aria-label": tooltipText, style: {
66
+ display: 'inline-flex',
67
+ alignItems: 'center',
68
+ justifyContent: 'center',
69
+ width: 28,
70
+ height: 28,
71
+ padding: 0,
72
+ border: 'none',
73
+ background: 'none',
74
+ cursor: 'default',
75
+ lineHeight: 0,
76
+ }, children: _jsx(Box, { as: "span", sx: {
77
+ display: 'inline-block',
78
+ width: 12,
79
+ height: 12,
80
+ borderRadius: '50%',
81
+ bg: SKILLS_STATUS_COLORS[aggregate],
82
+ flexShrink: 0,
83
+ ...(aggregate === 'loading' && {
84
+ animation: 'skills-pulse 1.5s ease-in-out infinite',
85
+ }),
86
+ } }) }) }));
87
+ }
88
+ export default SkillsStatusIndicator;
@@ -15,3 +15,4 @@ export { McpStatusIndicator, type McpStatusIndicatorProps, } from './McpStatusIn
15
15
  export type { McpServerStatus, McpAggregateStatus } from '../../types/mcp';
16
16
  export { SandboxStatusIndicator, type SandboxStatusIndicatorProps, } from './SandboxStatusIndicator';
17
17
  export type { SandboxAggregateStatus, SandboxWsStatus, } from '../../types/sandbox';
18
+ export { SkillsStatusIndicator, type SkillsStatusIndicatorProps, } from './SkillsStatusIndicator';
@@ -17,3 +17,4 @@
17
17
  */
18
18
  export { McpStatusIndicator, } from './McpStatusIndicator';
19
19
  export { SandboxStatusIndicator, } from './SandboxStatusIndicator';
20
+ export { SkillsStatusIndicator, } from './SkillsStatusIndicator';