@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
package/lib/hooks/useSkills.js
CHANGED
|
@@ -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 {
|
|
6
|
-
import {
|
|
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
|
-
*
|
|
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(
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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():
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export declare function
|
|
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<
|
|
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:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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 {};
|
|
@@ -3,99 +3,348 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* Agent tool approval hooks.
|
|
6
|
+
* Agent tool approval hooks (WebSocket-only).
|
|
7
|
+
*
|
|
8
|
+
* All tool-approval interactions flow over the AI Agents websocket stream.
|
|
9
|
+
* There are no REST endpoints — the server publishes approval snapshots and
|
|
10
|
+
* `tool_approval_*` events, and the client sends `tool_approval_decision`
|
|
11
|
+
* messages to approve/reject pending requests.
|
|
12
|
+
*
|
|
13
|
+
* Consumers use the familiar React Query-style API below. Internally the
|
|
14
|
+
* hooks:
|
|
15
|
+
* - subscribe to the shared AI Agents WS once per component tree
|
|
16
|
+
* - seed/update the `['tool-approvals', filters]` query cache from events
|
|
17
|
+
* - send decision messages over the same socket for mutations
|
|
7
18
|
*
|
|
8
19
|
* @module hooks/useToolApprovals
|
|
9
20
|
*/
|
|
10
|
-
import { useMemo } from 'react';
|
|
11
|
-
import { useQuery,
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
21
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
22
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
23
|
+
import { useAIAgentsWebSocket } from './useAIAgentsWebSocket';
|
|
24
|
+
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
25
|
+
const APPROVALS_ROOT_KEY = ['tool-approvals'];
|
|
26
|
+
function str(value) {
|
|
27
|
+
return typeof value === 'string' ? value : value == null ? '' : String(value);
|
|
28
|
+
}
|
|
29
|
+
function normalizeApproval(raw) {
|
|
30
|
+
if (!raw || typeof raw !== 'object')
|
|
31
|
+
return null;
|
|
32
|
+
const rec = raw;
|
|
33
|
+
const id = typeof rec.id === 'string' ? rec.id : undefined;
|
|
34
|
+
if (!id)
|
|
35
|
+
return null;
|
|
36
|
+
const agent = str(rec.agent_id ?? rec.agentId);
|
|
37
|
+
const pod = str(rec.pod_name ?? rec.podName);
|
|
38
|
+
const tool = str(rec.tool_name ?? rec.toolName ?? 'unknown');
|
|
39
|
+
const toolCall = rec.tool_call_id ?? rec.toolCallId;
|
|
40
|
+
const args = rec.tool_args ??
|
|
41
|
+
rec.toolArgs ??
|
|
42
|
+
{};
|
|
43
|
+
const created = str(rec.created_at ?? rec.createdAt);
|
|
44
|
+
const updated = str(rec.updated_at ?? rec.updatedAt);
|
|
45
|
+
return {
|
|
46
|
+
id,
|
|
47
|
+
agent_id: agent,
|
|
48
|
+
agentId: agent,
|
|
49
|
+
pod_name: pod,
|
|
50
|
+
podName: pod,
|
|
51
|
+
tool_name: tool,
|
|
52
|
+
toolName: tool,
|
|
53
|
+
tool_call_id: typeof toolCall === 'string' ? toolCall : undefined,
|
|
54
|
+
toolCallId: typeof toolCall === 'string' ? toolCall : undefined,
|
|
55
|
+
tool_args: args,
|
|
56
|
+
toolArgs: args,
|
|
57
|
+
status: str(rec.status ?? 'pending'),
|
|
58
|
+
note: typeof rec.note === 'string'
|
|
59
|
+
? rec.note
|
|
60
|
+
: rec.note === null
|
|
61
|
+
? null
|
|
62
|
+
: undefined,
|
|
63
|
+
created_at: created,
|
|
64
|
+
createdAt: created,
|
|
65
|
+
updated_at: updated || undefined,
|
|
66
|
+
updatedAt: updated || undefined,
|
|
67
|
+
read: typeof rec.read === 'boolean' ? rec.read : undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function matchesFilter(a, filters) {
|
|
71
|
+
if (!filters)
|
|
72
|
+
return true;
|
|
73
|
+
if (filters.agentId && a.agent_id !== filters.agentId)
|
|
74
|
+
return false;
|
|
75
|
+
if (filters.status && a.status !== filters.status)
|
|
76
|
+
return false;
|
|
77
|
+
if (filters.toolName && a.tool_name !== filters.toolName)
|
|
78
|
+
return false;
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
function upsertApproval(list, approval) {
|
|
82
|
+
const idx = list.findIndex(item => item.id === approval.id);
|
|
83
|
+
if (idx === -1)
|
|
84
|
+
return [approval, ...list];
|
|
85
|
+
const copy = list.slice();
|
|
86
|
+
copy[idx] = { ...copy[idx], ...approval };
|
|
87
|
+
return copy;
|
|
88
|
+
}
|
|
89
|
+
function removeApproval(list, id) {
|
|
90
|
+
return list.filter(item => item.id !== id);
|
|
91
|
+
}
|
|
92
|
+
function writeSnapshot(queryClient, approvals) {
|
|
93
|
+
const queries = queryClient
|
|
94
|
+
.getQueryCache()
|
|
95
|
+
.findAll({ queryKey: APPROVALS_ROOT_KEY });
|
|
96
|
+
for (const q of queries) {
|
|
97
|
+
const [, second] = q.queryKey;
|
|
98
|
+
if (second === 'pending-count') {
|
|
99
|
+
queryClient.setQueryData(q.queryKey, {
|
|
100
|
+
count: approvals.filter(a => a.status === 'pending').length,
|
|
101
|
+
});
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const filters = (second ?? undefined);
|
|
105
|
+
const filtered = approvals.filter(a => matchesFilter(a, filters));
|
|
106
|
+
queryClient.setQueryData(q.queryKey, {
|
|
107
|
+
approvals: filtered,
|
|
108
|
+
total: filtered.length,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// Ensure there's always a root cache entry so downstream consumers can
|
|
112
|
+
// compute derived values (like pending-count refetches) even when no
|
|
113
|
+
// filter query has been mounted yet.
|
|
114
|
+
queryClient.setQueryData(APPROVALS_ROOT_KEY, {
|
|
115
|
+
approvals,
|
|
116
|
+
total: approvals.length,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function patchApproval(queryClient, approval, mode) {
|
|
120
|
+
const queries = queryClient
|
|
121
|
+
.getQueryCache()
|
|
122
|
+
.findAll({ queryKey: APPROVALS_ROOT_KEY });
|
|
123
|
+
for (const q of queries) {
|
|
124
|
+
const [, second] = q.queryKey;
|
|
125
|
+
if (second === 'pending-count') {
|
|
126
|
+
// Recompute from the root cache after we finish patching below.
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const filters = (second ?? undefined);
|
|
130
|
+
const current = queryClient.getQueryData(q.queryKey)?.approvals ?? [];
|
|
131
|
+
let next;
|
|
132
|
+
if (mode === 'remove') {
|
|
133
|
+
next = removeApproval(current, approval.id);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
next = matchesFilter(approval, filters)
|
|
137
|
+
? upsertApproval(current, approval)
|
|
138
|
+
: removeApproval(current, approval.id);
|
|
139
|
+
}
|
|
140
|
+
queryClient.setQueryData(q.queryKey, {
|
|
141
|
+
approvals: next,
|
|
142
|
+
total: next.length,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// Recompute pending-count from the unfiltered root cache.
|
|
146
|
+
const root = queryClient.getQueryData(APPROVALS_ROOT_KEY)?.approvals;
|
|
147
|
+
if (root) {
|
|
148
|
+
queryClient.setQueryData(['tool-approvals', 'pending-count'], { count: root.filter(a => a.status === 'pending').length });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// ─── Shared WS bridge ─────────────────────────────────────────────────
|
|
152
|
+
/**
|
|
153
|
+
* Opens a WS connection and streams snapshots + events into the React
|
|
154
|
+
* Query cache. Returns a `send` function so sibling hooks can dispatch
|
|
155
|
+
* decisions over the same socket.
|
|
156
|
+
*/
|
|
157
|
+
function useApprovalsSocket() {
|
|
158
|
+
const queryClient = useQueryClient();
|
|
159
|
+
const { connectionState, send } = useAIAgentsWebSocket({
|
|
160
|
+
onMessage: msg => {
|
|
161
|
+
const type = (msg.type ?? msg.event);
|
|
162
|
+
if (!type)
|
|
163
|
+
return;
|
|
164
|
+
// Response to our { type: 'tool-approvals-history' } request.
|
|
165
|
+
// Shape: { type: "tool-approvals-history", data: { approvals: [...] } }
|
|
166
|
+
if (type === 'tool-approvals-history') {
|
|
167
|
+
const data = msg.data;
|
|
168
|
+
const rawList = Array.isArray(data?.approvals) ? data.approvals : [];
|
|
169
|
+
const list = rawList
|
|
170
|
+
.map(normalizeApproval)
|
|
171
|
+
.filter((a) => a !== null)
|
|
172
|
+
.filter(a => a.status !== 'deleted');
|
|
173
|
+
writeSnapshot(queryClient, list);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
// Incremental broadcast events from datalayer-ai-agents.
|
|
177
|
+
// Shape: { channel: "user:<uid>", event: "tool_approval_*", data: record }
|
|
178
|
+
if (type.startsWith('tool_approval_')) {
|
|
179
|
+
const rawPayload = msg.payload ??
|
|
180
|
+
msg.data ??
|
|
181
|
+
undefined;
|
|
182
|
+
const approval = normalizeApproval(rawPayload);
|
|
183
|
+
if (!approval)
|
|
184
|
+
return;
|
|
185
|
+
patchApproval(queryClient, approval, type === 'tool_approval_deleted' ? 'remove' : 'upsert');
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
// Request the full approval history once connected so the sidebar badge
|
|
190
|
+
// and any pending-count consumers always show the correct count.
|
|
191
|
+
const historyAskedRef = useRef(false);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (connectionState !== 'connected') {
|
|
194
|
+
historyAskedRef.current = false;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (historyAskedRef.current)
|
|
198
|
+
return;
|
|
199
|
+
historyAskedRef.current = send({ type: 'tool-approvals-history' });
|
|
200
|
+
}, [connectionState, send]);
|
|
201
|
+
return { send, connectionState };
|
|
23
202
|
}
|
|
24
203
|
// ─── Base hooks ──────────────────────────────────────────────────────
|
|
25
204
|
export function useToolApprovalsQuery(filters) {
|
|
26
|
-
|
|
27
|
-
const
|
|
205
|
+
useApprovalsSocket();
|
|
206
|
+
const queryClient = useQueryClient();
|
|
207
|
+
const queryKey = useMemo(() => ['tool-approvals', filters], [filters]);
|
|
28
208
|
return useQuery({
|
|
29
|
-
queryKey
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
209
|
+
queryKey,
|
|
210
|
+
// Data is populated via the WS bridge. On invalidation, return the
|
|
211
|
+
// cached value so the fresh WS snapshot is preserved.
|
|
212
|
+
queryFn: async () => queryClient.getQueryData(queryKey) ?? {
|
|
213
|
+
approvals: [],
|
|
214
|
+
total: 0,
|
|
215
|
+
},
|
|
216
|
+
staleTime: Number.POSITIVE_INFINITY,
|
|
217
|
+
refetchOnMount: false,
|
|
218
|
+
refetchOnWindowFocus: false,
|
|
219
|
+
refetchOnReconnect: false,
|
|
33
220
|
});
|
|
34
221
|
}
|
|
35
222
|
export function usePendingApprovalCount() {
|
|
36
|
-
|
|
37
|
-
const
|
|
223
|
+
useApprovalsSocket();
|
|
224
|
+
const queryClient = useQueryClient();
|
|
38
225
|
return useQuery({
|
|
39
226
|
queryKey: ['tool-approvals', 'pending-count'],
|
|
40
|
-
queryFn: () =>
|
|
41
|
-
|
|
42
|
-
|
|
227
|
+
queryFn: async () => {
|
|
228
|
+
const cached = queryClient.getQueryData([
|
|
229
|
+
'tool-approvals',
|
|
230
|
+
'pending-count',
|
|
231
|
+
]);
|
|
232
|
+
if (cached && typeof cached.count === 'number')
|
|
233
|
+
return cached;
|
|
234
|
+
const root = queryClient.getQueryData(APPROVALS_ROOT_KEY);
|
|
235
|
+
const pending = (root?.approvals ?? []).filter(a => a.status === 'pending').length;
|
|
236
|
+
return { count: pending };
|
|
237
|
+
},
|
|
238
|
+
staleTime: Number.POSITIVE_INFINITY,
|
|
239
|
+
refetchOnMount: false,
|
|
240
|
+
refetchOnWindowFocus: false,
|
|
241
|
+
refetchOnReconnect: false,
|
|
43
242
|
});
|
|
44
243
|
}
|
|
244
|
+
/** Build a mutation-style object that sends a WS decision. */
|
|
245
|
+
function useDecisionMutation(approved) {
|
|
246
|
+
const { send } = useApprovalsSocket();
|
|
247
|
+
const [isPending, setIsPending] = useState(false);
|
|
248
|
+
const mutateAsync = useCallback(async ({ id, note }) => {
|
|
249
|
+
setIsPending(true);
|
|
250
|
+
try {
|
|
251
|
+
const ok = send({
|
|
252
|
+
type: 'tool_approval_decision',
|
|
253
|
+
approvalId: id,
|
|
254
|
+
approved,
|
|
255
|
+
...(note ? { note } : {}),
|
|
256
|
+
});
|
|
257
|
+
if (!ok) {
|
|
258
|
+
throw new Error('Approvals WebSocket is not connected; decision was not sent');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
setIsPending(false);
|
|
263
|
+
}
|
|
264
|
+
}, [approved, send]);
|
|
265
|
+
const mutate = useCallback((vars) => {
|
|
266
|
+
void mutateAsync(vars);
|
|
267
|
+
}, [mutateAsync]);
|
|
268
|
+
return { isPending, mutate, mutateAsync };
|
|
269
|
+
}
|
|
45
270
|
export function useApproveToolRequest() {
|
|
46
|
-
|
|
47
|
-
const baseUrl = useDashboardBaseUrl();
|
|
48
|
-
const queryClient = useQueryClient();
|
|
49
|
-
return useMutation({
|
|
50
|
-
mutationFn: ({ id, note }) => toolApprovals.approveToolRequest(token, id, note, baseUrl),
|
|
51
|
-
onSuccess: () => {
|
|
52
|
-
queryClient.invalidateQueries({ queryKey: ['tool-approvals'] });
|
|
53
|
-
},
|
|
54
|
-
});
|
|
271
|
+
return useDecisionMutation(true);
|
|
55
272
|
}
|
|
56
273
|
export function useRejectToolRequest() {
|
|
57
|
-
|
|
58
|
-
|
|
274
|
+
return useDecisionMutation(false);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Mark a tool approval as read/unread in the local cache.
|
|
278
|
+
*
|
|
279
|
+
* Read-state isn't part of the websocket contract, so this hook patches
|
|
280
|
+
* only the React Query cache. The patch survives until the next snapshot.
|
|
281
|
+
*/
|
|
282
|
+
function useLocalReadMutation(target) {
|
|
59
283
|
const queryClient = useQueryClient();
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
284
|
+
const [isPending, setIsPending] = useState(false);
|
|
285
|
+
const mutateAsync = useCallback(async ({ id }) => {
|
|
286
|
+
setIsPending(true);
|
|
287
|
+
try {
|
|
288
|
+
const queries = queryClient
|
|
289
|
+
.getQueryCache()
|
|
290
|
+
.findAll({ queryKey: APPROVALS_ROOT_KEY });
|
|
291
|
+
for (const q of queries) {
|
|
292
|
+
const [, second] = q.queryKey;
|
|
293
|
+
if (second === 'pending-count')
|
|
294
|
+
continue;
|
|
295
|
+
const current = queryClient.getQueryData(q.queryKey);
|
|
296
|
+
if (!current?.approvals)
|
|
297
|
+
continue;
|
|
298
|
+
queryClient.setQueryData(q.queryKey, {
|
|
299
|
+
...current,
|
|
300
|
+
approvals: current.approvals.map(a => a.id === id ? { ...a, read: target } : a),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
finally {
|
|
305
|
+
setIsPending(false);
|
|
306
|
+
}
|
|
307
|
+
}, [queryClient, target]);
|
|
308
|
+
const mutate = useCallback((vars) => {
|
|
309
|
+
void mutateAsync(vars);
|
|
310
|
+
}, [mutateAsync]);
|
|
311
|
+
return { isPending, mutate, mutateAsync };
|
|
66
312
|
}
|
|
67
313
|
export function useMarkToolApprovalRead() {
|
|
68
|
-
|
|
69
|
-
const baseUrl = useDashboardBaseUrl();
|
|
70
|
-
const queryClient = useQueryClient();
|
|
71
|
-
return useMutation({
|
|
72
|
-
mutationFn: ({ id }) => toolApprovals.markToolApprovalRead(token, id, baseUrl),
|
|
73
|
-
onSuccess: () => {
|
|
74
|
-
queryClient.invalidateQueries({ queryKey: ['tool-approvals'] });
|
|
75
|
-
},
|
|
76
|
-
});
|
|
314
|
+
return useLocalReadMutation(true);
|
|
77
315
|
}
|
|
78
316
|
export function useMarkToolApprovalUnread() {
|
|
79
|
-
|
|
80
|
-
const baseUrl = useDashboardBaseUrl();
|
|
81
|
-
const queryClient = useQueryClient();
|
|
82
|
-
return useMutation({
|
|
83
|
-
mutationFn: ({ id }) => toolApprovals.markToolApprovalUnread(token, id, baseUrl),
|
|
84
|
-
onSuccess: () => {
|
|
85
|
-
queryClient.invalidateQueries({ queryKey: ['tool-approvals'] });
|
|
86
|
-
},
|
|
87
|
-
});
|
|
317
|
+
return useLocalReadMutation(false);
|
|
88
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Delete a tool approval.
|
|
321
|
+
*
|
|
322
|
+
* Sends a ``tool_approval_delete`` message over the shared websocket.
|
|
323
|
+
* The local cache is updated only after the server emits
|
|
324
|
+
* ``tool_approval_deleted``.
|
|
325
|
+
*/
|
|
89
326
|
export function useDeleteToolApproval() {
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
327
|
+
const { send } = useApprovalsSocket();
|
|
328
|
+
const [isPending, setIsPending] = useState(false);
|
|
329
|
+
const mutateAsync = useCallback(async ({ id }) => {
|
|
330
|
+
setIsPending(true);
|
|
331
|
+
try {
|
|
332
|
+
const ok = send({
|
|
333
|
+
type: 'tool_approval_delete',
|
|
334
|
+
approvalId: id,
|
|
335
|
+
});
|
|
336
|
+
if (!ok) {
|
|
337
|
+
throw new Error('Approvals WebSocket is not connected; delete was not sent');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
finally {
|
|
341
|
+
setIsPending(false);
|
|
342
|
+
}
|
|
343
|
+
}, [send]);
|
|
344
|
+
const mutate = useCallback((vars) => {
|
|
345
|
+
void mutateAsync(vars);
|
|
346
|
+
}, [mutateAsync]);
|
|
347
|
+
return { isPending, mutate, mutateAsync };
|
|
99
348
|
}
|
|
100
349
|
// ─── Composite hook ──────────────────────────────────────────────────
|
|
101
350
|
export function useToolApprovals(filters) {
|