@datalayer/agent-runtimes 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/README.md +182 -1
  2. package/lib/AgentNode.d.ts +3 -0
  3. package/lib/AgentNode.js +676 -0
  4. package/lib/App.js +1 -1
  5. package/lib/agent-node/themeStore.d.ts +3 -0
  6. package/lib/agent-node/themeStore.js +156 -0
  7. package/lib/agent-node-main.d.ts +1 -0
  8. package/lib/agent-node-main.js +14 -0
  9. package/lib/agents/AgentDetails.d.ts +22 -1
  10. package/lib/agents/AgentDetails.js +34 -47
  11. package/lib/api/index.d.ts +0 -1
  12. package/lib/api/index.js +4 -2
  13. package/lib/chat/Chat.d.ts +5 -106
  14. package/lib/chat/Chat.js +20 -14
  15. package/lib/chat/ChatFloating.d.ts +7 -140
  16. package/lib/chat/ChatFloating.js +3 -3
  17. package/lib/chat/ChatPopupStandalone.d.ts +8 -47
  18. package/lib/chat/ChatPopupStandalone.js +3 -3
  19. package/lib/chat/ChatSidebar.d.ts +4 -69
  20. package/lib/chat/ChatSidebar.js +83 -51
  21. package/lib/chat/ChatStandalone.d.ts +4 -54
  22. package/lib/chat/ChatStandalone.js +3 -3
  23. package/lib/chat/base/ChatBase.js +1414 -174
  24. package/lib/chat/display/FloatingBrandButton.js +8 -1
  25. package/lib/chat/header/ChatHeader.d.ts +3 -1
  26. package/lib/chat/header/ChatHeader.js +15 -12
  27. package/lib/chat/header/ChatHeaderBase.d.ts +30 -5
  28. package/lib/chat/header/ChatHeaderBase.js +41 -16
  29. package/lib/chat/indicators/McpStatusIndicator.d.ts +7 -4
  30. package/lib/chat/indicators/McpStatusIndicator.js +7 -32
  31. package/lib/chat/indicators/SandboxStatusIndicator.d.ts +4 -1
  32. package/lib/chat/indicators/SandboxStatusIndicator.js +91 -56
  33. package/lib/chat/indicators/SkillsStatusIndicator.d.ts +7 -0
  34. package/lib/chat/indicators/SkillsStatusIndicator.js +88 -0
  35. package/lib/chat/indicators/index.d.ts +1 -0
  36. package/lib/chat/indicators/index.js +1 -0
  37. package/lib/chat/messages/ChatMessageList.d.ts +1 -1
  38. package/lib/chat/messages/ChatMessageList.js +154 -114
  39. package/lib/chat/messages/ChatMessages.js +6 -2
  40. package/lib/chat/prompt/InputFooter.d.ts +21 -6
  41. package/lib/chat/prompt/InputFooter.js +76 -20
  42. package/lib/chat/prompt/InputPrompt.d.ts +5 -1
  43. package/lib/chat/prompt/InputPrompt.js +4 -4
  44. package/lib/chat/prompt/InputPromptFooter.d.ts +3 -1
  45. package/lib/chat/prompt/InputPromptFooter.js +3 -3
  46. package/lib/chat/prompt/InputPromptLexical.d.ts +3 -1
  47. package/lib/chat/prompt/InputPromptLexical.js +12 -5
  48. package/lib/chat/prompt/InputPromptText.d.ts +3 -1
  49. package/lib/chat/prompt/InputPromptText.js +2 -2
  50. package/lib/chat/tools/ToolApprovalBanner.js +1 -1
  51. package/lib/chat/tools/ToolCallDisplay.d.ts +3 -1
  52. package/lib/chat/tools/ToolCallDisplay.js +2 -2
  53. package/lib/chat/usage/TokenUsageBar.js +20 -2
  54. package/lib/client/AgentRuntimesClientContext.d.ts +53 -0
  55. package/lib/client/AgentRuntimesClientContext.js +55 -0
  56. package/lib/client/AgentsMixin.d.ts +0 -18
  57. package/lib/client/AgentsMixin.js +20 -30
  58. package/lib/client/IAgentRuntimesClient.d.ts +215 -0
  59. package/lib/client/IAgentRuntimesClient.js +5 -0
  60. package/lib/client/SdkAgentRuntimesClient.d.ts +151 -0
  61. package/lib/client/SdkAgentRuntimesClient.js +134 -0
  62. package/lib/client/index.d.ts +4 -1
  63. package/lib/client/index.js +3 -1
  64. package/lib/components/NotificationEventCard.js +5 -1
  65. package/lib/config/AgentConfiguration.d.ts +22 -0
  66. package/lib/config/AgentConfiguration.js +319 -64
  67. package/lib/context/ContextDistribution.d.ts +3 -1
  68. package/lib/context/ContextDistribution.js +8 -27
  69. package/lib/context/ContextInspector.d.ts +3 -1
  70. package/lib/context/ContextInspector.js +19 -67
  71. package/lib/context/ContextPanel.d.ts +3 -1
  72. package/lib/context/ContextPanel.js +104 -64
  73. package/lib/context/ContextUsage.d.ts +3 -1
  74. package/lib/context/ContextUsage.js +3 -3
  75. package/lib/context/CostTracker.d.ts +9 -3
  76. package/lib/context/CostTracker.js +26 -47
  77. package/lib/context/CostUsageChart.d.ts +12 -0
  78. package/lib/context/CostUsageChart.js +378 -0
  79. package/lib/context/GraphFlowChart.d.ts +16 -0
  80. package/lib/context/GraphFlowChart.js +182 -0
  81. package/lib/context/TokenUsageChart.d.ts +8 -1
  82. package/lib/context/TokenUsageChart.js +349 -211
  83. package/lib/context/TurnGraphChart.d.ts +39 -0
  84. package/lib/context/TurnGraphChart.js +538 -0
  85. package/lib/context/otelWsPool.d.ts +20 -0
  86. package/lib/context/otelWsPool.js +69 -0
  87. package/lib/examples/A2UiComponentGalleryExample.d.ts +0 -17
  88. package/lib/examples/A2UiComponentGalleryExample.js +315 -522
  89. package/lib/examples/A2UiContactCardExample.d.ts +0 -18
  90. package/lib/examples/A2UiContactCardExample.js +154 -411
  91. package/lib/examples/A2UiRestaurantExample.d.ts +0 -30
  92. package/lib/examples/A2UiRestaurantExample.js +114 -212
  93. package/lib/examples/A2UiViewerExample.d.ts +0 -18
  94. package/lib/examples/A2UiViewerExample.js +283 -532
  95. package/lib/examples/AgUiBackendToolRenderingExample.js +1 -1
  96. package/lib/examples/AgUiHaikuGenUiExample.d.ts +1 -1
  97. package/lib/examples/AgUiHaikuGenUiExample.js +1 -1
  98. package/lib/examples/AgUiSharedStateExample.js +2 -1
  99. package/lib/examples/AgentCheckpointsExample.js +14 -28
  100. package/lib/examples/AgentCodemodeExample.d.ts +4 -6
  101. package/lib/examples/AgentCodemodeExample.js +603 -169
  102. package/lib/examples/AgentEvalsExample.js +339 -53
  103. package/lib/examples/AgentGuardrailsExample.js +383 -66
  104. package/lib/examples/AgentHooksExample.d.ts +3 -0
  105. package/lib/examples/AgentHooksExample.js +122 -0
  106. package/lib/examples/AgentInferenceProviderExample.d.ts +3 -0
  107. package/lib/examples/AgentInferenceProviderExample.js +329 -0
  108. package/lib/examples/AgentMCPExample.d.ts +3 -0
  109. package/lib/examples/AgentMCPExample.js +481 -0
  110. package/lib/examples/AgentMemoryExample.d.ts +1 -2
  111. package/lib/examples/AgentMemoryExample.js +78 -33
  112. package/lib/examples/AgentMonitoringExample.js +261 -200
  113. package/lib/examples/AgentNotificationsExample.d.ts +1 -2
  114. package/lib/examples/AgentNotificationsExample.js +114 -33
  115. package/lib/examples/AgentOtelExample.js +32 -42
  116. package/lib/examples/AgentOutputsExample.d.ts +11 -6
  117. package/lib/examples/AgentOutputsExample.js +433 -81
  118. package/lib/examples/AgentParametersExample.d.ts +3 -0
  119. package/lib/examples/AgentParametersExample.js +248 -0
  120. package/lib/examples/AgentSandboxExample.d.ts +3 -3
  121. package/lib/examples/AgentSandboxExample.js +74 -45
  122. package/lib/examples/AgentSkillsExample.js +95 -103
  123. package/lib/examples/AgentSubagentsExample.d.ts +14 -0
  124. package/lib/examples/AgentSubagentsExample.js +228 -0
  125. package/lib/examples/AgentToolApprovalsExample.js +49 -561
  126. package/lib/examples/AgentTriggersExample.js +823 -569
  127. package/lib/examples/{AgentspecExample.d.ts → AgentspecsExample.d.ts} +2 -2
  128. package/lib/examples/AgentspecsExample.js +1096 -0
  129. package/lib/examples/ChatCustomExample.js +16 -28
  130. package/lib/examples/ChatExample.js +13 -29
  131. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  132. package/lib/examples/CopilotKitNotebookExample.js +2 -1
  133. package/lib/examples/HomeExample.d.ts +15 -0
  134. package/lib/examples/HomeExample.js +77 -0
  135. package/lib/examples/Lexical2Example.js +4 -2
  136. package/lib/examples/{LexicalExample.d.ts → LexicalAgentExample.d.ts} +4 -4
  137. package/lib/examples/{LexicalExample.js → LexicalAgentExample.js} +66 -17
  138. package/lib/examples/{LexicalSidebarExample.d.ts → LexicalAgentSidebarExample.d.ts} +5 -5
  139. package/lib/examples/LexicalAgentSidebarExample.js +261 -0
  140. package/lib/examples/NotebookAgentExample.d.ts +9 -0
  141. package/lib/examples/NotebookAgentExample.js +192 -0
  142. package/lib/examples/{NotebookSidebarExample.d.ts → NotebookAgentSidebarExample.d.ts} +2 -2
  143. package/lib/examples/NotebookAgentSidebarExample.js +221 -0
  144. package/lib/examples/{DatalayerNotebookExample.d.ts → NotebookCollaborationExample.d.ts} +4 -4
  145. package/lib/examples/{DatalayerNotebookExample.js → NotebookCollaborationExample.js} +3 -3
  146. package/lib/examples/NotebookExample.d.ts +4 -7
  147. package/lib/examples/NotebookExample.js +14 -146
  148. package/lib/examples/components/AuthRequiredView.d.ts +6 -0
  149. package/lib/examples/components/AuthRequiredView.js +33 -0
  150. package/lib/examples/components/ExampleWrapper.d.ts +9 -3
  151. package/lib/examples/components/ExampleWrapper.js +45 -9
  152. package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.js +1 -1
  153. package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.js +1 -1
  154. package/lib/examples/{ag-ui → components}/haiku/index.d.ts +1 -1
  155. package/lib/examples/{ag-ui → components}/haiku/index.js +1 -1
  156. package/lib/examples/components/index.d.ts +3 -0
  157. package/lib/examples/components/index.js +4 -0
  158. package/lib/examples/{ag-ui → components}/weather/index.d.ts +1 -1
  159. package/lib/examples/{ag-ui → components}/weather/index.js +1 -1
  160. package/lib/examples/example-selector.d.ts +17 -4
  161. package/lib/examples/example-selector.js +108 -41
  162. package/lib/examples/index.d.ts +10 -6
  163. package/lib/examples/index.js +10 -6
  164. package/lib/examples/lexical/initial-content.json +6 -6
  165. package/lib/examples/main.js +257 -27
  166. package/lib/examples/utils/a2ui.d.ts +18 -0
  167. package/lib/examples/utils/a2ui.js +69 -0
  168. package/lib/examples/utils/a2uiMarkdownProvider.d.ts +7 -0
  169. package/lib/examples/utils/a2uiMarkdownProvider.js +9 -0
  170. package/lib/examples/utils/agentId.d.ts +18 -0
  171. package/lib/examples/utils/agentId.js +54 -0
  172. package/lib/examples/utils/agents/earthquake-detector.json +11 -11
  173. package/lib/examples/utils/agents/sales-forecaster.json +11 -11
  174. package/lib/examples/utils/agents/social-post-generator.json +11 -11
  175. package/lib/examples/utils/agents/stock-market.json +11 -11
  176. package/lib/examples/utils/examplesStore.js +82 -27
  177. package/lib/examples/utils/useExampleAgentRuntimesUrl.d.ts +5 -0
  178. package/lib/examples/utils/useExampleAgentRuntimesUrl.js +19 -0
  179. package/lib/hooks/index.d.ts +8 -8
  180. package/lib/hooks/index.js +7 -7
  181. package/lib/hooks/useA2A.d.ts +2 -3
  182. package/lib/hooks/useAIAgentsWebSocket.d.ts +43 -4
  183. package/lib/hooks/useAIAgentsWebSocket.js +153 -12
  184. package/lib/hooks/useAcp.d.ts +1 -2
  185. package/lib/hooks/useAgUi.d.ts +1 -1
  186. package/lib/hooks/{useAgents.d.ts → useAgentRuntimes.d.ts} +70 -4
  187. package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +237 -32
  188. package/lib/hooks/useAgentsCatalog.js +1 -1
  189. package/lib/hooks/useAgentsService.d.ts +2 -2
  190. package/lib/hooks/useAgentsService.js +7 -7
  191. package/lib/hooks/useCheckpoints.js +1 -1
  192. package/lib/hooks/useConfig.d.ts +4 -1
  193. package/lib/hooks/useConfig.js +10 -3
  194. package/lib/hooks/useContextSnapshot.d.ts +9 -4
  195. package/lib/hooks/useContextSnapshot.js +9 -37
  196. package/lib/hooks/useMonitoring.js +3 -0
  197. package/lib/hooks/useSandbox.d.ts +20 -8
  198. package/lib/hooks/useSandbox.js +105 -40
  199. package/lib/hooks/useSkills.d.ts +23 -5
  200. package/lib/hooks/useSkills.js +94 -39
  201. package/lib/hooks/useToolApprovals.d.ts +60 -36
  202. package/lib/hooks/useToolApprovals.js +318 -69
  203. package/lib/hooks/useVercelAI.d.ts +1 -1
  204. package/lib/index.d.ts +2 -1
  205. package/lib/index.js +1 -0
  206. package/lib/inference/index.d.ts +0 -1
  207. package/lib/middleware/index.d.ts +0 -1
  208. package/lib/protocols/AGUIAdapter.js +6 -0
  209. package/lib/protocols/VercelAIAdapter.d.ts +7 -0
  210. package/lib/protocols/VercelAIAdapter.js +59 -7
  211. package/lib/specs/agents/agents.d.ts +21 -4
  212. package/lib/specs/agents/agents.js +2879 -316
  213. package/lib/specs/agents/index.js +3 -1
  214. package/lib/specs/benchmarks.d.ts +20 -0
  215. package/lib/specs/benchmarks.js +205 -0
  216. package/lib/specs/envvars.js +27 -20
  217. package/lib/specs/evals.d.ts +10 -9
  218. package/lib/specs/evals.js +128 -88
  219. package/lib/specs/events.d.ts +3 -10
  220. package/lib/specs/events.js +127 -84
  221. package/lib/specs/frontendTools.js +2 -2
  222. package/lib/specs/guardrails.d.ts +0 -7
  223. package/lib/specs/guardrails.js +240 -159
  224. package/lib/specs/mcpServers.js +35 -6
  225. package/lib/specs/memory.d.ts +0 -2
  226. package/lib/specs/memory.js +4 -17
  227. package/lib/specs/models.d.ts +0 -2
  228. package/lib/specs/models.js +20 -15
  229. package/lib/specs/notifications.js +102 -18
  230. package/lib/specs/outputs.js +15 -9
  231. package/lib/specs/personas.d.ts +41 -0
  232. package/lib/specs/personas.js +168 -0
  233. package/lib/specs/skills.d.ts +1 -1
  234. package/lib/specs/skills.js +23 -23
  235. package/lib/specs/teams/index.js +3 -1
  236. package/lib/specs/teams/teams.js +468 -348
  237. package/lib/specs/tools.js +4 -4
  238. package/lib/specs/triggers.js +61 -11
  239. package/lib/stores/agentRuntimeStore.d.ts +208 -0
  240. package/lib/stores/agentRuntimeStore.js +650 -0
  241. package/lib/stores/conversationStore.js +2 -2
  242. package/lib/stores/index.d.ts +1 -1
  243. package/lib/stores/index.js +1 -1
  244. package/lib/tools/adapters/copilotkit/lexicalHooks.d.ts +1 -2
  245. package/lib/tools/adapters/copilotkit/lexicalHooks.js +1 -3
  246. package/lib/tools/adapters/copilotkit/notebookHooks.d.ts +1 -2
  247. package/lib/tools/adapters/copilotkit/notebookHooks.js +1 -3
  248. package/lib/tools/index.d.ts +0 -2
  249. package/lib/tools/index.js +0 -1
  250. package/lib/types/agents-lifecycle.d.ts +18 -0
  251. package/lib/types/agents.d.ts +6 -0
  252. package/lib/types/agentspecs.d.ts +54 -1
  253. package/lib/types/benchmarks.d.ts +43 -0
  254. package/lib/types/benchmarks.js +5 -0
  255. package/lib/types/chat.d.ts +325 -8
  256. package/lib/types/context.d.ts +27 -0
  257. package/lib/types/cost.d.ts +2 -2
  258. package/lib/types/evals.d.ts +26 -17
  259. package/lib/types/index.d.ts +3 -0
  260. package/lib/types/index.js +3 -0
  261. package/lib/types/mcp.d.ts +8 -0
  262. package/lib/types/models.d.ts +2 -2
  263. package/lib/types/personas.d.ts +25 -0
  264. package/lib/types/personas.js +5 -0
  265. package/lib/types/skills.d.ts +43 -1
  266. package/lib/types/stream.d.ts +110 -0
  267. package/lib/types/stream.js +36 -0
  268. package/lib/utils/utils.d.ts +9 -5
  269. package/lib/utils/utils.js +9 -5
  270. package/package.json +19 -11
  271. package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
  272. package/scripts/codegen/__pycache__/generate_benchmarks.cpython-313.pyc +0 -0
  273. package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
  274. package/scripts/codegen/__pycache__/generate_events.cpython-313.pyc +0 -0
  275. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
  276. package/scripts/codegen/generate_agents.py +187 -45
  277. package/scripts/codegen/generate_benchmarks.py +441 -0
  278. package/scripts/codegen/generate_evals.py +94 -16
  279. package/scripts/codegen/generate_events.py +35 -14
  280. package/scripts/codegen/generate_personas.py +319 -0
  281. package/scripts/codegen/generate_skills.py +9 -9
  282. package/scripts/sync-jupyter.sh +26 -7
  283. package/lib/api/tool-approvals.d.ts +0 -62
  284. package/lib/api/tool-approvals.js +0 -145
  285. package/lib/examples/AgentspecExample.js +0 -705
  286. package/lib/examples/LexicalSidebarExample.js +0 -163
  287. package/lib/examples/NotebookSidebarExample.js +0 -119
  288. package/lib/examples/NotebookSimpleExample.d.ts +0 -6
  289. package/lib/examples/NotebookSimpleExample.js +0 -22
  290. package/lib/examples/ag-ui/index.d.ts +0 -10
  291. package/lib/examples/ag-ui/index.js +0 -16
  292. package/lib/hooks/useAgentsRegistry.d.ts +0 -10
  293. package/lib/hooks/useAgentsRegistry.js +0 -20
  294. package/lib/stores/agentsStore.d.ts +0 -123
  295. package/lib/stores/agentsStore.js +0 -270
  296. /package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.d.ts +0 -0
  297. /package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.d.ts +0 -0
  298. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.d.ts +0 -0
  299. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.js +0 -0
@@ -7,7 +7,7 @@
7
7
  import { type ReactNode, type RefObject } from 'react';
8
8
  import type { DisplayItem, AvatarConfig, RenderToolResult } from '../../types/chat';
9
9
  export interface ToolApprovalConfig {
10
- /** Base API URL for the agent runtime (e.g., http://localhost:8765/api/v1) */
10
+ /** Base API URL for the agent runtime (e.g., `http://localhost:8765/api/v1`). */
11
11
  apiBaseUrl: string;
12
12
  /** Auth token for API calls */
13
13
  authToken?: string;
@@ -9,138 +9,177 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
9
9
  *
10
10
  * @module chat/messages/MessageList
11
11
  */
12
- import { useState, useEffect, useCallback, } from 'react';
12
+ import { useState, useCallback, useMemo, } from 'react';
13
13
  import { Text } from '@primer/react';
14
14
  import { Box } from '@datalayer/primer-addons';
15
15
  import { Streamdown } from 'streamdown';
16
16
  import { streamdownMarkdownStyles, streamdownCodeBlockStyles, } from '../styles/streamdownStyles';
17
17
  import { ToolCallDisplay } from '../tools/ToolCallDisplay';
18
18
  import { isToolCallMessage, getMessageText } from '../../utils';
19
+ import { useAgentRuntimeStore } from '../../stores/agentRuntimeStore';
19
20
  // ---------------------------------------------------------------------------
20
- // DefaultToolCallRenderer — handles tool approval for a single tool call
21
+ // Normalize helper
21
22
  // ---------------------------------------------------------------------------
22
- function DefaultToolCallRenderer({ item, approvalConfig, onRespond, }) {
23
+ function normalizeName(name) {
24
+ return name
25
+ .replace(/:[0-9]+\.[0-9]+\.[0-9]+.*$/, '')
26
+ .replace(/[-_]/g, '')
27
+ .toLowerCase();
28
+ }
29
+ /**
30
+ * Expand a single-line markdown table (rows joined with `||`) into one row
31
+ * per line so a markdown renderer can produce a real HTML table. Idempotent:
32
+ * any pre-existing `|\n|` boundaries are unaffected.
33
+ */
34
+ function expandCompactMarkdownTableBody(body) {
35
+ if (!body)
36
+ return body;
37
+ if (!/\|\s*[-:| ]{3,}\|/.test(body))
38
+ return body;
39
+ if (!body.includes('||'))
40
+ return body;
41
+ return body.replace(/\|\|/g, '|\n|');
42
+ }
43
+ /**
44
+ * Pre-process assistant text for Streamdown:
45
+ * - Unwrap ```markdown / ```md fenced blocks so tables render as HTML
46
+ * instead of as a code block.
47
+ * - Inside any fenced block (or top-level body), expand compact one-line
48
+ * markdown tables.
49
+ */
50
+ function normalizeAssistantMarkdown(text) {
51
+ if (!text)
52
+ return text;
53
+ // Unwrap explicit ```markdown / ```md fences and expand compact tables.
54
+ let out = text.replace(/```(markdown|md)\s*\n([\s\S]*?)```/gi, (_match, _lang, body) => {
55
+ const expanded = expandCompactMarkdownTableBody(body.trim());
56
+ return `\n\n${expanded}\n\n`;
57
+ });
58
+ // Also expand compact tables inside other fenced blocks that happen to
59
+ // carry a markdown table (e.g. bare ``` fences).
60
+ out = out.replace(/```([a-zA-Z0-9_+-]*)\n([\s\S]*?)```/g, (match, lang, body) => {
61
+ const expanded = expandCompactMarkdownTableBody(body);
62
+ if (expanded === body)
63
+ return match;
64
+ // If we expanded an unlabeled fence containing a real markdown table,
65
+ // unwrap it so it renders as a table.
66
+ if (!lang || /^(text|plain)$/i.test(lang)) {
67
+ return `\n\n${expanded.trim()}\n\n`;
68
+ }
69
+ return `\`\`\`${lang}\n${expanded}\`\`\``;
70
+ });
71
+ return out;
72
+ }
73
+ // ---------------------------------------------------------------------------
74
+ // DefaultToolCallRenderer — reads approvals from the Zustand store,
75
+ // sends decisions over the shared WebSocket (no REST polling).
76
+ // ---------------------------------------------------------------------------
77
+ function DefaultToolCallRenderer({ item, onRespond, }) {
23
78
  const resultObject = item.result && typeof item.result === 'object'
24
79
  ? item.result
25
80
  : undefined;
26
81
  // Detect pending approval from tool result (pydantic-deferred mode)
27
82
  const isPendingFromResult = item.status === 'inProgress' && resultObject?.pending_approval === true;
83
+ // Extract the approval_id carried in the tool result by the adapter.
84
+ // This is always available when the SSE stream contained a
85
+ // `tool-approval-request` event, even when the Zustand store has not been
86
+ // populated (e.g. no monitoring WS open).
87
+ const resultApprovalId = typeof resultObject?.approval_id === 'string'
88
+ ? resultObject.approval_id
89
+ : undefined;
28
90
  // In ai-agents-wrapper mode, the server blocks waiting for approval and
29
91
  // never sends a tool-result with pending_approval. The tool stays in
30
- // 'executing' status. We proactively poll the approval API to detect if
31
- // a pending approval record exists for this tool.
92
+ // 'executing' status.
32
93
  const isExecuting = item.status === 'executing';
33
- const [matchedApprovalId, setMatchedApprovalId] = useState(null);
34
94
  const [decision, setDecision] = useState();
35
- const [loading, setLoading] = useState(false);
36
- // Should we poll the approval API?
37
- const shouldPoll = (isPendingFromResult || isExecuting) &&
38
- !!approvalConfig?.apiBaseUrl &&
39
- !decision;
40
- // Normalize tool name for matching (strip version suffix, dashes/underscores, lowercase)
41
- const normalizedToolName = item.toolName
42
- .replace(/:[0-9]+\.[0-9]+\.[0-9]+.*$/, '')
43
- .replace(/[-_]/g, '')
44
- .toLowerCase();
45
- // Poll for matching approval record when tool is executing or pending approval
46
- useEffect(() => {
47
- if (!shouldPoll || !approvalConfig?.apiBaseUrl)
95
+ const normalizedToolName = useMemo(() => normalizeName(item.toolName), [item.toolName]);
96
+ // Skill tools (`run_skill_script`, `load_skill`, `read_skill_resource`) are
97
+ // gated by the server under a synthetic approval ``tool_name`` of the form
98
+ // ``skill:<skill_id>`` (see ``SkillsGuardrailCapability._request_skill_approval``).
99
+ // Capture the skill id carried in the tool-call arguments so we can match
100
+ // the inline approval button against that synthetic approval entry.
101
+ const skillApprovalKey = useMemo(() => {
102
+ const SKILL_TOOLS = new Set([
103
+ 'run_skill_script',
104
+ 'load_skill',
105
+ 'read_skill_resource',
106
+ ]);
107
+ if (!SKILL_TOOLS.has(item.toolName))
108
+ return null;
109
+ const a = (item.args ?? {});
110
+ const raw = a.skill_name ?? a.skill ?? a.name;
111
+ if (typeof raw !== 'string' || raw.length === 0)
112
+ return null;
113
+ // Strip optional ``<name>:<version>`` suffix to the base skill id.
114
+ const base = raw.split(':', 1)[0] || raw;
115
+ return `skill:${base}`.toLowerCase();
116
+ }, [item.toolName, item.args]);
117
+ // Read pending approvals from the Zustand store (fed by the agent-runtime WS).
118
+ const approvals = useAgentRuntimeStore(s => s.approvals);
119
+ // Prefer an exact match by approval id (when present in SSE tool result).
120
+ const matchedByResultId = useMemo(() => {
121
+ if (!resultApprovalId)
122
+ return null;
123
+ return approvals.find(a => a.id === resultApprovalId) ?? null;
124
+ }, [approvals, resultApprovalId]);
125
+ // Match the synthetic ``skill:<id>`` approval for skill tool calls. This
126
+ // runs whenever the tool call is a skill tool so the inline Approve/Deny
127
+ // buttons surface even when the server is blocked in
128
+ // ``ToolApprovalManager.request_and_wait`` (status stays ``executing`` and
129
+ // no ``pending_approval=True`` result is ever emitted).
130
+ const matchedBySkill = useMemo(() => {
131
+ if (!skillApprovalKey)
132
+ return null;
133
+ return (approvals.find(a => a.tool_name?.toLowerCase() === skillApprovalKey) ??
134
+ null);
135
+ }, [approvals, skillApprovalKey]);
136
+ // Fallback to matching by tool name for inline approval flows that don't
137
+ // always provide approval_id in the tool result payload.
138
+ const matchedByName = useMemo(() => {
139
+ if (!isPendingFromResult && !isExecuting)
140
+ return null;
141
+ return (approvals.find(a => normalizeName(a.tool_name) === normalizedToolName) ??
142
+ null);
143
+ }, [approvals, normalizedToolName, isPendingFromResult, isExecuting]);
144
+ const matchedApproval = matchedByResultId ?? matchedBySkill ?? matchedByName;
145
+ // Prefer the store-matched approval id, fall back to the id from the tool
146
+ // result (SSE path). This ensures the approve/deny buttons work even when
147
+ // the monitoring WS is not connected.
148
+ const effectiveApprovalId = matchedApproval?.id ?? resultApprovalId ?? null;
149
+ // Reflect approval decisions that happened outside this card (e.g. sidebar).
150
+ const externalDecision = matchedApproval?.status === 'approved'
151
+ ? 'approved'
152
+ : matchedApproval?.status === 'rejected'
153
+ ? 'denied'
154
+ : undefined;
155
+ const makeDecision = useCallback((action) => {
156
+ if (!effectiveApprovalId)
48
157
  return;
49
- let cancelled = false;
50
- const poll = async () => {
51
- try {
52
- const headers = {
53
- 'Content-Type': 'application/json',
54
- };
55
- if (approvalConfig.authToken) {
56
- headers['Authorization'] = `Bearer ${approvalConfig.authToken}`;
57
- }
58
- const res = await fetch(`${approvalConfig.apiBaseUrl}/tool-approvals?status=pending`, { headers });
59
- if (!res.ok || cancelled)
60
- return;
61
- const data = await res.json();
62
- const approvals = Array.isArray(data)
63
- ? data
64
- : (data.approvals ??
65
- data.requests ??
66
- []);
67
- // Match by normalized tool name
68
- const match = approvals.find(a => {
69
- const aNormalized = String(a.tool_name || '')
70
- .replace(/:[0-9]+\.[0-9]+\.[0-9]+.*$/, '')
71
- .replace(/[-_]/g, '')
72
- .toLowerCase();
73
- return aNormalized === normalizedToolName && a.status === 'pending';
74
- });
75
- if (match && !cancelled) {
76
- setMatchedApprovalId(match.id);
77
- }
78
- }
79
- catch {
80
- // Retry on next interval
81
- }
82
- };
83
- poll();
84
- const interval = setInterval(poll, 3000);
85
- return () => {
86
- cancelled = true;
87
- clearInterval(interval);
88
- };
89
- }, [
90
- shouldPoll,
91
- approvalConfig?.apiBaseUrl,
92
- approvalConfig?.authToken,
93
- normalizedToolName,
94
- ]);
95
- const makeDecision = useCallback(async (action) => {
96
- if (!matchedApprovalId || !approvalConfig?.apiBaseUrl)
97
- return;
98
- setLoading(true);
99
- try {
100
- const headers = {
101
- 'Content-Type': 'application/json',
102
- };
103
- if (approvalConfig.authToken) {
104
- headers['Authorization'] = `Bearer ${approvalConfig.authToken}`;
105
- }
106
- const res = await fetch(`${approvalConfig.apiBaseUrl}/tool-approvals/${matchedApprovalId}/${action}`, {
107
- method: 'POST',
108
- headers,
109
- body: JSON.stringify({}),
110
- });
111
- if (res.ok) {
112
- setDecision(action === 'approve' ? 'approved' : 'denied');
113
- onRespond({
114
- type: 'tool-approval-decision',
115
- approved: action === 'approve',
116
- approvalId: matchedApprovalId,
117
- toolName: item.toolName,
118
- });
119
- }
120
- }
121
- catch {
122
- // Allow retry
123
- }
124
- finally {
125
- setLoading(false);
126
- }
127
- }, [
128
- matchedApprovalId,
129
- approvalConfig?.apiBaseUrl,
130
- approvalConfig?.authToken,
131
- onRespond,
132
- item.toolCallId,
133
- item.toolName,
134
- ]);
158
+ const approved = action === 'approve';
159
+ // Use ONLY the adapter continuation path (SSE/POST). Sending both WS and
160
+ // continuation decisions can trigger duplicate approval cycles.
161
+ setDecision(approved ? 'approved' : 'denied');
162
+ onRespond({
163
+ type: 'tool-approval-decision',
164
+ approved,
165
+ approvalId: effectiveApprovalId,
166
+ toolName: item.toolName,
167
+ });
168
+ }, [effectiveApprovalId, onRespond, item.toolName]);
135
169
  // Show approval UI when we have a confirmed pending approval (from result
136
- // or discovered via polling) or after a decision has been made.
137
- const hasApproval = isPendingFromResult || !!matchedApprovalId;
138
- const approvalState = decision || (hasApproval ? 'pending' : undefined);
139
- return (_jsx(ToolCallDisplay, { toolCallId: item.toolCallId, toolName: item.toolName, args: item.args, result: item.result, status: item.status, error: item.error, executionError: item.executionError, codeError: item.codeError, exitCode: item.exitCode, approvalRequired: hasApproval, approvalState: approvalState, onApprove: matchedApprovalId && !decision
140
- ? () => void makeDecision('approve')
141
- : undefined, onDeny: matchedApprovalId && !decision
142
- ? () => void makeDecision('reject')
143
- : undefined, approvalLoading: loading }));
170
+ // or discovered via the store) or after a decision has been made.
171
+ const hasApproval = isPendingFromResult || !!effectiveApprovalId || !!externalDecision;
172
+ const approvalState = decision || externalDecision || (hasApproval ? 'pending' : undefined);
173
+ const approvalDecisionSource = decision
174
+ ? 'inline'
175
+ : externalDecision
176
+ ? 'external'
177
+ : undefined;
178
+ return (_jsx(ToolCallDisplay, { toolCallId: item.toolCallId, toolName: item.toolName, args: item.args, result: item.result, status: item.status, error: item.error, executionError: item.executionError, codeError: item.codeError, exitCode: item.exitCode, approvalRequired: hasApproval, approvalState: approvalState, approvalDecisionSource: approvalDecisionSource, onApprove: effectiveApprovalId && !decision && !externalDecision
179
+ ? () => makeDecision('approve')
180
+ : undefined, onDeny: effectiveApprovalId && !decision && !externalDecision
181
+ ? () => makeDecision('reject')
182
+ : undefined, approvalLoading: false }));
144
183
  }
145
184
  // ---------------------------------------------------------------------------
146
185
  // Component
@@ -271,6 +310,7 @@ export function ChatMessageList({ displayItems, isLoading, isStreaming, showLoad
271
310
  : avatarConfig.assistantAvatar })), _jsx(Box, { sx: {
272
311
  maxWidth: '85%',
273
312
  p: 2,
313
+ overflowX: 'auto',
274
314
  borderRadius: 2,
275
315
  backgroundColor: isUser ? 'accent.emphasis' : 'canvas.subtle',
276
316
  // Use primary-button text token for better contrast when
@@ -283,7 +323,7 @@ export function ChatMessageList({ displayItems, isLoading, isStreaming, showLoad
283
323
  fontSize: 1,
284
324
  whiteSpace: 'pre-wrap',
285
325
  wordBreak: 'break-word',
286
- }, children: getMessageText(message) })) : (_jsx(Box, { sx: streamdownMarkdownStyles, children: _jsx(Streamdown, { children: getMessageText(message) || (isStreaming ? '...' : '') }) })) })] }) }, message.id));
326
+ }, children: getMessageText(message) })) : (_jsx(Box, { sx: streamdownMarkdownStyles, children: _jsx(Streamdown, { children: normalizeAssistantMarkdown(getMessageText(message) || (isStreaming ? '...' : '')) }) })) })] }) }, message.id));
287
327
  }), showLoadingIndicator && (isLoading || isStreaming) && (_jsx(Box, { sx: {
288
328
  display: 'flex',
289
329
  alignItems: 'flex-start',
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  */
12
12
  import { useRef, useEffect } from 'react';
13
13
  import { Text, RelativeTime } from '@primer/react';
14
- import { Box } from '@datalayer/primer-addons';
14
+ import { Box, useThemeStore, getColorPalette } from '@datalayer/primer-addons';
15
15
  import { PersonIcon, ToolsIcon } from '@primer/octicons-react';
16
16
  import { AiAgentIcon } from '@datalayer/icons-react';
17
17
  import { useChatMessages, useChatExtensionRegistry, } from '../../stores/chatStore';
@@ -24,6 +24,10 @@ export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps
24
24
  const extensionRegistry = extensionRegistryProp ?? storeExtensionRegistry;
25
25
  const containerRef = useRef(null);
26
26
  const lastMessageRef = useRef(null);
27
+ // Resolve a light color from the current user theme palette so the
28
+ // assistant icon contrasts the saturated `accent.emphasis` avatar bg.
29
+ const { theme, colorMode } = useThemeStore();
30
+ const assistantIconColor = getColorPalette(theme, colorMode).primary;
27
31
  // Auto-scroll to bottom on new messages
28
32
  useEffect(() => {
29
33
  if (autoScroll && lastMessageRef.current) {
@@ -73,7 +77,7 @@ export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps
73
77
  color: message.role === 'user'
74
78
  ? 'fg.default'
75
79
  : 'var(--button-primary-fgColor-rest, var(--fgColor-onEmphasis))',
76
- }, children: message.role === 'user' ? (_jsx(PersonIcon, { size: 16 })) : message.role === 'assistant' ? (_jsx(AiAgentIcon, { size: 16 })) : (_jsx(ToolsIcon, { size: 16 })) }) })), _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: {
80
+ }, children: message.role === 'user' ? (_jsx(PersonIcon, { size: 16 })) : message.role === 'assistant' ? (_jsx(AiAgentIcon, { size: 16, color: assistantIconColor })) : (_jsx(ToolsIcon, { size: 16 })) }) })), _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: {
77
81
  display: 'flex',
78
82
  alignItems: 'center',
79
83
  gap: 2,
@@ -1,20 +1,29 @@
1
- import type { BuiltinTool, ContextSnapshotData, MCPServerConfig, ModelConfig, SkillInfo } from '../../types';
1
+ import type { KernelMessage } from '@jupyterlab/services';
2
+ import type { BuiltinTool, ContextSnapshotData, MCPServerConfig, McpToolsetsStatusResponse, ModelConfig, SkillInfo } from '../../types';
2
3
  export interface InputToolbarProps {
3
4
  input: string;
4
5
  setInput: (value: string) => void;
5
6
  isLoading: boolean;
7
+ kernelStatus?: KernelMessage.Status;
8
+ connectionConfirmed: boolean;
6
9
  placeholder?: string;
7
10
  autoFocus: boolean;
8
11
  focusTrigger?: number;
9
12
  padding: number;
10
13
  onSend: () => void;
11
14
  onStop: () => void;
15
+ disableInputPrompt?: boolean;
12
16
  showTokenUsage: boolean;
13
17
  agentUsage?: ContextSnapshotData;
14
18
  showModelSelector: boolean;
15
19
  showToolsMenu: boolean;
16
20
  showSkillsMenu: boolean;
17
21
  codemodeEnabled: boolean;
22
+ /**
23
+ * Optional callback invoked when the user toggles codemode from the Tools
24
+ * menu. When omitted the toggle renders as read-only.
25
+ */
26
+ onToggleCodemode?: (enabled: boolean) => void | Promise<void>;
18
27
  isA2AProtocol: boolean;
19
28
  hasConfigData: boolean;
20
29
  hasSkillsData: boolean;
@@ -28,16 +37,22 @@ export interface InputToolbarProps {
28
37
  enabledMcpToolCount: number;
29
38
  onToggleMcpTool: (serverId: string, toolName: string) => void;
30
39
  onToggleAllMcpServerTools: (serverId: string, toolNames: string[], enable: boolean) => void;
40
+ /** Approved MCP tools per server (default: all tools approved) */
41
+ approvedMcpTools: Map<string, Set<string>>;
42
+ onToggleMcpToolApproval: (serverId: string, toolName: string) => void;
31
43
  skills: SkillInfo[];
32
44
  skillsLoading: boolean;
33
45
  enabledSkills: Set<string>;
34
46
  onToggleSkill: (skillId: string) => void;
35
47
  onToggleAllSkills: (skillIds: string[], enable: boolean) => void;
36
- /** API base URL passed to MCP / Sandbox indicators */
48
+ /** Approved skills set (default: all skills approved) */
49
+ approvedSkills: Set<string>;
50
+ onToggleSkillApproval: (skillId: string) => void;
51
+ /** API base URL passed to MCP indicator */
37
52
  apiBase?: string;
38
- /** Auth token passed to MCP / Sandbox indicators */
53
+ /** Auth token passed to MCP indicator */
39
54
  authToken?: string;
40
- /** Agent ID passed to Sandbox indicator for agent-scoped status */
41
- agentId?: string;
55
+ /** Pre-fetched MCP status from WebSocket bypasses REST polling */
56
+ mcpStatusData?: McpToolsetsStatusResponse | null;
42
57
  }
43
- export declare function InputToolbar({ input, setInput, isLoading, placeholder, autoFocus, focusTrigger, padding, onSend, onStop, showTokenUsage, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, codemodeEnabled, isA2AProtocol, hasConfigData, hasSkillsData, models, selectedModel, onModelSelect, availableTools, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, apiBase, authToken, agentId, }: InputToolbarProps): import("react/jsx-runtime").JSX.Element;
58
+ export declare function InputToolbar({ input, setInput, isLoading, kernelStatus, connectionConfirmed, placeholder, autoFocus, focusTrigger, padding, onSend, onStop, disableInputPrompt, showTokenUsage, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, models, selectedModel, onModelSelect, availableTools, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, approvedMcpTools, onToggleMcpToolApproval, skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, apiBase, authToken, mcpStatusData, }: InputToolbarProps): import("react/jsx-runtime").JSX.Element;
@@ -17,30 +17,49 @@ import { ToolsIcon, BriefcaseIcon, AiModelIcon } from '@primer/octicons-react';
17
17
  import { InputPrompt } from './InputPrompt';
18
18
  import { TokenUsageBar } from '../usage/TokenUsageBar';
19
19
  import { McpStatusIndicator } from '../indicators/McpStatusIndicator';
20
- import { SandboxStatusIndicator } from '../indicators/SandboxStatusIndicator';
20
+ import { SkillsStatusIndicator } from '../indicators/SkillsStatusIndicator';
21
21
  // ---------------------------------------------------------------------------
22
22
  // Component
23
23
  // ---------------------------------------------------------------------------
24
- export function InputToolbar({ input, setInput, isLoading, placeholder, autoFocus, focusTrigger, padding, onSend, onStop, showTokenUsage, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, codemodeEnabled, isA2AProtocol, hasConfigData, hasSkillsData, models, selectedModel, onModelSelect, availableTools, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, apiBase, authToken, agentId, }) {
24
+ export function InputToolbar({ input, setInput, isLoading, kernelStatus, connectionConfirmed, placeholder, autoFocus, focusTrigger, padding, onSend, onStop, disableInputPrompt = false, showTokenUsage, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, models, selectedModel, onModelSelect, availableTools, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, approvedMcpTools, onToggleMcpToolApproval, skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, apiBase, authToken, mcpStatusData, }) {
25
+ const isKernelBusy = kernelStatus === 'busy';
26
+ const showSelectorsBar = showModelSelector || showToolsMenu || showSkillsMenu;
27
+ const hasSelectorsContent = hasConfigData || hasSkillsData;
25
28
  // Show token usage when we have valid context data
26
- const hasContext = agentUsage && !agentUsage.error && agentUsage.totalTokens > 0;
27
- return (_jsxs(Box, { children: [_jsx(InputPrompt, { placeholder: placeholder || 'Type a message...', isLoading: isLoading, onSend: onSend, onStop: onStop, autoFocus: autoFocus, focusTrigger: focusTrigger, padding: padding, value: input, onChange: setInput, footerRightContent: _jsxs(_Fragment, { children: [_jsx(SandboxStatusIndicator, { apiBase: apiBase, authToken: authToken, agentId: agentId }), _jsx(McpStatusIndicator, { apiBase: apiBase, authToken: authToken })] }) }), showTokenUsage && hasContext && (_jsx(TokenUsageBar, { agentUsage: agentUsage, padding: padding })), (showModelSelector || showToolsMenu || showSkillsMenu) &&
28
- (hasConfigData || hasSkillsData) && (_jsxs(Box, { sx: {
29
+ const hasContext = Boolean(agentUsage && !agentUsage.error && agentUsage.totalTokens > 0);
30
+ return (_jsxs(Box, { children: [_jsx(InputPrompt, { placeholder: placeholder || 'Type a message...', isLoading: isLoading, isKernelBusy: isKernelBusy, disabled: disableInputPrompt, readOnly: !connectionConfirmed, onSend: onSend, onStop: onStop, autoFocus: autoFocus, focusTrigger: focusTrigger, padding: padding, value: input, onChange: setInput, footerRightContent: _jsxs(_Fragment, { children: [_jsx(McpStatusIndicator, { apiBase: apiBase, authToken: authToken, data: mcpStatusData }), _jsx(SkillsStatusIndicator, { skillsCount: skills.length, enabledCount: enabledSkills.size, loading: skillsLoading })] }) }), showTokenUsage && (_jsx(Box, { sx: { minHeight: hasContext && agentUsage ? 28 : 8 }, children: hasContext && agentUsage ? (_jsx(TokenUsageBar, { agentUsage: agentUsage, padding: padding })) : null })), showSelectorsBar && (_jsx(Box, { sx: {
29
31
  display: 'flex',
30
32
  gap: 2,
31
33
  px: padding,
32
- py: 1,
34
+ py: 0.5,
35
+ minHeight: 36,
33
36
  borderTop: '1px solid',
34
37
  borderColor: 'border.default',
35
38
  alignItems: 'center',
36
39
  bg: 'canvas.subtle',
37
- }, children: [showToolsMenu && (_jsx(ToolsMenu, { codemodeEnabled: codemodeEnabled, mcpServers: mcpServers, enabledMcpTools: enabledMcpTools, enabledMcpToolCount: enabledMcpToolCount, onToggleMcpTool: onToggleMcpTool, onToggleAllMcpServerTools: onToggleAllMcpServerTools, availableTools: availableTools })), showSkillsMenu && (_jsx(SkillsMenu, { skills: skills, skillsLoading: skillsLoading, enabledSkills: enabledSkills, onToggleSkill: onToggleSkill, onToggleAllSkills: onToggleAllSkills })), showModelSelector && models.length > 0 && selectedModel && (_jsx(ModelSelector, { models: models, selectedModel: selectedModel, onModelSelect: onModelSelect, isA2AProtocol: isA2AProtocol }))] }))] }));
40
+ }, children: hasSelectorsContent ? (_jsxs(_Fragment, { children: [showToolsMenu && (_jsx(ToolsMenu, { codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, mcpServers: mcpServers, enabledMcpTools: enabledMcpTools, enabledMcpToolCount: enabledMcpToolCount, onToggleMcpTool: onToggleMcpTool, onToggleAllMcpServerTools: onToggleAllMcpServerTools, approvedMcpTools: approvedMcpTools, onToggleMcpToolApproval: onToggleMcpToolApproval, availableTools: availableTools })), showSkillsMenu && (_jsx(SkillsMenu, { skills: skills, skillsLoading: skillsLoading, enabledSkills: enabledSkills, onToggleSkill: onToggleSkill, onToggleAllSkills: onToggleAllSkills, approvedSkills: approvedSkills, onToggleSkillApproval: onToggleSkillApproval })), showModelSelector && models.length > 0 && selectedModel && (_jsx(ModelSelector, { models: models, selectedModel: selectedModel, onModelSelect: onModelSelect, isA2AProtocol: isA2AProtocol }))] })) : (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Loading controls..." })) }))] }));
38
41
  }
39
42
  // ---------------------------------------------------------------------------
40
43
  // ToolsMenu (private sub-component)
41
44
  // ---------------------------------------------------------------------------
42
- function ToolsMenu({ codemodeEnabled, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, availableTools, }) {
43
- return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { type: "button", variant: "invisible", size: "small", leadingVisual: ToolsIcon, children: _jsxs(Text, { sx: { fontSize: 0 }, children: ["Tools", enabledMcpToolCount > 0 && ` (${enabledMcpToolCount})`] }) }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "start", width: "large", children: _jsx(Box, { sx: { maxHeight: '60vh', overflowY: 'auto' }, children: _jsxs(ActionList, { children: [codemodeEnabled && (_jsx(ActionList.Group, { title: "Codemode", children: _jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "MCP tools are accessible via Codemode meta-tools (search_tools, list_tool_names, execute_code)." }) }) })), mcpServers.length > 0 ? (mcpServers.map(server => {
45
+ function ToolsMenu({ codemodeEnabled, onToggleCodemode, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, approvedMcpTools, onToggleMcpToolApproval, availableTools: _availableTools, }) {
46
+ const hasUsableMcpServers = mcpServers.some(server => server.isAvailable);
47
+ return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { type: "button", variant: "invisible", size: "small", leadingVisual: ToolsIcon, children: _jsxs(Text, { sx: { fontSize: 0 }, children: ["Tools", enabledMcpToolCount > 0 && ` (${enabledMcpToolCount})`] }) }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "start", width: "large", children: _jsx(Box, { sx: { maxHeight: '60vh', overflowY: 'auto' }, children: _jsxs(ActionList, { children: [_jsx(ActionList.Group, { title: "Codemode", children: _jsxs(Box, { sx: {
48
+ display: 'flex',
49
+ alignItems: 'center',
50
+ justifyContent: 'space-between',
51
+ px: 3,
52
+ py: 2,
53
+ borderBottom: '1px solid',
54
+ borderColor: 'border.muted',
55
+ gap: 2,
56
+ }, children: [_jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsx(Text, { id: "toggle-codemode", sx: { fontWeight: 'semibold', display: 'block' }, children: "Enable Codemode" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: codemodeEnabled
57
+ ? 'MCP tools accessible via meta-tools (search_tools, execute_code).'
58
+ : 'Expose MCP tools directly to the model.' })] }), _jsx(ToggleSwitch, { size: "small", checked: codemodeEnabled, disabled: !onToggleCodemode, onClick: () => {
59
+ if (onToggleCodemode) {
60
+ void onToggleCodemode(!codemodeEnabled);
61
+ }
62
+ }, "aria-labelledby": "toggle-codemode" })] }) }), mcpServers.length > 0 && hasUsableMcpServers ? (mcpServers.map(server => {
44
63
  const serverTools = enabledMcpTools.get(server.id);
45
64
  const allToolNames = server.tools.map(t => t.name);
46
65
  const enabledCount = serverTools?.size ?? 0;
@@ -60,6 +79,10 @@ function ToolsMenu({ codemodeEnabled, mcpServers, enabledMcpTools, enabledMcpToo
60
79
  color: 'fg.muted',
61
80
  }, children: ["Enable all (", enabledCount, "/", allToolNames.length, ")"] }), _jsx(ToggleSwitch, { size: "small", checked: allEnabled, onClick: () => onToggleAllMcpServerTools(server.id, allToolNames, !allEnabled), "aria-labelledby": `toggle-all-${server.id}` })] })), server.isAvailable && server.tools.length > 0 ? (server.tools.map(tool => {
62
81
  const isEnabled = serverTools?.has(tool.name) ?? false;
82
+ const serverApproved = approvedMcpTools.get(server.id);
83
+ // Tools default to NOT approved — user must explicitly
84
+ // approve each one (matches the Skills approval UX).
85
+ const isApproved = serverApproved?.has(tool.name) ?? false;
63
86
  return (_jsxs(Box, { sx: {
64
87
  display: 'flex',
65
88
  alignItems: 'center',
@@ -76,20 +99,29 @@ function ToolsMenu({ codemodeEnabled, mcpServers, enabledMcpTools, enabledMcpToo
76
99
  overflow: 'hidden',
77
100
  textOverflow: 'ellipsis',
78
101
  whiteSpace: 'nowrap',
79
- }, children: tool.description }))] }), _jsx(ToggleSwitch, { size: "small", checked: isEnabled, onClick: () => onToggleMcpTool(server.id, tool.name), "aria-labelledby": `toggle-tool-${server.id}-${tool.name}` })] }, `${server.id}-${tool.name}`));
102
+ }, children: tool.description }))] }), _jsxs(Box, { sx: {
103
+ display: 'flex',
104
+ alignItems: 'center',
105
+ gap: 3,
106
+ }, children: [_jsxs(Box, { sx: {
107
+ display: 'flex',
108
+ flexDirection: 'column',
109
+ alignItems: 'center',
110
+ gap: '2px',
111
+ }, children: [_jsx(Text, { sx: { fontSize: '10px', color: 'fg.muted' }, children: "Enabled" }), _jsx(ToggleSwitch, { size: "small", checked: isEnabled, onClick: () => onToggleMcpTool(server.id, tool.name), "aria-labelledby": `toggle-tool-${server.id}-${tool.name}` })] }), _jsxs(Box, { sx: {
112
+ display: 'flex',
113
+ flexDirection: 'column',
114
+ alignItems: 'center',
115
+ gap: '2px',
116
+ }, children: [_jsx(Text, { sx: { fontSize: '10px', color: 'fg.muted' }, children: "Approved" }), _jsx(ToggleSwitch, { size: "small", checked: isApproved, onClick: () => onToggleMcpToolApproval(server.id, tool.name), "aria-labelledby": `toggle-tool-${server.id}-${tool.name}` })] })] })] }, `${server.id}-${tool.name}`));
80
117
  })) : server.isAvailable ? (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted', fontStyle: 'italic' }, children: "No tools discovered" }) })) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted', fontStyle: 'italic' }, children: "Server unavailable" }) }))] }, server.id));
81
- })) : (_jsx(ActionList.Group, { title: "Available Tools", children: availableTools.length > 0 ? (availableTools.map(tool => (_jsxs(ActionList.Item, { disabled: true, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(Box, { sx: {
82
- width: 8,
83
- height: 8,
84
- borderRadius: '50%',
85
- backgroundColor: 'success.emphasis',
86
- } }) }), tool.name] }, tool.id)))) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted', fontStyle: 'italic' }, children: "No tools available" }) })) }))] }) }) })] }));
118
+ })) : (_jsx(ActionList.Group, { title: "No MCP Servers", children: _jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted', fontStyle: 'italic' }, children: "No MCP Servers" }) }) }))] }) }) })] }));
87
119
  }
88
120
  // ---------------------------------------------------------------------------
89
121
  // SkillsMenu (private sub-component)
90
122
  // ---------------------------------------------------------------------------
91
- function SkillsMenu({ skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, }) {
92
- return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { type: "button", variant: "invisible", size: "small", leadingVisual: BriefcaseIcon, children: _jsxs(Text, { sx: { fontSize: 0 }, children: ["Skills", enabledSkills.size > 0 && ` (${enabledSkills.size})`] }) }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "start", width: "large", children: _jsx(Box, { sx: { maxHeight: '60vh', overflowY: 'auto' }, children: _jsx(ActionList, { children: skillsLoading ? (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted' }, children: "Loading skills..." }) })) : skills.length > 0 ? (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
123
+ function SkillsMenu({ skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, }) {
124
+ return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { type: "button", variant: "invisible", size: "small", leadingVisual: BriefcaseIcon, children: _jsxs(Text, { sx: { fontSize: 0 }, children: ["Skills", skills.length > 0 && ` (${skills.length})`] }) }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "start", width: "large", children: _jsx(Box, { sx: { maxHeight: '60vh', overflowY: 'auto' }, children: _jsx(ActionList, { children: skillsLoading ? (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted' }, children: "Loading skills..." }) })) : skills.length > 0 ? (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
93
125
  display: 'flex',
94
126
  alignItems: 'center',
95
127
  justifyContent: 'space-between',
@@ -110,14 +142,38 @@ function SkillsMenu({ skills, skillsLoading, enabledSkills, onToggleSkill, onTog
110
142
  '&:hover': {
111
143
  backgroundColor: 'canvas.subtle',
112
144
  },
113
- }, children: [_jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsx(Text, { id: `toggle-skill-${skill.id}`, sx: { fontWeight: 'semibold' }, children: skill.name }), skill.description && (_jsx(Text, { sx: {
145
+ }, children: [_jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [_jsx(Text, { id: `toggle-skill-${skill.id}`, sx: { fontWeight: 'semibold' }, children: skill.name }), skill.status && (_jsx(Text, { sx: {
146
+ fontSize: '10px',
147
+ px: 1,
148
+ borderRadius: 2,
149
+ bg: skill.status === 'loaded'
150
+ ? 'success.subtle'
151
+ : skill.status === 'enabled'
152
+ ? 'attention.subtle'
153
+ : 'neutral.subtle',
154
+ color: skill.status === 'loaded'
155
+ ? 'success.fg'
156
+ : skill.status === 'enabled'
157
+ ? 'attention.fg'
158
+ : 'fg.muted',
159
+ }, children: skill.status }))] }), skill.description && (_jsx(Text, { sx: {
114
160
  display: 'block',
115
161
  fontSize: 0,
116
162
  color: 'fg.muted',
117
163
  overflow: 'hidden',
118
164
  textOverflow: 'ellipsis',
119
165
  whiteSpace: 'nowrap',
120
- }, children: skill.description }))] }), _jsx(ToggleSwitch, { size: "small", checked: enabledSkills.has(skill.id), onClick: () => onToggleSkill(skill.id), "aria-labelledby": `toggle-skill-${skill.id}` })] }, skill.id)))] })) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted', fontStyle: 'italic' }, children: "No skills available" }) })) }) }) })] }));
166
+ }, children: skill.description }))] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 3 }, children: [_jsxs(Box, { sx: {
167
+ display: 'flex',
168
+ flexDirection: 'column',
169
+ alignItems: 'center',
170
+ gap: '2px',
171
+ }, children: [_jsx(Text, { sx: { fontSize: '10px', color: 'fg.muted' }, children: "Enabled" }), _jsx(ToggleSwitch, { size: "small", checked: enabledSkills.has(skill.id), onClick: () => onToggleSkill(skill.id), "aria-labelledby": `toggle-skill-${skill.id}` })] }), _jsxs(Box, { sx: {
172
+ display: 'flex',
173
+ flexDirection: 'column',
174
+ alignItems: 'center',
175
+ gap: '2px',
176
+ }, children: [_jsx(Text, { sx: { fontSize: '10px', color: 'fg.muted' }, children: "Approved" }), _jsx(ToggleSwitch, { size: "small", checked: approvedSkills.has(skill.id), onClick: () => onToggleSkillApproval(skill.id), "aria-labelledby": `toggle-skill-${skill.id}` })] })] })] }, skill.id)))] })) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted', fontStyle: 'italic' }, children: "No skills available" }) })) }) }) })] }));
121
177
  }
122
178
  // ---------------------------------------------------------------------------
123
179
  // ModelSelector (private sub-component)
@@ -24,6 +24,8 @@ export interface InputPromptProps {
24
24
  placeholder?: string;
25
25
  /** Whether the agent is loading / streaming */
26
26
  isLoading?: boolean;
27
+ /** Whether the connected kernel is currently busy */
28
+ isKernelBusy?: boolean;
27
29
  /** Callback when a message is submitted */
28
30
  onSend: (message: string) => void;
29
31
  /** Callback when the stop button is clicked */
@@ -40,6 +42,8 @@ export interface InputPromptProps {
40
42
  padding?: number;
41
43
  /** Whether the prompt is disabled */
42
44
  disabled?: boolean;
45
+ /** Whether the prompt is read-only */
46
+ readOnly?: boolean;
43
47
  /** Additional sx props for the outer container */
44
48
  sx?: Record<string, unknown>;
45
49
  /** Controlled input value (external state) */
@@ -56,5 +60,5 @@ export interface InputPromptProps {
56
60
  /**
57
61
  * InputPrompt — Integrated chat input with header, input area, and footer.
58
62
  */
59
- export declare function InputPrompt({ variant, placeholder, isLoading, onSend, onStop, autoFocus, focusTrigger, showBorderTop, showBackground, padding, disabled, sx, value: controlledValue, onChange: controlledOnChange, headerContent, footerContent, footerRightContent, }: InputPromptProps): import("react/jsx-runtime").JSX.Element;
63
+ export declare function InputPrompt({ variant, placeholder, isLoading, isKernelBusy, onSend, onStop, autoFocus, focusTrigger, showBorderTop, showBackground, padding, disabled, readOnly, sx, value: controlledValue, onChange: controlledOnChange, headerContent, footerContent, footerRightContent, }: InputPromptProps): import("react/jsx-runtime").JSX.Element;
60
64
  export default InputPrompt;