@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
@@ -2,43 +2,15 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- import { useContext } from 'react';
6
- import { useQuery, QueryClientContext } from '@tanstack/react-query';
7
- import { getApiBaseFromConfig } from '../utils';
8
5
  /**
9
- * Hook to poll agent context-snapshot from the backend.
10
- * Returns cumulative token usage (input/output breakdown) tracked by the agent server.
6
+ * Hook that previously polled agent context-snapshot from the backend.
7
+ *
8
+ * The REST endpoint has been removed — context snapshot data is now
9
+ * delivered via the WebSocket stream (`agent.snapshot` messages).
10
+ * This hook is kept as a no-op so existing call-sites compile without
11
+ * changes; the token-usage bar simply stays hidden until a WS-based
12
+ * replacement is wired in.
11
13
  */
12
- export function useContextSnapshot(enabled, configEndpoint, agentId, authToken) {
13
- const queryClient = useContext(QueryClientContext);
14
- if (!queryClient) {
15
- return { data: undefined, isLoading: false, isError: false, error: null };
16
- }
17
- const snapshotUrl = configEndpoint && agentId
18
- ? `${getApiBaseFromConfig(configEndpoint)}/configure/agents/${encodeURIComponent(agentId)}/context-snapshot`
19
- : undefined;
20
- // eslint-disable-next-line react-hooks/rules-of-hooks
21
- return useQuery({
22
- queryKey: ['context-snapshot-header', agentId, snapshotUrl],
23
- queryFn: async () => {
24
- if (!snapshotUrl) {
25
- throw new Error('No context-snapshot URL available');
26
- }
27
- const headers = { 'Content-Type': 'application/json' };
28
- if (authToken) {
29
- headers['Authorization'] = `Bearer ${authToken}`;
30
- }
31
- const response = await fetch(snapshotUrl, { headers });
32
- if (!response.ok) {
33
- throw new Error(`Context snapshot fetch failed: ${response.statusText}`);
34
- }
35
- return response.json();
36
- },
37
- enabled: enabled && !!snapshotUrl,
38
- // Poll every 10s, but stop after an error (e.g. runtime terminated).
39
- refetchInterval: query => (query.state.status === 'error' ? false : 10_000),
40
- refetchOnMount: 'always',
41
- staleTime: 0,
42
- retry: 1,
43
- });
14
+ export function useContextSnapshot(_enabled, _configEndpoint, _agentId, _authToken) {
15
+ return { data: undefined, isLoading: false, isError: false, error: null };
44
16
  }
@@ -25,6 +25,9 @@ export function toMetricValue(row) {
25
25
  return 0;
26
26
  }
27
27
  export async function fetchOtelMetricRows({ metric, serviceName, runUrl, apiKey, limit = 500, }) {
28
+ if (!runUrl || !apiKey) {
29
+ return [];
30
+ }
28
31
  const client = createOtelClient({
29
32
  baseUrl: runUrl,
30
33
  token: apiKey,
@@ -1,12 +1,24 @@
1
1
  import type { SandboxStatusData } from '../types/context';
2
2
  /**
3
- * Hook to poll sandbox execution status from the backend.
4
- * Returns whether a sandbox is available and if code is currently executing.
3
+ * Subscribe to the sandbox execution status via the
4
+ * `/api/v1/configure/sandbox/ws` WebSocket.
5
+ *
6
+ * This hook replaces the previous REST-polling implementation — the server
7
+ * now pushes status updates in real time and accepts interrupt requests over
8
+ * the same connection.
9
+ *
10
+ * @param enabled Whether to open the WebSocket connection.
11
+ * @param configEndpoint Base `configEndpoint` used by other chat hooks
12
+ * (e.g. `http://localhost:8765/api/v1/configure/config`).
13
+ * @param authToken Optional bearer token (passed as `?token=` query param
14
+ * because browsers cannot set headers on WebSocket).
15
+ * @param agentId Optional agent id; the backend returns an
16
+ * agent-scoped status when provided.
17
+ * @returns `{ data, interrupt }` where `data` is the latest status (or
18
+ * `undefined` until the first message) and `interrupt()` sends an
19
+ * `{ action: 'interrupt' }` message over the same WebSocket.
5
20
  */
6
- export declare function useSandbox(enabled: boolean, configEndpoint?: string, authToken?: string): import("@tanstack/query-core").QueryObserverRefetchErrorResult<SandboxStatusData, Error> | import("@tanstack/query-core").QueryObserverSuccessResult<SandboxStatusData, Error> | import("@tanstack/query-core").QueryObserverLoadingErrorResult<SandboxStatusData, Error> | import("@tanstack/query-core").QueryObserverPendingResult<SandboxStatusData, Error> | import("@tanstack/query-core").QueryObserverPlaceholderResult<SandboxStatusData, Error> | {
7
- data: undefined;
8
- isLoading: boolean;
9
- isError: boolean;
10
- error: null;
11
- refetch: () => Promise<any>;
21
+ export declare function useSandbox(enabled: boolean, configEndpoint?: string, authToken?: string, agentId?: string): {
22
+ data: SandboxStatusData | undefined;
23
+ interrupt: () => void;
12
24
  };
@@ -2,48 +2,113 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- import { useContext } from 'react';
6
- import { useQuery, QueryClientContext } from '@tanstack/react-query';
5
+ import { useCallback, useEffect, useRef, useState } from 'react';
7
6
  import { getApiBaseFromConfig } from '../utils';
7
+ const RECONNECT_BASE_MS = 1000;
8
+ const RECONNECT_MAX_MS = 30000;
9
+ function isRetryableCloseCode(code) {
10
+ // Avoid reconnect loops for policy/auth/protocol closures.
11
+ const nonRetryable = new Set([1002, 1003, 1007, 1008, 4401, 4403]);
12
+ return !nonRetryable.has(code);
13
+ }
8
14
  /**
9
- * Hook to poll sandbox execution status from the backend.
10
- * Returns whether a sandbox is available and if code is currently executing.
15
+ * Subscribe to the sandbox execution status via the
16
+ * `/api/v1/configure/sandbox/ws` WebSocket.
17
+ *
18
+ * This hook replaces the previous REST-polling implementation — the server
19
+ * now pushes status updates in real time and accepts interrupt requests over
20
+ * the same connection.
21
+ *
22
+ * @param enabled Whether to open the WebSocket connection.
23
+ * @param configEndpoint Base `configEndpoint` used by other chat hooks
24
+ * (e.g. `http://localhost:8765/api/v1/configure/config`).
25
+ * @param authToken Optional bearer token (passed as `?token=` query param
26
+ * because browsers cannot set headers on WebSocket).
27
+ * @param agentId Optional agent id; the backend returns an
28
+ * agent-scoped status when provided.
29
+ * @returns `{ data, interrupt }` where `data` is the latest status (or
30
+ * `undefined` until the first message) and `interrupt()` sends an
31
+ * `{ action: 'interrupt' }` message over the same WebSocket.
11
32
  */
12
- export function useSandbox(enabled, configEndpoint, authToken) {
13
- const queryClient = useContext(QueryClientContext);
14
- if (!queryClient) {
15
- return {
16
- data: undefined,
17
- isLoading: false,
18
- isError: false,
19
- error: null,
20
- refetch: () => Promise.resolve({}),
33
+ export function useSandbox(enabled, configEndpoint, authToken, agentId) {
34
+ const [data, setData] = useState(undefined);
35
+ const wsRef = useRef(null);
36
+ useEffect(() => {
37
+ if (!enabled || !configEndpoint) {
38
+ setData(undefined);
39
+ return;
40
+ }
41
+ const apiBase = getApiBaseFromConfig(configEndpoint);
42
+ if (!apiBase)
43
+ return;
44
+ const wsBase = apiBase.replace(/^http/, 'ws');
45
+ const params = [];
46
+ if (agentId) {
47
+ params.push(`agent_id=${encodeURIComponent(agentId)}`);
48
+ }
49
+ if (authToken) {
50
+ // WebSocket API cannot set custom headers — pass token via query param.
51
+ params.push(`token=${encodeURIComponent(authToken)}`);
52
+ }
53
+ const url = `${wsBase}/configure/sandbox/ws${params.length ? `?${params.join('&')}` : ''}`;
54
+ let disposed = false;
55
+ let reconnectTimer;
56
+ let reconnectAttempts = 0;
57
+ const scheduleReconnect = (code) => {
58
+ if (disposed || !isRetryableCloseCode(code))
59
+ return;
60
+ const delay = Math.min(RECONNECT_BASE_MS * 2 ** reconnectAttempts, RECONNECT_MAX_MS);
61
+ reconnectAttempts += 1;
62
+ reconnectTimer = setTimeout(connect, delay);
63
+ };
64
+ const connect = () => {
65
+ if (disposed)
66
+ return;
67
+ const ws = new WebSocket(url);
68
+ wsRef.current = ws;
69
+ ws.onopen = () => {
70
+ reconnectAttempts = 0;
71
+ };
72
+ ws.onmessage = event => {
73
+ try {
74
+ const msg = JSON.parse(event.data);
75
+ if (msg && msg.action === 'interrupt')
76
+ return;
77
+ const variant = typeof msg?.variant === 'string' ? msg.variant : '';
78
+ setData({
79
+ available: variant !== 'unavailable' && variant !== 'error',
80
+ sandbox_running: Boolean(msg?.sandbox_running),
81
+ is_executing: Boolean(msg?.is_executing),
82
+ variant,
83
+ });
84
+ }
85
+ catch {
86
+ // Ignore malformed messages.
87
+ }
88
+ };
89
+ ws.onclose = event => {
90
+ wsRef.current = null;
91
+ scheduleReconnect(event.code);
92
+ };
93
+ ws.onerror = () => {
94
+ ws.close();
95
+ };
96
+ };
97
+ connect();
98
+ return () => {
99
+ disposed = true;
100
+ if (reconnectTimer)
101
+ clearTimeout(reconnectTimer);
102
+ wsRef.current?.close();
103
+ wsRef.current = null;
104
+ setData(undefined);
21
105
  };
22
- }
23
- const statusUrl = configEndpoint
24
- ? `${getApiBaseFromConfig(configEndpoint)}/configure/sandbox-status`
25
- : undefined;
26
- // eslint-disable-next-line react-hooks/rules-of-hooks
27
- return useQuery({
28
- queryKey: ['sandbox-status', statusUrl],
29
- queryFn: async () => {
30
- if (!statusUrl) {
31
- throw new Error('No sandbox status URL available');
32
- }
33
- const headers = { 'Content-Type': 'application/json' };
34
- if (authToken) {
35
- headers['Authorization'] = `Bearer ${authToken}`;
36
- }
37
- const response = await fetch(statusUrl, { headers });
38
- if (!response.ok) {
39
- throw new Error(`Sandbox status fetch failed: ${response.statusText}`);
40
- }
41
- return response.json();
42
- },
43
- enabled: enabled && !!statusUrl,
44
- refetchInterval: query => (query.state.status === 'error' ? false : 2_000),
45
- refetchOnMount: 'always',
46
- staleTime: 0,
47
- retry: 1,
48
- });
106
+ }, [enabled, configEndpoint, authToken, agentId]);
107
+ const interrupt = useCallback(() => {
108
+ const ws = wsRef.current;
109
+ if (ws && ws.readyState === WebSocket.OPEN) {
110
+ ws.send(JSON.stringify({ action: 'interrupt' }));
111
+ }
112
+ }, []);
113
+ return { data, interrupt };
49
114
  }
@@ -1,13 +1,31 @@
1
- import type { SkillsResponse } from '../types/skills';
1
+ import type { SkillInfo } from '../types/skills';
2
2
  /**
3
- * Hook to fetch available skills from backend.
3
+ * Derive the list of skills from the WS-pushed `codemodeStatus`.
4
+ *
5
+ * The server-side SkillsArea pushes per-skill status (`available`,
6
+ * `enabled`, `loaded`) via the monitoring WebSocket inside the
7
+ * `codemodeStatus.skills` array. This hook reads from the Zustand
8
+ * store — no REST call is made.
4
9
  */
5
- export declare function useSkills(enabled: boolean, baseEndpoint?: string, authToken?: string): import("@tanstack/query-core").QueryObserverRefetchErrorResult<SkillsResponse, Error> | import("@tanstack/query-core").QueryObserverSuccessResult<SkillsResponse, Error> | import("@tanstack/query-core").QueryObserverLoadingErrorResult<SkillsResponse, Error> | import("@tanstack/query-core").QueryObserverPendingResult<SkillsResponse, Error> | import("@tanstack/query-core").QueryObserverPlaceholderResult<SkillsResponse, Error> | {
6
- data: undefined;
10
+ export declare function useSkills(_enabled: boolean, _baseEndpoint?: string, _authToken?: string): {
11
+ data: {
12
+ skills: SkillInfo[];
13
+ total: number;
14
+ } | undefined;
7
15
  isLoading: boolean;
8
16
  isError: boolean;
9
17
  error: null;
10
18
  refetch: () => Promise<{
11
- data: undefined;
19
+ data: {
20
+ skills: SkillInfo[];
21
+ total: number;
22
+ } | undefined;
12
23
  }>;
13
24
  };
25
+ export declare function useSkillActions(agentId?: string): {
26
+ enableSkill: (skillId: string) => boolean;
27
+ disableSkill: (skillId: string) => boolean;
28
+ approveSkill: (skillId: string) => boolean;
29
+ unapproveSkill: (skillId: string) => boolean;
30
+ };
31
+ export { useAgentRuntimeLoadedSkills } from '../stores';
@@ -2,45 +2,100 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- import { useContext } from 'react';
6
- import { useQuery, QueryClientContext } from '@tanstack/react-query';
5
+ import { useCallback, useMemo } from 'react';
6
+ import { agentRuntimeStore, useAgentRuntimeCodemodeStatus } from '../stores';
7
+ function parseSkillStatus(value) {
8
+ if (value === 'available' || value === 'enabled' || value === 'loaded') {
9
+ return value;
10
+ }
11
+ return 'available';
12
+ }
13
+ // ---------------------------------------------------------------------------
14
+ // Skills from WS snapshot
15
+ // ---------------------------------------------------------------------------
7
16
  /**
8
- * Hook to fetch available skills from backend.
17
+ * Derive the list of skills from the WS-pushed `codemodeStatus`.
18
+ *
19
+ * The server-side SkillsArea pushes per-skill status (`available`,
20
+ * `enabled`, `loaded`) via the monitoring WebSocket inside the
21
+ * `codemodeStatus.skills` array. This hook reads from the Zustand
22
+ * store — no REST call is made.
9
23
  */
10
- export function useSkills(enabled, baseEndpoint, authToken) {
11
- const queryClient = useContext(QueryClientContext);
12
- if (!queryClient) {
13
- return {
14
- data: undefined,
15
- isLoading: false,
16
- isError: false,
17
- error: null,
18
- refetch: () => Promise.resolve({ data: undefined }),
19
- };
20
- }
21
- // eslint-disable-next-line react-hooks/rules-of-hooks
22
- return useQuery({
23
- queryFn: async () => {
24
- if (!baseEndpoint) {
25
- return { skills: [], total: 0 };
26
- }
27
- // Derive skills endpoint from config endpoint.
28
- const skillsEndpoint = baseEndpoint.replace('/configure', '/skills');
29
- const headers = {
30
- 'Content-Type': 'application/json',
31
- };
32
- if (authToken) {
33
- headers['Authorization'] = `Bearer ${authToken}`;
34
- }
35
- const response = await fetch(skillsEndpoint, { headers });
36
- if (!response.ok) {
37
- throw new Error(`Skills fetch failed: ${response.statusText}`);
38
- }
39
- return response.json();
40
- },
41
- queryKey: ['skills', baseEndpoint || 'jupyter'],
42
- enabled,
43
- staleTime: 5 * 60 * 1000,
44
- retry: 1,
45
- });
24
+ export function useSkills(_enabled, _baseEndpoint, _authToken) {
25
+ const codemodeStatus = useAgentRuntimeCodemodeStatus();
26
+ const data = useMemo(() => {
27
+ if (!codemodeStatus) {
28
+ return undefined;
29
+ }
30
+ const skills = (codemodeStatus.skills ?? []).map(s => ({
31
+ id: s.id ?? s.name,
32
+ name: s.name,
33
+ description: s.description,
34
+ tags: s.tags,
35
+ has_scripts: s.has_scripts,
36
+ has_resources: s.has_resources,
37
+ status: parseSkillStatus(s.status),
38
+ approved: s.approved !== false,
39
+ skill_definition: s.skill_definition ?? null,
40
+ source_variant: s.source_variant,
41
+ module: s.module,
42
+ package: s.package,
43
+ method: s.method,
44
+ path: s.path,
45
+ }));
46
+ return { skills, total: skills.length };
47
+ }, [codemodeStatus]);
48
+ return {
49
+ data,
50
+ isLoading: false,
51
+ isError: false,
52
+ error: null,
53
+ refetch: () => Promise.resolve({ data }),
54
+ };
55
+ }
56
+ // ---------------------------------------------------------------------------
57
+ // Skill enable / disable via WebSocket
58
+ // ---------------------------------------------------------------------------
59
+ export function useSkillActions(agentId) {
60
+ const enableSkill = useCallback((skillId) => {
61
+ const ok = agentRuntimeStore
62
+ .getState()
63
+ .sendRawMessage({ type: 'skill_enable', skillId }, agentId);
64
+ if (!ok) {
65
+ console.warn('[useSkillActions] skill_enable dropped: websocket not ready');
66
+ }
67
+ return ok;
68
+ }, [agentId]);
69
+ const disableSkill = useCallback((skillId) => {
70
+ const ok = agentRuntimeStore
71
+ .getState()
72
+ .sendRawMessage({ type: 'skill_disable', skillId }, agentId);
73
+ if (!ok) {
74
+ console.warn('[useSkillActions] skill_disable dropped: websocket not ready');
75
+ }
76
+ return ok;
77
+ }, [agentId]);
78
+ const approveSkill = useCallback((skillId) => {
79
+ const ok = agentRuntimeStore
80
+ .getState()
81
+ .sendRawMessage({ type: 'skill_approve', skillId }, agentId);
82
+ if (!ok) {
83
+ console.warn('[useSkillActions] skill_approve dropped: websocket not ready');
84
+ }
85
+ return ok;
86
+ }, [agentId]);
87
+ const unapproveSkill = useCallback((skillId) => {
88
+ const ok = agentRuntimeStore
89
+ .getState()
90
+ .sendRawMessage({ type: 'skill_unapprove', skillId }, agentId);
91
+ if (!ok) {
92
+ console.warn('[useSkillActions] skill_unapprove dropped: websocket not ready');
93
+ }
94
+ return ok;
95
+ }, [agentId]);
96
+ return { enableSkill, disableSkill, approveSkill, unapproveSkill };
46
97
  }
98
+ // ---------------------------------------------------------------------------
99
+ // Loaded skills (kept for backward compat with AgentSkillsExample sidebar)
100
+ // ---------------------------------------------------------------------------
101
+ export { useAgentRuntimeLoadedSkills } from '../stores';
@@ -1,45 +1,69 @@
1
1
  import type { ToolApprovalFilters } from '../types/tool-approvals';
2
- export declare function useToolApprovalsQuery(filters?: ToolApprovalFilters): import("@tanstack/react-query").UseQueryResult<import("../types").ToolApproval[], Error>;
2
+ /**
3
+ * Normalised approval record. Both snake_case (server-native) and
4
+ * camelCase (TypeScript-idiomatic) keys are present so existing UI
5
+ * consumers keep working regardless of which naming they read.
6
+ */
7
+ export type ApprovalRecord = {
8
+ id: string;
9
+ agent_id: string;
10
+ agentId: string;
11
+ pod_name: string;
12
+ podName: string;
13
+ tool_name: string;
14
+ toolName: string;
15
+ tool_call_id?: string;
16
+ toolCallId?: string;
17
+ tool_args: Record<string, unknown>;
18
+ toolArgs: Record<string, unknown>;
19
+ status: string;
20
+ note?: string | null;
21
+ created_at: string;
22
+ createdAt: string;
23
+ updated_at?: string;
24
+ updatedAt?: string;
25
+ read?: boolean;
26
+ };
27
+ interface ApprovalsQueryData {
28
+ approvals: ApprovalRecord[];
29
+ total: number;
30
+ }
31
+ interface MutationResult {
32
+ isPending: boolean;
33
+ mutate: (vars: {
34
+ id: string;
35
+ note?: string;
36
+ }) => void;
37
+ mutateAsync: (vars: {
38
+ id: string;
39
+ note?: string;
40
+ }) => Promise<void>;
41
+ }
42
+ export declare function useToolApprovalsQuery(filters?: ToolApprovalFilters): import("@tanstack/react-query").UseQueryResult<ApprovalsQueryData, Error>;
3
43
  export declare function usePendingApprovalCount(): import("@tanstack/react-query").UseQueryResult<{
4
44
  count: number;
5
45
  }, Error>;
6
- export declare function useApproveToolRequest(): import("@tanstack/react-query").UseMutationResult<void, Error, {
7
- id: string;
8
- note?: string;
9
- }, unknown>;
10
- export declare function useRejectToolRequest(): import("@tanstack/react-query").UseMutationResult<void, Error, {
11
- id: string;
12
- note?: string;
13
- }, unknown>;
14
- export declare function useMarkToolApprovalRead(): import("@tanstack/react-query").UseMutationResult<void, Error, {
15
- id: string;
16
- }, unknown>;
17
- export declare function useMarkToolApprovalUnread(): import("@tanstack/react-query").UseMutationResult<void, Error, {
18
- id: string;
19
- }, unknown>;
20
- export declare function useDeleteToolApproval(): import("@tanstack/react-query").UseMutationResult<void, Error, {
21
- id: string;
22
- }, unknown>;
46
+ export declare function useApproveToolRequest(): MutationResult;
47
+ export declare function useRejectToolRequest(): MutationResult;
48
+ export declare function useMarkToolApprovalRead(): MutationResult;
49
+ export declare function useMarkToolApprovalUnread(): MutationResult;
50
+ /**
51
+ * Delete a tool approval.
52
+ *
53
+ * Sends a ``tool_approval_delete`` message over the shared websocket.
54
+ * The local cache is updated only after the server emits
55
+ * ``tool_approval_deleted``.
56
+ */
57
+ export declare function useDeleteToolApproval(): MutationResult;
23
58
  export declare function useToolApprovals(filters?: ToolApprovalFilters): {
24
- approvalsQuery: import("@tanstack/react-query").UseQueryResult<import("../types").ToolApproval[], Error>;
59
+ approvalsQuery: import("@tanstack/react-query").UseQueryResult<ApprovalsQueryData, Error>;
25
60
  pendingCountQuery: import("@tanstack/react-query").UseQueryResult<{
26
61
  count: number;
27
62
  }, Error>;
28
- approve: import("@tanstack/react-query").UseMutationResult<void, Error, {
29
- id: string;
30
- note?: string;
31
- }, unknown>;
32
- reject: import("@tanstack/react-query").UseMutationResult<void, Error, {
33
- id: string;
34
- note?: string;
35
- }, unknown>;
36
- markRead: import("@tanstack/react-query").UseMutationResult<void, Error, {
37
- id: string;
38
- }, unknown>;
39
- markUnread: import("@tanstack/react-query").UseMutationResult<void, Error, {
40
- id: string;
41
- }, unknown>;
42
- remove: import("@tanstack/react-query").UseMutationResult<void, Error, {
43
- id: string;
44
- }, unknown>;
63
+ approve: MutationResult;
64
+ reject: MutationResult;
65
+ markRead: MutationResult;
66
+ markUnread: MutationResult;
67
+ remove: MutationResult;
45
68
  };
69
+ export {};