@datalayer/agent-runtimes 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/README.md +34 -0
  2. package/lib/App.js +1 -1
  3. package/lib/agents/AgentDetails.d.ts +22 -1
  4. package/lib/agents/AgentDetails.js +34 -47
  5. package/lib/api/index.d.ts +0 -1
  6. package/lib/api/index.js +4 -2
  7. package/lib/chat/Chat.d.ts +5 -106
  8. package/lib/chat/Chat.js +4 -4
  9. package/lib/chat/ChatFloating.d.ts +7 -140
  10. package/lib/chat/ChatFloating.js +2 -2
  11. package/lib/chat/ChatPopupStandalone.d.ts +8 -47
  12. package/lib/chat/ChatPopupStandalone.js +3 -3
  13. package/lib/chat/ChatSidebar.d.ts +4 -69
  14. package/lib/chat/ChatSidebar.js +2 -2
  15. package/lib/chat/ChatStandalone.d.ts +4 -54
  16. package/lib/chat/ChatStandalone.js +3 -3
  17. package/lib/chat/base/ChatBase.js +1083 -157
  18. package/lib/chat/header/ChatHeaderBase.d.ts +11 -6
  19. package/lib/chat/header/ChatHeaderBase.js +18 -16
  20. package/lib/chat/indicators/McpStatusIndicator.d.ts +7 -4
  21. package/lib/chat/indicators/McpStatusIndicator.js +7 -32
  22. package/lib/chat/indicators/SandboxStatusIndicator.d.ts +4 -1
  23. package/lib/chat/indicators/SandboxStatusIndicator.js +9 -9
  24. package/lib/chat/indicators/SkillsStatusIndicator.d.ts +7 -0
  25. package/lib/chat/indicators/SkillsStatusIndicator.js +88 -0
  26. package/lib/chat/indicators/index.d.ts +1 -0
  27. package/lib/chat/indicators/index.js +1 -0
  28. package/lib/chat/messages/ChatMessageList.d.ts +1 -1
  29. package/lib/chat/messages/ChatMessageList.js +108 -113
  30. package/lib/chat/prompt/InputFooter.d.ts +19 -6
  31. package/lib/chat/prompt/InputFooter.js +71 -18
  32. package/lib/chat/prompt/InputPrompt.d.ts +3 -1
  33. package/lib/chat/prompt/InputPrompt.js +4 -4
  34. package/lib/chat/prompt/InputPromptFooter.js +1 -1
  35. package/lib/chat/prompt/InputPromptLexical.d.ts +3 -1
  36. package/lib/chat/prompt/InputPromptLexical.js +12 -5
  37. package/lib/chat/prompt/InputPromptText.d.ts +3 -1
  38. package/lib/chat/prompt/InputPromptText.js +2 -2
  39. package/lib/chat/tools/ToolApprovalBanner.js +1 -1
  40. package/lib/chat/tools/ToolCallDisplay.d.ts +3 -1
  41. package/lib/chat/tools/ToolCallDisplay.js +2 -2
  42. package/lib/chat/usage/TokenUsageBar.js +20 -2
  43. package/lib/client/AgentRuntimesClientContext.d.ts +53 -0
  44. package/lib/client/AgentRuntimesClientContext.js +55 -0
  45. package/lib/client/AgentsMixin.d.ts +0 -18
  46. package/lib/client/AgentsMixin.js +6 -30
  47. package/lib/client/IAgentRuntimesClient.d.ts +215 -0
  48. package/lib/client/IAgentRuntimesClient.js +5 -0
  49. package/lib/client/SdkAgentRuntimesClient.d.ts +151 -0
  50. package/lib/client/SdkAgentRuntimesClient.js +134 -0
  51. package/lib/client/index.d.ts +4 -1
  52. package/lib/client/index.js +3 -1
  53. package/lib/components/NotificationEventCard.js +5 -1
  54. package/lib/config/AgentConfiguration.js +3 -3
  55. package/lib/context/ContextDistribution.d.ts +3 -1
  56. package/lib/context/ContextDistribution.js +8 -27
  57. package/lib/context/ContextInspector.d.ts +3 -1
  58. package/lib/context/ContextInspector.js +19 -67
  59. package/lib/context/ContextPanel.d.ts +3 -1
  60. package/lib/context/ContextPanel.js +104 -64
  61. package/lib/context/ContextUsage.d.ts +3 -1
  62. package/lib/context/ContextUsage.js +3 -3
  63. package/lib/context/CostTracker.d.ts +9 -3
  64. package/lib/context/CostTracker.js +26 -47
  65. package/lib/context/CostUsageChart.d.ts +12 -0
  66. package/lib/context/CostUsageChart.js +378 -0
  67. package/lib/context/GraphFlowChart.d.ts +16 -0
  68. package/lib/context/GraphFlowChart.js +182 -0
  69. package/lib/context/TokenUsageChart.d.ts +8 -1
  70. package/lib/context/TokenUsageChart.js +349 -211
  71. package/lib/context/TurnGraphChart.d.ts +39 -0
  72. package/lib/context/TurnGraphChart.js +538 -0
  73. package/lib/context/otelWsPool.d.ts +20 -0
  74. package/lib/context/otelWsPool.js +69 -0
  75. package/lib/examples/A2UiComponentGalleryExample.d.ts +0 -17
  76. package/lib/examples/A2UiComponentGalleryExample.js +315 -522
  77. package/lib/examples/A2UiContactCardExample.d.ts +0 -18
  78. package/lib/examples/A2UiContactCardExample.js +154 -411
  79. package/lib/examples/A2UiRestaurantExample.d.ts +0 -30
  80. package/lib/examples/A2UiRestaurantExample.js +114 -212
  81. package/lib/examples/A2UiViewerExample.d.ts +0 -18
  82. package/lib/examples/A2UiViewerExample.js +283 -532
  83. package/lib/examples/AgUiBackendToolRenderingExample.js +1 -1
  84. package/lib/examples/AgUiHaikuGenUiExample.d.ts +1 -1
  85. package/lib/examples/AgUiHaikuGenUiExample.js +1 -1
  86. package/lib/examples/AgentCheckpointsExample.js +13 -27
  87. package/lib/examples/AgentCodemodeExample.d.ts +4 -6
  88. package/lib/examples/AgentCodemodeExample.js +591 -169
  89. package/lib/examples/AgentEvalsExample.js +12 -16
  90. package/lib/examples/AgentGuardrailsExample.js +370 -64
  91. package/lib/examples/AgentHooksExample.d.ts +3 -0
  92. package/lib/examples/AgentHooksExample.js +104 -0
  93. package/lib/examples/AgentMCPExample.d.ts +3 -0
  94. package/lib/examples/AgentMCPExample.js +480 -0
  95. package/lib/examples/AgentMemoryExample.js +13 -17
  96. package/lib/examples/AgentMonitoringExample.js +260 -199
  97. package/lib/examples/AgentNotificationsExample.js +49 -17
  98. package/lib/examples/AgentOtelExample.js +2 -3
  99. package/lib/examples/AgentOutputsExample.d.ts +11 -6
  100. package/lib/examples/AgentOutputsExample.js +382 -81
  101. package/lib/examples/AgentParametersExample.d.ts +3 -0
  102. package/lib/examples/AgentParametersExample.js +246 -0
  103. package/lib/examples/AgentSandboxExample.d.ts +2 -2
  104. package/lib/examples/AgentSandboxExample.js +68 -40
  105. package/lib/examples/AgentSkillsExample.js +91 -99
  106. package/lib/examples/{AgentspecExample.js → AgentSpecsExample.js} +10 -21
  107. package/lib/examples/AgentSubagentsExample.d.ts +14 -0
  108. package/lib/examples/AgentSubagentsExample.js +228 -0
  109. package/lib/examples/AgentToolApprovalsExample.js +29 -557
  110. package/lib/examples/AgentTriggersExample.js +819 -565
  111. package/lib/examples/ChatCustomExample.js +11 -24
  112. package/lib/examples/ChatExample.js +7 -24
  113. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  114. package/lib/examples/CopilotKitNotebookExample.js +2 -1
  115. package/lib/examples/HomeExample.d.ts +15 -0
  116. package/lib/examples/HomeExample.js +77 -0
  117. package/lib/examples/Lexical2Example.js +4 -2
  118. package/lib/examples/{LexicalExample.d.ts → LexicalAgentExample.d.ts} +4 -4
  119. package/lib/examples/{LexicalExample.js → LexicalAgentExample.js} +65 -16
  120. package/lib/examples/{LexicalSidebarExample.d.ts → LexicalAgentSidebarExample.d.ts} +5 -5
  121. package/lib/examples/LexicalAgentSidebarExample.js +261 -0
  122. package/lib/examples/NotebookAgentExample.d.ts +9 -0
  123. package/lib/examples/NotebookAgentExample.js +192 -0
  124. package/lib/examples/{NotebookSidebarExample.d.ts → NotebookAgentSidebarExample.d.ts} +2 -2
  125. package/lib/examples/NotebookAgentSidebarExample.js +221 -0
  126. package/lib/examples/{DatalayerNotebookExample.d.ts → NotebookCollaborationExample.d.ts} +4 -4
  127. package/lib/examples/{DatalayerNotebookExample.js → NotebookCollaborationExample.js} +3 -3
  128. package/lib/examples/NotebookExample.d.ts +4 -7
  129. package/lib/examples/NotebookExample.js +14 -146
  130. package/lib/examples/components/AuthRequiredView.d.ts +6 -0
  131. package/lib/examples/components/AuthRequiredView.js +33 -0
  132. package/lib/examples/components/ExampleWrapper.d.ts +7 -0
  133. package/lib/examples/components/ExampleWrapper.js +25 -6
  134. package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.js +1 -1
  135. package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.js +1 -1
  136. package/lib/examples/{ag-ui → components}/haiku/index.d.ts +1 -1
  137. package/lib/examples/{ag-ui → components}/haiku/index.js +1 -1
  138. package/lib/examples/components/index.d.ts +3 -0
  139. package/lib/examples/components/index.js +4 -0
  140. package/lib/examples/{ag-ui → components}/weather/index.d.ts +1 -1
  141. package/lib/examples/{ag-ui → components}/weather/index.js +1 -1
  142. package/lib/examples/example-selector.d.ts +17 -4
  143. package/lib/examples/example-selector.js +107 -41
  144. package/lib/examples/index.d.ts +9 -6
  145. package/lib/examples/index.js +9 -6
  146. package/lib/examples/main.js +217 -27
  147. package/lib/examples/utils/a2ui.d.ts +18 -0
  148. package/lib/examples/utils/a2ui.js +69 -0
  149. package/lib/examples/utils/a2uiMarkdownProvider.d.ts +7 -0
  150. package/lib/examples/utils/a2uiMarkdownProvider.js +9 -0
  151. package/lib/examples/utils/agentId.d.ts +18 -0
  152. package/lib/examples/utils/agentId.js +54 -0
  153. package/lib/examples/utils/agents/earthquake-detector.json +11 -11
  154. package/lib/examples/utils/agents/sales-forecaster.json +11 -11
  155. package/lib/examples/utils/agents/social-post-generator.json +11 -11
  156. package/lib/examples/utils/agents/stock-market.json +11 -11
  157. package/lib/examples/utils/examplesStore.js +82 -27
  158. package/lib/hooks/index.d.ts +8 -8
  159. package/lib/hooks/index.js +7 -7
  160. package/lib/hooks/useA2A.d.ts +2 -3
  161. package/lib/hooks/useAIAgentsWebSocket.d.ts +43 -4
  162. package/lib/hooks/useAIAgentsWebSocket.js +118 -12
  163. package/lib/hooks/useAcp.d.ts +1 -2
  164. package/lib/hooks/useAgUi.d.ts +1 -1
  165. package/lib/hooks/{useAgents.d.ts → useAgentRuntimes.d.ts} +39 -2
  166. package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +125 -15
  167. package/lib/hooks/useAgentsCatalog.js +1 -1
  168. package/lib/hooks/useAgentsService.d.ts +2 -2
  169. package/lib/hooks/useAgentsService.js +7 -7
  170. package/lib/hooks/useCheckpoints.js +1 -1
  171. package/lib/hooks/useConfig.d.ts +4 -1
  172. package/lib/hooks/useConfig.js +10 -3
  173. package/lib/hooks/useContextSnapshot.d.ts +9 -4
  174. package/lib/hooks/useContextSnapshot.js +9 -37
  175. package/lib/hooks/useMonitoring.js +3 -0
  176. package/lib/hooks/useSandbox.d.ts +20 -8
  177. package/lib/hooks/useSandbox.js +105 -40
  178. package/lib/hooks/useSkills.d.ts +23 -5
  179. package/lib/hooks/useSkills.js +94 -39
  180. package/lib/hooks/useToolApprovals.d.ts +60 -36
  181. package/lib/hooks/useToolApprovals.js +318 -69
  182. package/lib/hooks/useVercelAI.d.ts +1 -1
  183. package/lib/index.d.ts +2 -1
  184. package/lib/index.js +1 -0
  185. package/lib/inference/index.d.ts +0 -1
  186. package/lib/middleware/index.d.ts +0 -1
  187. package/lib/protocols/AGUIAdapter.js +6 -0
  188. package/lib/protocols/VercelAIAdapter.d.ts +7 -0
  189. package/lib/protocols/VercelAIAdapter.js +59 -7
  190. package/lib/specs/agents/agents.d.ts +10 -0
  191. package/lib/specs/agents/agents.js +2139 -262
  192. package/lib/specs/agents/index.js +3 -1
  193. package/lib/specs/envvars.d.ts +1 -0
  194. package/lib/specs/envvars.js +38 -20
  195. package/lib/specs/evals.js +6 -6
  196. package/lib/specs/events.d.ts +3 -10
  197. package/lib/specs/events.js +127 -84
  198. package/lib/specs/frontendTools.js +2 -2
  199. package/lib/specs/guardrails.d.ts +0 -7
  200. package/lib/specs/guardrails.js +240 -159
  201. package/lib/specs/index.d.ts +1 -0
  202. package/lib/specs/index.js +1 -0
  203. package/lib/specs/mcpServers.js +35 -6
  204. package/lib/specs/memory.d.ts +0 -2
  205. package/lib/specs/memory.js +4 -17
  206. package/lib/specs/models.js +25 -5
  207. package/lib/specs/notifications.js +102 -18
  208. package/lib/specs/outputs.js +15 -9
  209. package/lib/specs/personas.d.ts +41 -0
  210. package/lib/specs/personas.js +168 -0
  211. package/lib/specs/skills.d.ts +2 -1
  212. package/lib/specs/skills.js +41 -23
  213. package/lib/specs/teams/index.js +3 -1
  214. package/lib/specs/teams/teams.js +468 -348
  215. package/lib/specs/tools.js +4 -4
  216. package/lib/specs/triggers.js +61 -11
  217. package/lib/stores/agentRuntimeStore.d.ts +204 -0
  218. package/lib/stores/agentRuntimeStore.js +636 -0
  219. package/lib/stores/index.d.ts +1 -1
  220. package/lib/stores/index.js +1 -1
  221. package/lib/tools/adapters/copilotkit/lexicalHooks.d.ts +1 -2
  222. package/lib/tools/adapters/copilotkit/lexicalHooks.js +1 -3
  223. package/lib/tools/adapters/copilotkit/notebookHooks.d.ts +1 -2
  224. package/lib/tools/adapters/copilotkit/notebookHooks.js +1 -3
  225. package/lib/tools/index.d.ts +0 -2
  226. package/lib/tools/index.js +0 -1
  227. package/lib/types/agentspecs.d.ts +50 -1
  228. package/lib/types/chat.d.ts +309 -8
  229. package/lib/types/context.d.ts +27 -0
  230. package/lib/types/cost.d.ts +2 -2
  231. package/lib/types/index.d.ts +2 -0
  232. package/lib/types/index.js +2 -0
  233. package/lib/types/mcp.d.ts +8 -0
  234. package/lib/types/models.d.ts +2 -2
  235. package/lib/types/personas.d.ts +25 -0
  236. package/lib/types/personas.js +5 -0
  237. package/lib/types/skills.d.ts +43 -1
  238. package/lib/types/stream.d.ts +110 -0
  239. package/lib/types/stream.js +36 -0
  240. package/lib/utils/utils.d.ts +9 -5
  241. package/lib/utils/utils.js +9 -5
  242. package/package.json +13 -9
  243. package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
  244. package/scripts/codegen/__pycache__/generate_events.cpython-313.pyc +0 -0
  245. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
  246. package/scripts/codegen/generate_agents.py +102 -6
  247. package/scripts/codegen/generate_events.py +35 -13
  248. package/scripts/codegen/generate_personas.py +319 -0
  249. package/scripts/codegen/generate_skills.py +9 -9
  250. package/scripts/sync-jupyter.sh +26 -7
  251. package/lib/api/tool-approvals.d.ts +0 -62
  252. package/lib/api/tool-approvals.js +0 -145
  253. package/lib/examples/LexicalSidebarExample.js +0 -163
  254. package/lib/examples/NotebookSidebarExample.js +0 -119
  255. package/lib/examples/NotebookSimpleExample.d.ts +0 -6
  256. package/lib/examples/NotebookSimpleExample.js +0 -22
  257. package/lib/examples/ag-ui/index.d.ts +0 -10
  258. package/lib/examples/ag-ui/index.js +0 -16
  259. package/lib/hooks/useAgentsRegistry.d.ts +0 -10
  260. package/lib/hooks/useAgentsRegistry.js +0 -20
  261. package/lib/stores/agentsStore.d.ts +0 -123
  262. package/lib/stores/agentsStore.js +0 -270
  263. /package/lib/examples/{AgentspecExample.d.ts → AgentSpecsExample.d.ts} +0 -0
  264. /package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.d.ts +0 -0
  265. /package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.d.ts +0 -0
  266. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.d.ts +0 -0
  267. /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.js +0 -0
@@ -1,20 +1,27 @@
1
- import type { BuiltinTool, ContextSnapshotData, MCPServerConfig, ModelConfig, SkillInfo } from '../../types';
1
+ import type { BuiltinTool, ContextSnapshotData, MCPServerConfig, McpToolsetsStatusResponse, ModelConfig, SkillInfo } from '../../types';
2
2
  export interface InputToolbarProps {
3
3
  input: string;
4
4
  setInput: (value: string) => void;
5
5
  isLoading: boolean;
6
+ connectionConfirmed: boolean;
6
7
  placeholder?: string;
7
8
  autoFocus: boolean;
8
9
  focusTrigger?: number;
9
10
  padding: number;
10
11
  onSend: () => void;
11
12
  onStop: () => void;
13
+ disableInputPrompt?: boolean;
12
14
  showTokenUsage: boolean;
13
15
  agentUsage?: ContextSnapshotData;
14
16
  showModelSelector: boolean;
15
17
  showToolsMenu: boolean;
16
18
  showSkillsMenu: boolean;
17
19
  codemodeEnabled: boolean;
20
+ /**
21
+ * Optional callback invoked when the user toggles codemode from the Tools
22
+ * menu. When omitted the toggle renders as read-only.
23
+ */
24
+ onToggleCodemode?: (enabled: boolean) => void | Promise<void>;
18
25
  isA2AProtocol: boolean;
19
26
  hasConfigData: boolean;
20
27
  hasSkillsData: boolean;
@@ -28,16 +35,22 @@ export interface InputToolbarProps {
28
35
  enabledMcpToolCount: number;
29
36
  onToggleMcpTool: (serverId: string, toolName: string) => void;
30
37
  onToggleAllMcpServerTools: (serverId: string, toolNames: string[], enable: boolean) => void;
38
+ /** Approved MCP tools per server (default: all tools approved) */
39
+ approvedMcpTools: Map<string, Set<string>>;
40
+ onToggleMcpToolApproval: (serverId: string, toolName: string) => void;
31
41
  skills: SkillInfo[];
32
42
  skillsLoading: boolean;
33
43
  enabledSkills: Set<string>;
34
44
  onToggleSkill: (skillId: string) => void;
35
45
  onToggleAllSkills: (skillIds: string[], enable: boolean) => void;
36
- /** API base URL passed to MCP / Sandbox indicators */
46
+ /** Approved skills set (default: all skills approved) */
47
+ approvedSkills: Set<string>;
48
+ onToggleSkillApproval: (skillId: string) => void;
49
+ /** API base URL passed to MCP indicator */
37
50
  apiBase?: string;
38
- /** Auth token passed to MCP / Sandbox indicators */
51
+ /** Auth token passed to MCP indicator */
39
52
  authToken?: string;
40
- /** Agent ID passed to Sandbox indicator for agent-scoped status */
41
- agentId?: string;
53
+ /** Pre-fetched MCP status from WebSocket bypasses REST polling */
54
+ mcpStatusData?: McpToolsetsStatusResponse | null;
42
55
  }
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;
56
+ export declare function InputToolbar({ input, setInput, isLoading, 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,14 +17,14 @@ 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, 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
25
  // 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) &&
26
+ const hasContext = Boolean(agentUsage && !agentUsage.error && agentUsage.totalTokens > 0);
27
+ return (_jsxs(Box, { children: [_jsx(InputPrompt, { placeholder: placeholder || 'Type a message...', isLoading: isLoading, 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 && hasContext && agentUsage && (_jsx(TokenUsageBar, { agentUsage: agentUsage, padding: padding })), (showModelSelector || showToolsMenu || showSkillsMenu) &&
28
28
  (hasConfigData || hasSkillsData) && (_jsxs(Box, { sx: {
29
29
  display: 'flex',
30
30
  gap: 2,
@@ -34,13 +34,29 @@ export function InputToolbar({ input, setInput, isLoading, placeholder, autoFocu
34
34
  borderColor: 'border.default',
35
35
  alignItems: 'center',
36
36
  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 }))] }))] }));
37
+ }, 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 }))] }))] }));
38
38
  }
39
39
  // ---------------------------------------------------------------------------
40
40
  // ToolsMenu (private sub-component)
41
41
  // ---------------------------------------------------------------------------
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 => {
42
+ function ToolsMenu({ codemodeEnabled, onToggleCodemode, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, approvedMcpTools, onToggleMcpToolApproval, availableTools: _availableTools, }) {
43
+ const hasUsableMcpServers = mcpServers.some(server => server.isAvailable);
44
+ 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: {
45
+ display: 'flex',
46
+ alignItems: 'center',
47
+ justifyContent: 'space-between',
48
+ px: 3,
49
+ py: 2,
50
+ borderBottom: '1px solid',
51
+ borderColor: 'border.muted',
52
+ gap: 2,
53
+ }, 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
54
+ ? 'MCP tools accessible via meta-tools (search_tools, execute_code).'
55
+ : 'Expose MCP tools directly to the model.' })] }), _jsx(ToggleSwitch, { size: "small", checked: codemodeEnabled, disabled: !onToggleCodemode, onClick: () => {
56
+ if (onToggleCodemode) {
57
+ void onToggleCodemode(!codemodeEnabled);
58
+ }
59
+ }, "aria-labelledby": "toggle-codemode" })] }) }), mcpServers.length > 0 && hasUsableMcpServers ? (mcpServers.map(server => {
44
60
  const serverTools = enabledMcpTools.get(server.id);
45
61
  const allToolNames = server.tools.map(t => t.name);
46
62
  const enabledCount = serverTools?.size ?? 0;
@@ -60,6 +76,10 @@ function ToolsMenu({ codemodeEnabled, mcpServers, enabledMcpTools, enabledMcpToo
60
76
  color: 'fg.muted',
61
77
  }, 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
78
  const isEnabled = serverTools?.has(tool.name) ?? false;
79
+ const serverApproved = approvedMcpTools.get(server.id);
80
+ // Tools default to NOT approved — user must explicitly
81
+ // approve each one (matches the Skills approval UX).
82
+ const isApproved = serverApproved?.has(tool.name) ?? false;
63
83
  return (_jsxs(Box, { sx: {
64
84
  display: 'flex',
65
85
  alignItems: 'center',
@@ -76,20 +96,29 @@ function ToolsMenu({ codemodeEnabled, mcpServers, enabledMcpTools, enabledMcpToo
76
96
  overflow: 'hidden',
77
97
  textOverflow: 'ellipsis',
78
98
  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}`));
99
+ }, children: tool.description }))] }), _jsxs(Box, { sx: {
100
+ display: 'flex',
101
+ alignItems: 'center',
102
+ gap: 3,
103
+ }, children: [_jsxs(Box, { sx: {
104
+ display: 'flex',
105
+ flexDirection: 'column',
106
+ alignItems: 'center',
107
+ gap: '2px',
108
+ }, 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: {
109
+ display: 'flex',
110
+ flexDirection: 'column',
111
+ alignItems: 'center',
112
+ gap: '2px',
113
+ }, 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
114
  })) : 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" }) })) }))] }) }) })] }));
115
+ })) : (_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
116
  }
88
117
  // ---------------------------------------------------------------------------
89
118
  // SkillsMenu (private sub-component)
90
119
  // ---------------------------------------------------------------------------
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: {
120
+ function SkillsMenu({ skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, }) {
121
+ 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
122
  display: 'flex',
94
123
  alignItems: 'center',
95
124
  justifyContent: 'space-between',
@@ -110,14 +139,38 @@ function SkillsMenu({ skills, skillsLoading, enabledSkills, onToggleSkill, onTog
110
139
  '&:hover': {
111
140
  backgroundColor: 'canvas.subtle',
112
141
  },
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: {
142
+ }, 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: {
143
+ fontSize: '10px',
144
+ px: 1,
145
+ borderRadius: 2,
146
+ bg: skill.status === 'loaded'
147
+ ? 'success.subtle'
148
+ : skill.status === 'enabled'
149
+ ? 'attention.subtle'
150
+ : 'neutral.subtle',
151
+ color: skill.status === 'loaded'
152
+ ? 'success.fg'
153
+ : skill.status === 'enabled'
154
+ ? 'attention.fg'
155
+ : 'fg.muted',
156
+ }, children: skill.status }))] }), skill.description && (_jsx(Text, { sx: {
114
157
  display: 'block',
115
158
  fontSize: 0,
116
159
  color: 'fg.muted',
117
160
  overflow: 'hidden',
118
161
  textOverflow: 'ellipsis',
119
162
  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" }) })) }) }) })] }));
163
+ }, children: skill.description }))] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 3 }, children: [_jsxs(Box, { sx: {
164
+ display: 'flex',
165
+ flexDirection: 'column',
166
+ alignItems: 'center',
167
+ gap: '2px',
168
+ }, 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: {
169
+ display: 'flex',
170
+ flexDirection: 'column',
171
+ alignItems: 'center',
172
+ gap: '2px',
173
+ }, 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
174
  }
122
175
  // ---------------------------------------------------------------------------
123
176
  // ModelSelector (private sub-component)
@@ -40,6 +40,8 @@ export interface InputPromptProps {
40
40
  padding?: number;
41
41
  /** Whether the prompt is disabled */
42
42
  disabled?: boolean;
43
+ /** Whether the prompt is read-only */
44
+ readOnly?: boolean;
43
45
  /** Additional sx props for the outer container */
44
46
  sx?: Record<string, unknown>;
45
47
  /** Controlled input value (external state) */
@@ -56,5 +58,5 @@ export interface InputPromptProps {
56
58
  /**
57
59
  * InputPrompt — Integrated chat input with header, input area, and footer.
58
60
  */
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;
61
+ export declare function InputPrompt({ variant, placeholder, isLoading, 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
62
  export default InputPrompt;
@@ -8,7 +8,7 @@ import { InputPromptLexical } from './InputPromptLexical';
8
8
  /**
9
9
  * InputPrompt — Integrated chat input with header, input area, and footer.
10
10
  */
11
- export function InputPrompt({ variant = 'text', placeholder = 'Ask anything…', isLoading = false, onSend, onStop, autoFocus = false, focusTrigger, showBorderTop = true, showBackground = true, padding = 3, disabled = false, sx, value: controlledValue, onChange: controlledOnChange, headerContent, footerContent, footerRightContent, }) {
11
+ export function InputPrompt({ variant = 'text', placeholder = 'Ask anything…', isLoading = false, onSend, onStop, autoFocus = false, focusTrigger, showBorderTop = true, showBackground = true, padding = 3, disabled = false, readOnly = false, sx, value: controlledValue, onChange: controlledOnChange, headerContent, footerContent, footerRightContent, }) {
12
12
  // ---- Controlled / uncontrolled state -----------------------------------
13
13
  const [internalInput, setInternalInput] = useState('');
14
14
  const input = controlledValue !== undefined ? controlledValue : internalInput;
@@ -46,14 +46,14 @@ export function InputPrompt({ variant = 'text', placeholder = 'Ask anything…',
46
46
  }, [isLoading, variant]);
47
47
  // ---- Send / Stop handlers ----------------------------------------------
48
48
  const handleSend = useCallback(() => {
49
- if (!input.trim() || isLoading || disabled)
49
+ if (!input.trim() || isLoading || disabled || readOnly)
50
50
  return;
51
51
  const message = input.trim();
52
52
  if (controlledValue === undefined) {
53
53
  setInput('');
54
54
  }
55
55
  onSend(message);
56
- }, [input, isLoading, disabled, onSend, setInput, controlledValue]);
56
+ }, [input, isLoading, disabled, readOnly, onSend, setInput, controlledValue]);
57
57
  const handleStop = useCallback(() => {
58
58
  onStop?.();
59
59
  }, [onStop]);
@@ -78,6 +78,6 @@ export function InputPrompt({ variant = 'text', placeholder = 'Ask anything…',
78
78
  borderColor: 'accent.fg',
79
79
  boxShadow: (t) => `0 0 0 1px ${t?.colors?.accent?.fg ?? '#0969da'}`,
80
80
  },
81
- }, children: [_jsx(InputPromptHeader, { children: headerContent }), variant === 'lexical' ? (_jsx(InputPromptLexical, { value: input, onChange: setInput, placeholder: placeholder, disabled: isLoading || disabled, onSubmit: handleSend, autoFocus: autoFocus })) : (_jsx(InputPromptText, { value: input, onChange: setInput, placeholder: placeholder, disabled: isLoading || disabled, onSubmit: handleSend, inputRef: inputRef })), _jsx(InputPromptFooter, { isLoading: isLoading, sendDisabled: !input.trim() || disabled, onSend: handleSend, onStop: handleStop, rightContent: footerRightContent, children: footerContent })] }) }) }));
81
+ }, children: [_jsx(InputPromptHeader, { children: headerContent }), variant === 'lexical' ? (_jsx(InputPromptLexical, { value: input, onChange: setInput, placeholder: placeholder, disabled: isLoading || disabled, readOnly: readOnly, onSubmit: handleSend, autoFocus: autoFocus })) : (_jsx(InputPromptText, { value: input, onChange: setInput, placeholder: placeholder, disabled: isLoading || disabled, readOnly: readOnly, onSubmit: handleSend, inputRef: inputRef })), _jsx(InputPromptFooter, { isLoading: isLoading, sendDisabled: !input.trim() || disabled || readOnly, onSend: handleSend, onStop: handleStop, rightContent: footerRightContent, children: footerContent })] }) }) }));
82
82
  }
83
83
  export default InputPrompt;
@@ -10,6 +10,6 @@ export function InputPromptFooter({ children, rightContent, isLoading = false, s
10
10
  px: 2,
11
11
  pt: 1,
12
12
  pb: 2,
13
- }, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, flex: 1 }, children: children }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [rightContent, isLoading ? (_jsx(IconButton, { icon: SquareCircleIcon, "aria-label": "Stop", onClick: onStop, size: "small" })) : (_jsx(IconButton, { icon: PaperAirplaneIcon, "aria-label": "Send", onClick: onSend, disabled: sendDisabled, size: "small" }))] })] }));
13
+ }, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, flex: 1 }, children: children }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [rightContent, isLoading ? (_jsx(IconButton, { icon: SquareCircleIcon, "aria-label": "Stop", onClick: onStop, size: "small", variant: "invisible" })) : (_jsx(IconButton, { icon: PaperAirplaneIcon, "aria-label": "Send", onClick: onSend, disabled: sendDisabled, size: "small", variant: "invisible" }))] })] }));
14
14
  }
15
15
  export default InputPromptFooter;
@@ -7,10 +7,12 @@ export interface InputPromptLexicalProps {
7
7
  placeholder?: string;
8
8
  /** Whether the input is disabled */
9
9
  disabled?: boolean;
10
+ /** Whether the input is read-only */
11
+ readOnly?: boolean;
10
12
  /** Callback when the user presses Enter (without Shift) */
11
13
  onSubmit?: () => void;
12
14
  /** Auto-focus the editor on mount */
13
15
  autoFocus?: boolean;
14
16
  }
15
- export declare function InputPromptLexical({ value, onChange, placeholder, disabled, onSubmit, autoFocus, }: InputPromptLexicalProps): import("react/jsx-runtime").JSX.Element;
17
+ export declare function InputPromptLexical({ value, onChange, placeholder, disabled, readOnly, onSubmit, autoFocus, }: InputPromptLexicalProps): import("react/jsx-runtime").JSX.Element;
16
18
  export default InputPromptLexical;
@@ -38,17 +38,17 @@ const EDITOR_CONFIG = {
38
38
  },
39
39
  };
40
40
  // ---- Enter-to-submit plugin ---------------------------------------------
41
- function EnterSubmitPlugin({ onSubmit, disabled, }) {
41
+ function EnterSubmitPlugin({ onSubmit, disabled, readOnly, }) {
42
42
  const [editor] = useLexicalComposerContext();
43
43
  useEffect(() => {
44
44
  return editor.registerCommand(KEY_ENTER_COMMAND, (event) => {
45
- if (event?.shiftKey || disabled)
45
+ if (event?.shiftKey || disabled || readOnly)
46
46
  return false;
47
47
  event?.preventDefault();
48
48
  onSubmit?.();
49
49
  return true;
50
50
  }, COMMAND_PRIORITY_HIGH);
51
- }, [editor, onSubmit, disabled]);
51
+ }, [editor, onSubmit, disabled, readOnly]);
52
52
  return null;
53
53
  }
54
54
  // ---- Sync plugin (controlled component bridge) --------------------------
@@ -94,7 +94,14 @@ function AutoFocusPlugin({ autoFocus }) {
94
94
  }, [editor, autoFocus]);
95
95
  return null;
96
96
  }
97
- export function InputPromptLexical({ value, onChange, placeholder = 'Ask anything…', disabled = false, onSubmit, autoFocus = false, }) {
97
+ function ReadOnlyPlugin({ readOnly }) {
98
+ const [editor] = useLexicalComposerContext();
99
+ useEffect(() => {
100
+ editor.setEditable(!readOnly);
101
+ }, [editor, readOnly]);
102
+ return null;
103
+ }
104
+ export function InputPromptLexical({ value, onChange, placeholder = 'Ask anything…', disabled = false, readOnly = false, onSubmit, autoFocus = false, }) {
98
105
  return (_jsx(Box, { sx: {
99
106
  px: 2,
100
107
  py: 1,
@@ -117,6 +124,6 @@ export function InputPromptLexical({ value, onChange, placeholder = 'Ask anythin
117
124
  fontSize: 1,
118
125
  pointerEvents: 'none',
119
126
  userSelect: 'none',
120
- }, children: placeholder }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, {}), _jsx(SyncPlugin, { value: value, onChange: onChange }), _jsx(EnterSubmitPlugin, { onSubmit: onSubmit, disabled: disabled }), _jsx(AutoFocusPlugin, { autoFocus: autoFocus })] }) }));
127
+ }, children: placeholder }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, {}), _jsx(SyncPlugin, { value: value, onChange: onChange }), _jsx(ReadOnlyPlugin, { readOnly: readOnly || disabled }), _jsx(EnterSubmitPlugin, { onSubmit: onSubmit, disabled: disabled, readOnly: readOnly }), _jsx(AutoFocusPlugin, { autoFocus: autoFocus })] }) }));
121
128
  }
122
129
  export default InputPromptLexical;
@@ -15,10 +15,12 @@ export interface InputPromptTextProps {
15
15
  placeholder?: string;
16
16
  /** Whether the input is disabled */
17
17
  disabled?: boolean;
18
+ /** Whether the input is read-only */
19
+ readOnly?: boolean;
18
20
  /** Callback when the user presses Enter (without Shift) */
19
21
  onSubmit?: () => void;
20
22
  /** Ref forwarded to the underlying textarea */
21
23
  inputRef?: Ref<HTMLTextAreaElement>;
22
24
  }
23
- export declare function InputPromptText({ value, onChange, placeholder, disabled, onSubmit, inputRef, }: InputPromptTextProps): import("react/jsx-runtime").JSX.Element;
25
+ export declare function InputPromptText({ value, onChange, placeholder, disabled, readOnly, onSubmit, inputRef, }: InputPromptTextProps): import("react/jsx-runtime").JSX.Element;
24
26
  export default InputPromptText;
@@ -13,7 +13,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
13
13
  import { useCallback, useEffect, } from 'react';
14
14
  import { Textarea } from '@primer/react';
15
15
  import { Box } from '@datalayer/primer-addons';
16
- export function InputPromptText({ value, onChange, placeholder = 'Ask anything…', disabled = false, onSubmit, inputRef, }) {
16
+ export function InputPromptText({ value, onChange, placeholder = 'Ask anything…', disabled = false, readOnly = false, onSubmit, inputRef, }) {
17
17
  // Auto-resize
18
18
  const adjustHeight = useCallback(() => {
19
19
  const el = inputRef && 'current' in inputRef
@@ -41,7 +41,7 @@ export function InputPromptText({ value, onChange, placeholder = 'Ask anything
41
41
  onSubmit?.();
42
42
  }
43
43
  }, [onSubmit]);
44
- return (_jsx(Box, { sx: { px: 2, py: 1 }, children: _jsx(Textarea, { ref: inputRef, value: value, onChange: e => onChange(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, sx: {
44
+ return (_jsx(Box, { sx: { px: 2, py: 1 }, children: _jsx(Textarea, { ref: inputRef, value: value, onChange: e => onChange(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, readOnly: readOnly, sx: {
45
45
  width: '100%',
46
46
  resize: 'none',
47
47
  minHeight: '40px',
@@ -46,7 +46,7 @@ export function ToolApprovalBanner({ pendingApprovals, onReview, onApproveAll, o
46
46
  justifyContent: 'space-between',
47
47
  px: 3,
48
48
  py: 2,
49
- }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(AlertIcon, { size: 16 }), _jsxs(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: [pendingApprovals.length, " tool", ' ', pendingApprovals.length === 1 ? 'approval' : 'approvals', " pending"] })] }), _jsxs(Box, { sx: { display: 'flex', gap: 1 }, children: [onApproveAll && pendingApprovals.length > 1 && (_jsx(Button, { size: "small", variant: "primary", leadingVisual: CheckIcon, onClick: onApproveAll, children: "Approve all" })), collapsible && (_jsx(Button, { size: "small", variant: "invisible", onClick: () => setCollapsed(true), children: "Collapse" })), onDismiss && (_jsx(Button, { size: "small", variant: "invisible", leadingVisual: XIcon, onClick: onDismiss, "aria-label": "Dismiss" }))] })] }), _jsx(Box, { sx: { px: 3, pb: 2 }, children: pendingApprovals.map(approval => (_jsxs(Box, { sx: {
49
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(AlertIcon, { size: 16 }), _jsxs(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: [pendingApprovals.length, " tool", ' ', pendingApprovals.length === 1 ? 'approval' : 'approvals', " pending"] })] }), _jsxs(Box, { sx: { display: 'flex', gap: 1 }, children: [onApproveAll && pendingApprovals.length >= 1 && (_jsx(Button, { size: "small", variant: "primary", leadingVisual: CheckIcon, onClick: onApproveAll, children: "Approve all" })), collapsible && (_jsx(Button, { size: "small", variant: "invisible", onClick: () => setCollapsed(true), children: "Collapse" })), onDismiss && (_jsx(Button, { size: "small", variant: "invisible", leadingVisual: XIcon, onClick: onDismiss, "aria-label": "Dismiss" }))] })] }), _jsx(Box, { sx: { px: 3, pb: 2 }, children: pendingApprovals.map(approval => (_jsxs(Box, { sx: {
50
50
  display: 'flex',
51
51
  alignItems: 'center',
52
52
  justifyContent: 'space-between',
@@ -28,6 +28,8 @@ export interface ToolCallDisplayProps {
28
28
  approvalRequired?: boolean;
29
29
  /** Approval state for this tool call */
30
30
  approvalState?: 'pending' | 'approved' | 'denied';
31
+ /** Where the approval decision originated */
32
+ approvalDecisionSource?: 'inline' | 'external';
31
33
  /** Called when user approves this tool call */
32
34
  onApprove?: () => void;
33
35
  /** Called when user denies this tool call */
@@ -45,5 +47,5 @@ export interface ToolCallDisplayProps {
45
47
  * - Color-coded status indicators
46
48
  * - Rich error display distinguishing execution errors from code errors
47
49
  */
48
- export declare function ToolCallDisplay({ toolCallId, toolName, args, result, status, error, executionResult, codeError, exitCode, executionError, approvalRequired, approvalState, onApprove, onDeny, approvalLoading, }: ToolCallDisplayProps): import("react/jsx-runtime").JSX.Element;
50
+ export declare function ToolCallDisplay({ toolCallId, toolName, args, result, status, error, executionResult, codeError, exitCode, executionError, approvalRequired, approvalState, approvalDecisionSource, onApprove, onDeny, approvalLoading, }: ToolCallDisplayProps): import("react/jsx-runtime").JSX.Element;
49
51
  export default ToolCallDisplay;
@@ -124,7 +124,7 @@ function getArgsSummary(args) {
124
124
  * - Color-coded status indicators
125
125
  * - Rich error display distinguishing execution errors from code errors
126
126
  */
127
- export function ToolCallDisplay({ toolCallId, toolName, args, result, status, error, executionResult, codeError, exitCode, executionError, approvalRequired = false, approvalState, onApprove, onDeny, approvalLoading = false, }) {
127
+ export function ToolCallDisplay({ toolCallId, toolName, args, result, status, error, executionResult, codeError, exitCode, executionError, approvalRequired = false, approvalState, approvalDecisionSource, onApprove, onDeny, approvalLoading = false, }) {
128
128
  const [isExpanded, setIsExpanded] = useState(false);
129
129
  // Determine effective exit code from props or execution result
130
130
  const effectiveExitCode = exitCode ?? executionResult?.exit_code;
@@ -265,7 +265,7 @@ export function ToolCallDisplay({ toolCallId, toolName, args, result, status, er
265
265
  wordBreak: 'break-word',
266
266
  }, children: typeof result === 'string'
267
267
  ? result
268
- : JSON.stringify(result, null, 2) }) })] })), approvalRequired && (_jsx(Box, { sx: { mt: status === 'complete' && result !== undefined ? 3 : 0 }, children: approvalState === 'approved' ? (_jsx(Text, { sx: { color: 'success.fg', fontSize: 1 }, children: "Approved. Executing tool." })) : approvalState === 'denied' ? (_jsx(Text, { sx: { color: 'danger.fg', fontSize: 1 }, children: "Denied. Tool will not run." })) : (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 1, color: 'fg.default' }, children: "This tool requires your approval to run." }), _jsxs(Box, { sx: { mt: 2, display: 'flex', gap: 2 }, children: [_jsx(Button, { size: "small", onClick: onApprove, disabled: approvalLoading || !onApprove, children: "Approve" }), _jsx(Button, { size: "small", variant: "danger", onClick: onDeny, disabled: approvalLoading || !onDeny, children: "Deny" })] })] })) })), status === 'error' && effectiveExecutionError && (_jsxs(Box, { children: [_jsxs(Text, { sx: {
268
+ : JSON.stringify(result, null, 2) }) })] })), approvalRequired && (_jsx(Box, { sx: { mt: status === 'complete' && result !== undefined ? 3 : 0 }, children: approvalState === 'approved' ? (_jsxs(Box, { children: [_jsx(Text, { sx: { color: 'success.fg', fontSize: 1, display: 'block' }, children: "Approved. Executing tool." }), approvalDecisionSource === 'external' && (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 0, display: 'block' }, children: "Approved from sidebar." }))] })) : approvalState === 'denied' ? (_jsxs(Box, { children: [_jsx(Text, { sx: { color: 'danger.fg', fontSize: 1, display: 'block' }, children: "Denied. Tool will not run." }), approvalDecisionSource === 'external' && (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 0, display: 'block' }, children: "Decision came from sidebar." }))] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 1, color: 'fg.default' }, children: "This tool requires your approval to run." }), _jsxs(Box, { sx: { mt: 2, display: 'flex', gap: 2 }, children: [_jsx(Button, { size: "small", onClick: onApprove, disabled: approvalLoading || !onApprove, children: "Approve" }), _jsx(Button, { size: "small", variant: "danger", onClick: onDeny, disabled: approvalLoading || !onDeny, children: "Deny" })] })] })) })), status === 'error' && effectiveExecutionError && (_jsxs(Box, { children: [_jsxs(Text, { sx: {
269
269
  display: 'block',
270
270
  fontSize: 0,
271
271
  fontWeight: 'semibold',
@@ -29,6 +29,19 @@ export function TokenUsageBar({ agentUsage, padding }) {
29
29
  const hasSession = agentUsage.sessionUsage &&
30
30
  (agentUsage.sessionUsage.inputTokens > 0 ||
31
31
  agentUsage.sessionUsage.outputTokens > 0);
32
+ const hasCostUsage = !!agentUsage.costUsage &&
33
+ (agentUsage.costUsage.cumulativeCostUsd > 0 ||
34
+ agentUsage.costUsage.lastTurnCostUsd > 0);
35
+ const sessionUsage = agentUsage.sessionUsage;
36
+ const turnUsage = agentUsage.turnUsage;
37
+ const costUsage = agentUsage.costUsage;
38
+ const formatUsd = (amount) => {
39
+ if (amount < 0.01)
40
+ return `$${amount.toFixed(4)}`;
41
+ if (amount < 1)
42
+ return `$${amount.toFixed(3)}`;
43
+ return `$${amount.toFixed(2)}`;
44
+ };
32
45
  // Build pie chart data
33
46
  const usedTokens = agentUsage.totalTokens;
34
47
  const windowTokens = agentUsage.contextWindow;
@@ -180,7 +193,12 @@ export function TokenUsageBar({ agentUsage, padding }) {
180
193
  color: 'fg.default',
181
194
  display: 'block',
182
195
  mb: 2,
183
- }, children: "Context Window" }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block', mb: 1 }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default' }, children: formatTokenCount(usedTokens) }), ' / ', formatTokenCount(windowTokens), ' tokens'] }), _jsxs(Text, { sx: {
196
+ }, children: "Context Window" }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block', mb: 1 }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default' }, children: formatTokenCount(usedTokens) }), ' / ', formatTokenCount(windowTokens), ' tokens'] }), agentUsage.costUsage && (_jsxs(Text, { sx: {
197
+ fontSize: 0,
198
+ color: 'fg.muted',
199
+ display: 'block',
200
+ mb: 2,
201
+ }, children: ['cost ', _jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default' }, children: formatUsd(agentUsage.costUsage.lastTurnCostUsd) }), ' turn · ', _jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default' }, children: formatUsd(agentUsage.costUsage.cumulativeCostUsd) }), ' total'] })), _jsxs(Text, { sx: {
184
202
  fontSize: 0,
185
203
  color: pct > 90
186
204
  ? 'danger.fg'
@@ -209,5 +227,5 @@ export function TokenUsageBar({ agentUsage, padding }) {
209
227
  display: 'block',
210
228
  mt: 2,
211
229
  fontStyle: 'italic',
212
- }, children: "Quality may decline as limit nears." }))] }))] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default', fontSize: 0 }, children: formatTokenCount(agentUsage.totalTokens) }), ' / ', formatTokenCount(agentUsage.contextWindow), ' ctx'] }), hasSession && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: ['· ', formatTokenCount(agentUsage.sessionUsage.inputTokens), _jsx(Text, { as: "span", sx: { color: 'success.fg', fontSize: 0 }, children: '▲' }), ' ', formatTokenCount(agentUsage.sessionUsage.outputTokens), _jsx(Text, { as: "span", sx: { color: 'attention.fg', fontSize: 0 }, children: '▼' })] })), hasTurn && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: ['· turn ', formatTokenCount(agentUsage.turnUsage.inputTokens), _jsx(Text, { as: "span", sx: { color: 'success.fg', fontSize: 0 }, children: '▲' }), ' ', formatTokenCount(agentUsage.turnUsage.outputTokens), _jsx(Text, { as: "span", sx: { color: 'attention.fg', fontSize: 0 }, children: '▼' })] }))] }));
230
+ }, children: "Quality may decline as limit nears." }))] }))] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default', fontSize: 0 }, children: formatTokenCount(agentUsage.totalTokens) }), ' / ', formatTokenCount(agentUsage.contextWindow), ' ctx'] }), hasCostUsage && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: ['· cost ', _jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default', fontSize: 0 }, children: formatUsd(costUsage?.lastTurnCostUsd ?? 0) }), ' turn / ', _jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default', fontSize: 0 }, children: formatUsd(costUsage?.cumulativeCostUsd ?? 0) })] })), hasSession && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: ['· ', formatTokenCount(sessionUsage?.inputTokens ?? 0), _jsx(Text, { as: "span", sx: { color: 'success.fg', fontSize: 0 }, children: '▲' }), ' ', formatTokenCount(sessionUsage?.outputTokens ?? 0), _jsx(Text, { as: "span", sx: { color: 'attention.fg', fontSize: 0 }, children: '▼' })] })), hasTurn && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: ['· turn ', formatTokenCount(turnUsage?.inputTokens ?? 0), _jsx(Text, { as: "span", sx: { color: 'success.fg', fontSize: 0 }, children: '▲' }), ' ', formatTokenCount(turnUsage?.outputTokens ?? 0), _jsx(Text, { as: "span", sx: { color: 'attention.fg', fontSize: 0 }, children: '▼' })] }))] }));
213
231
  }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * React context and hook for injecting an {@link IAgentRuntimesClient}
3
+ * implementation into the chat component tree.
4
+ *
5
+ * Consumers wrap the `<Chat>` component with `<AgentRuntimesClientProvider>`
6
+ * and pass the implementation of their choice — the default
7
+ * `SdkAgentRuntimesClient` for browser / Node, or a bridge
8
+ * implementation in the VSCode webview sandbox. Components and hooks inside
9
+ * the tree call {@link useAgentRuntimesClient} to retrieve the current client
10
+ * without knowing anything about how it talks to the backend.
11
+ *
12
+ * @module client/AgentRuntimesClientContext
13
+ */
14
+ import type { ReactNode } from 'react';
15
+ import type { IAgentRuntimesClient } from './IAgentRuntimesClient';
16
+ /** Props for {@link AgentRuntimesClientProvider}. */
17
+ export interface AgentRuntimesClientProviderProps {
18
+ /** Client implementation to make available to descendants. */
19
+ client: IAgentRuntimesClient;
20
+ /** Descendants that should see the provided client. */
21
+ children: ReactNode;
22
+ }
23
+ /**
24
+ * Provides an {@link IAgentRuntimesClient} instance to every descendant.
25
+ *
26
+ * @param props - Provider props.
27
+ *
28
+ * @returns A React element that exposes `props.client` to descendants.
29
+ */
30
+ export declare function AgentRuntimesClientProvider(props: AgentRuntimesClientProviderProps): JSX.Element;
31
+ /**
32
+ * Reads the active {@link IAgentRuntimesClient} from context.
33
+ *
34
+ * @returns The active client.
35
+ *
36
+ * @throws When called outside an {@link AgentRuntimesClientProvider}. Prefer a
37
+ * loud failure to silently falling back to a default client, because a
38
+ * missing provider usually indicates a wiring bug (especially in the VSCode
39
+ * webview where the default client would make forbidden direct HTTP calls).
40
+ */
41
+ export declare function useAgentRuntimesClient(): IAgentRuntimesClient;
42
+ /**
43
+ * Reads the current {@link IAgentRuntimesClient} from context without
44
+ * throwing.
45
+ *
46
+ * Use this variant in code paths that want to fall back to some other behavior
47
+ * when no provider is mounted (for example, legacy hook implementations that
48
+ * currently still make direct network calls and are being migrated
49
+ * incrementally).
50
+ *
51
+ * @returns The active client, or `null` when no provider is mounted.
52
+ */
53
+ export declare function useOptionalAgentRuntimesClient(): IAgentRuntimesClient | null;
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useMemo } from 'react';
3
+ /**
4
+ * React context carrying the active {@link IAgentRuntimesClient}. `null` when
5
+ * no provider is mounted — in that case {@link useAgentRuntimesClient} throws
6
+ * so callers fail loudly rather than silently making no-op HTTP calls.
7
+ */
8
+ const AgentRuntimesClientContext = createContext(null);
9
+ /**
10
+ * Provides an {@link IAgentRuntimesClient} instance to every descendant.
11
+ *
12
+ * @param props - Provider props.
13
+ *
14
+ * @returns A React element that exposes `props.client` to descendants.
15
+ */
16
+ export function AgentRuntimesClientProvider(props) {
17
+ const { client, children } = props;
18
+ // Memoize the context value so identity only changes when the client
19
+ // reference itself changes.
20
+ const value = useMemo(() => client, [client]);
21
+ return (_jsx(AgentRuntimesClientContext.Provider, { value: value, children: children }));
22
+ }
23
+ /**
24
+ * Reads the active {@link IAgentRuntimesClient} from context.
25
+ *
26
+ * @returns The active client.
27
+ *
28
+ * @throws When called outside an {@link AgentRuntimesClientProvider}. Prefer a
29
+ * loud failure to silently falling back to a default client, because a
30
+ * missing provider usually indicates a wiring bug (especially in the VSCode
31
+ * webview where the default client would make forbidden direct HTTP calls).
32
+ */
33
+ export function useAgentRuntimesClient() {
34
+ const client = useContext(AgentRuntimesClientContext);
35
+ if (client === null) {
36
+ throw new Error('useAgentRuntimesClient must be called inside an <AgentRuntimesClientProvider>. ' +
37
+ 'Wrap your <Chat> component (or the subtree that uses agent-runtimes hooks) ' +
38
+ 'with <AgentRuntimesClientProvider client={...}>.');
39
+ }
40
+ return client;
41
+ }
42
+ /**
43
+ * Reads the current {@link IAgentRuntimesClient} from context without
44
+ * throwing.
45
+ *
46
+ * Use this variant in code paths that want to fall back to some other behavior
47
+ * when no provider is mounted (for example, legacy hook implementations that
48
+ * currently still make direct network calls and are being migrated
49
+ * incrementally).
50
+ *
51
+ * @returns The active client, or `null` when no provider is mounted.
52
+ */
53
+ export function useOptionalAgentRuntimesClient() {
54
+ return useContext(AgentRuntimesClientContext);
55
+ }