@datalayer/agent-runtimes 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -119
- package/lib/App.js +1 -1
- package/lib/agents/AgentDetails.d.ts +22 -1
- package/lib/agents/AgentDetails.js +34 -47
- package/lib/api/index.d.ts +0 -1
- package/lib/api/index.js +4 -2
- package/lib/chat/Chat.d.ts +5 -104
- package/lib/chat/Chat.js +4 -4
- package/lib/chat/ChatFloating.d.ts +7 -140
- package/lib/chat/ChatFloating.js +2 -2
- package/lib/chat/ChatPopupStandalone.d.ts +8 -47
- package/lib/chat/ChatPopupStandalone.js +3 -3
- package/lib/chat/ChatSidebar.d.ts +4 -69
- package/lib/chat/ChatSidebar.js +2 -2
- package/lib/chat/ChatStandalone.d.ts +4 -54
- package/lib/chat/ChatStandalone.js +3 -3
- package/lib/chat/base/ChatBase.js +1118 -141
- package/lib/chat/header/ChatHeaderBase.d.ts +11 -6
- package/lib/chat/header/ChatHeaderBase.js +18 -16
- package/lib/chat/indicators/McpStatusIndicator.d.ts +7 -4
- package/lib/chat/indicators/McpStatusIndicator.js +7 -32
- package/lib/chat/indicators/SandboxStatusIndicator.d.ts +4 -1
- package/lib/chat/indicators/SandboxStatusIndicator.js +9 -9
- package/lib/chat/indicators/SkillsStatusIndicator.d.ts +7 -0
- package/lib/chat/indicators/SkillsStatusIndicator.js +88 -0
- package/lib/chat/indicators/index.d.ts +1 -0
- package/lib/chat/indicators/index.js +1 -0
- package/lib/chat/messages/ChatMessageList.d.ts +1 -1
- package/lib/chat/messages/ChatMessageList.js +110 -102
- package/lib/chat/prompt/InputFooter.d.ts +19 -6
- package/lib/chat/prompt/InputFooter.js +71 -18
- package/lib/chat/prompt/InputPrompt.d.ts +3 -1
- package/lib/chat/prompt/InputPrompt.js +4 -4
- package/lib/chat/prompt/InputPromptFooter.js +1 -1
- package/lib/chat/prompt/InputPromptLexical.d.ts +3 -1
- package/lib/chat/prompt/InputPromptLexical.js +12 -5
- package/lib/chat/prompt/InputPromptText.d.ts +3 -1
- package/lib/chat/prompt/InputPromptText.js +2 -2
- package/lib/chat/tools/ToolApprovalBanner.js +1 -1
- package/lib/chat/tools/ToolCallDisplay.d.ts +3 -1
- package/lib/chat/tools/ToolCallDisplay.js +2 -2
- package/lib/chat/usage/TokenUsageBar.js +20 -2
- package/lib/client/AgentRuntimesClientContext.d.ts +53 -0
- package/lib/client/AgentRuntimesClientContext.js +55 -0
- package/lib/client/AgentsMixin.d.ts +48 -19
- package/lib/client/AgentsMixin.js +115 -30
- package/lib/client/IAgentRuntimesClient.d.ts +215 -0
- package/lib/client/IAgentRuntimesClient.js +5 -0
- package/lib/client/SdkAgentRuntimesClient.d.ts +151 -0
- package/lib/client/SdkAgentRuntimesClient.js +134 -0
- package/lib/client/index.d.ts +4 -1
- package/lib/client/index.js +3 -1
- package/lib/components/NotificationEventCard.js +55 -26
- package/lib/components/OutputCard.js +21 -7
- package/lib/components/ToolApprovalCard.js +20 -2
- package/lib/config/AgentConfiguration.js +3 -3
- package/lib/context/ContextDistribution.d.ts +3 -1
- package/lib/context/ContextDistribution.js +8 -27
- package/lib/context/ContextInspector.d.ts +3 -1
- package/lib/context/ContextInspector.js +19 -67
- package/lib/context/ContextPanel.d.ts +3 -1
- package/lib/context/ContextPanel.js +104 -64
- package/lib/context/ContextUsage.d.ts +3 -1
- package/lib/context/ContextUsage.js +3 -3
- package/lib/context/CostTracker.d.ts +9 -3
- package/lib/context/CostTracker.js +26 -47
- package/lib/context/CostUsageChart.d.ts +12 -0
- package/lib/context/CostUsageChart.js +378 -0
- package/lib/context/GraphFlowChart.d.ts +16 -0
- package/lib/context/GraphFlowChart.js +182 -0
- package/lib/context/TokenUsageChart.d.ts +8 -1
- package/lib/context/TokenUsageChart.js +349 -211
- package/lib/context/TurnGraphChart.d.ts +39 -0
- package/lib/context/TurnGraphChart.js +538 -0
- package/lib/context/otelWsPool.d.ts +20 -0
- package/lib/context/otelWsPool.js +69 -0
- package/lib/examples/A2UiComponentGalleryExample.d.ts +0 -17
- package/lib/examples/A2UiComponentGalleryExample.js +315 -522
- package/lib/examples/A2UiContactCardExample.d.ts +0 -18
- package/lib/examples/A2UiContactCardExample.js +154 -411
- package/lib/examples/A2UiRestaurantExample.d.ts +0 -30
- package/lib/examples/A2UiRestaurantExample.js +114 -212
- package/lib/examples/A2UiViewerExample.d.ts +0 -18
- package/lib/examples/A2UiViewerExample.js +283 -532
- package/lib/examples/AgUiBackendToolRenderingExample.js +1 -1
- package/lib/examples/AgUiHaikuGenUiExample.d.ts +1 -1
- package/lib/examples/AgUiHaikuGenUiExample.js +1 -1
- package/lib/examples/AgentCheckpointsExample.js +14 -34
- package/lib/examples/AgentCodemodeExample.d.ts +4 -6
- package/lib/examples/AgentCodemodeExample.js +591 -175
- package/lib/examples/AgentEvalsExample.js +13 -23
- package/lib/examples/AgentGuardrailsExample.js +371 -71
- package/lib/examples/AgentHooksExample.d.ts +3 -0
- package/lib/examples/AgentHooksExample.js +104 -0
- package/lib/examples/AgentMCPExample.d.ts +3 -0
- package/lib/examples/AgentMCPExample.js +480 -0
- package/lib/examples/AgentMemoryExample.js +14 -24
- package/lib/examples/AgentMonitoringExample.js +261 -206
- package/lib/examples/AgentNotificationsExample.js +50 -24
- package/lib/examples/AgentOtelExample.js +2 -3
- package/lib/examples/AgentOutputsExample.d.ts +11 -6
- package/lib/examples/AgentOutputsExample.js +383 -88
- package/lib/examples/AgentParametersExample.d.ts +3 -0
- package/lib/examples/AgentParametersExample.js +246 -0
- package/lib/examples/AgentSandboxExample.d.ts +2 -2
- package/lib/examples/AgentSandboxExample.js +69 -47
- package/lib/examples/AgentSkillsExample.js +92 -106
- package/lib/examples/{AgentspecExample.js → AgentSpecsExample.js} +10 -21
- package/lib/examples/AgentSubagentsExample.d.ts +14 -0
- package/lib/examples/AgentSubagentsExample.js +228 -0
- package/lib/examples/AgentToolApprovalsExample.js +30 -493
- package/lib/examples/AgentTriggersExample.js +1067 -246
- package/lib/examples/ChatCustomExample.js +11 -24
- package/lib/examples/ChatExample.js +9 -34
- package/lib/examples/CopilotKitLexicalExample.js +2 -1
- package/lib/examples/CopilotKitNotebookExample.js +2 -1
- package/lib/examples/HomeExample.d.ts +15 -0
- package/lib/examples/HomeExample.js +77 -0
- package/lib/examples/Lexical2Example.js +4 -2
- package/lib/examples/{LexicalExample.d.ts → LexicalAgentExample.d.ts} +4 -4
- package/lib/examples/{LexicalExample.js → LexicalAgentExample.js} +65 -16
- package/lib/examples/{LexicalSidebarExample.d.ts → LexicalAgentSidebarExample.d.ts} +5 -5
- package/lib/examples/LexicalAgentSidebarExample.js +261 -0
- package/lib/examples/NotebookAgentExample.d.ts +9 -0
- package/lib/examples/NotebookAgentExample.js +192 -0
- package/lib/examples/{NotebookSidebarExample.d.ts → NotebookAgentSidebarExample.d.ts} +2 -2
- package/lib/examples/NotebookAgentSidebarExample.js +221 -0
- package/lib/examples/{DatalayerNotebookExample.d.ts → NotebookCollaborationExample.d.ts} +4 -4
- package/lib/examples/{DatalayerNotebookExample.js → NotebookCollaborationExample.js} +3 -3
- package/lib/examples/NotebookExample.d.ts +4 -7
- package/lib/examples/NotebookExample.js +14 -146
- package/lib/examples/components/AuthRequiredView.d.ts +6 -0
- package/lib/examples/components/AuthRequiredView.js +33 -0
- package/lib/examples/components/ErrorView.d.ts +14 -0
- package/lib/examples/components/ErrorView.js +20 -0
- package/lib/examples/components/ExampleWrapper.d.ts +7 -0
- package/lib/examples/components/ExampleWrapper.js +25 -6
- package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.js +1 -1
- package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.js +1 -1
- package/lib/examples/{ag-ui → components}/haiku/index.d.ts +1 -1
- package/lib/examples/{ag-ui → components}/haiku/index.js +1 -1
- package/lib/examples/components/index.d.ts +5 -0
- package/lib/examples/components/index.js +5 -0
- package/lib/examples/{ag-ui → components}/weather/index.d.ts +1 -1
- package/lib/examples/{ag-ui → components}/weather/index.js +1 -1
- package/lib/examples/example-selector.d.ts +17 -4
- package/lib/examples/example-selector.js +107 -41
- package/lib/examples/index.d.ts +9 -6
- package/lib/examples/index.js +9 -6
- package/lib/examples/main.d.ts +1 -0
- package/lib/examples/main.js +218 -27
- package/lib/examples/utils/a2ui.d.ts +18 -0
- package/lib/examples/utils/a2ui.js +69 -0
- package/lib/examples/utils/a2uiMarkdownProvider.d.ts +7 -0
- package/lib/examples/utils/a2uiMarkdownProvider.js +9 -0
- package/lib/examples/utils/agentId.d.ts +18 -0
- package/lib/examples/utils/agentId.js +54 -0
- package/lib/examples/utils/agents/earthquake-detector.json +11 -11
- package/lib/examples/utils/agents/sales-forecaster.json +11 -11
- package/lib/examples/utils/agents/social-post-generator.json +11 -11
- package/lib/examples/utils/agents/stock-market.json +11 -11
- package/lib/examples/utils/examplesStore.js +82 -27
- package/lib/hooks/index.d.ts +8 -8
- package/lib/hooks/index.js +7 -7
- package/lib/hooks/useA2A.d.ts +2 -3
- package/lib/hooks/useAIAgentsWebSocket.d.ts +43 -4
- package/lib/hooks/useAIAgentsWebSocket.js +118 -12
- package/lib/hooks/useAcp.d.ts +1 -2
- package/lib/hooks/useAgUi.d.ts +1 -1
- package/lib/hooks/{useAgents.d.ts → useAgentRuntimes.d.ts} +39 -2
- package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +125 -15
- package/lib/hooks/useAgentsCatalog.js +1 -1
- package/lib/hooks/useAgentsService.d.ts +2 -2
- package/lib/hooks/useAgentsService.js +7 -7
- package/lib/hooks/useCheckpoints.js +1 -1
- package/lib/hooks/useConfig.d.ts +4 -1
- package/lib/hooks/useConfig.js +10 -3
- package/lib/hooks/useContextSnapshot.d.ts +9 -4
- package/lib/hooks/useContextSnapshot.js +9 -37
- package/lib/hooks/useMonitoring.js +3 -0
- package/lib/hooks/useSandbox.d.ts +20 -8
- package/lib/hooks/useSandbox.js +105 -40
- package/lib/hooks/useSkills.d.ts +23 -5
- package/lib/hooks/useSkills.js +94 -39
- package/lib/hooks/useToolApprovals.d.ts +60 -36
- package/lib/hooks/useToolApprovals.js +318 -69
- package/lib/hooks/useVercelAI.d.ts +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +1 -0
- package/lib/inference/index.d.ts +0 -1
- package/lib/middleware/index.d.ts +0 -1
- package/lib/protocols/AGUIAdapter.js +6 -0
- package/lib/protocols/VercelAIAdapter.d.ts +9 -0
- package/lib/protocols/VercelAIAdapter.js +144 -26
- package/lib/shims/json5.d.ts +4 -0
- package/lib/shims/json5.js +8 -0
- package/lib/specs/agents/agents.d.ts +10 -0
- package/lib/specs/agents/agents.js +752 -24
- package/lib/specs/envvars.d.ts +1 -0
- package/lib/specs/envvars.js +11 -0
- package/lib/specs/events.d.ts +1 -0
- package/lib/specs/events.js +1 -0
- package/lib/specs/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- package/lib/specs/personas.d.ts +41 -0
- package/lib/specs/personas.js +168 -0
- package/lib/specs/skills.d.ts +2 -1
- package/lib/specs/skills.js +23 -5
- package/lib/specs/tools.js +3 -0
- package/lib/stores/agentRuntimeStore.d.ts +204 -0
- package/lib/stores/agentRuntimeStore.js +636 -0
- package/lib/stores/index.d.ts +1 -1
- package/lib/stores/index.js +1 -1
- package/lib/tools/adapters/copilotkit/lexicalHooks.d.ts +1 -2
- package/lib/tools/adapters/copilotkit/lexicalHooks.js +1 -3
- package/lib/tools/adapters/copilotkit/notebookHooks.d.ts +1 -2
- package/lib/tools/adapters/copilotkit/notebookHooks.js +1 -3
- package/lib/tools/index.d.ts +0 -2
- package/lib/tools/index.js +0 -1
- package/lib/types/agentspecs.d.ts +50 -1
- package/lib/types/chat.d.ts +309 -8
- package/lib/types/context.d.ts +27 -0
- package/lib/types/cost.d.ts +2 -2
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.js +2 -0
- package/lib/types/mcp.d.ts +8 -0
- package/lib/types/models.d.ts +2 -2
- package/lib/types/personas.d.ts +25 -0
- package/lib/types/personas.js +5 -0
- package/lib/types/skills.d.ts +43 -1
- package/lib/types/stream.d.ts +110 -0
- package/lib/types/stream.js +36 -0
- package/lib/types/tools.d.ts +2 -0
- package/lib/utils/utils.d.ts +9 -5
- package/lib/utils/utils.js +9 -5
- package/package.json +13 -9
- package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_events.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
- package/scripts/codegen/generate_agents.py +106 -7
- package/scripts/codegen/generate_events.py +47 -17
- package/scripts/codegen/generate_personas.py +319 -0
- package/scripts/codegen/generate_skills.py +9 -9
- package/scripts/codegen/generate_tools.py +20 -0
- package/scripts/sync-jupyter.sh +26 -7
- package/style/primer-primitives.css +1 -6
- package/lib/api/tool-approvals.d.ts +0 -62
- package/lib/api/tool-approvals.js +0 -145
- package/lib/examples/LexicalSidebarExample.js +0 -163
- package/lib/examples/NotebookSidebarExample.js +0 -119
- package/lib/examples/NotebookSimpleExample.d.ts +0 -6
- package/lib/examples/NotebookSimpleExample.js +0 -22
- package/lib/examples/ag-ui/index.d.ts +0 -10
- package/lib/examples/ag-ui/index.js +0 -16
- package/lib/hooks/useAgentsRegistry.d.ts +0 -10
- package/lib/hooks/useAgentsRegistry.js +0 -20
- package/lib/stores/agentsStore.d.ts +0 -123
- package/lib/stores/agentsStore.js +0 -270
- package/scripts/codegen/__pycache__/generate_envvars.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_guardrails.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_mcp_servers.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_memory.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_models.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_notifications.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_outputs.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_skills.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_teams.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_tools.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_triggers.cpython-313.pyc +0 -0
- /package/lib/examples/{AgentspecExample.d.ts → AgentSpecsExample.d.ts} +0 -0
- /package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.d.ts +0 -0
- /package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.d.ts +0 -0
- /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.d.ts +0 -0
- /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.js +0 -0
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* Unified hook for managing
|
|
6
|
+
* Unified hook for managing agent runtimes.
|
|
7
7
|
*
|
|
8
8
|
* Combines agent lifecycle management (ephemeral/durable),
|
|
9
9
|
* runtime catalog (React Query CRUD), lifecycle/catalog stores,
|
|
10
|
-
*
|
|
10
|
+
* the AI Agents REST API, and the agent-runtime WebSocket stream.
|
|
11
11
|
*
|
|
12
|
-
* @module hooks/
|
|
12
|
+
* @module hooks/useAgentRuntimes
|
|
13
13
|
*/
|
|
14
14
|
import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
|
|
15
15
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
@@ -17,7 +17,8 @@ import { create } from 'zustand';
|
|
|
17
17
|
import { persist, createJSONStorage } from 'zustand/middleware';
|
|
18
18
|
import { useCoreStore, useDatalayer } from '@datalayer/core';
|
|
19
19
|
import { useIAMStore } from '@datalayer/core/lib/state';
|
|
20
|
-
import {
|
|
20
|
+
import { agentRuntimeStore, useAgentRuntimeStore, useAgentRuntimeConnection, useAgentRuntimeStatus, useAgentRuntimeError, useAgentRuntimeIsLaunching, } from '../stores/agentRuntimeStore';
|
|
21
|
+
import { parseAgentStreamMessage, } from '../types/stream';
|
|
21
22
|
import { DEFAULT_AGENT_CONFIG } from '../types/config';
|
|
22
23
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
23
24
|
// Constants
|
|
@@ -103,18 +104,18 @@ function toAgentRuntimeData(raw) {
|
|
|
103
104
|
* });
|
|
104
105
|
* ```
|
|
105
106
|
*/
|
|
106
|
-
export function
|
|
107
|
+
export function useAgentRuntimes(options = {}) {
|
|
107
108
|
const { agentSpecId, agentConfig, autoCreateAgent = true, autoStart = false, agentSpec, } = options;
|
|
108
109
|
// Base store state
|
|
109
|
-
const runtime =
|
|
110
|
-
const baseStatus =
|
|
111
|
-
const storeError =
|
|
112
|
-
const isLaunching =
|
|
110
|
+
const runtime = useAgentRuntimeConnection();
|
|
111
|
+
const baseStatus = useAgentRuntimeStatus();
|
|
112
|
+
const storeError = useAgentRuntimeError();
|
|
113
|
+
const isLaunching = useAgentRuntimeIsLaunching();
|
|
113
114
|
// Store actions
|
|
114
|
-
const storeLaunchAgent =
|
|
115
|
-
const storeConnectAgent =
|
|
116
|
-
const storeCreateAgent =
|
|
117
|
-
const storeDisconnect =
|
|
115
|
+
const storeLaunchAgent = useAgentRuntimeStore(state => state.launchAgent);
|
|
116
|
+
const storeConnectAgent = useAgentRuntimeStore(state => state.connectAgent);
|
|
117
|
+
const storeCreateAgent = useAgentRuntimeStore(state => state.createAgent);
|
|
118
|
+
const storeDisconnect = useAgentRuntimeStore(state => state.disconnect);
|
|
118
119
|
// Lifecycle local state
|
|
119
120
|
const [lifecycleStatus, setLifecycleStatus] = useState('idle');
|
|
120
121
|
const [lifecycleError, setLifecycleError] = useState(null);
|
|
@@ -408,7 +409,7 @@ export function useAgents(options = {}) {
|
|
|
408
409
|
* The backend returns active runtimes from the operator **plus** paused
|
|
409
410
|
* runtimes synthesised from Solr checkpoint records (with ``status="paused"``).
|
|
410
411
|
*/
|
|
411
|
-
export function
|
|
412
|
+
export function useAgentRuntimesQuery() {
|
|
412
413
|
const { configuration } = useCoreStore();
|
|
413
414
|
const { requestDatalayer } = useDatalayer({ notifyOnError: false });
|
|
414
415
|
const { user } = useIAMStore();
|
|
@@ -607,7 +608,7 @@ export const useAgentLifecycleStore = create()(persist((set, get) => ({
|
|
|
607
608
|
* Consolidated runtime list and mutations.
|
|
608
609
|
*/
|
|
609
610
|
export function useAgentsRuntimes() {
|
|
610
|
-
const runtimesQuery =
|
|
611
|
+
const runtimesQuery = useAgentRuntimesQuery();
|
|
611
612
|
const createRuntimeMutation = useCreateAgentRuntime();
|
|
612
613
|
const deleteRuntimeMutation = useDeleteAgentRuntime();
|
|
613
614
|
const refreshRuntimes = useRefreshAgentRuntimes();
|
|
@@ -631,3 +632,112 @@ export function useAgentsRuntimes() {
|
|
|
631
632
|
deleteRuntimeMutation,
|
|
632
633
|
]);
|
|
633
634
|
}
|
|
635
|
+
const DEFAULT_WS_PATH = '/api/v1/tool-approvals/ws';
|
|
636
|
+
const DEFAULT_RECONNECT_DELAY_MS = 3_000;
|
|
637
|
+
/**
|
|
638
|
+
* Connect to the agent-runtime monitoring WebSocket.
|
|
639
|
+
*
|
|
640
|
+
* The hook writes all incoming data into the `useAgentRuntimeStore` Zustand
|
|
641
|
+
* store. Components that need approvals, MCP status, context snapshots, or
|
|
642
|
+
* full-context data simply read from the store.
|
|
643
|
+
*
|
|
644
|
+
* Mount this hook **once** near the top of your component tree (e.g. in
|
|
645
|
+
* the example root or in `ChatBase`). All other components read from the
|
|
646
|
+
* store — no extra WebSocket connections needed.
|
|
647
|
+
*/
|
|
648
|
+
export function useAgentRuntimeWebSocket(options) {
|
|
649
|
+
const { enabled = true, baseUrl, authToken, agentId, autoReconnect = true, reconnectDelayMs = DEFAULT_RECONNECT_DELAY_MS, maxReconnectAttempts, } = options;
|
|
650
|
+
const onMessageRef = useRef(options.onMessage);
|
|
651
|
+
onMessageRef.current = options.onMessage;
|
|
652
|
+
useEffect(() => {
|
|
653
|
+
if (!enabled || !baseUrl) {
|
|
654
|
+
agentRuntimeStore.getState().setWsState('closed');
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
let disposed = false;
|
|
658
|
+
let reconnectAttempts = 0;
|
|
659
|
+
let reconnectTimer = null;
|
|
660
|
+
function buildWsUrl() {
|
|
661
|
+
const httpUrl = `${baseUrl}${DEFAULT_WS_PATH}`;
|
|
662
|
+
const url = new URL(httpUrl.replace(/^http/, 'ws'));
|
|
663
|
+
if (authToken) {
|
|
664
|
+
url.searchParams.set('token', authToken);
|
|
665
|
+
}
|
|
666
|
+
if (agentId) {
|
|
667
|
+
url.searchParams.set('agent_id', agentId);
|
|
668
|
+
}
|
|
669
|
+
return url.toString();
|
|
670
|
+
}
|
|
671
|
+
function connect() {
|
|
672
|
+
if (disposed)
|
|
673
|
+
return;
|
|
674
|
+
const wsUrl = buildWsUrl();
|
|
675
|
+
agentRuntimeStore.getState().setWsState('connecting');
|
|
676
|
+
const ws = new WebSocket(wsUrl);
|
|
677
|
+
agentRuntimeStore.getState().setWs(ws, agentId);
|
|
678
|
+
ws.onopen = () => {
|
|
679
|
+
reconnectAttempts = 0;
|
|
680
|
+
agentRuntimeStore.getState().setWsState('connected');
|
|
681
|
+
};
|
|
682
|
+
ws.onmessage = (ev) => {
|
|
683
|
+
let raw;
|
|
684
|
+
try {
|
|
685
|
+
raw = JSON.parse(String(ev.data));
|
|
686
|
+
}
|
|
687
|
+
catch {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
const parsed = parseAgentStreamMessage(raw);
|
|
691
|
+
onMessageRef.current?.({
|
|
692
|
+
type: parsed?.type,
|
|
693
|
+
payload: parsed?.payload,
|
|
694
|
+
raw,
|
|
695
|
+
});
|
|
696
|
+
if (!parsed)
|
|
697
|
+
return;
|
|
698
|
+
const state = agentRuntimeStore.getState();
|
|
699
|
+
if (parsed.type === 'agent.snapshot') {
|
|
700
|
+
state.applySnapshot(parsed.payload);
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
ws.onclose = () => {
|
|
705
|
+
agentRuntimeStore.getState().setWs(null, agentId);
|
|
706
|
+
agentRuntimeStore.getState().setWsState('closed');
|
|
707
|
+
if (disposed || !autoReconnect)
|
|
708
|
+
return;
|
|
709
|
+
reconnectAttempts += 1;
|
|
710
|
+
if (typeof maxReconnectAttempts === 'number' &&
|
|
711
|
+
reconnectAttempts > maxReconnectAttempts) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
const delay = typeof reconnectDelayMs === 'function'
|
|
715
|
+
? reconnectDelayMs(reconnectAttempts)
|
|
716
|
+
: reconnectDelayMs;
|
|
717
|
+
reconnectTimer = setTimeout(connect, Math.max(0, delay));
|
|
718
|
+
};
|
|
719
|
+
ws.onerror = () => {
|
|
720
|
+
if (ws.readyState === WebSocket.CONNECTING ||
|
|
721
|
+
ws.readyState === WebSocket.OPEN) {
|
|
722
|
+
ws.close();
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
connect();
|
|
727
|
+
return () => {
|
|
728
|
+
disposed = true;
|
|
729
|
+
if (reconnectTimer)
|
|
730
|
+
clearTimeout(reconnectTimer);
|
|
731
|
+
agentRuntimeStore.getState().setWs(null, agentId);
|
|
732
|
+
agentRuntimeStore.getState().resetWs();
|
|
733
|
+
};
|
|
734
|
+
}, [
|
|
735
|
+
enabled,
|
|
736
|
+
baseUrl,
|
|
737
|
+
authToken,
|
|
738
|
+
agentId,
|
|
739
|
+
autoReconnect,
|
|
740
|
+
reconnectDelayMs,
|
|
741
|
+
maxReconnectAttempts,
|
|
742
|
+
]);
|
|
743
|
+
}
|
|
@@ -4,7 +4,7 @@ export type RequestOptions = {
|
|
|
4
4
|
};
|
|
5
5
|
export type RoomType = 'notebook_persist' | 'notebook_memory' | 'doc_memory';
|
|
6
6
|
/**
|
|
7
|
-
* @deprecated Use
|
|
7
|
+
* @deprecated Use useAgentRuntimes instead
|
|
8
8
|
*/
|
|
9
9
|
export declare const useAgentsService: (baseUrlOverride?: string) => {
|
|
10
10
|
createAgent: (documentId: string, documentType: RoomType, ingress?: string, token?: string, kernelId?: string, { signal, baseUrl }?: RequestOptions) => Promise<any>;
|
|
@@ -17,6 +17,6 @@ export declare const useAgentsService: (baseUrlOverride?: string) => {
|
|
|
17
17
|
* Get the notebook AI agent if any.
|
|
18
18
|
*
|
|
19
19
|
* This performs a periodic liveness check and keeps the local store in sync.
|
|
20
|
-
* @deprecated Use
|
|
20
|
+
* @deprecated Use useAgentRuntimes instead
|
|
21
21
|
*/
|
|
22
22
|
export declare function useNotebookAgents(notebookId: string): import("..").AgentRegistryEntry | undefined;
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
import { useEffect } from 'react';
|
|
12
12
|
import { useCoreStore, useDatalayer } from '@datalayer/core';
|
|
13
13
|
import { URLExt } from '@jupyterlab/coreutils';
|
|
14
|
-
import {
|
|
14
|
+
import { useAgentRuntimeStore } from '../stores/agentRuntimeStore';
|
|
15
15
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
16
16
|
// Agents Service REST API hook.
|
|
17
17
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
18
18
|
/**
|
|
19
|
-
* @deprecated Use
|
|
19
|
+
* @deprecated Use useAgentRuntimes instead
|
|
20
20
|
*/
|
|
21
21
|
export const useAgentsService = (baseUrlOverride = 'api/ai-agents/v1') => {
|
|
22
22
|
const { configuration } = useCoreStore();
|
|
@@ -86,14 +86,14 @@ export const useAgentsService = (baseUrlOverride = 'api/ai-agents/v1') => {
|
|
|
86
86
|
* Get the notebook AI agent if any.
|
|
87
87
|
*
|
|
88
88
|
* This performs a periodic liveness check and keeps the local store in sync.
|
|
89
|
-
* @deprecated Use
|
|
89
|
+
* @deprecated Use useAgentRuntimes instead
|
|
90
90
|
*/
|
|
91
91
|
export function useNotebookAgents(notebookId) {
|
|
92
92
|
const { getAgent } = useAgentsService();
|
|
93
|
-
const agents =
|
|
94
|
-
const upsertAgent =
|
|
95
|
-
const deleteAgent =
|
|
96
|
-
const getAgentById =
|
|
93
|
+
const agents = useAgentRuntimeStore(state => state.agents);
|
|
94
|
+
const upsertAgent = useAgentRuntimeStore(state => state.upsertAgent);
|
|
95
|
+
const deleteAgent = useAgentRuntimeStore(state => state.deleteAgent);
|
|
96
|
+
const getAgentById = useAgentRuntimeStore(state => state.getAgentById);
|
|
97
97
|
useEffect(() => {
|
|
98
98
|
let abortController;
|
|
99
99
|
const refreshAIAgent = async () => {
|
|
@@ -14,7 +14,7 @@ import { useMemo, useState, useCallback } from 'react';
|
|
|
14
14
|
import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
|
|
15
15
|
import { useIAMStore } from '@datalayer/core/lib/state';
|
|
16
16
|
import { useCoreStore, useDatalayer } from '@datalayer/core';
|
|
17
|
-
import { agentQueryKeys, AGENT_QUERY_OPTIONS } from './
|
|
17
|
+
import { agentQueryKeys, AGENT_QUERY_OPTIONS } from './useAgentRuntimes';
|
|
18
18
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
19
19
|
// Query hooks
|
|
20
20
|
// ═══════════════════════════════════════════════════════════════════════════
|
package/lib/hooks/useConfig.d.ts
CHANGED
|
@@ -3,9 +3,12 @@ import type { RemoteConfig } from '../types/config';
|
|
|
3
3
|
* Hook to safely use query when QueryClient is available.
|
|
4
4
|
* Returns a mock result if no QueryClientProvider is present.
|
|
5
5
|
*/
|
|
6
|
-
export declare function useConfig(enabled: boolean, configEndpoint?: string, authToken?: string): import("@tanstack/query-core").QueryObserverRefetchErrorResult<RemoteConfig, Error> | import("@tanstack/query-core").QueryObserverSuccessResult<RemoteConfig, Error> | import("@tanstack/query-core").QueryObserverLoadingErrorResult<RemoteConfig, Error> | import("@tanstack/query-core").QueryObserverPendingResult<RemoteConfig, Error> | import("@tanstack/query-core").QueryObserverPlaceholderResult<RemoteConfig, Error> | {
|
|
6
|
+
export declare function useConfig(enabled: boolean, configEndpoint?: string, authToken?: string, agentId?: string): import("@tanstack/query-core").QueryObserverRefetchErrorResult<RemoteConfig, Error> | import("@tanstack/query-core").QueryObserverSuccessResult<RemoteConfig, Error> | import("@tanstack/query-core").QueryObserverLoadingErrorResult<RemoteConfig, Error> | import("@tanstack/query-core").QueryObserverPendingResult<RemoteConfig, Error> | import("@tanstack/query-core").QueryObserverPlaceholderResult<RemoteConfig, Error> | {
|
|
7
7
|
data: undefined;
|
|
8
8
|
isLoading: boolean;
|
|
9
9
|
isError: boolean;
|
|
10
10
|
error: null;
|
|
11
|
+
refetch: () => Promise<{
|
|
12
|
+
data: undefined;
|
|
13
|
+
}>;
|
|
11
14
|
};
|
package/lib/hooks/useConfig.js
CHANGED
|
@@ -9,7 +9,7 @@ import { requestAPI } from '../api/handler';
|
|
|
9
9
|
* Hook to safely use query when QueryClient is available.
|
|
10
10
|
* Returns a mock result if no QueryClientProvider is present.
|
|
11
11
|
*/
|
|
12
|
-
export function useConfig(enabled, configEndpoint, authToken) {
|
|
12
|
+
export function useConfig(enabled, configEndpoint, authToken, agentId) {
|
|
13
13
|
const queryClient = useContext(QueryClientContext);
|
|
14
14
|
if (!queryClient) {
|
|
15
15
|
return {
|
|
@@ -17,6 +17,7 @@ export function useConfig(enabled, configEndpoint, authToken) {
|
|
|
17
17
|
isLoading: false,
|
|
18
18
|
isError: false,
|
|
19
19
|
error: null,
|
|
20
|
+
refetch: () => Promise.resolve({ data: undefined }),
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
@@ -30,7 +31,13 @@ export function useConfig(enabled, configEndpoint, authToken) {
|
|
|
30
31
|
if (authToken) {
|
|
31
32
|
headers['Authorization'] = `Bearer ${authToken}`;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
let endpoint = configEndpoint;
|
|
35
|
+
if (agentId) {
|
|
36
|
+
const url = new URL(configEndpoint, window.location.origin);
|
|
37
|
+
url.searchParams.set('agent_id', agentId);
|
|
38
|
+
endpoint = url.toString();
|
|
39
|
+
}
|
|
40
|
+
const response = await fetch(endpoint, { headers });
|
|
34
41
|
if (!response.ok) {
|
|
35
42
|
throw new Error(`Config fetch failed: ${response.statusText}`);
|
|
36
43
|
}
|
|
@@ -39,7 +46,7 @@ export function useConfig(enabled, configEndpoint, authToken) {
|
|
|
39
46
|
// Otherwise use Jupyter requestAPI.
|
|
40
47
|
return requestAPI('configure');
|
|
41
48
|
},
|
|
42
|
-
queryKey: ['models', configEndpoint || 'jupyter'],
|
|
49
|
+
queryKey: ['models', configEndpoint || 'jupyter', agentId || 'global'],
|
|
43
50
|
enabled,
|
|
44
51
|
retry: 1,
|
|
45
52
|
});
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { ContextSnapshotData } from '../types/context';
|
|
2
2
|
/**
|
|
3
|
-
* Hook
|
|
4
|
-
*
|
|
3
|
+
* Hook that previously polled agent context-snapshot from the backend.
|
|
4
|
+
*
|
|
5
|
+
* The REST endpoint has been removed — context snapshot data is now
|
|
6
|
+
* delivered via the WebSocket stream (`agent.snapshot` messages).
|
|
7
|
+
* This hook is kept as a no-op so existing call-sites compile without
|
|
8
|
+
* changes; the token-usage bar simply stays hidden until a WS-based
|
|
9
|
+
* replacement is wired in.
|
|
5
10
|
*/
|
|
6
|
-
export declare function useContextSnapshot(
|
|
7
|
-
data: undefined;
|
|
11
|
+
export declare function useContextSnapshot(_enabled: boolean, _configEndpoint?: string, _agentId?: string, _authToken?: string): {
|
|
12
|
+
data: ContextSnapshotData | undefined;
|
|
8
13
|
isLoading: boolean;
|
|
9
14
|
isError: boolean;
|
|
10
15
|
error: null;
|
|
@@ -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
|
|
10
|
-
*
|
|
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(
|
|
13
|
-
|
|
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
|
-
*
|
|
4
|
-
*
|
|
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
|
|
7
|
-
data: undefined;
|
|
8
|
-
|
|
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
|
};
|
package/lib/hooks/useSandbox.js
CHANGED
|
@@ -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 {
|
|
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
|
-
*
|
|
10
|
-
*
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
}
|
package/lib/hooks/useSkills.d.ts
CHANGED
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SkillInfo } from '../types/skills';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
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(
|
|
6
|
-
data:
|
|
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:
|
|
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';
|