@datalayer/agent-runtimes 1.0.29 → 1.3.11

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 (779) hide show
  1. package/lib/AgentDocument.js +5 -22
  2. package/lib/AgentNode.d.ts +1 -1
  3. package/lib/AgentNode.js +193 -23
  4. package/lib/AgentNotebook.js +1 -1
  5. package/lib/App.d.ts +1 -1
  6. package/lib/AppExamples2.d.ts +1 -1
  7. package/lib/agent-node/AgentNodeGallery.d.ts +14 -1
  8. package/lib/agent-node/AgentNodeGallery.js +19 -15
  9. package/lib/agent-node/themeStore.js +7 -6
  10. package/lib/agents/AgentDetails.d.ts +8 -2
  11. package/lib/agents/AgentDetails.js +41 -11
  12. package/lib/agents/AgentRuntimeChat.d.ts +3 -3
  13. package/lib/agents/AgentRuntimeChat.js +15 -15
  14. package/lib/api/agents.d.ts +10 -10
  15. package/lib/api/agents.js +15 -15
  16. package/lib/api/checkpoints.d.ts +40 -0
  17. package/lib/api/checkpoints.js +75 -0
  18. package/lib/api/index.d.ts +1 -0
  19. package/lib/api/index.js +2 -1
  20. package/lib/api/notifications.d.ts +21 -6
  21. package/lib/api/notifications.js +32 -14
  22. package/lib/api/runtimes/environments.d.ts +406 -4
  23. package/lib/api/runtimes/environments.js +641 -9
  24. package/lib/api/runtimes/runtimes.d.ts +19 -19
  25. package/lib/api/runtimes/runtimes.js +47 -46
  26. package/lib/api/spacer/notebooks.js +1 -1
  27. package/lib/chat/Chat.d.ts +16 -4
  28. package/lib/chat/Chat.js +18 -5
  29. package/lib/chat/ChatFloating.d.ts +20 -2
  30. package/lib/chat/ChatFloating.js +196 -118
  31. package/lib/chat/ChatInline.d.ts +15 -0
  32. package/lib/chat/ChatInline.js +0 -18
  33. package/lib/chat/ChatPopupStandalone.d.ts +1 -1
  34. package/lib/chat/ChatSidebar.d.ts +14 -2
  35. package/lib/chat/ChatSidebar.js +1 -1
  36. package/lib/chat/ChatStandalone.d.ts +1 -1
  37. package/lib/chat/EphemeralSurfaceControl.d.ts +30 -0
  38. package/lib/chat/EphemeralSurfaceControl.js +55 -0
  39. package/lib/chat/base/ChatAvailability.d.ts +34 -0
  40. package/lib/chat/base/ChatAvailability.js +18 -0
  41. package/lib/chat/base/ChatBase.d.ts +2 -1
  42. package/lib/chat/base/ChatBase.js +657 -28
  43. package/lib/chat/display/ButtonGlow.d.ts +9 -0
  44. package/lib/chat/display/ButtonGlow.js +60 -0
  45. package/lib/chat/display/EmptyState.d.ts +15 -1
  46. package/lib/chat/display/EmptyState.js +81 -12
  47. package/lib/chat/display/FloatingBrandButton.d.ts +1 -1
  48. package/lib/chat/display/PoweredByTag.d.ts +1 -1
  49. package/lib/chat/display/groupSuggestions.d.ts +21 -0
  50. package/lib/chat/display/groupSuggestions.js +18 -0
  51. package/lib/chat/document/EphemeralDocument.d.ts +70 -6
  52. package/lib/chat/document/EphemeralDocument.js +154 -52
  53. package/lib/chat/header/ChatHeader.d.ts +1 -1
  54. package/lib/chat/header/ChatHeaderBase.d.ts +10 -1
  55. package/lib/chat/header/ChatHeaderBase.js +21 -102
  56. package/lib/chat/header/ChatViewModeToggle.d.ts +29 -0
  57. package/lib/chat/header/ChatViewModeToggle.js +55 -0
  58. package/lib/chat/index.d.ts +5 -1
  59. package/lib/chat/index.js +9 -1
  60. package/lib/chat/indicators/McpStatusIndicator.d.ts +1 -1
  61. package/lib/chat/indicators/SandboxStatusIndicator.d.ts +1 -1
  62. package/lib/chat/indicators/SandboxStatusIndicator.js +1 -1
  63. package/lib/chat/indicators/SkillsStatusIndicator.d.ts +1 -1
  64. package/lib/chat/messages/ChatMessageList.d.ts +22 -1
  65. package/lib/chat/messages/ChatMessageList.js +370 -166
  66. package/lib/chat/messages/ChatMessages.d.ts +1 -1
  67. package/lib/chat/messages/NotebookToolSurfaces.d.ts +68 -0
  68. package/lib/chat/messages/NotebookToolSurfaces.js +302 -0
  69. package/lib/chat/messages/TurnFooter.d.ts +42 -0
  70. package/lib/chat/messages/TurnFooter.js +75 -0
  71. package/lib/chat/messages/assistantMarkdown.d.ts +22 -0
  72. package/lib/chat/messages/assistantMarkdown.js +56 -0
  73. package/lib/chat/notebook/EphemeralNotebook.d.ts +67 -6
  74. package/lib/chat/notebook/EphemeralNotebook.js +194 -61
  75. package/lib/chat/parts/DynamicToolPart.d.ts +1 -1
  76. package/lib/chat/parts/MessagePart.d.ts +1 -1
  77. package/lib/chat/parts/ReasoningPart.d.ts +8 -1
  78. package/lib/chat/parts/TextPart.d.ts +1 -1
  79. package/lib/chat/parts/ToolPart.d.ts +8 -1
  80. package/lib/chat/prompt/FloatingCard.d.ts +26 -0
  81. package/lib/chat/prompt/FloatingCard.js +80 -0
  82. package/lib/chat/prompt/InputPrompt.d.ts +206 -53
  83. package/lib/chat/prompt/InputPrompt.js +161 -80
  84. package/lib/chat/prompt/InputPromptBase.d.ts +112 -0
  85. package/lib/chat/prompt/InputPromptBase.js +225 -0
  86. package/lib/chat/prompt/InputPromptLexical.d.ts +18 -1
  87. package/lib/chat/prompt/InputPromptLexical.js +227 -30
  88. package/lib/chat/prompt/InputPromptText.d.ts +10 -2
  89. package/lib/chat/prompt/InputPromptText.js +35 -3
  90. package/lib/chat/prompt/footer/BelowPromptFooter.d.ts +25 -0
  91. package/lib/chat/prompt/footer/BelowPromptFooter.js +12 -0
  92. package/lib/chat/prompt/footer/InPromptFooter.d.ts +35 -0
  93. package/lib/chat/prompt/footer/InPromptFooter.js +16 -0
  94. package/lib/chat/prompt/footer/index.d.ts +7 -0
  95. package/lib/chat/prompt/footer/index.js +11 -0
  96. package/lib/chat/prompt/header/BelowPromptHeader.d.ts +23 -0
  97. package/lib/chat/prompt/header/BelowPromptHeader.js +12 -0
  98. package/lib/chat/prompt/header/InPromptHeader.d.ts +31 -0
  99. package/lib/chat/prompt/header/InPromptHeader.js +14 -0
  100. package/lib/chat/prompt/header/index.d.ts +7 -0
  101. package/lib/chat/prompt/header/index.js +11 -0
  102. package/lib/chat/prompt/index.d.ts +8 -4
  103. package/lib/chat/prompt/index.js +15 -3
  104. package/lib/chat/prompt/menus/AgentsMenu.d.ts +14 -0
  105. package/lib/chat/prompt/menus/AgentsMenu.js +42 -0
  106. package/lib/chat/prompt/menus/InlineAgentsMenu.d.ts +22 -0
  107. package/lib/chat/prompt/menus/InlineAgentsMenu.js +24 -0
  108. package/lib/chat/prompt/menus/ModelSelector.d.ts +7 -0
  109. package/lib/chat/prompt/menus/ModelSelector.js +27 -0
  110. package/lib/chat/prompt/menus/SkillsMenu.d.ts +10 -0
  111. package/lib/chat/prompt/menus/SkillsMenu.js +75 -0
  112. package/lib/chat/prompt/menus/SuggestionsMenu.d.ts +13 -0
  113. package/lib/chat/prompt/menus/SuggestionsMenu.js +31 -0
  114. package/lib/chat/prompt/menus/ToolsMenu.d.ts +13 -0
  115. package/lib/chat/prompt/menus/ToolsMenu.js +103 -0
  116. package/lib/chat/prompt/menus/index.d.ts +15 -0
  117. package/lib/chat/prompt/menus/index.js +19 -0
  118. package/lib/chat/prompt/plugins/AgentMentionPlugin.d.ts +54 -0
  119. package/lib/chat/prompt/plugins/AgentMentionPlugin.js +370 -0
  120. package/lib/chat/prompt/plugins/CommandPlugin.d.ts +52 -0
  121. package/lib/chat/prompt/plugins/CommandPlugin.js +359 -0
  122. package/lib/chat/prompt/plugins/MentionChip.d.ts +15 -0
  123. package/lib/chat/prompt/plugins/MentionChip.js +24 -0
  124. package/lib/chat/prompt/plugins/MentionNode.d.ts +66 -0
  125. package/lib/chat/prompt/plugins/MentionNode.js +77 -0
  126. package/lib/chat/prompt/promptHistory.d.ts +52 -0
  127. package/lib/chat/prompt/promptHistory.js +123 -0
  128. package/lib/chat/prompt/stack.d.ts +81 -0
  129. package/lib/chat/prompt/stack.js +41 -0
  130. package/lib/chat/prompt/useTypingPlaceholder.d.ts +52 -0
  131. package/lib/chat/prompt/useTypingPlaceholder.js +180 -0
  132. package/lib/chat/tools/A2AAgentDialog.d.ts +60 -0
  133. package/lib/chat/tools/A2AAgentDialog.js +265 -0
  134. package/lib/chat/tools/ToolApprovalBanner.d.ts +1 -1
  135. package/lib/chat/tools/ToolApprovalDialog.d.ts +1 -1
  136. package/lib/chat/tools/ToolCallDisplay.d.ts +8 -1
  137. package/lib/chat/tools/index.d.ts +1 -0
  138. package/lib/chat/tools/index.js +1 -0
  139. package/lib/chat/usage/ContextPie.d.ts +17 -0
  140. package/lib/chat/usage/{TokenUsageBar.js → ContextPie.js} +20 -14
  141. package/lib/chat/useViewportDrag.d.ts +11 -0
  142. package/lib/chat/useViewportDrag.js +61 -0
  143. package/lib/chat/viewModes.d.ts +51 -0
  144. package/lib/chat/viewModes.js +57 -0
  145. package/lib/client/AgentRuntimesClient.d.ts +10 -9
  146. package/lib/client/AgentRuntimesClient.js +1 -1
  147. package/lib/client/AgentRuntimesClientContext.d.ts +1 -0
  148. package/lib/client/AgentsMixin.d.ts +12 -12
  149. package/lib/client/AgentsMixin.js +31 -31
  150. package/lib/client/IAgentRuntimesClient.d.ts +10 -10
  151. package/lib/client/SdkAgentRuntimesClient.d.ts +10 -10
  152. package/lib/client/SdkAgentRuntimesClient.js +10 -10
  153. package/lib/client/mixins/RuntimesMixin.d.ts +37 -10
  154. package/lib/client/mixins/RuntimesMixin.js +54 -21
  155. package/lib/components/a2ui/A2uiSurfaceComposed.d.ts +1 -1
  156. package/lib/components/agents/AgentSummary.d.ts +13 -0
  157. package/lib/components/agents/AgentSummary.js +21 -4
  158. package/lib/components/cards/NotificationEventCard.d.ts +1 -1
  159. package/lib/components/cards/OutputCard.d.ts +1 -1
  160. package/lib/components/cards/ToolApprovalCard.d.ts +2 -2
  161. package/lib/components/cards/ToolApprovalCard.js +1 -0
  162. package/lib/components/code-sandboxes/CodeSandboxCellVariables.d.ts +1 -0
  163. package/lib/components/code-sandboxes/CodeSandboxCellVariables.js +0 -4
  164. package/lib/components/code-sandboxes/CodeSandboxCellVariablesDialog.d.ts +1 -0
  165. package/lib/components/code-sandboxes/CodeSandboxCellVariablesDialog.js +0 -4
  166. package/lib/components/code-sandboxes/CodeSandboxEnvironmentSelect.d.ts +35 -0
  167. package/lib/components/code-sandboxes/CodeSandboxEnvironmentSelect.js +27 -29
  168. package/lib/components/code-sandboxes/CodeSandboxLauncher.d.ts +9 -0
  169. package/lib/components/code-sandboxes/CodeSandboxLauncher.js +95 -23
  170. package/lib/components/code-sandboxes/CodeSandboxNames.js +7 -6
  171. package/lib/components/code-sandboxes/CodeSandboxPicker.js +89 -42
  172. package/lib/components/code-sandboxes/CodeSandboxPickerCell.d.ts +1 -0
  173. package/lib/components/code-sandboxes/CodeSandboxPickerCell.js +0 -4
  174. package/lib/components/code-sandboxes/CodeSandboxReservationControl.d.ts +1 -0
  175. package/lib/components/code-sandboxes/CodeSandboxReservationControl.js +5 -7
  176. package/lib/components/code-sandboxes/CodeSandboxTransfer.d.ts +5 -0
  177. package/lib/components/code-sandboxes/CodeSandboxUtils.d.ts +13 -1
  178. package/lib/components/code-sandboxes/CodeSandboxUtils.js +10 -4
  179. package/lib/components/code-sandboxes/NewCodeSandboxControls.d.ts +21 -15
  180. package/lib/components/code-sandboxes/NewCodeSandboxControls.js +28 -51
  181. package/lib/components/code-sandboxes/codeSandboxAllowance.d.ts +33 -0
  182. package/lib/components/code-sandboxes/codeSandboxAllowance.js +37 -0
  183. package/lib/components/code-sandboxes/codeSandboxEnvironments.d.ts +97 -0
  184. package/lib/components/code-sandboxes/codeSandboxEnvironments.js +184 -0
  185. package/lib/components/code-sandboxes/codeSandboxProviders.js +14 -5
  186. package/lib/components/code-sandboxes/index.d.ts +1 -0
  187. package/lib/components/code-sandboxes/index.js +1 -0
  188. package/lib/components/context/SpaceSelect.d.ts +1 -1
  189. package/lib/components/flashes/FlashDisclaimer.d.ts +1 -1
  190. package/lib/components/icons/ArtifactIcon.d.ts +1 -0
  191. package/lib/components/icons/ArtifactIcon.js +2 -1
  192. package/lib/components/nbgrader/NbGradesDetails.d.ts +1 -1
  193. package/lib/components/progress/CreditsIndicator.d.ts +1 -0
  194. package/lib/components/progress/CreditsIndicator.js +0 -4
  195. package/lib/components/scheduler/ScheduleMenu.d.ts +1 -1
  196. package/lib/components/snapshots/SandboxSnapshotMenu.d.ts +3 -2
  197. package/lib/components/snapshots/SandboxSnapshotMenu.js +11 -15
  198. package/lib/components/snippets/SnippetDialog.d.ts +1 -0
  199. package/lib/components/snippets/SnippetDialog.js +0 -4
  200. package/lib/components/specs/SpecPayloadPreview.d.ts +34 -0
  201. package/lib/components/specs/SpecPayloadPreview.js +102 -0
  202. package/lib/components/tokens/SpaceVariantToken.d.ts +1 -1
  203. package/lib/context/ContextDistribution.d.ts +1 -1
  204. package/lib/context/ContextInspector.d.ts +2 -1
  205. package/lib/context/ContextPanel.d.ts +1 -1
  206. package/lib/context/ContextUsage.d.ts +1 -1
  207. package/lib/context/CostTracker.d.ts +1 -1
  208. package/lib/context/CostUsageChart.d.ts +1 -1
  209. package/lib/context/CostUsageChart.js +4 -4
  210. package/lib/context/MessageHistory.d.ts +3 -2
  211. package/lib/context/TokenUsageChart.d.ts +1 -1
  212. package/lib/context/TokenUsageChart.js +4 -4
  213. package/lib/examples/A2UiAgentExample.js +15 -6
  214. package/lib/examples/A2UiComponentsGalleryExample.d.ts +10 -0
  215. package/lib/examples/A2UiComponentsGalleryExample.js +24 -855
  216. package/lib/examples/A2UiContactCardExample.d.ts +10 -0
  217. package/lib/examples/A2UiContactCardExample.js +23 -170
  218. package/lib/examples/A2UiJupyterOutputExample.d.ts +3 -0
  219. package/lib/examples/A2UiJupyterOutputExample.js +792 -0
  220. package/lib/examples/A2UiRestaurantExample.d.ts +10 -0
  221. package/lib/examples/A2UiRestaurantExample.js +24 -179
  222. package/lib/examples/A2UiViewerExample.d.ts +10 -0
  223. package/lib/examples/A2UiViewerExample.js +24 -378
  224. package/lib/examples/AgUiAgenticExample.js +2 -4
  225. package/lib/examples/AgUiBackendToolRenderingExample.js +3 -5
  226. package/lib/examples/AgUiHaikuGenUiExample.js +2 -4
  227. package/lib/examples/AgUiHumanInTheLoopExample.js +2 -4
  228. package/lib/examples/AgUiSharedStateExample.js +2 -4
  229. package/lib/examples/AgUiToolsBasedGenUiExample.js +2 -4
  230. package/lib/examples/AgentA2AExample.d.ts +17 -0
  231. package/lib/examples/AgentA2AExample.js +56 -0
  232. package/lib/examples/AgentCheckpointsExample.d.ts +9 -10
  233. package/lib/examples/AgentCheckpointsExample.js +72 -54
  234. package/lib/examples/AgentCodeSandboxExample.d.ts +7 -7
  235. package/lib/examples/AgentCodeSandboxExample.js +93 -303
  236. package/lib/examples/AgentCodeSandboxesExample.d.ts +10 -0
  237. package/lib/examples/AgentCodeSandboxesExample.js +56 -162
  238. package/lib/examples/AgentCodemodeExample.d.ts +7 -2
  239. package/lib/examples/AgentCodemodeExample.js +60 -125
  240. package/lib/examples/AgentCompactionExample.js +19 -135
  241. package/lib/examples/AgentEvalsExample.js +34 -66
  242. package/lib/examples/AgentGuardrailsExample.js +64 -112
  243. package/lib/examples/AgentHooksExample.d.ts +11 -0
  244. package/lib/examples/AgentHooksExample.js +30 -67
  245. package/lib/examples/AgentInferenceProviderExample.js +8 -32
  246. package/lib/examples/AgentLoopExample.d.ts +4 -2
  247. package/lib/examples/AgentLoopExample.js +45 -93
  248. package/lib/examples/AgentMCPExample.js +30 -79
  249. package/lib/examples/AgentMemoryExample.js +21 -64
  250. package/lib/examples/AgentMonitoringExample.js +33 -94
  251. package/lib/examples/AgentNotificationsExample.js +56 -95
  252. package/lib/examples/AgentOtelExample.js +35 -88
  253. package/lib/examples/AgentOutputsExample.d.ts +2 -1
  254. package/lib/examples/AgentOutputsExample.js +16 -137
  255. package/lib/examples/AgentParametersExample.js +7 -17
  256. package/lib/examples/AgentSkillsExample.js +40 -154
  257. package/lib/examples/AgentSubagentsExample.d.ts +7 -6
  258. package/lib/examples/AgentSubagentsExample.js +80 -191
  259. package/lib/examples/AgentToolApprovalsExample.js +35 -91
  260. package/lib/examples/AgentTriggersExample.js +43 -59
  261. package/lib/examples/AgentspecsExample.js +4 -4
  262. package/lib/examples/CellExample.d.ts +1 -1
  263. package/lib/examples/ChatExample.d.ts +12 -5
  264. package/lib/examples/ChatExample.js +38 -13
  265. package/lib/examples/CopilotKitLexicalExample.d.ts +1 -0
  266. package/lib/examples/CopilotKitLexicalExample.js +4 -41
  267. package/lib/examples/CopilotKitNotebookExample.d.ts +10 -0
  268. package/lib/examples/CopilotKitNotebookExample.js +2 -14
  269. package/lib/examples/DecksAgent.d.ts +21 -0
  270. package/lib/examples/DecksAgent.js +23 -0
  271. package/lib/examples/DocumentPageAgent.d.ts +19 -0
  272. package/lib/examples/DocumentPageAgent.js +15 -0
  273. package/lib/examples/Lexical2Example.d.ts +2 -2
  274. package/lib/examples/Lexical2Example.js +17 -38
  275. package/lib/examples/LexicalAgentExample.d.ts +2 -1
  276. package/lib/examples/LexicalAgentExample.js +29 -102
  277. package/lib/examples/LexicalAgentSidebarExample.d.ts +16 -5
  278. package/lib/examples/LexicalAgentSidebarExample.js +99 -131
  279. package/lib/examples/LoopShellExample.d.ts +38 -0
  280. package/lib/examples/LoopShellExample.js +68 -0
  281. package/lib/examples/LoopWorkspaceEmbed.d.ts +50 -0
  282. package/lib/examples/LoopWorkspaceEmbed.js +73 -0
  283. package/lib/examples/LoopWorkspaceExample.d.ts +150 -0
  284. package/lib/examples/LoopWorkspaceExample.js +151 -0
  285. package/lib/examples/NotebookAgentExample.d.ts +13 -0
  286. package/lib/examples/NotebookAgentExample.js +36 -86
  287. package/lib/examples/NotebookAgentSidebarExample.d.ts +10 -4
  288. package/lib/examples/NotebookAgentSidebarExample.js +39 -83
  289. package/lib/examples/NotebookCollaborationExample.d.ts +1 -1
  290. package/lib/examples/NotebookCollaborationExample.js +108 -20
  291. package/lib/examples/NotebookExample.d.ts +1 -1
  292. package/lib/examples/NotebookExample.js +20 -10
  293. package/lib/examples/NotebookPageAgent.d.ts +23 -0
  294. package/lib/examples/NotebookPageAgent.js +17 -0
  295. package/lib/examples/components/ExampleWrapper.d.ts +5 -0
  296. package/lib/examples/components/ExampleWrapper.js +36 -28
  297. package/lib/examples/components/LexicalEditor.d.ts +1 -1
  298. package/lib/examples/components/LexicalEditor.js +4 -24
  299. package/lib/examples/components/haiku/HaikuDisplay.d.ts +1 -1
  300. package/lib/examples/components/haiku/InlineHaikuCard.d.ts +1 -1
  301. package/lib/examples/components/weather/InlineWeatherCard.d.ts +1 -1
  302. package/lib/examples/example-selector.js +26 -6
  303. package/lib/examples/hooks/useCreateAgentOnce.d.ts +41 -0
  304. package/lib/examples/hooks/useCreateAgentOnce.js +51 -0
  305. package/lib/examples/hooks/useExampleAgentProtocol.d.ts +35 -0
  306. package/lib/examples/hooks/useExampleAgentProtocol.js +63 -0
  307. package/lib/examples/hooks/useExampleAgentRuntime.d.ts +16 -16
  308. package/lib/examples/hooks/useExampleAgentRuntime.js +26 -25
  309. package/lib/examples/hooks/useExampleAgentRuntimes.js +45 -61
  310. package/lib/examples/hooks/useExampleJupyterAgent.d.ts +91 -0
  311. package/lib/examples/hooks/useExampleJupyterAgent.js +151 -0
  312. package/lib/examples/index.d.ts +4 -0
  313. package/lib/examples/index.js +6 -0
  314. package/lib/examples/lexical/editorConfig.d.ts +2 -75
  315. package/lib/examples/lexical/editorConfig.js +26 -48
  316. package/lib/examples/lexical/lexical-theme.css +65 -47
  317. package/lib/examples/main.js +450 -322
  318. package/lib/examples/scenes/A2UiComponentsGalleryScene.d.ts +3 -0
  319. package/lib/examples/scenes/A2UiComponentsGalleryScene.js +872 -0
  320. package/lib/examples/scenes/A2UiContactCardScene.d.ts +3 -0
  321. package/lib/examples/scenes/A2UiContactCardScene.js +188 -0
  322. package/lib/examples/scenes/A2UiRestaurantScene.d.ts +3 -0
  323. package/lib/examples/scenes/A2UiRestaurantScene.js +196 -0
  324. package/lib/examples/scenes/A2UiViewerScene.d.ts +3 -0
  325. package/lib/examples/scenes/A2UiViewerScene.js +395 -0
  326. package/lib/examples/utils/agentSummaryStore.d.ts +7 -1
  327. package/lib/examples/utils/agents/earthquake-detector.ipynb.json +1 -1
  328. package/lib/examples/utils/agents/earthquake-detector.lexical.json +1 -1
  329. package/lib/examples/utils/agents/sales-forecaster.ipynb.json +1 -1
  330. package/lib/examples/utils/agents/sales-forecaster.lexical.json +1 -1
  331. package/lib/examples/utils/agents/social-post-generator.ipynb.json +1 -1
  332. package/lib/examples/utils/agents/social-post-generator.lexical.json +1 -1
  333. package/lib/examples/utils/exampleSurfaces.d.ts +31 -0
  334. package/lib/examples/utils/exampleSurfaces.js +38 -0
  335. package/lib/examples/utils/jupyterSandboxUrl.d.ts +27 -0
  336. package/lib/examples/utils/jupyterSandboxUrl.js +55 -0
  337. package/lib/examples/utils/localUrl.d.ts +2 -0
  338. package/lib/examples/utils/localUrl.js +49 -0
  339. package/lib/examples/utils/notebookToolbarItems.d.ts +46 -0
  340. package/lib/examples/utils/notebookToolbarItems.js +129 -0
  341. package/lib/examples/utils/runtimeTargetStore.d.ts +40 -1
  342. package/lib/examples/utils/runtimeTargetStore.js +82 -1
  343. package/lib/examples/utils/themedProvider.d.ts +7 -0
  344. package/lib/examples/utils/themedProvider.js +57 -4
  345. package/lib/examples/utils/useExampleAgentRuntimesUrl.js +30 -5
  346. package/lib/examples/utils/waitForAgent.d.ts +23 -0
  347. package/lib/examples/utils/waitForAgent.js +24 -0
  348. package/lib/examples2/CellExample.d.ts +1 -1
  349. package/lib/examples2/DatalayerNotebookExample.d.ts +1 -1
  350. package/lib/examples2/NotebookExample.d.ts +1 -1
  351. package/lib/examples2/NotebookMutationsKernel.d.ts +1 -1
  352. package/lib/examples2/NotebookMutationsServiceManager.d.ts +1 -1
  353. package/lib/examples2/main.js +24 -4
  354. package/lib/extensions/A2UIExtension.d.ts +1 -1
  355. package/lib/hooks/index.d.ts +8 -5
  356. package/lib/hooks/index.js +20 -5
  357. package/lib/hooks/useAgentRuntimes.d.ts +49 -22
  358. package/lib/hooks/useAgentRuntimes.js +214 -90
  359. package/lib/hooks/useAgentsCatalog.js +32 -4
  360. package/lib/hooks/useBrowserAgent.d.ts +26 -0
  361. package/lib/hooks/useBrowserAgent.js +75 -0
  362. package/lib/hooks/useBrowserInference.d.ts +27 -0
  363. package/lib/hooks/useBrowserInference.js +67 -0
  364. package/lib/hooks/useCheckpoints.d.ts +8 -17
  365. package/lib/hooks/useCheckpoints.js +25 -59
  366. package/lib/hooks/useConfig.d.ts +1 -1
  367. package/lib/hooks/useConfig.js +5 -1
  368. package/lib/hooks/useContextSnapshot.d.ts +18 -7
  369. package/lib/hooks/useContextSnapshot.js +14 -10
  370. package/lib/hooks/useNotifications.d.ts +11 -11
  371. package/lib/hooks/useNotifications.js +1 -1
  372. package/lib/hooks/useProjects.d.ts +7 -19
  373. package/lib/hooks/useProjects.js +5 -5
  374. package/lib/hooks/useSkills.d.ts +0 -1
  375. package/lib/hooks/useSkills.js +6 -2
  376. package/lib/hooks/useToolApprovals.d.ts +8 -8
  377. package/lib/hooks/useToolApprovals.js +3 -3
  378. package/lib/identity/AgentIdentity.d.ts +9 -2
  379. package/lib/index.d.ts +1 -0
  380. package/lib/index.js +4 -0
  381. package/lib/jupyter/index.d.ts +2 -1
  382. package/lib/jupyter/index.js +2 -1
  383. package/lib/jupyter/serverExecution.d.ts +78 -0
  384. package/lib/jupyter/serverExecution.js +258 -0
  385. package/lib/jupyter/useResumeServerExecutions.d.ts +11 -0
  386. package/lib/jupyter/useResumeServerExecutions.js +64 -0
  387. package/lib/lexical/ChatInlinePlugin.d.ts +33 -0
  388. package/lib/lexical/ChatInlinePlugin.js +0 -36
  389. package/lib/loop/core/focusRequests.d.ts +10 -0
  390. package/lib/loop/core/focusRequests.js +46 -0
  391. package/lib/loop/core/index.d.ts +903 -0
  392. package/lib/loop/core/index.js +264 -0
  393. package/lib/loop/core/promptStore.d.ts +44 -0
  394. package/lib/loop/core/promptStore.js +63 -0
  395. package/lib/loop/core/surfaceRequests.d.ts +33 -0
  396. package/lib/loop/core/surfaceRequests.js +29 -0
  397. package/lib/loop/core/toolbar.d.ts +28 -0
  398. package/lib/loop/core/toolbar.js +59 -0
  399. package/lib/loop/embed/LoopEmbed.d.ts +57 -0
  400. package/lib/loop/embed/LoopEmbed.js +124 -0
  401. package/lib/loop/extensions.d.ts +10 -0
  402. package/lib/loop/extensions.js +49 -0
  403. package/lib/loop/index.d.ts +68 -0
  404. package/lib/loop/index.js +87 -0
  405. package/lib/loop/plugins/a2a-sidebar/A2ASidebar.d.ts +48 -0
  406. package/lib/loop/plugins/a2a-sidebar/A2ASidebar.js +190 -0
  407. package/lib/loop/plugins/a2a-sidebar/index.d.ts +5 -0
  408. package/lib/loop/plugins/a2a-sidebar/index.js +39 -0
  409. package/lib/loop/plugins/a2ui/A2uiView.d.ts +12 -0
  410. package/lib/loop/plugins/a2ui/A2uiView.js +97 -0
  411. package/lib/loop/plugins/a2ui/index.d.ts +3 -0
  412. package/lib/loop/plugins/a2ui/index.js +50 -0
  413. package/lib/loop/plugins/a2ui-scene/index.d.ts +36 -0
  414. package/lib/loop/plugins/a2ui-scene/index.js +28 -0
  415. package/lib/loop/plugins/agent-a2a/index.d.ts +3 -0
  416. package/lib/loop/plugins/agent-a2a/index.js +26 -0
  417. package/lib/loop/plugins/agent-a2ui/index.d.ts +3 -0
  418. package/lib/loop/plugins/agent-a2ui/index.js +42 -0
  419. package/lib/loop/plugins/agent-a2ui-jupyter-output/index.d.ts +3 -0
  420. package/lib/loop/plugins/agent-a2ui-jupyter-output/index.js +53 -0
  421. package/lib/loop/plugins/agent-capacity/index.d.ts +39 -0
  422. package/lib/loop/plugins/agent-capacity/index.js +52 -0
  423. package/lib/loop/plugins/agent-checkpoints/index.d.ts +3 -0
  424. package/lib/loop/plugins/agent-checkpoints/index.js +26 -0
  425. package/lib/loop/plugins/agent-code-sandbox/index.d.ts +3 -0
  426. package/lib/loop/plugins/agent-code-sandbox/index.js +24 -0
  427. package/lib/loop/plugins/agent-code-sandboxes/index.d.ts +13 -0
  428. package/lib/loop/plugins/agent-code-sandboxes/index.js +85 -0
  429. package/lib/loop/plugins/agent-codemode/index.d.ts +5 -0
  430. package/lib/loop/plugins/agent-codemode/index.js +41 -0
  431. package/lib/loop/plugins/agent-compaction/index.d.ts +10 -0
  432. package/lib/loop/plugins/agent-compaction/index.js +34 -0
  433. package/lib/loop/plugins/agent-evals/index.d.ts +3 -0
  434. package/lib/loop/plugins/agent-evals/index.js +25 -0
  435. package/lib/loop/plugins/agent-guardrails/index.d.ts +3 -0
  436. package/lib/loop/plugins/agent-guardrails/index.js +25 -0
  437. package/lib/loop/plugins/agent-hooks/index.d.ts +3 -0
  438. package/lib/loop/plugins/agent-hooks/index.js +28 -0
  439. package/lib/loop/plugins/agent-inference/index.d.ts +7 -0
  440. package/lib/loop/plugins/agent-inference/index.js +29 -0
  441. package/lib/loop/plugins/agent-mcp/index.d.ts +3 -0
  442. package/lib/loop/plugins/agent-mcp/index.js +25 -0
  443. package/lib/loop/plugins/agent-memory/index.d.ts +3 -0
  444. package/lib/loop/plugins/agent-memory/index.js +25 -0
  445. package/lib/loop/plugins/agent-monitoring/index.d.ts +3 -0
  446. package/lib/loop/plugins/agent-monitoring/index.js +25 -0
  447. package/lib/loop/plugins/agent-notifications/index.d.ts +3 -0
  448. package/lib/loop/plugins/agent-notifications/index.js +25 -0
  449. package/lib/loop/plugins/agent-otel/index.d.ts +3 -0
  450. package/lib/loop/plugins/agent-otel/index.js +25 -0
  451. package/lib/loop/plugins/agent-outputs/index.d.ts +3 -0
  452. package/lib/loop/plugins/agent-outputs/index.js +25 -0
  453. package/lib/loop/plugins/agent-parameters/index.d.ts +3 -0
  454. package/lib/loop/plugins/agent-parameters/index.js +25 -0
  455. package/lib/loop/plugins/agent-skills/index.d.ts +3 -0
  456. package/lib/loop/plugins/agent-skills/index.js +25 -0
  457. package/lib/loop/plugins/agent-subagents/index.d.ts +3 -0
  458. package/lib/loop/plugins/agent-subagents/index.js +25 -0
  459. package/lib/loop/plugins/agent-tool-approvals/index.d.ts +11 -0
  460. package/lib/loop/plugins/agent-tool-approvals/index.js +38 -0
  461. package/lib/loop/plugins/agent-triggers/index.d.ts +3 -0
  462. package/lib/loop/plugins/agent-triggers/index.js +26 -0
  463. package/lib/loop/plugins/agents/AgentSummaryPanel.d.ts +20 -0
  464. package/lib/loop/plugins/agents/AgentSummaryPanel.js +50 -0
  465. package/lib/loop/plugins/agents/AnonymousKeyBadge.d.ts +35 -0
  466. package/lib/loop/plugins/agents/AnonymousKeyBadge.js +53 -0
  467. package/lib/loop/plugins/agents/DatalayerAgentBridge.d.ts +24 -0
  468. package/lib/loop/plugins/agents/DatalayerAgentBridge.js +102 -0
  469. package/lib/loop/plugins/agents/DatalayerAgentMount.d.ts +18 -0
  470. package/lib/loop/plugins/agents/DatalayerAgentMount.js +12 -0
  471. package/lib/loop/plugins/agents/SandboxSelector.d.ts +18 -0
  472. package/lib/loop/plugins/agents/SandboxSelector.js +99 -0
  473. package/lib/loop/plugins/agents/SandboxStatusBridge.d.ts +19 -0
  474. package/lib/loop/plugins/agents/SandboxStatusBridge.js +23 -0
  475. package/lib/loop/plugins/agents/SandboxView.d.ts +11 -0
  476. package/lib/loop/plugins/agents/SandboxView.js +43 -0
  477. package/lib/loop/plugins/agents/TeamMemberPicker.d.ts +17 -0
  478. package/lib/loop/plugins/agents/TeamMemberPicker.js +60 -0
  479. package/lib/loop/plugins/agents/agentIcons.d.ts +23 -0
  480. package/lib/loop/plugins/agents/agentIcons.js +88 -0
  481. package/lib/loop/plugins/agents/browserService.d.ts +50 -0
  482. package/lib/loop/plugins/agents/browserService.js +233 -0
  483. package/lib/loop/plugins/agents/index.d.ts +13 -0
  484. package/lib/loop/plugins/agents/index.js +17 -0
  485. package/lib/loop/plugins/agents/plugin.d.ts +91 -0
  486. package/lib/loop/plugins/agents/plugin.js +201 -0
  487. package/lib/loop/plugins/agents/service.d.ts +99 -0
  488. package/lib/loop/plugins/agents/service.js +167 -0
  489. package/lib/loop/plugins/agents/specSubagents.d.ts +18 -0
  490. package/lib/loop/plugins/agents/specSubagents.js +33 -0
  491. package/lib/loop/plugins/agents/switchable.d.ts +107 -0
  492. package/lib/loop/plugins/agents/switchable.js +405 -0
  493. package/lib/loop/plugins/agents/team.d.ts +87 -0
  494. package/lib/loop/plugins/agents/team.js +143 -0
  495. package/lib/loop/plugins/agents/useSandboxService.d.ts +11 -0
  496. package/lib/loop/plugins/agents/useSandboxService.js +36 -0
  497. package/lib/loop/plugins/agents/useTeamSelection.d.ts +10 -0
  498. package/lib/loop/plugins/agents/useTeamSelection.js +27 -0
  499. package/lib/loop/plugins/agentspecs/AgentspecPicker.d.ts +15 -0
  500. package/lib/loop/plugins/agentspecs/AgentspecPicker.js +90 -0
  501. package/lib/loop/plugins/agentspecs/index.d.ts +6 -0
  502. package/lib/loop/plugins/agentspecs/index.js +63 -0
  503. package/lib/loop/plugins/chat/ChatView.d.ts +20 -0
  504. package/lib/loop/plugins/chat/ChatView.js +1679 -0
  505. package/lib/loop/plugins/chat/chatViewTools.d.ts +41 -0
  506. package/lib/loop/plugins/chat/chatViewTools.js +92 -0
  507. package/lib/loop/plugins/chat/index.d.ts +111 -0
  508. package/lib/loop/plugins/chat/index.js +143 -0
  509. package/lib/loop/plugins/chat/turnState.d.ts +41 -0
  510. package/lib/loop/plugins/chat/turnState.js +90 -0
  511. package/lib/loop/plugins/chat-extras/index.d.ts +14 -0
  512. package/lib/loop/plugins/chat-extras/index.js +51 -0
  513. package/lib/loop/plugins/chat-header/HeaderView.d.ts +13 -0
  514. package/lib/loop/plugins/chat-header/HeaderView.js +6 -0
  515. package/lib/loop/plugins/chat-header/index.d.ts +3 -0
  516. package/lib/loop/plugins/chat-header/index.js +21 -0
  517. package/lib/loop/plugins/chat-view/index.d.ts +10 -0
  518. package/lib/loop/plugins/chat-view/index.js +29 -0
  519. package/lib/loop/plugins/checkpoints-sidebar/CheckpointsSidebar.d.ts +27 -0
  520. package/lib/loop/plugins/checkpoints-sidebar/CheckpointsSidebar.js +174 -0
  521. package/lib/loop/plugins/checkpoints-sidebar/index.d.ts +5 -0
  522. package/lib/loop/plugins/checkpoints-sidebar/index.js +39 -0
  523. package/lib/loop/plugins/commands/index.d.ts +20 -0
  524. package/lib/loop/plugins/commands/index.js +148 -0
  525. package/lib/loop/plugins/decks/DeckSurface.d.ts +10 -0
  526. package/lib/loop/plugins/decks/DeckSurface.js +6 -0
  527. package/lib/loop/plugins/decks/DecksMenu.d.ts +11 -0
  528. package/lib/loop/plugins/decks/DecksMenu.js +42 -0
  529. package/lib/loop/plugins/decks/index.d.ts +19 -0
  530. package/lib/loop/plugins/decks/index.js +133 -0
  531. package/lib/loop/plugins/decks/surface.d.ts +7 -0
  532. package/lib/loop/plugins/decks/surface.js +11 -0
  533. package/lib/loop/plugins/document/DocumentView.d.ts +12 -0
  534. package/lib/loop/plugins/document/DocumentView.js +68 -0
  535. package/lib/loop/plugins/document/index.d.ts +3 -0
  536. package/lib/loop/plugins/document/index.js +86 -0
  537. package/lib/loop/plugins/document/openingDocument.d.ts +28 -0
  538. package/lib/loop/plugins/document/openingDocument.js +122 -0
  539. package/lib/loop/plugins/document-toolbar/SandboxStatusItem.d.ts +16 -0
  540. package/lib/loop/plugins/document-toolbar/SandboxStatusItem.js +31 -0
  541. package/lib/loop/plugins/document-toolbar/index.d.ts +4 -0
  542. package/lib/loop/plugins/document-toolbar/index.js +35 -0
  543. package/lib/loop/plugins/document-toolbar/names.d.ts +6 -0
  544. package/lib/loop/plugins/document-toolbar/names.js +10 -0
  545. package/lib/loop/plugins/document-toolbar/plugin.d.ts +2 -0
  546. package/lib/loop/plugins/document-toolbar/plugin.js +38 -0
  547. package/lib/loop/plugins/document-view/index.d.ts +10 -0
  548. package/lib/loop/plugins/document-view/index.js +28 -0
  549. package/lib/loop/plugins/graph/GraphToggle.d.ts +17 -0
  550. package/lib/loop/plugins/graph/GraphToggle.js +22 -0
  551. package/lib/loop/plugins/graph/GraphView.d.ts +16 -0
  552. package/lib/loop/plugins/graph/GraphView.js +6 -0
  553. package/lib/loop/plugins/graph/index.d.ts +5 -0
  554. package/lib/loop/plugins/graph/index.js +80 -0
  555. package/lib/loop/plugins/graph/viewType.d.ts +10 -0
  556. package/lib/loop/plugins/graph/viewType.js +14 -0
  557. package/lib/loop/plugins/input-prompt/ComposerView.d.ts +15 -0
  558. package/lib/loop/plugins/input-prompt/ComposerView.js +25 -0
  559. package/lib/loop/plugins/input-prompt/index.d.ts +25 -0
  560. package/lib/loop/plugins/input-prompt/index.js +26 -0
  561. package/lib/loop/plugins/models/index.d.ts +3 -0
  562. package/lib/loop/plugins/models/index.js +49 -0
  563. package/lib/loop/plugins/notebook/NotebookView.d.ts +9 -0
  564. package/lib/loop/plugins/notebook/NotebookView.js +189 -0
  565. package/lib/loop/plugins/notebook/index.d.ts +3 -0
  566. package/lib/loop/plugins/notebook/index.js +91 -0
  567. package/lib/loop/plugins/notebook/openingNotebook.d.ts +72 -0
  568. package/lib/loop/plugins/notebook/openingNotebook.js +90 -0
  569. package/lib/loop/plugins/notebook-toolbar/index.d.ts +4 -0
  570. package/lib/loop/plugins/notebook-toolbar/index.js +45 -0
  571. package/lib/loop/plugins/notebook-toolbar/items.d.ts +10 -0
  572. package/lib/loop/plugins/notebook-toolbar/items.js +45 -0
  573. package/lib/loop/plugins/notebook-toolbar/names.d.ts +9 -0
  574. package/lib/loop/plugins/notebook-toolbar/names.js +13 -0
  575. package/lib/loop/plugins/notebook-toolbar/plugin.d.ts +2 -0
  576. package/lib/loop/plugins/notebook-toolbar/plugin.js +24 -0
  577. package/lib/loop/plugins/notebook-view/index.d.ts +10 -0
  578. package/lib/loop/plugins/notebook-view/index.js +30 -0
  579. package/lib/loop/plugins/page-layout/TurnPanel.d.ts +25 -0
  580. package/lib/loop/plugins/page-layout/TurnPanel.js +126 -0
  581. package/lib/loop/plugins/page-layout/index.d.ts +71 -0
  582. package/lib/loop/plugins/page-layout/index.js +160 -0
  583. package/lib/loop/plugins/plugins-panel/index.d.ts +14 -0
  584. package/lib/loop/plugins/plugins-panel/index.js +74 -0
  585. package/lib/loop/plugins/prompt/NewChatAction.d.ts +19 -0
  586. package/lib/loop/plugins/prompt/NewChatAction.js +10 -0
  587. package/lib/loop/plugins/prompt/index.d.ts +11 -0
  588. package/lib/loop/plugins/prompt/index.js +73 -0
  589. package/lib/loop/plugins/shell/editorChoice.d.ts +31 -0
  590. package/lib/loop/plugins/shell/editorChoice.js +58 -0
  591. package/lib/loop/plugins/shell/index.d.ts +47 -0
  592. package/lib/loop/plugins/shell/index.js +142 -0
  593. package/lib/loop/plugins/shell/useEditorPreviews.d.ts +32 -0
  594. package/lib/loop/plugins/shell/useEditorPreviews.js +217 -0
  595. package/lib/loop/plugins/subagent-activity/SubagentPulse.d.ts +16 -0
  596. package/lib/loop/plugins/subagent-activity/SubagentPulse.js +45 -0
  597. package/lib/loop/plugins/subagent-activity/index.d.ts +12 -0
  598. package/lib/loop/plugins/subagent-activity/index.js +42 -0
  599. package/lib/loop/plugins/view-switch/index.d.ts +46 -0
  600. package/lib/loop/plugins/view-switch/index.js +68 -0
  601. package/lib/loop/plugins/window-frame/WindowFrame.d.ts +48 -0
  602. package/lib/loop/plugins/window-frame/WindowFrame.js +63 -0
  603. package/lib/loop/plugins/window-frame/index.d.ts +6 -0
  604. package/lib/loop/plugins/window-frame/index.js +79 -0
  605. package/lib/loop/plugins/window-frame/slots.d.ts +26 -0
  606. package/lib/loop/plugins/window-frame/slots.js +30 -0
  607. package/lib/loop/presets.d.ts +181 -0
  608. package/lib/loop/presets.js +141 -0
  609. package/lib/loop/shell/LoopWorkspace.d.ts +109 -0
  610. package/lib/loop/shell/LoopWorkspace.js +231 -0
  611. package/lib/loop/shell/PluginToggles.d.ts +21 -0
  612. package/lib/loop/shell/PluginToggles.js +41 -0
  613. package/lib/loop/shell/ViewSwitcher.d.ts +24 -0
  614. package/lib/loop/shell/ViewSwitcher.js +43 -0
  615. package/lib/loop/shell/WorkspaceFullScreenAction.d.ts +25 -0
  616. package/lib/loop/shell/WorkspaceFullScreenAction.js +24 -0
  617. package/lib/loop/shell/index.d.ts +10 -0
  618. package/lib/loop/shell/index.js +14 -0
  619. package/lib/loop/shell/useWorkspaceFullScreen.d.ts +93 -0
  620. package/lib/loop/shell/useWorkspaceFullScreen.js +167 -0
  621. package/lib/loop-example-main.d.ts +1 -0
  622. package/lib/loop-example-main.js +42 -0
  623. package/lib/loop-main.d.ts +1 -0
  624. package/lib/loop-main.js +138 -0
  625. package/lib/mcp/McpServerManager.d.ts +1 -1
  626. package/lib/models/CodeSandboxSnapshotDTO.d.ts +1 -1
  627. package/lib/models/CodeSandboxVariant.d.ts +13 -5
  628. package/lib/models/CodeSandboxVariant.js +17 -5
  629. package/lib/models/Content.d.ts +26 -0
  630. package/lib/models/Deck.d.ts +14 -0
  631. package/lib/models/Environment.d.ts +678 -5
  632. package/lib/models/Environment.js +12 -1
  633. package/lib/models/EnvironmentDTO.d.ts +24 -6
  634. package/lib/models/EnvironmentDTO.js +13 -2
  635. package/lib/models/Exercise.d.ts +0 -2
  636. package/lib/models/ItemType.d.ts +1 -1
  637. package/lib/models/Library.d.ts +0 -2
  638. package/lib/models/Notebook.d.ts +0 -2
  639. package/lib/models/ProjectDTO.d.ts +2 -2
  640. package/lib/models/ProjectDTO.js +4 -4
  641. package/lib/models/Runtime.d.ts +17 -11
  642. package/lib/models/RuntimeDTO.d.ts +16 -7
  643. package/lib/models/RuntimeDTO.js +9 -9
  644. package/lib/models/Space.d.ts +10 -0
  645. package/lib/models/Space.js +1 -1
  646. package/lib/models/SpaceDTO.d.ts +1 -1
  647. package/lib/models/SpaceItem.d.ts +2 -2
  648. package/lib/models/environments.generated.d.ts +141 -0
  649. package/lib/models/environments.generated.js +13 -0
  650. package/lib/models/index.d.ts +1 -1
  651. package/lib/models/index.js +1 -1
  652. package/lib/protocols/A2AAdapter.d.ts +39 -6
  653. package/lib/protocols/A2AAdapter.js +180 -11
  654. package/lib/protocols/ACPAdapter.js +40 -37
  655. package/lib/protocols/AGUIAdapter.d.ts +14 -0
  656. package/lib/protocols/AGUIAdapter.js +34 -9
  657. package/lib/protocols/BrowserAgentAdapter.d.ts +125 -0
  658. package/lib/protocols/BrowserAgentAdapter.js +317 -0
  659. package/lib/protocols/VercelAIAdapter.d.ts +10 -0
  660. package/lib/protocols/VercelAIAdapter.js +27 -7
  661. package/lib/protocols/index.d.ts +1 -0
  662. package/lib/protocols/index.js +1 -0
  663. package/lib/runtimes/actions.d.ts +8 -3
  664. package/lib/runtimes/actions.js +46 -14
  665. package/lib/runtimes/apis.d.ts +13 -4
  666. package/lib/runtimes/browser/agent.d.ts +54 -0
  667. package/lib/runtimes/browser/agent.js +63 -0
  668. package/lib/runtimes/browser/anonymousToken.d.ts +121 -0
  669. package/lib/runtimes/browser/anonymousToken.js +300 -0
  670. package/lib/runtimes/browser/frontendTools.d.ts +61 -0
  671. package/lib/runtimes/browser/frontendTools.js +152 -0
  672. package/lib/runtimes/browser/index.d.ts +11 -0
  673. package/lib/runtimes/browser/index.js +20 -0
  674. package/lib/runtimes/browser/model.d.ts +52 -0
  675. package/lib/runtimes/browser/model.js +110 -0
  676. package/lib/runtimes/browser/protocol.d.ts +54 -0
  677. package/lib/runtimes/browser/protocol.js +35 -0
  678. package/lib/runtimes/browser/subagents.d.ts +131 -0
  679. package/lib/runtimes/browser/subagents.js +241 -0
  680. package/lib/runtimes/index.d.ts +3 -0
  681. package/lib/runtimes/index.js +3 -0
  682. package/lib/runtimes/lifecycle.d.ts +76 -0
  683. package/lib/runtimes/lifecycle.js +99 -0
  684. package/lib/runtimes/variants.d.ts +172 -0
  685. package/lib/runtimes/variants.js +187 -0
  686. package/lib/services/reconnectToRuntime.d.ts +2 -2
  687. package/lib/services/reconnectToRuntime.js +3 -3
  688. package/lib/services/sandboxServiceManagers.d.ts +2 -2
  689. package/lib/services/sandboxServiceManagers.js +11 -11
  690. package/lib/specs/agents/agents.d.ts +129 -112
  691. package/lib/specs/agents/agents.js +8200 -5044
  692. package/lib/specs/evals.js +9 -0
  693. package/lib/specs/frontendTools.d.ts +4 -0
  694. package/lib/specs/frontendTools.js +77 -3
  695. package/lib/specs/mcpServers.d.ts +1 -0
  696. package/lib/specs/mcpServers.js +38 -15
  697. package/lib/specs/models.d.ts +18 -0
  698. package/lib/specs/models.js +154 -0
  699. package/lib/specs/outputs.d.ts +2 -0
  700. package/lib/specs/outputs.js +31 -0
  701. package/lib/specs/personas.js +1 -1
  702. package/lib/specs/skills.js +7 -7
  703. package/lib/specs/teams/teams.d.ts +2 -0
  704. package/lib/specs/teams/teams.js +628 -111
  705. package/lib/state/index.d.ts +1 -1
  706. package/lib/state/index.js +1 -1
  707. package/lib/state/substates/RuntimesState.d.ts +5 -5
  708. package/lib/state/substates/RuntimesState.js +14 -14
  709. package/lib/state/substates/index.d.ts +1 -1
  710. package/lib/state/substates/index.js +1 -1
  711. package/lib/stores/agentRuntimeStore.d.ts +43 -1
  712. package/lib/stores/agentRuntimeStore.js +100 -11
  713. package/lib/stores/index.d.ts +1 -1
  714. package/lib/stores/index.js +1 -1
  715. package/lib/stories/Button.d.ts +1 -1
  716. package/lib/stories/Button.stories.d.ts +1 -1
  717. package/lib/stories/Header.d.ts +1 -1
  718. package/lib/stories/Header.stories.d.ts +1 -1
  719. package/lib/test-setup.js +66 -0
  720. package/lib/tools/adapters/agent-runtimes/lexicalHooks.d.ts +15 -0
  721. package/lib/tools/adapters/agent-runtimes/lexicalHooks.js +43 -2
  722. package/lib/tools/adapters/agent-runtimes/notebookHooks.d.ts +9 -0
  723. package/lib/tools/adapters/agent-runtimes/notebookHooks.js +12 -0
  724. package/lib/tools/adapters/commands/frontendCommandTools.d.ts +11 -0
  725. package/lib/tools/adapters/commands/frontendCommandTools.js +60 -0
  726. package/lib/tools/adapters/commands/index.d.ts +1 -0
  727. package/lib/tools/adapters/commands/index.js +5 -0
  728. package/lib/tools/adapters/copilotkit/lexicalHooks.js +7 -3
  729. package/lib/tools/index.d.ts +1 -0
  730. package/lib/tools/index.js +1 -0
  731. package/lib/types/a2a.d.ts +15 -1
  732. package/lib/types/agents-lifecycle.d.ts +11 -6
  733. package/lib/types/agents.d.ts +31 -6
  734. package/lib/types/agentspecs.d.ts +120 -4
  735. package/lib/types/chat.d.ts +251 -3
  736. package/lib/types/checkpoints.d.ts +20 -0
  737. package/lib/types/config.d.ts +13 -1
  738. package/lib/types/connection.d.ts +1 -1
  739. package/lib/types/evals.d.ts +2 -0
  740. package/lib/types/messages.d.ts +60 -0
  741. package/lib/types/models.d.ts +26 -0
  742. package/lib/types/notifications.d.ts +15 -6
  743. package/lib/types/outputs.d.ts +2 -0
  744. package/lib/types/protocol.d.ts +7 -2
  745. package/lib/types/stream.d.ts +48 -3
  746. package/lib/types/stream.js +5 -0
  747. package/lib/types/teams.d.ts +102 -4
  748. package/lib/types/tool-approvals.d.ts +1 -1
  749. package/lib/types/tools.d.ts +8 -2
  750. package/lib/utils/utils.js +7 -1
  751. package/package.json +376 -361
  752. package/scripts/codegen/compose.py +208 -0
  753. package/scripts/codegen/generate_agents.py +202 -31
  754. package/scripts/codegen/generate_evals.py +2 -0
  755. package/scripts/codegen/generate_frontend_tools.py +35 -6
  756. package/scripts/codegen/generate_mcp_servers.py +2 -2
  757. package/scripts/codegen/generate_models.py +40 -15
  758. package/scripts/codegen/generate_outputs.py +2 -0
  759. package/scripts/codegen/generate_sandbox_agents.py +76 -62
  760. package/scripts/codegen/generate_skills.py +2 -2
  761. package/scripts/codegen/generate_teams.py +178 -4
  762. package/scripts/codegen/generate_tools.py +6 -4
  763. package/scripts/gen_workers.py +278 -110
  764. package/scripts/generate-environments-types.py +404 -0
  765. package/lib/chat/prompt/InputFooter.d.ts +0 -58
  766. package/lib/chat/prompt/InputFooter.js +0 -194
  767. package/lib/chat/prompt/InputPromptFooter.d.ts +0 -27
  768. package/lib/chat/prompt/InputPromptFooter.js +0 -15
  769. package/lib/chat/prompt/InputPromptHeader.d.ts +0 -15
  770. package/lib/chat/prompt/InputPromptHeader.js +0 -15
  771. package/lib/chat/usage/TokenUsageBar.d.ts +0 -8
  772. package/lib/hooks/useRuntimes.d.ts +0 -5
  773. package/lib/hooks/useRuntimes.js +0 -34
  774. package/lib/jupyter/kernelsHandler.d.ts +0 -8
  775. package/lib/jupyter/kernelsHandler.js +0 -39
  776. package/lib/models/Dataset.d.ts +0 -16
  777. package/lib/utils/Cells.d.ts +0 -2
  778. package/lib/utils/Cells.js +0 -28
  779. /package/lib/models/{Dataset.js → Deck.js} +0 -0
@@ -0,0 +1,1679 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { Box, IconButton, SegmentedControl, Text, Truncate, } from '@primer/react';
4
+ import { useColorPalette } from '@datalayer/primer-addons';
5
+ import { ScreenFullIcon, ScreenNormalIcon } from '@primer/octicons-react';
6
+ import { signal } from '@datalayer/reactor';
7
+ /* A signal to read when the layout publishes no live stance: the hook needs one. */
8
+ const NO_STANCE = signal(undefined);
9
+ import { useContributions, useSignalValue, useGate, useReactorPlatform, ReactorLazy, ReactorSlot, useSlotComponents, } from '@datalayer/reactor/react';
10
+ import { ChatBase } from '../../../chat/base/ChatBase';
11
+ import { SUGGESTION_CHIP_WIDTH } from '../../../chat/display/EmptyState';
12
+ import { AnonymousKeyExpired } from '@datalayer/core/lib/components/anonymous/AnonymousKeyExpired';
13
+ import { useAnonymousSessionStore } from '../../../runtimes/browser/anonymousToken';
14
+ import { browserProtocolConfig } from '../../../runtimes/browser';
15
+ import { useBrowserInference } from '../../../hooks/useBrowserInference';
16
+ import { getAgentspecs } from '../../../specs/agents';
17
+ import { targetRunsAgentInPage, } from '../agents/switchable';
18
+ import { agentIcon } from '../agents/agentIcons';
19
+ import { specSubagents } from '../agents/specSubagents';
20
+ import { subagentsFor } from '../agents/team';
21
+ import { useOptionalTeamSelection } from '../agents/useTeamSelection';
22
+ /**
23
+ * Stands in for a workspace with no team.
24
+ *
25
+ * `useSignalValue` needs a signal on every render, and a hook cannot be
26
+ * skipped for the ordinary single-agent case.
27
+ */
28
+ const EMPTY_SELECTION = signal('');
29
+ /* A signal to read when no example contributed chat extras, so the hook
30
+ below always has one — a hook cannot be called conditionally. */
31
+ const EMPTY_CHAT_EXTRAS = signal({});
32
+ import { useWorkspaceFullScreen } from '../../shell/useWorkspaceFullScreen';
33
+ import { CHAT_PLUGIN_NAME } from './index';
34
+ import { INPUT_PROMPT_PLUGIN_NAME, } from '../input-prompt';
35
+ import { agentRuntimeStore, useAgentRuntimeContextSnapshot, } from '../../../stores';
36
+ import { useIAMStore } from '../../../state';
37
+ import { useConfig } from '../../../hooks/useConfig';
38
+ import { useSkills, useSkillActions } from '../../../hooks/useSkills';
39
+ import { AI_MODEL_CATALOGUE } from '../../../specs/models';
40
+ import { LoopAgentBlueprint, LoopAgentGate, LoopChatComposer, LoopChatExtras, LoopChatHeader, LoopChatLayout, LoopChatSuggestion, LoopChatSurface, LoopChatTurn, LoopFrontendTool, LoopSlots, canOpenView, onPromptFocusRequest, onSurfaceRequest, useLoopPromptStore, } from '../../core';
41
+ import { turnWritersOf } from './turnState';
42
+ import { isInactiveSurfaceContribution, orderToolContributions, toolsForChatView, } from './chatViewTools';
43
+ import { useAgentCommandTools } from '../../../tools/adapters/commands';
44
+ import { NONE_EDITOR, seedEditorChoice, setEditorOptions, } from '../shell/editorChoice';
45
+ /** No editor: the conversation on its own. */
46
+ const NO_SURFACE = '';
47
+ /**
48
+ * How fast the full-screen control breathes once it starts asking.
49
+ *
50
+ * Slow enough to read as breathing rather than blinking — a fast pulse on a
51
+ * small control reads as an error state, and this one is an invitation.
52
+ */
53
+ const FULLSCREEN_HINT_PERIOD_MS = 1400;
54
+ export default function ChatView({ workspace }) {
55
+ /* The active theme's own colours. Read from the store rather than a
56
+ provider, so a workspace mounted without the addons theme still gets the
57
+ default palette instead of throwing. */
58
+ const palette = useColorPalette();
59
+ const controlsRef = useRef(null);
60
+ const { setViewControls } = workspace;
61
+ const [transient, setTransient] = useState(null);
62
+ // Read from the reactor rather than taken as a prop: the placeholder and the
63
+ // default editor are the chat plugin's own configuration, so a host sets them
64
+ // where it sets anything else about this plugin.
65
+ const reactor = useReactorPlatform();
66
+ const config = reactor.getConfig(CHAT_PLUGIN_NAME);
67
+ const placeholder = config?.placeholder ?? 'Ask anything, type / for commands or @ for mention';
68
+ const defaultSurface = config?.defaultSurface ?? 'notebook';
69
+ /*
70
+ * The input-prompt plugin's own configuration, read from here rather than
71
+ * from that plugin's component.
72
+ *
73
+ * `firstPromptHook` has to fire for *every* way a message reaches
74
+ * `handleSend` below — typed and sent through the composer, a suggestion
75
+ * chip clicked, or a host's own `suggestLoopPrompt(text, {submit: true})`
76
+ * arriving through the prompt store — and this view is the only thing all
77
+ * three of those already go through. The composer that renders is
78
+ * swappable (a host may contribute a different one to `LoopChatComposer`
79
+ * entirely) and does not see the other two paths at all, so watching for
80
+ * the first send from inside it would miss most of what "first prompt"
81
+ * needs to mean.
82
+ */
83
+ const inputPromptConfig = reactor.getConfig(INPUT_PROMPT_PLUGIN_NAME);
84
+ /*
85
+ * The default editor is the opening selection, not something switched on
86
+ * later.
87
+ *
88
+ * It used to be applied by an effect that waited for the surface to become
89
+ * openable, which meant the picker read `None` until a sandbox existed —
90
+ * a workspace configured for the notebook opened saying it was configured
91
+ * for nothing. What the host configured is true from the first render; what
92
+ * the surface can do about it yet is the surface's business.
93
+ */
94
+ /*
95
+ * Nothing open yet, whatever the host asked for.
96
+ *
97
+ * Opening the default on the first render puts an editor on screen before
98
+ * its surface exists — plugins arrive as they activate — and before the
99
+ * sandbox it needs is running. What the reader saw then was not the notebook
100
+ * but the notebook's "needs a running sandbox" placeholder, which is a worse
101
+ * answer than an empty chat that fills in a moment later.
102
+ *
103
+ * The effect below opens it the moment it can actually be opened.
104
+ */
105
+ const [surfaceId, setSurfaceId] = useState(NO_SURFACE);
106
+ /**
107
+ * Whether the reader has chosen a surface themselves.
108
+ *
109
+ * Once they have, the default is spent. Without this it re-applies every
110
+ * time a new surface lands — so closing the notebook would reopen it as soon
111
+ * as any other plugin activated, which reads as the close button not working.
112
+ */
113
+ const surfaceChosen = useRef(false);
114
+ const surfaces = useContributions(LoopChatSurface);
115
+ /* The composer and the title bar arrive as plugins: this view assembles
116
+ their props, whoever contributed renders them. First contribution wins —
117
+ these are single-occupant surfaces, not lists. */
118
+ const composerEntries = useContributions(LoopChatComposer);
119
+ const headerEntries = useContributions(LoopChatHeader);
120
+ /* Live per-example chat extras — an error banner, the codemode toggle, the
121
+ footer's MCP/codemode status. Carried as a signal so the example can
122
+ update it without the plugin being rebuilt; first contribution wins. */
123
+ /* How the parts are arranged. No contribution: the split below. A layout
124
+ plugin — the page layout — takes the same wired parts and arranges them
125
+ its own way; first contribution wins. */
126
+ const layoutEntries = useContributions(LoopChatLayout);
127
+ const layout = layoutEntries[0]?.value;
128
+ // The composer's stance: live when the layout publishes one (the page
129
+ // layout, moving the composer as the display mode changes), its starting
130
+ // stance otherwise.
131
+ const liveStance = useSignalValue(layout?.promptStance ?? NO_STANCE);
132
+ const promptStance = liveStance ?? layout?.prompt;
133
+ /* The current turn, kept for whoever shows the conversation without the
134
+ transcript. The chat plugin contributed the feed at build; this view is
135
+ its writer — begin on send, the reply as it arrives, end when it stops. */
136
+ const turnEntries = useContributions(LoopChatTurn);
137
+ const turnFeed = turnWritersOf(turnEntries[0]?.value);
138
+ /* Reached through a ref from the send and loading handlers, which are
139
+ memoised with empty deps and must not go stale on it. */
140
+ const turnFeedRef = useRef(turnFeed);
141
+ turnFeedRef.current = turnFeed;
142
+ /* Who is working, for the activity line — read through a ref by a
143
+ handler memoised with empty deps. Set once the spec is resolved below. */
144
+ const agentNameRef = useRef('');
145
+ const extrasEntries = useContributions(LoopChatExtras);
146
+ const chatExtras = useSignalValue(extrasEntries[0]?.value.extras ?? EMPTY_CHAT_EXTRAS);
147
+ /* Which editors have ever been mounted; they are never unmounted after —
148
+ see the render below. A ref, not state: adding to it during render is
149
+ idempotent and must not schedule another one. */
150
+ const everMounted = useRef(new Set());
151
+ // Asked, not assumed: the chat cannot know whether anything is listening,
152
+ // and whichever plugin does answers the gate. Re-asked on every render with
153
+ // the live workspace, so switching the sandbox switches the prompt.
154
+ const usable = useGate(LoopAgentGate, workspace);
155
+ /* The sandbox's answer. It is not the only thing that can close the chat —
156
+ an anonymous visitor's trial key runs out too — so the two are combined
157
+ further down, once the key is known. */
158
+ const gateBlocked = !usable.allowed;
159
+ const gateReason = gateBlocked
160
+ ? (usable.reason ?? 'Chat is unavailable for this sandbox')
161
+ : undefined;
162
+ // Stable: ChatBase calls these in effects, and a new identity every render
163
+ // would make it re-publish on every render.
164
+ /*
165
+ * Whether the agent is working, held here rather than read back from the
166
+ * shell.
167
+ *
168
+ * It used to live only in `workspace.viewControls`, written by two
169
+ * callbacks that each replaced the whole object — and `onSendReady` fires
170
+ * again during a send, because ChatBase rebuilds its `handleSend` when the
171
+ * send starts. So the sequence was: loading true, then a fresh
172
+ * `{ stop }` with no `busy` at all. The prompt went live again one tick
173
+ * after the agent started, which is exactly when it must not be.
174
+ *
175
+ * Local state, and the shell is told from one place below.
176
+ */
177
+ const [busy, setBusy] = useState(false);
178
+ const [sendReady, setSendReady] = useState(false);
179
+ const handleSendReady = useCallback((controls) => {
180
+ controlsRef.current = controls;
181
+ setSendReady(!!controls);
182
+ }, []);
183
+ /*
184
+ * What the agent is doing, for the turn feed.
185
+ *
186
+ * The newest tool call that has not returned is the activity: "Analyst is
187
+ * adding a cell…". Named by what the tool does in the page rather than by
188
+ * the tool — a reader watching the notebook is told what to look for.
189
+ * Cleared when nothing is running.
190
+ */
191
+ const handleDisplayItemsChange = useCallback((items) => {
192
+ const feed = turnFeedRef.current;
193
+ if (!feed) {
194
+ return;
195
+ }
196
+ const SETTLED = new Set([
197
+ 'complete',
198
+ 'completed',
199
+ 'done',
200
+ 'error',
201
+ 'failed',
202
+ 'cancelled',
203
+ 'canceled',
204
+ 'rejected',
205
+ 'denied',
206
+ 'stopped',
207
+ ]);
208
+ const VERBS = {
209
+ insertcell: 'adding a cell',
210
+ updatecell: 'editing a cell',
211
+ runcell: 'running a cell',
212
+ deletecells: 'removing cells',
213
+ readallcells: 'reading the notebook',
214
+ readcell: 'reading a cell',
215
+ executecode: 'running code',
216
+ insertblock: 'adding a block',
217
+ updateblock: 'editing a block',
218
+ runblock: 'running a block',
219
+ readdocument: 'reading the document',
220
+ };
221
+ let running;
222
+ for (let index = items.length - 1; index >= 0; index -= 1) {
223
+ const item = items[index];
224
+ if (typeof item.toolName === 'string' && item.toolCallId) {
225
+ if (!SETTLED.has(String(item.status).toLowerCase())) {
226
+ running = item;
227
+ }
228
+ break;
229
+ }
230
+ }
231
+ if (!running) {
232
+ feed.activity(undefined);
233
+ return;
234
+ }
235
+ const key = running.toolName
236
+ .toLowerCase()
237
+ .replace(/^datalayer_/, '')
238
+ .replace(/_lexical$/, '')
239
+ .replace(/[^a-z]/g, '');
240
+ const verb = VERBS[key] ?? `using ${running.toolName}`;
241
+ feed.activity(`${agentNameRef.current || 'The agent'} is ${verb}…`);
242
+ }, []);
243
+ /* Read through a ref by the snapshot handler, memoised with empty deps. */
244
+ const chatExtrasRef = useRef(chatExtras);
245
+ chatExtrasRef.current = chatExtras;
246
+ // The window, for the prompt band, the turn feed, and a host counting it.
247
+ const handleContextSnapshot = useCallback((snapshot) => {
248
+ setChatUsage(snapshot);
249
+ turnFeedRef.current?.usage(snapshot);
250
+ chatExtrasRef.current.onUsage?.(snapshot);
251
+ }, []);
252
+ const handleLoadingChange = useCallback((next) => {
253
+ setBusy(next);
254
+ if (!next) {
255
+ turnFeedRef.current?.end('done');
256
+ }
257
+ }, []);
258
+ /*
259
+ * The reply as it arrives, for the turn feed.
260
+ *
261
+ * `onMessagesChange` reports the conversation on every change of it,
262
+ * streaming included; the last assistant message is the one the current
263
+ * turn is about. Text only — a tool call is shown where it acted, in the
264
+ * editor, and the panel is for what the agent *said*.
265
+ */
266
+ const handleMessagesChange = useCallback((messages) => {
267
+ const feed = turnFeedRef.current;
268
+ if (!feed) {
269
+ return;
270
+ }
271
+ /*
272
+ * The whole reply since the person last spoke, not the latest segment.
273
+ *
274
+ * An agent that calls a tool speaks in more than one message per turn —
275
+ * "I'll start…", the tool, "Good, the frame shows…" — and a reader
276
+ * watching the panel wants all of it, in order. Text only: the tool's
277
+ * work shows where it acted, in the editor.
278
+ */
279
+ let lastUser = -1;
280
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
281
+ if (messages[index].role === 'user') {
282
+ lastUser = index;
283
+ break;
284
+ }
285
+ }
286
+ const textOf = (message) => {
287
+ const content = message.content;
288
+ return typeof content === 'string'
289
+ ? content
290
+ : content
291
+ .map(part => typeof part === 'string'
292
+ ? part
293
+ : (part.text ?? ''))
294
+ .join('');
295
+ };
296
+ const reply = messages
297
+ .slice(lastUser + 1)
298
+ .filter(message => message.role === 'assistant')
299
+ .map(textOf)
300
+ .filter(text => text.trim().length > 0)
301
+ .join('\n\n');
302
+ feed.assistant(reply);
303
+ }, []);
304
+ // One writer, so neither fact can erase the other.
305
+ useEffect(() => {
306
+ setViewControls(sendReady
307
+ ? {
308
+ busy,
309
+ stop: controlsRef.current?.stop,
310
+ newChat: controlsRef.current?.newChat,
311
+ }
312
+ : null);
313
+ }, [busy, sendReady, setViewControls]);
314
+ // Stop reporting when the view goes away, or the shell would keep a stop
315
+ // button for something that is no longer there.
316
+ useEffect(() => () => setViewControls(null), [setViewControls]);
317
+ useEffect(() => workspace.prompts.subscribe(message => {
318
+ // A prompt typed before the adapter is ready is dropped rather than
319
+ // queued: silently sending it minutes later, out of context, is worse
320
+ // than saying nothing happened.
321
+ controlsRef.current?.send(message);
322
+ }), [workspace.prompts]);
323
+ /*
324
+ * Fired once, on the first real send, for the life of this mounted chat —
325
+ * a ref rather than a signal, because nothing here needs to re-render when
326
+ * it flips; the host's hook is the only thing that reacts.
327
+ */
328
+ const firstPromptFired = useRef(false);
329
+ const handleSend = useCallback(async (message) => {
330
+ /*
331
+ * Every way a message reaches this view runs through here — typed and
332
+ * sent through the composer, a suggestion chip clicked, or a host's own
333
+ * `suggestLoopPrompt(text, {submit: true})` arriving through the prompt
334
+ * store (see the `suggestion.submit` effect below) — so this is the one
335
+ * place "the first prompt" means the same thing regardless of which one
336
+ * a person or a host used.
337
+ */
338
+ if (!firstPromptFired.current) {
339
+ firstPromptFired.current = true;
340
+ inputPromptConfig?.firstPromptHook?.();
341
+ }
342
+ setTransient(null);
343
+ // A new turn: whatever the panel showed is gone, this message is it.
344
+ turnFeedRef.current?.begin(message);
345
+ const outcome = await workspace.submit(message);
346
+ if (!outcome.handled) {
347
+ /*
348
+ * Nothing took it, so the words stay where they were typed.
349
+ *
350
+ * The reason appears under the prompt; emptying the box as well would
351
+ * make a failed send look like a successful one and cost the person
352
+ * the sentence they wrote.
353
+ */
354
+ setTransient(outcome.reason ?? null);
355
+ return;
356
+ }
357
+ /*
358
+ * Sent, so the composer is empty again.
359
+ *
360
+ * `InputPromptBase` clears itself only when it owns its own text. This
361
+ * view hands it a controlled value — a page embedding the workspace can
362
+ * offer a prompt, which needs somewhere outside the editor to put it —
363
+ * and a controlled value is the owner's to clear. It was not being
364
+ * cleared at all: the sentence stayed in the box after the agent had
365
+ * answered it, so the next thing typed was appended to the last thing
366
+ * asked, and the placeholder that suggests what to ask never came back.
367
+ */
368
+ setDraft('');
369
+ if (outcome.result?.content) {
370
+ setTransient(outcome.result.content);
371
+ }
372
+ }, [workspace, inputPromptConfig?.firstPromptHook]);
373
+ const active = useMemo(() => surfaces.find(entry => entry.value.surfaceId === surfaceId)?.value, [surfaces, surfaceId]);
374
+ /**
375
+ * The reader picking a surface, including picking none.
376
+ *
377
+ * It records the choice as well as making it: from here on the host's
378
+ * default is spent, so a surface closed stays closed.
379
+ */
380
+ const chooseSurface = useCallback((next) => {
381
+ surfaceChosen.current = true;
382
+ setSurfaceId(next);
383
+ }, []);
384
+ /*
385
+ * A command asking for a surface — `/notebook`, `/document`, or their
386
+ * shortcuts. Treated exactly like a reader picking one from the strip, so
387
+ * the choice sticks the same way and the host's default is spent.
388
+ */
389
+ useEffect(() => onSurfaceRequest(chooseSurface), [chooseSurface]);
390
+ /*
391
+ * The editor choice store — what the shell's selector, the `/editor`
392
+ * command and the reactor shell's cycle read — learns two things only the
393
+ * chat knows: which editors exist, and which one is on screen. Nobody fed
394
+ * it the options before, so "cycle the editors" cycled an empty list and
395
+ * always landed on none; and a surface opened by a tool or a plugin, past
396
+ * the store, left it pointing at the wrong one. Seeded, not chosen: the
397
+ * chat already shows it, there is nothing to announce.
398
+ */
399
+ useEffect(() => {
400
+ setEditorOptions(surfaces.map(entry => entry.value.surfaceId));
401
+ }, [surfaces]);
402
+ useEffect(() => {
403
+ seedEditorChoice(surfaceId && surfaceId !== NO_SURFACE ? surfaceId : NONE_EDITOR);
404
+ }, [surfaceId]);
405
+ /** The surface the host asked to open, once its plugin has contributed it. */
406
+ const wanted = useMemo(() => defaultSurface === NO_SURFACE || defaultSurface === 'none'
407
+ ? undefined
408
+ : surfaces.find(entry => entry.value.surfaceId === defaultSurface), [surfaces, defaultSurface]);
409
+ /*
410
+ * Open the default the moment it can be opened.
411
+ *
412
+ * Both conditions are the point. A surface arrives when its plugin
413
+ * activates, so it may not exist on the first render; and these two need a
414
+ * running sandbox, which takes as long as a Pyodide kernel takes to start.
415
+ * Waiting for both is what turns "configured for the notebook" into a
416
+ * notebook rather than a placeholder.
417
+ */
418
+ useEffect(() => {
419
+ if (surfaceChosen.current || !wanted) {
420
+ return;
421
+ }
422
+ if (canOpenView(wanted.value, workspace)) {
423
+ setSurfaceId(defaultSurface);
424
+ }
425
+ }, [wanted, workspace, defaultSurface]);
426
+ /*
427
+ * An editor that stops being openable — its sandbox went away — should not
428
+ * stay on screen claiming otherwise.
429
+ *
430
+ * Closed, but remembered. This used to set the surface to none and stop
431
+ * there, which spent the reader's choice on an interruption: a sandbox
432
+ * blinking during a target switch closed the notebook, `surfaceChosen`
433
+ * meant the default could not reopen it, and the control settled on None
434
+ * for a workspace nobody had asked to empty. What was on screen and what
435
+ * the control said agreed with each other and with neither the reader nor
436
+ * the configuration.
437
+ */
438
+ /* State, not a ref: the layout below keeps this surface's column while it
439
+ is away, and a ref changing re-renders nothing. */
440
+ const [suspendedId, setSuspendedId] = useState(null);
441
+ /*
442
+ * Full screen: the whole view, on the whole screen.
443
+ *
444
+ * Everything the workspace is showing goes — the editor as well as the
445
+ * conversation. An earlier version hid the editor, on the theory that a long
446
+ * transcript wants the room; but somebody working on a notebook who asks for
447
+ * more room is asking for more room *for the work*, and taking the notebook
448
+ * away to give it is answering a question they did not ask.
449
+ *
450
+ * Done with the Fullscreen API rather than by drawing a big box, because a
451
+ * component cannot know what it is inside of. `position: fixed` escapes only
452
+ * as far as the nearest ancestor with a transform, and a page that animates
453
+ * its sections in — the landing does — leaves one behind permanently: the
454
+ * "full screen" chat then filled the card it was already in. The API
455
+ * promotes the element to the browser's top layer, where no ancestor can
456
+ * hold it, and unlike a portal it does not move in the DOM — so every
457
+ * inherited theme variable the editors read still resolves.
458
+ *
459
+ * The CSS overlay is kept as the fallback for where the API is refused: an
460
+ * iframe without `allow="fullscreen"`, mostly. Confined to a card is a worse
461
+ * answer than full screen and a better one than nothing.
462
+ */
463
+ const viewRef = useRef(null);
464
+ // A host's own fixed header, drawn outside this workspace entirely —
465
+ // reserved at the top of the overlay below, and forcing full screen onto
466
+ // the overlay door for good; see `ChatPluginConfig.fullScreenTopOffset`.
467
+ const fullScreenTopOffset = config?.fullScreenTopOffset ?? 0;
468
+ /* Shared with the workspace header's icon — same machinery, two doors in.
469
+ See `useWorkspaceFullScreen` for why it is the browser's API rather than
470
+ a big box, and for the CSS fallback the styling below paints. */
471
+ const { fullScreen, usingApi: usingFullscreenApi, toggle: toggleFullScreen, topOffsetPx: fullScreenTopOffsetPx, } = useWorkspaceFullScreen(viewRef, {
472
+ forceOverlay: Boolean(fullScreenTopOffset),
473
+ topOffset: fullScreenTopOffset,
474
+ });
475
+ /*
476
+ * The full-screen control, pointed at from the first run onwards.
477
+ *
478
+ * This view is usually a panel inside somebody else's page — a few hundred
479
+ * pixels of a landing page or an example. The moment it becomes worth more
480
+ * room is the moment it starts answering, and that is also the moment a
481
+ * reader's attention is on the transcript rather than on a small grey icon
482
+ * in a corner. So from the first answer the icon takes the theme's own
483
+ * colour and breathes.
484
+ *
485
+ * It does not stop on a timer. A reader who is deep in the first answer has
486
+ * not yet had the thought "this is too small", and a hint that has already
487
+ * expired by the time they have it is a hint that was never given. What ends
488
+ * it is the reader: taking the offer, or reaching the thing it was offering.
489
+ *
490
+ * `hinted` is a ref because it must not itself cause a render, and because
491
+ * it has to survive `busy` going true again for the second message — this
492
+ * starts once, on the first run, not on every turn.
493
+ */
494
+ const [hintFullScreen, setHintFullScreen] = useState(false);
495
+ const hinted = useRef(false);
496
+ useEffect(() => {
497
+ if (!busy || hinted.current || fullScreen) {
498
+ return;
499
+ }
500
+ hinted.current = true;
501
+ setHintFullScreen(true);
502
+ }, [busy, fullScreen]);
503
+ // Arriving is the end of it, however they got there — the button, a
504
+ // keyboard shortcut, the host promoting the workspace. There is nothing
505
+ // left to point at, and it never comes back.
506
+ useEffect(() => {
507
+ if (fullScreen) {
508
+ setHintFullScreen(false);
509
+ }
510
+ }, [fullScreen]);
511
+ useEffect(() => {
512
+ if (active && !canOpenView(active, workspace)) {
513
+ setSuspendedId(active.surfaceId);
514
+ setSurfaceId(NO_SURFACE);
515
+ }
516
+ }, [active, workspace]);
517
+ /**
518
+ * The surface that is closed but coming back.
519
+ *
520
+ * Held so the layout can keep its column: what is missing is the editor,
521
+ * not the half of the workspace it lives in.
522
+ */
523
+ const waiting = useMemo(() => {
524
+ if (suspendedId) {
525
+ return surfaces.find(item => item.value.surfaceId === suspendedId)?.value;
526
+ }
527
+ /*
528
+ * The host's default, before it has managed to open.
529
+ *
530
+ * It needs a sandbox, and a sandbox takes as long as a kernel takes to
531
+ * start — so for the first seconds of every workspace there is a
532
+ * configured notebook that is not open yet. Counting it as "waiting" is
533
+ * what lets the control say Notebook from the outset and the column hold
534
+ * its half of the split, instead of both saying None and then changing
535
+ * their minds.
536
+ *
537
+ * Only until somebody chooses for themselves. After that the default is
538
+ * spent, and a reader who picked None must not be shown Notebook.
539
+ */
540
+ if (!surfaceChosen.current && surfaceId === NO_SURFACE) {
541
+ return wanted?.value;
542
+ }
543
+ return undefined;
544
+ }, [surfaces, suspendedId, surfaceId, wanted]);
545
+ // And opened again when it can be. The reader asked for it once; an
546
+ // interruption is not them changing their mind.
547
+ useEffect(() => {
548
+ if (!suspendedId || surfaceId !== NO_SURFACE) {
549
+ return;
550
+ }
551
+ const entry = surfaces.find(item => item.value.surfaceId === suspendedId);
552
+ if (entry && canOpenView(entry.value, workspace)) {
553
+ setSuspendedId(null);
554
+ setSurfaceId(suspendedId);
555
+ }
556
+ }, [surfaces, surfaceId, suspendedId, workspace]);
557
+ // The AG-UI mount is per agent, and `default` is the agent a server runs when
558
+ // it was not told otherwise, so a workspace opened without one still talks to
559
+ // something.
560
+ const agentId = workspace.agentId?.trim() || 'default';
561
+ /*
562
+ * Which protocol, decided by where the sandbox is.
563
+ *
564
+ * On the browser target there is no server to mount an agent on, so the loop
565
+ * turns in this page with the Vercel AI SDK and the chat calls it rather
566
+ * than addressing it. Everywhere else the agent lives on the runtime and the
567
+ * chat speaks AG-UI to it, exactly as before.
568
+ *
569
+ * The spec's own `harness` does not decide this. It says where the agent
570
+ * normally runs, and a page cannot turn a pydantic-ai loop whatever it asks
571
+ * for — so the location wins, which is the same rule the examples follow.
572
+ */
573
+ const inPage = targetRunsAgentInPage(workspace.sandbox.target ?? 'local');
574
+ /*
575
+ * Who is being addressed, when this workspace runs a team.
576
+ *
577
+ * The team's own answer, not the workspace's: `workspace.agentId` is the
578
+ * session's agent, and a team has several behind one front door. Picking a
579
+ * member changes which spec the next prompt is answered by, and nothing
580
+ * else — the sandbox, the notebook and the transcript stay where they are.
581
+ */
582
+ const team = useOptionalTeamSelection();
583
+ const selectedMemberId = useSignalValue(team?.selected ?? EMPTY_SELECTION);
584
+ const member = team?.members.find(entry => entry.id === selectedMemberId);
585
+ /*
586
+ * Looked up through `getAgentspecs`, not by subscripting the record.
587
+ *
588
+ * Agent ids reach this component versioned as often as not —
589
+ * `jupyter-data-analyst:0.0.1` — and the record is keyed on the bare id, so
590
+ * a raw subscript on a versioned id silently misses. What follows from a
591
+ * miss is not an error but a quiet wrong answer: no model, no openers, no
592
+ * name. `getAgentspecs` strips the version and retries, which is the whole
593
+ * reason the specs package exports it.
594
+ */
595
+ /*
596
+ * The spec, found two ways.
597
+ *
598
+ * By the agent's id first — a team member's spec id, or a host that names
599
+ * the spec as the agent, as the Loop Shell does. Failing that, by the
600
+ * blueprint a capacity plugin contributed: an agent it created on a server
601
+ * carries an instance name (`subagents-example-agent-…`), not a spec id, and
602
+ * the same agent turned in this page had no prompt, no subagents and no
603
+ * icon for want of this lookup — it used the document tools and called
604
+ * nobody.
605
+ */
606
+ const blueprintSpecId = useContributions(LoopAgentBlueprint)[0]?.value.specId;
607
+ const spec = getAgentspecs(member?.specId ?? agentId) ??
608
+ (blueprintSpecId ? getAgentspecs(blueprintSpecId) : undefined);
609
+ /*
610
+ * The team's name for a member first, then the spec's own.
611
+ *
612
+ * A spec is named for the catalogue — "Jupyter Data Analyst" — and a team
613
+ * names the same agent for the person working with it — "Analyst", which is
614
+ * what the picker in the header says. The header, the empty state and the
615
+ * openers' heading say the same word as the picker, or a person reads two
616
+ * names for one agent on one screen.
617
+ */
618
+ agentNameRef.current = member?.name ?? spec?.name ?? agentId;
619
+ /*
620
+ * The openers: a capacity plugin's first, then the team's with the
621
+ * addressed member's own beneath, then — with no team — the agent's own.
622
+ *
623
+ * A contribution to `LoopChatSuggestion` is a deliberate statement about
624
+ * what this workspace is worth asking, so it replaces the spec's generic
625
+ * list rather than joining it.
626
+ *
627
+ * With a team, its openers are always shown: they are what the *team* can
628
+ * be asked, whoever is addressed, and a person who has just opened the
629
+ * workspace should see the whole before the part. Under them, in a block
630
+ * with its name on it, come the addressed member's own openers — picking
631
+ * the Reviewer used to change nothing on screen, and the reverse (the
632
+ * member's only) hid the team. An opener the team already lists is not
633
+ * repeated.
634
+ */
635
+ const suggestionEntries = useContributions(LoopChatSuggestion);
636
+ const hiddenOpeners = chatExtras.hiddenOpeners;
637
+ const suggestions = useMemo(() => {
638
+ // What the host took off this page, by the chip's words — the same key
639
+ // the two-group merge below uses to tell openers apart.
640
+ const hidden = new Set(hiddenOpeners ?? []);
641
+ const offered = (items) => hidden.size === 0 ? items : items.filter(item => !hidden.has(item.text));
642
+ const contributed = suggestionEntries.flatMap(entry => entry.value.suggestions);
643
+ if (contributed.length > 0) {
644
+ return offered(contributed);
645
+ }
646
+ const own = offered(spec?.suggestions ?? []);
647
+ const teams = offered(team?.team.suggestions ?? []);
648
+ if (teams.length === 0) {
649
+ return own;
650
+ }
651
+ // Two groups, named for the two levels the empty state draws: the
652
+ // team's openers under the team's name, the member's own under its.
653
+ const listed = new Set(teams.map(item => item.text));
654
+ const teamName = team?.team.name ?? 'Team';
655
+ const memberName = member?.name ?? spec?.name ?? agentId;
656
+ return [
657
+ ...teams.map(item => ({ ...item, group: teamName })),
658
+ ...own
659
+ .filter(item => !listed.has(item.text))
660
+ .map(item => ({ ...item, group: memberName })),
661
+ ];
662
+ }, [suggestionEntries, team, spec, member, agentId, hiddenOpeners]);
663
+ /*
664
+ * The same openers in the shape the chat's empty state asks for.
665
+ *
666
+ * `Suggestion` is a title and a message — what a chip shows and what it
667
+ * sends. A spec's opener with a `summary` shows that and sends its text;
668
+ * one without shows the whole request; a contributed opener may split the
669
+ * two its own way.
670
+ */
671
+ const chatSuggestions = useMemo(() => suggestions.map(item => ({
672
+ title: item.summary ?? item.text,
673
+ message: item.message ?? item.text,
674
+ group: item.group,
675
+ })), [suggestions]);
676
+ /* The icon its spec asked for, at the size the empty state draws. */
677
+ const BrandIcon = agentIcon(spec?.icon);
678
+ /* And the team's, for the level above it. */
679
+ const TeamIcon = agentIcon(team?.team.icon);
680
+ /*
681
+ * How an in-page agent reaches the notebook.
682
+ *
683
+ * On every other target the agent runs on a server with a sandbox of its
684
+ * own, and "run this cell" happens there — in the same kernel the notebook
685
+ * is bound to, which is why it appears to work by magic. In the browser
686
+ * there is no server and no second kernel: the only way to touch the
687
+ * notebook is a frontend tool, executed here.
688
+ *
689
+ * Without these the browser agent could hold a conversation and do nothing,
690
+ * which is exactly how it behaved — it answered, and the cell never ran.
691
+ *
692
+ * From the chat's own extension point rather than a hard-wired notebook
693
+ * import: the notebook plugin contributes its cell tools, the document its
694
+ * lexical ones, and a chat mounted without either simply has fewer tools.
695
+ * Each factory runs against the live workspace, so tools addressed by
696
+ * `surfaceId` can never point at a different notebook than the one on
697
+ * screen.
698
+ */
699
+ const toolContributions = useContributions(LoopFrontendTool);
700
+ const notebookTools = useMemo(() => {
701
+ /*
702
+ * One tool per name, first contribution wins — so the order is decided
703
+ * here rather than by which lazy import landed first. The notebook and
704
+ * the document each ship a kernel tool — `executeCodeInNotebook` runs in the
705
+ * sandbox and streams onto the conversation, the document's runs on the
706
+ * document's own kernel and returns silently. The editor on screen owns
707
+ * the shared names; with none on screen the notebook does, since its
708
+ * outputs are what the conversation shows. The document's block tools
709
+ * (runBlock, insertBlock…) keep their own names and are untouched.
710
+ */
711
+ const merged = [];
712
+ const owners = new Map();
713
+ // Tools a contribution vouched for in the chat view; see `chatView`.
714
+ const keep = new Set();
715
+ // See `isInactiveSurfaceContribution`: with an editor on screen, only
716
+ // its own `${surfaceId}-tools` contribution survives this filter.
717
+ const surfaceIds = new Set(surfaces.map(entry => entry.value.surfaceId));
718
+ for (const entry of orderToolContributions(toolContributions, active?.surfaceId)) {
719
+ if (isInactiveSurfaceContribution(entry.value.id, surfaceIds, active?.surfaceId)) {
720
+ continue;
721
+ }
722
+ let tools;
723
+ try {
724
+ tools = entry.value.tools(workspace);
725
+ }
726
+ catch (error) {
727
+ // One broken contributor must not cost the agent every tool.
728
+ console.warn(`[loop] frontend tools from '${entry.plugin}' failed:`, error);
729
+ continue;
730
+ }
731
+ for (const tool of tools) {
732
+ const owner = owners.get(tool.name);
733
+ if (owner) {
734
+ console.warn(`[loop] frontend tool '${tool.name}' from '${entry.plugin}' is shadowed by the one from '${owner}'.`);
735
+ continue;
736
+ }
737
+ owners.set(tool.name, entry.plugin);
738
+ merged.push(tool);
739
+ if (entry.value.chatView) {
740
+ keep.add(tool.name);
741
+ }
742
+ }
743
+ }
744
+ // A host example's own frontend tools, folded in last (first name wins),
745
+ // so a bespoke client tool like A2UI's run_jupyter_output_demo joins the
746
+ // agent's toolset without a plugin of its own.
747
+ for (const tool of chatExtras.frontendTools ?? []) {
748
+ if (owners.has(tool.name)) {
749
+ continue;
750
+ }
751
+ owners.set(tool.name, 'chat-extras');
752
+ merged.push(tool);
753
+ }
754
+ /*
755
+ * The view decides the toolset. With an editor on screen the agent has
756
+ * that editor's own tools (`belongsToInactiveSurface` above, plus
757
+ * whatever isn't tied to a surface at all); with none — the chat view —
758
+ * it keeps only what leaves the editors alone: the `executeCodeIn…`
759
+ * tools, whose outputs stream onto the conversation as surfaces, and
760
+ * the read tools. The editors stay mounted out of sight either way, so
761
+ * what it can still call, works.
762
+ */
763
+ return active ? merged : toolsForChatView(merged, keep);
764
+ // The workspace object is rebuilt when its facts change; surfaceId is
765
+ // the one the factories address by.
766
+ // eslint-disable-next-line react-hooks/exhaustive-deps
767
+ }, [
768
+ toolContributions,
769
+ workspace.surfaceId,
770
+ workspace.agentId,
771
+ chatExtras.frontendTools,
772
+ active?.surfaceId,
773
+ surfaces,
774
+ ]);
775
+ /*
776
+ * The commands the mounted plugins declared for an agent — the reactor's
777
+ * `AgentTools` point — executed on this workspace's own reactor. Folded in
778
+ * after the contributions, which keep their names, so a plugin with a
779
+ * richer in-page implementation of the same tool (the decks plugin's) is
780
+ * what the agent gets, and the bundle's command is the fallback. The agent
781
+ * spec names none of these: they follow the plugin.
782
+ */
783
+ const commandTools = useAgentCommandTools();
784
+ const agentTools = useMemo(() => {
785
+ if (commandTools.length === 0) {
786
+ return notebookTools;
787
+ }
788
+ const taken = new Set(notebookTools.map(tool => tool.name));
789
+ return [
790
+ ...notebookTools,
791
+ ...commandTools.filter(tool => !taken.has(tool.name)),
792
+ ];
793
+ }, [notebookTools, commandTools]);
794
+ /*
795
+ * Where this workspace's agent actually lives.
796
+ *
797
+ * A Datalayer runtime brings its own agent-runtimes server: the agent is
798
+ * created on the pod from an agentspec, not on the server the workspace was
799
+ * opened against. Addressing the latter would be talking to a machine that
800
+ * has never heard of it. The same value for every other target, where the
801
+ * two are the same server.
802
+ */
803
+ const agentServerUrl = workspace.sandbox.agentBaseUrl || workspace.serverUrl;
804
+ /*
805
+ * Who a single request may be addressed to.
806
+ *
807
+ * The same set the harness gets as tools, and deliberately so: a menu that
808
+ * offered a name the model could not reach would be a menu that produces
809
+ * silent no-ops. Derived here rather than in the plugin because it changes
810
+ * with the selected member.
811
+ */
812
+ const mentionable = useMemo(() => {
813
+ if (!team || !member) {
814
+ // No team: the spec's own subagents, which the in-page loop can reach
815
+ // and a server-side one already could.
816
+ return specSubagents(spec).map(subagent => ({
817
+ name: subagent.name,
818
+ description: subagent.description,
819
+ icon: agentIcon(subagent.icon),
820
+ }));
821
+ }
822
+ /*
823
+ * The team, plus whatever specialists the selected member brings.
824
+ *
825
+ * It used to be `subagentsFor(member)` alone, which made the list read as
826
+ * arbitrary: a team of two offered one name, and the name was whichever
827
+ * member you were *not* talking to. Everyone the team contains is listed,
828
+ * because "who is on this team" is the question a person types `@` to
829
+ * ask; the one already being addressed is shown greyed rather than
830
+ * dropped, so the list is stable as the selection moves.
831
+ */
832
+ const roster = team.members.map(entry => ({
833
+ name: entry.name,
834
+ description: entry.description,
835
+ emoji: entry.emoji,
836
+ icon: agentIcon(entry.icon),
837
+ disabled: entry.id === member.id,
838
+ disabledReason: entry.id === member.id
839
+ ? `You are already talking to ${entry.name}`
840
+ : undefined,
841
+ }));
842
+ const specialists = subagentsFor(team.team, member.id)
843
+ .filter(subagent => !team.members.some(m => m.name === subagent.name))
844
+ .map(subagent => ({
845
+ name: subagent.name,
846
+ description: subagent.description,
847
+ icon: agentIcon(subagent.icon),
848
+ }));
849
+ return [...roster, ...specialists];
850
+ }, [team, member, spec]);
851
+ /*
852
+ * Where the model is reached, and on whose key.
853
+ *
854
+ * Told whether an in-page agent is going to ask at all: a workspace on a
855
+ * server-backed agent authenticates itself, and minting a visitor's one
856
+ * trial key for it would start a clock against a conversation that never
857
+ * spends it.
858
+ */
859
+ const { inference, anonymous } = useBrowserInference(inPage);
860
+ /* Read from the store rather than passed in: the sign-in form inside the
861
+ expiry panel writes here, so the same render that gains a member gains
862
+ the wording that goes with one. */
863
+ const signedInUser = useIAMStore(state => state.user);
864
+ /* The trial is over, and this chat is the thing that stops working. Only
865
+ for an in-page agent: everywhere else the runtime holds its own
866
+ credentials and never saw the visitor's key. */
867
+ const keyExpired = inPage && anonymous.status === 'expired';
868
+ /*
869
+ * Whose key ran out, which decides what the chat is allowed to call it.
870
+ *
871
+ * The anonymous store only ever holds the trial key, so reaching this
872
+ * through `anonymous.status` means the reader never signed in. A member
873
+ * whose own session expires arrives by a different route and must not be
874
+ * told a temporary key ran out — they never had one, and would go looking
875
+ * for something that was never theirs.
876
+ */
877
+ const expiredKeyIsTemporary = !signedInUser;
878
+ const chatDisabled = gateBlocked || keyExpired;
879
+ const disabledReason = keyExpired
880
+ ? expiredKeyIsTemporary
881
+ ? 'This demo runs on a shared key, and its time is up. Sign in to keep going.'
882
+ : 'Your key has expired. Sign in to keep going.'
883
+ : gateReason;
884
+ /*
885
+ * The prompt's text, held here rather than inside `InputPrompt`.
886
+ *
887
+ * A page embedding the workspace can offer a request — "try asking it
888
+ * this" — and the only honest place to put such an offer is the box the
889
+ * visitor would have typed it into, where they can read it, change their
890
+ * mind, or send it. That needs a controlled prompt, which needs the text to
891
+ * live somewhere both the suggestion and the composer can reach.
892
+ */
893
+ const [draft, setDraft] = useState('');
894
+ /*
895
+ * The model this conversation is on.
896
+ *
897
+ * Three answers, and the order matters more than it looks. The reader's own
898
+ * pick wins, then the *agent's spec*, then whatever the host mounted the
899
+ * workspace with.
900
+ *
901
+ * The spec used to come last, behind `workspace.model` — which is a
902
+ * workspace-wide default, set once for whichever agent happens to be
903
+ * addressed. So launching an agent whose spec names one model showed a
904
+ * different one ticked in the menu, and the footer disagreed with the thing
905
+ * actually answering. An agent's spec is the definition of that agent; a
906
+ * host default is a fallback for when there is no definition, and a fallback
907
+ * that overrides the definition is not a fallback.
908
+ *
909
+ * Held here rather than in the workspace because the choice belongs to the
910
+ * conversation: it is what the next message is sent with, and it resets when
911
+ * the member being addressed changes.
912
+ */
913
+ const [pickedModel, setPickedModel] = useState();
914
+ const activeModel = pickedModel ?? spec?.model ?? workspace.model ?? '';
915
+ /*
916
+ * The team, in the shape the footer asks for.
917
+ *
918
+ * The footer knows nothing about teamspecs, and should not: it renders a
919
+ * list of names and reports which one was chosen. Everything that makes a
920
+ * member a member stays on this side of the boundary.
921
+ */
922
+ /* What is left of the context window, as the runtime last reported it. The
923
+ footer shows nothing at all until there is a real number, so a workspace
924
+ whose agent has not answered yet simply has no bar. */
925
+ /*
926
+ * The window, and only for the agent being addressed now.
927
+ *
928
+ * The store keeps one snapshot rather than one per agent, so switching
929
+ * member left the previous one's numbers on screen under the new one's
930
+ * name. Ignored until the store hands back a different object, which is the
931
+ * only evidence available that the new agent has reported.
932
+ */
933
+ const storedUsage = useAgentRuntimeContextSnapshot();
934
+ const usageAtSwitch = useRef(storedUsage);
935
+ const addressedAgent = useRef(selectedMemberId);
936
+ const storedUsageRef = useRef(storedUsage);
937
+ storedUsageRef.current = storedUsage;
938
+ useEffect(() => {
939
+ if (addressedAgent.current === selectedMemberId) {
940
+ return;
941
+ }
942
+ addressedAgent.current = selectedMemberId;
943
+ usageAtSwitch.current = storedUsageRef.current;
944
+ // And the model goes back to whatever the new agent's spec asks for. A
945
+ // pick made for one member is not a statement about the next.
946
+ setPickedModel(undefined);
947
+ }, [selectedMemberId]);
948
+ const fromStore = storedUsage && storedUsage !== usageAtSwitch.current ? storedUsage : null;
949
+ /*
950
+ * What the chat itself accounts for, when nothing else does.
951
+ *
952
+ * A server-side runtime pushes a snapshot over the socket and that is what
953
+ * `fromStore` holds. An in-page agent has no server to push one, and its
954
+ * only account of the window is the totals its harness reports — which
955
+ * `ChatBase` assembles and hands out. Without this the browser agent's
956
+ * footer had no numbers at all, and the space kept for them showed as a
957
+ * white stripe under the prompt.
958
+ */
959
+ const [chatUsage, setChatUsage] = useState();
960
+ const contextUsage = fromStore ?? chatUsage ?? null;
961
+ // Read to decide whether the strip exists at all, not to render it.
962
+ const inpromptMenu = useSlotComponents(LoopSlots.inpromptMenu);
963
+ // Buttons for the prompt's footer bar, beside the session menus. Asked
964
+ // whether anyone contributed before anything is drawn, for the same reason
965
+ // as the in-prompt menu: an empty slot is still an element.
966
+ const promptActions = useSlotComponents(LoopSlots.promptAction);
967
+ // Same question for the chat's own title bar, which plugins may add to.
968
+ const chatHeaderItems = useSlotComponents(LoopSlots.chatHeader);
969
+ const footerAgents = useMemo(() => team?.members.map(entry => ({
970
+ id: entry.id,
971
+ name: entry.name,
972
+ description: entry.description,
973
+ icon: agentIcon(entry.icon),
974
+ })) ?? [], [team]);
975
+ const suggestion = useLoopPromptStore(state => state.pending);
976
+ /*
977
+ * `/prompt`, or its keystroke, asking for the caret.
978
+ *
979
+ * A command runs outside the component tree, so the ask arrives on the
980
+ * focus channel; each one bumps a nonce that joins the suggestion's in
981
+ * `focusTrigger` below. The chat answers wherever its composer is —
982
+ * docked, in-column, or floating.
983
+ */
984
+ const [focusAsked, setFocusAsked] = useState(0);
985
+ useEffect(() => onPromptFocusRequest(() => setFocusAsked(nonce => nonce + 1)), []);
986
+ useEffect(() => {
987
+ if (!suggestion) {
988
+ return;
989
+ }
990
+ setDraft(suggestion.text);
991
+ // Taken, so a second click on the same button offers it again rather than
992
+ // finding the store already holding it.
993
+ useLoopPromptStore.getState().consume();
994
+ if (suggestion.submit) {
995
+ void handleSend(suggestion.text);
996
+ }
997
+ // `handleSend` is stable for the life of a connection; re-running this on
998
+ // a new identity would resend the last suggestion.
999
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1000
+ }, [suggestion]);
1001
+ const protocol = useMemo(() => inPage
1002
+ ? browserProtocolConfig({
1003
+ agentId: member?.specId ?? agentId,
1004
+ instructions: spec?.systemPrompt,
1005
+ model: activeModel || undefined,
1006
+ inference,
1007
+ // Who this member may hand work to, and what they are told. Both
1008
+ // come from the teamspec: the supervisor routes to the others, a
1009
+ // member reaches only its own specialists, and `context.sharing`
1010
+ // decides whether the child sees this conversation.
1011
+ // The agent runs them itself, so they go to the harness rather
1012
+ // than to the chat — handing them to both would run each tool
1013
+ // twice.
1014
+ frontendTools: agentTools,
1015
+ // A team's members and specialists when there is a team; the
1016
+ // spec's own subagents otherwise — a researcher and a writer are
1017
+ // as much the agent's as its prompt is.
1018
+ subagents: team && member
1019
+ ? subagentsFor(team.team, member.id)
1020
+ : specSubagents(spec),
1021
+ sharing: team?.sharing,
1022
+ // What a delegated run does lands in the same store the
1023
+ // server's monitoring stream fills, so the side panel and the
1024
+ // composer's pulse draw an in-page delegation like any other.
1025
+ onSubagentEvent: event => agentRuntimeStore.getState().appendSubagentEvent(event),
1026
+ })
1027
+ : {
1028
+ type: 'ag-ui',
1029
+ endpoint: `${agentServerUrl}/api/v1/ag-ui/${agentId}/`,
1030
+ agentId,
1031
+ // `/api/v1/configure`, not `/api/v1/configure/config`: the hooks
1032
+ // strip one trailing `config`/`configure` segment to find the API
1033
+ // base, so the longer form doubles it.
1034
+ configEndpoint: `${agentServerUrl}/api/v1/configure`,
1035
+ enableConfigQuery: true,
1036
+ }, [
1037
+ agentId,
1038
+ inPage,
1039
+ inference,
1040
+ member,
1041
+ agentTools,
1042
+ team,
1043
+ spec,
1044
+ activeModel,
1045
+ agentServerUrl,
1046
+ ]);
1047
+ /*
1048
+ * The model catalogue, and the builtin tools and skills the agent reports.
1049
+ *
1050
+ * Read here rather than left to `ChatBase`, because this view draws its own
1051
+ * prompt: the toolbar under it can only offer what this component hands it.
1052
+ *
1053
+ * `useSkills` reads the runtime's codemode status out of the store, so it
1054
+ * needs no endpoint and works for an in-page agent too. `useConfig` does
1055
+ * need one, and a browser agent has none — hence the menus below being
1056
+ * gated on the data rather than switched on unconditionally.
1057
+ */
1058
+ const configQuery = useConfig(!inPage, protocol.type === 'ag-ui' ? protocol.configEndpoint : undefined, undefined, agentId);
1059
+ const skillsQuery = useSkills(true);
1060
+ const skillActions = useSkillActions(agentId);
1061
+ const [catalogModels, setCatalogModels] = useState([]);
1062
+ useEffect(() => {
1063
+ /*
1064
+ * Only where there is a server to ask.
1065
+ *
1066
+ * An in-page agent has none, and `agentServerUrl` then holds whatever the
1067
+ * host defaulted to — on a public page that was a developer's localhost,
1068
+ * so every visitor's console carried a connection-refused error for a
1069
+ * catalogue that was never going to arrive. The agentspecs catalogue below
1070
+ * is the answer for this case and needs no request at all.
1071
+ */
1072
+ if (inPage) {
1073
+ return undefined;
1074
+ }
1075
+ let cancelled = false;
1076
+ void fetch(`${agentServerUrl}/api/v1/configure/models`)
1077
+ .then(response => (response.ok ? response.json() : { models: [] }))
1078
+ .then(payload => {
1079
+ if (!cancelled) {
1080
+ setCatalogModels(payload.models ?? []);
1081
+ }
1082
+ })
1083
+ // No catalogue is not an error worth a banner: the menu simply has
1084
+ // nothing to offer, and the agent answers on whatever it was given.
1085
+ .catch(() => undefined);
1086
+ return () => {
1087
+ cancelled = true;
1088
+ };
1089
+ }, [agentServerUrl, inPage]);
1090
+ /*
1091
+ * What to offer when the server has no catalogue of its own.
1092
+ *
1093
+ * `/configure/models` answers for a server-backed agent and is
1094
+ * authoritative. An in-page agent has no server to ask, and a model menu
1095
+ * with nothing in it is a control that opens onto an empty list — so the
1096
+ * agentspecs catalogue stands in, filtered to the models marked available.
1097
+ */
1098
+ const offeredModels = useMemo(() => catalogModels.length > 0
1099
+ ? catalogModels
1100
+ : Object.values(AI_MODEL_CATALOGUE)
1101
+ .filter(model => model.available)
1102
+ .map(model => ({
1103
+ id: model.id,
1104
+ name: model.name,
1105
+ provider: model.provider,
1106
+ })), [catalogModels]);
1107
+ /*
1108
+ * The tools the agent can call, from both sides.
1109
+ *
1110
+ * The server's builtins, and the notebook tools this workspace hands the
1111
+ * harness — which are the only ones an in-page agent has, and the reason
1112
+ * "run this cell" works at all. Listing a tool and running it are different
1113
+ * jobs, so a tool the chat does not execute still belongs in the menu.
1114
+ */
1115
+ const offeredTools = useMemo(() => {
1116
+ const fromServer = configQuery.data?.builtinTools ?? [];
1117
+ const seen = new Set(fromServer.map(tool => tool.name));
1118
+ return [
1119
+ ...fromServer,
1120
+ ...agentTools
1121
+ .filter(tool => !seen.has(tool.name))
1122
+ .map(tool => ({ id: tool.name, name: tool.name })),
1123
+ ];
1124
+ }, [configQuery.data?.builtinTools, agentTools]);
1125
+ /* Which skills are on, as the runtime last reported them. Derived rather
1126
+ than held: the source of truth is the agent, and a local copy would drift
1127
+ the moment a skill was enabled from anywhere else. */
1128
+ const enabledSkills = useMemo(() => new Set((skillsQuery.data?.skills ?? [])
1129
+ // `enabled` and `loaded` both mean the agent has it; `available`
1130
+ // means it could. There is no `disabled` — a skill that is off is
1131
+ // simply one that is merely available.
1132
+ .filter(skill => skill.status === 'enabled' || skill.status === 'loaded')
1133
+ .map(skill => skill.id)), [skillsQuery.data]);
1134
+ /*
1135
+ * Taken here first, then told to the server.
1136
+ *
1137
+ * Locally, because an in-page agent has no server to tell and its model is
1138
+ * whatever this view hands the harness — without recording the choice, the
1139
+ * menu ticked a row for a moment and the next message went to the old model.
1140
+ * And on the server for the agents that live there, where the chip in its
1141
+ * header makes the same call: whichever control a person used, the other has
1142
+ * to agree.
1143
+ */
1144
+ const selectModel = useCallback(async (model) => {
1145
+ setPickedModel(model);
1146
+ if (inPage) {
1147
+ return;
1148
+ }
1149
+ await fetch(`${agentServerUrl}/api/v1/configure/inference/provider`, {
1150
+ method: 'PUT',
1151
+ headers: { 'content-type': 'application/json' },
1152
+ body: JSON.stringify({ model }),
1153
+ }).catch(() => undefined);
1154
+ }, [agentServerUrl, inPage]);
1155
+ /* `bottom-chat` keeps the prompt in the chat column; `bottom`, the default,
1156
+ spans the workspace. */
1157
+ const besideChat = config?.promptPlacement === 'bottom-chat';
1158
+ /* `floating` renders the same composer in a draggable card over the
1159
+ workspace — the prompt below simply gains `draggable`, and the card takes
1160
+ itself out of the layout. */
1161
+ const floatingPrompt = config?.promptPlacement === 'floating';
1162
+ /* `top` puts the composer above the editors as a horizontal bar, with the
1163
+ openers as chips under it — for a host whose editor is the point. */
1164
+ const topPrompt = config?.promptPlacement === 'top';
1165
+ /* No prompt at all, for a workspace where something else owns the typing —
1166
+ by configuration, or live through the extras channel when the host
1167
+ changes its mind while the workspace is running. */
1168
+ const promptHidden = config?.hidePrompt === true || chatExtras.hidePrompt === true;
1169
+ /*
1170
+ * The composer, assembled here and rendered by whoever contributed to the
1171
+ * `LoopChatComposer` point — the input-prompt plugin, normally.
1172
+ *
1173
+ * The wiring is this view's knowledge: the controlled draft (so an outside
1174
+ * suggestion has somewhere to land, with `focusTrigger` putting the caret
1175
+ * back), the lexical variant (the `@` menu needs it), the menus' data with
1176
+ * every control drawn even when its list is empty (a menu that opens onto
1177
+ * "0" is a statement; a missing control is not), the `promptAction` and
1178
+ * `inpromptMenu` slots (asked whether anyone contributed first — an empty
1179
+ * `ReactorSlot` is still an element and would pad out a band of nothing).
1180
+ * The surface is the plugin's: no contribution, no composer, which is the
1181
+ * honest outcome for a workspace mounted without one.
1182
+ */
1183
+ const composerProps = {
1184
+ onSend: () => void handleSend(draft),
1185
+ input: draft,
1186
+ setInput: setDraft,
1187
+ focusTrigger: suggestion || focusAsked
1188
+ ? (suggestion?.nonce ?? 0) + focusAsked
1189
+ : undefined,
1190
+ // In a draggable card over the workspace when the host asked for the
1191
+ // floating placement; the dragging is the prompt's own feature.
1192
+ placement: floatingPrompt ||
1193
+ promptStance === 'floating' ||
1194
+ promptStance === 'floating-top' ||
1195
+ promptStance === 'floating-bottom'
1196
+ ? 'floating'
1197
+ : 'docked',
1198
+ // Where a floating card starts: the page layout wants it over the top
1199
+ // of the sheet, like a document's title bar; everyone else, bottom.
1200
+ floatingAnchor: promptStance === 'floating-top' ? 'top' : 'bottom',
1201
+ // The agent is working: `isLoading` disables the editor and turns the
1202
+ // send button into a stop — the person keeps a way out.
1203
+ isLoading: busy,
1204
+ onStop: () => workspace.viewControls.stop?.(),
1205
+ // The reason where the typing would go, read before the person types.
1206
+ placeholder: disabledReason ?? placeholder,
1207
+ typingSuggestions: suggestions.map(item => item.text),
1208
+ // The same openers behind the composer's suggestions control.
1209
+ suggestions: chatSuggestions,
1210
+ disableInputPrompt: chatDisabled,
1211
+ connectionConfirmed: !chatDisabled,
1212
+ // Tighter on top: every pixel of the composer is a pixel of the editor
1213
+ // under it pushed down, and the bar reads as a command line at this size.
1214
+ // Both of the page layout's placements are that bar — one floating over
1215
+ // the page, one docked above it — so both take the tighter padding.
1216
+ padding: topPrompt ||
1217
+ promptStance === 'floating-top' ||
1218
+ promptStance === 'floating-bottom' ||
1219
+ promptStance === 'docked-top'
1220
+ ? 2
1221
+ : 3,
1222
+ promptVariant: 'lexical',
1223
+ // True unless the host asked otherwise: the workspace is there to be typed
1224
+ // into. A page that embeds the loop as one section among many turns it off,
1225
+ // because focusing an input on mount scrolls the browser to it.
1226
+ autoFocus: config?.autoFocusPrompt ?? true,
1227
+ mentionableAgents: mentionable,
1228
+ headerContent: inpromptMenu.length > 0 ? (_jsx(ReactorSlot, { slot: LoopSlots.inpromptMenu, props: { workspace } })) : undefined,
1229
+ footerExtras: promptActions.length > 0 ? (_jsx(ReactorSlot, { slot: LoopSlots.promptAction, props: { workspace } })) : undefined,
1230
+ showAgentsMenu: true,
1231
+ showInlineAgentsMenu: false,
1232
+ agents: footerAgents,
1233
+ selectedAgentId: selectedMemberId,
1234
+ onSelectAgent: id => team?.select(id),
1235
+ // On unless the host said otherwise: a public page turns the counters
1236
+ // off, because they answer questions a visitor is not asking.
1237
+ showTokenUsage: chatExtras.showTokenUsage ?? true,
1238
+ showContextRing: true,
1239
+ agentUsage: contextUsage ?? undefined,
1240
+ showModelSelector: true,
1241
+ models: offeredModels,
1242
+ selectedModel: activeModel,
1243
+ onModelSelect: model => void selectModel(model),
1244
+ showToolsMenu: true,
1245
+ availableTools: offeredTools,
1246
+ mcpServers: configQuery.data?.mcpServers ?? [],
1247
+ // Live from the host example, when one feeds it: the footer's MCP status
1248
+ // indicator. (Codemode surfaces through the toggle below, not a blob.)
1249
+ mcpStatusData: chatExtras.mcpStatusData ?? undefined,
1250
+ showSkillsMenu: true,
1251
+ skills: skillsQuery.data?.skills ?? [],
1252
+ skillsLoading: skillsQuery.isLoading,
1253
+ enabledSkills,
1254
+ onToggleSkill: skillId => enabledSkills.has(skillId)
1255
+ ? skillActions.disableSkill(skillId)
1256
+ : skillActions.enableSkill(skillId),
1257
+ onToggleAllSkills: (skillIds, enable) => skillIds.forEach(skillId => enable
1258
+ ? skillActions.enableSkill(skillId)
1259
+ : skillActions.disableSkill(skillId)),
1260
+ codemodeEnabled: chatExtras.codemodeEnabled ?? false,
1261
+ onToggleCodemode: chatExtras.onToggleCodemode,
1262
+ isA2AProtocol: false,
1263
+ hasConfigData: !!configQuery.data,
1264
+ hasSkillsData: !!skillsQuery.data,
1265
+ };
1266
+ const ComposerComponent = composerEntries[0]?.value.Component;
1267
+ const HeaderComponent = headerEntries[0]?.value.Component;
1268
+ const prompt = ComposerComponent ? (_jsx(ComposerComponent, { workspace: workspace, composer: composerProps })) : null;
1269
+ /*
1270
+ * The parts, named, so a layout can arrange them.
1271
+ *
1272
+ * Each is wired here — the surfaces' hidden mounts, the transcript's
1273
+ * stream, the composer's draft — and only *placed* below: by the split
1274
+ * this view has always drawn, or by whatever `LoopChatLayout` contributed.
1275
+ */
1276
+ const pickerEl = surfaces.length > 0 && config?.showSurfaceSelector !== false ? (_jsx(SurfacePicker, { surfaces: surfaces.map(entry => entry.value),
1277
+ /*
1278
+ What is on screen, not what was asked for.
1279
+
1280
+ It read `surfaceId` — the request — while the column beside it
1281
+ renders `active`, the surface that request actually resolved to.
1282
+ The two are the same once everything has settled and differ
1283
+ exactly while it has not: a surface still arriving, or one closed
1284
+ because its sandbox went away. So the control could say None over
1285
+ a notebook, which is the one thing a control must never do.
1286
+
1287
+ Reporting the rendered surface makes them agree by construction.
1288
+ */
1289
+ active: active?.surfaceId ?? waiting?.surfaceId ?? NO_SURFACE, onChange: chooseSurface, workspace: workspace })) : null;
1290
+ const editorsEl = (_jsxs(_Fragment, { children: [surfaces.map(entry => {
1291
+ const surface = entry.value;
1292
+ const shown = active?.surfaceId === surface.surfaceId;
1293
+ const mount = shown ||
1294
+ everMounted.current.has(surface.surfaceId) ||
1295
+ canOpenView(surface, workspace);
1296
+ if (!mount) {
1297
+ return null;
1298
+ }
1299
+ everMounted.current.add(surface.surfaceId);
1300
+ return (_jsx(Box, { sx: shown
1301
+ ? { flex: '1 1 0', minWidth: 0, minHeight: 0 }
1302
+ : {
1303
+ position: 'absolute',
1304
+ inset: 0,
1305
+ visibility: 'hidden',
1306
+ pointerEvents: 'none',
1307
+ zIndex: -1,
1308
+ }, children: _jsx(ReactorLazy, { load: surface.load, props: { surfaceId: surface.surfaceId, workspace }, fallback: _jsxs(Centered, { children: ["Loading ", surface.title, "\u2026"] }), errorFallback: error => (_jsxs(Centered, { children: [surface.title, " failed to load: ", error.message] })) }) }, surface.surfaceId));
1309
+ }), !active && waiting ? (_jsx(Box, { sx: { flex: '1 1 0', minWidth: 0, minHeight: 0 }, children: _jsxs(Centered, { children: ["Starting ", waiting.title, "\u2026"] }) })) : null] }));
1310
+ const transcriptEl = (_jsx(_Fragment, { children: _jsxs(Box, { sx: {
1311
+ /*
1312
+ Beside an editor, with the prompt on top, the conversation is
1313
+ the narrower column: the editor is what a `top` host put the
1314
+ prompt over, and the transcript reads fine at a third of the
1315
+ width. Every other placement splits the row evenly.
1316
+ */
1317
+ flex: topPrompt && active ? '0 0 34%' : '1 1 0',
1318
+ maxWidth: topPrompt && active ? 460 : undefined,
1319
+ minWidth: 0,
1320
+ minHeight: 0,
1321
+ display: 'flex',
1322
+ flexDirection: 'column',
1323
+ borderLeft: active ? '1px solid' : undefined,
1324
+ borderColor: 'border.default',
1325
+ // So the expired-key panel below can cover exactly this column and
1326
+ // nothing else: the notebook beside it still works.
1327
+ position: 'relative',
1328
+ }, children: [chatExtras.errorBanner ? (_jsx(Box, { sx: {
1329
+ flexShrink: 0,
1330
+ px: 3,
1331
+ py: 2,
1332
+ fontSize: 1,
1333
+ borderBottom: '1px solid',
1334
+ bg: chatExtras.errorBanner.variant === 'warning'
1335
+ ? 'attention.subtle'
1336
+ : 'danger.subtle',
1337
+ color: chatExtras.errorBanner.variant === 'warning'
1338
+ ? 'attention.fg'
1339
+ : 'danger.fg',
1340
+ borderColor: chatExtras.errorBanner.variant === 'warning'
1341
+ ? 'attention.muted'
1342
+ : 'danger.muted',
1343
+ }, children: chatExtras.errorBanner.message })) : null, _jsx(Box, { sx: { flex: '1 1 auto', minHeight: 0, display: 'flex' }, children: _jsx(ChatBase
1344
+ // The header says why, beside the title, for the same reason the
1345
+ // placeholder does: a dead control with no explanation is worse
1346
+ // than an absent one.
1347
+ , {
1348
+ // The header says why, beside the title, for the same reason the
1349
+ // placeholder does: a dead control with no explanation is worse
1350
+ // than an absent one.
1351
+ disabled: chatDisabled, disableReason: disabledReason, protocol: protocol,
1352
+ /*
1353
+ Who is answering, in the words its spec uses.
1354
+
1355
+ `ChatBase` falls back to "Start a conversation with the AI
1356
+ agent", which is true of every chat ever built and says
1357
+ nothing about this one. The empty state is the first thing a
1358
+ person sees and the only place the agent introduces itself.
1359
+ */
1360
+ title: member?.name ?? spec?.name ?? agentId, description: spec?.description,
1361
+ /*
1362
+ Two sizes, because it is drawn in two places.
1363
+
1364
+ The header wants a mark beside a line of text; the empty state
1365
+ wants the thing a person's eye lands on first. One `brandIcon`
1366
+ served both, so a 48px icon meant for the empty state sat in
1367
+ the header at three times the height of the words next to it.
1368
+ */
1369
+ // Big enough to read as the agent's mark rather than as
1370
+ // punctuation before its name, and still short enough not to
1371
+ // set the header's height.
1372
+ brandIcon: _jsx(BrandIcon, { size: 20 }), emptyState: {
1373
+ icon: _jsx(BrandIcon, { size: 48 }),
1374
+ // `ChatEmptyState` reads its heading from here and nowhere
1375
+ // else — the `title` above reaches the header only — so
1376
+ // without this the agent introduced itself as "Start a
1377
+ // conversation".
1378
+ title: member?.name ?? spec?.name ?? agentId,
1379
+ /*
1380
+ Two levels when there is a team: the team first — its name,
1381
+ what it is for, what it can be asked — and under it the
1382
+ member being addressed, with its own description and its
1383
+ own openers. The groups match the ones the openers carry.
1384
+ */
1385
+ ...(team && member
1386
+ ? {
1387
+ sections: [
1388
+ {
1389
+ group: team.team.name,
1390
+ icon: _jsx(TeamIcon, { size: 48 }),
1391
+ title: team.team.name,
1392
+ subtitle: team.team.description,
1393
+ },
1394
+ {
1395
+ group: member.name,
1396
+ icon: _jsx(BrandIcon, { size: 32 }),
1397
+ title: member.name,
1398
+ subtitle: spec?.description,
1399
+ },
1400
+ ],
1401
+ }
1402
+ : null),
1403
+ },
1404
+ /*
1405
+ And what it can be asked. From the team rather than the
1406
+ member: the supervisor answers first, and somebody who has
1407
+ just opened the workspace does not yet know there are two
1408
+ agents behind it.
1409
+ */
1410
+ // With the prompt on top the openers are already chips under
1411
+ // it; the empty state repeating them was the same three buttons
1412
+ // twice on one screen.
1413
+ suggestions: topPrompt || layout ? [] : chatSuggestions,
1414
+ /*
1415
+ The title bar arrives as a plugin: the chat-header plugin
1416
+ contributes the component, this view hands it the assembled
1417
+ props through `renderHeader`. No contribution — or a host
1418
+ that set `hideHeader` — means no bar.
1419
+ */
1420
+ showHeader: !config?.hideHeader && !!HeaderComponent, renderHeader: HeaderComponent
1421
+ ? headerProps => (_jsx(HeaderComponent, { workspace: workspace, header: headerProps }))
1422
+ : undefined,
1423
+ /*
1424
+ The (i), which opens the agent's details over the transcript.
1425
+
1426
+ Worth having here in particular: this workspace can be moved
1427
+ between four runtimes and several agents, and the details pane
1428
+ is the only place that says which one is actually answering
1429
+ and where it is running.
1430
+ */
1431
+ showInformation: true,
1432
+ /*
1433
+ Full screen: this view, on the whole screen, editor and all.
1434
+ See `toggleFullScreen` for why it is the browser's API rather
1435
+ than a big box.
1436
+ */
1437
+ headerActions: _jsxs(Box, { sx: { display: 'inline-flex', alignItems: 'center', gap: 2 }, children: [workspace.chatHeaderActions, chatHeaderItems.length > 0 ? (_jsx(ReactorSlot, { slot: LoopSlots.chatHeader, props: { workspace } })) : null, _jsx(IconButton, { icon: fullScreen ? ScreenNormalIcon : ScreenFullIcon, "aria-label": fullScreen ? 'Exit full screen' : 'Enter full screen', variant: "invisible", size: "small", onClick: () => {
1438
+ // Whether or not it takes, the hint has been answered:
1439
+ // they found the control, which is all it was for.
1440
+ setHintFullScreen(false);
1441
+ toggleFullScreen();
1442
+ }, sx: hintFullScreen
1443
+ ? {
1444
+ /*
1445
+ Colour and opacity, never geometry.
1446
+
1447
+ The colour is the theme's own `primary` — the
1448
+ same brand the page around this workspace is
1449
+ already using — because a grey icon in a row of
1450
+ grey icons cannot be picked out however hard it
1451
+ fades. Colour is what makes it findable; the
1452
+ fade is what makes it look like it is asking.
1453
+
1454
+ It never fades to nothing: a control that
1455
+ vanishes and returns reads as a rendering fault,
1456
+ not an invitation. And nothing here moves or
1457
+ resizes, so the header does not reflow and the
1458
+ controls beside it stay where a reader last saw
1459
+ them.
1460
+ */
1461
+ '@keyframes dla-fullscreen-hint': {
1462
+ '0%, 100%': { opacity: 1 },
1463
+ '50%': { opacity: 0.4 },
1464
+ },
1465
+ /* `&&` doubles the specificity, because Primer
1466
+ ships Button as a CSS module and
1467
+ `prc-Button-*` outranks a single generated
1468
+ class — without it the keyframes register and
1469
+ the button sits there at `animation: none`.
1470
+ The colour needs the same treatment: an
1471
+ invisible IconButton sets `color` itself.
1472
+
1473
+ And no `:focus` or `:hover` clause to stop it:
1474
+ anything that matches the button while it is
1475
+ being pointed at would cancel the pointing —
1476
+ which is exactly how the first version of this
1477
+ silently did nothing. It ends when the reader
1478
+ takes the offer, or arrives without it. */
1479
+ '&&': {
1480
+ /*
1481
+ The brand on the box, not on the glyph.
1482
+
1483
+ Painting the icon `palette.primary` was the
1484
+ obvious reading and it is measurably
1485
+ backwards: in the theme this workspace ships
1486
+ on, `primary` is #FFC107, which sits at 1.55
1487
+ contrast against the header — where the
1488
+ icon's ordinary olive sits at 5.07. Recolouring
1489
+ the stroke made the control roughly three
1490
+ times harder to see, and at the bottom of the
1491
+ fade it was 1.2 and simply gone.
1492
+
1493
+ A ring and a wash carry the same colour on a
1494
+ far larger area, and they do it without asking
1495
+ the glyph to be legible in a brand colour that
1496
+ changes with every theme — this palette has
1497
+ six, from a dark teal to this amber, and a
1498
+ rule that works for one fails for another. The
1499
+ glyph keeps the colour it can be read in.
1500
+ */
1501
+ bg: `color-mix(in srgb, ${palette.primary} 16%, transparent)`,
1502
+ boxShadow: `inset 0 0 0 1.5px ${palette.primary}`,
1503
+ animation: `dla-fullscreen-hint ${FULLSCREEN_HINT_PERIOD_MS}ms ease-in-out infinite`,
1504
+ },
1505
+ // A reader who asked the machine to hold still
1506
+ // keeps the colour and loses the knocking: the
1507
+ // control is still the findable one, it just
1508
+ // holds still.
1509
+ '@media (prefers-reduced-motion: reduce)': {
1510
+ '&&': { animation: 'none' },
1511
+ },
1512
+ }
1513
+ : undefined })] }),
1514
+ // This view owns the prompt, below, so the chat does not draw a
1515
+ // second one: two input boxes on one screen is one too many.
1516
+ showInput: false, onSendReady: handleSendReady,
1517
+ /*
1518
+ The notebook tools' results, drawn into the transcript — the
1519
+ chat's own machinery, switched on only while no editor is on
1520
+ screen: the hidden notebook is where the agent works, and the
1521
+ transcript is then the one place the reader can see what a
1522
+ tool did. With an editor open the change is visible where it
1523
+ happened, and the default tool row is enough.
1524
+ */
1525
+ notebookToolSurfacesId: active ? undefined : workspace.surfaceId,
1526
+ // A host example's own tool-result renderer, when it feeds one
1527
+ // through the extras channel — the A2UI examples draw their
1528
+ // surface here. Wins over the notebook surfaces in ChatBase.
1529
+ renderToolResult: chatExtras.renderToolResult, showTurnFooter: chatExtras.showTokenUsage ?? true, onContextSnapshot: handleContextSnapshot, onLoadingChange: handleLoadingChange, onItemsChange: handleMessagesChange, onDisplayItemsChange: handleDisplayItemsChange, enableStreaming: true }) }), besideChat && !promptHidden ? prompt : null] }) }));
1530
+ const chipsEl = chatSuggestions.length > 0 ? (_jsx(Box, { "aria-label": "Suggested prompts", sx: { display: 'flex', flexWrap: 'wrap', gap: 2, px: 2, pb: '6px' }, children: chatSuggestions.map(item => (_jsx(Box, { as: "button", type: "button",
1531
+ // The whole request, where the chip shows only its summary.
1532
+ title: item.message, disabled: busy || chatDisabled, onClick: () => void handleSend(item.message), sx: {
1533
+ appearance: 'none',
1534
+ font: 'inherit',
1535
+ fontSize: 0,
1536
+ fontWeight: 'semibold',
1537
+ // Room for descenders: the truncated span clips what falls
1538
+ // below its line, and at a line-height of one that is the
1539
+ // tail of every "y".
1540
+ lineHeight: 1.25,
1541
+ px: '10px',
1542
+ py: '5px',
1543
+ width: SUGGESTION_CHIP_WIDTH,
1544
+ flex: `0 0 ${SUGGESTION_CHIP_WIDTH}px`,
1545
+ borderRadius: '999px',
1546
+ border: '1px solid',
1547
+ borderColor: 'border.default',
1548
+ bg: 'canvas.subtle',
1549
+ color: 'fg.default',
1550
+ cursor: 'pointer',
1551
+ whiteSpace: 'nowrap',
1552
+ '&:hover:not(:disabled)': {
1553
+ borderColor: 'accent.fg',
1554
+ color: 'accent.fg',
1555
+ },
1556
+ '&:disabled': { opacity: 0.5, cursor: 'default' },
1557
+ }, children: _jsx(Truncate, { title: item.title, maxWidth: "100%", sx: { minWidth: 0 }, children: item.title }) }, item.title))) })) : null;
1558
+ const transientEl = transient ? (_jsx(Box, { sx: {
1559
+ flex: '0 0 auto',
1560
+ px: 3,
1561
+ py: 2,
1562
+ borderTop: '1px solid',
1563
+ borderColor: 'border.default',
1564
+ bg: 'canvas.subtle',
1565
+ fontSize: 1,
1566
+ maxHeight: '40%',
1567
+ overflowY: 'auto',
1568
+ }, children: transient })) : null;
1569
+ return (_jsxs(Box, { ref: viewRef, sx: {
1570
+ height: '100%',
1571
+ minHeight: 0,
1572
+ display: 'flex',
1573
+ flexDirection: 'column',
1574
+ // The positioned root the expiry panel below covers.
1575
+ position: 'relative',
1576
+ /*
1577
+ A background of its own once it is full screen, in both modes.
1578
+
1579
+ The element is promoted out of whatever was painting behind it — the
1580
+ browser's top layer, or the page — and a workspace that inherited its
1581
+ canvas from an ancestor would arrive transparent over black.
1582
+ */
1583
+ ...(fullScreen ? { bg: 'canvas.default' } : null),
1584
+ // The fallback, for a host where the API was refused, or where
1585
+ // `fullScreenTopOffset` asked for the overlay outright. Covers as
1586
+ // much as the nearest transformed ancestor allows; see the note
1587
+ // above. `top` leaves room for a host's own fixed header rather than
1588
+ // `inset: 0`'s full viewport — 0 (no offset given) is the same rect
1589
+ // either way.
1590
+ ...(fullScreen && !usingFullscreenApi.current
1591
+ ? {
1592
+ position: 'fixed',
1593
+ top: fullScreenTopOffsetPx,
1594
+ right: 0,
1595
+ bottom: 0,
1596
+ left: 0,
1597
+ // Over the `height: '100%'` above, which would otherwise win
1598
+ // over `top`/`bottom` and push the bottom off the window.
1599
+ height: 'auto',
1600
+ zIndex: 1000,
1601
+ }
1602
+ : null),
1603
+ }, children: [layout ? (_jsx(layout.Component, { workspace: workspace, editors: editorsEl, hasEditor: !!active, transcript: transcriptEl, prompt: promptHidden ? null : prompt, chips: promptHidden ? null : chipsEl, picker: pickerEl, transient: transientEl })) : (_jsxs(_Fragment, { children: [topPrompt && !promptHidden ? (_jsxs(Box, { sx: {
1604
+ flex: '0 0 auto',
1605
+ borderBottom: '1px solid',
1606
+ borderColor: 'border.default',
1607
+ }, children: [prompt, chipsEl] })) : null, pickerEl, _jsxs(Box, { sx: {
1608
+ flex: '1 1 auto',
1609
+ minHeight: 0,
1610
+ display: 'flex',
1611
+ // The hidden editors position themselves against this row.
1612
+ position: 'relative',
1613
+ }, children: [editorsEl, transcriptEl] }), transientEl, besideChat || promptHidden || topPrompt ? null : prompt] })), keyExpired ? (_jsx(Box, { sx: {
1614
+ position: 'absolute',
1615
+ inset: 0,
1616
+ zIndex: 5,
1617
+ bg: 'canvas.default',
1618
+ display: 'flex',
1619
+ flexDirection: 'column',
1620
+ minHeight: 0,
1621
+ }, children: _jsx(AnonymousKeyExpired, { agentName: member?.name ?? spec?.name,
1622
+ // On the browser target the kernel is in this page and owes the
1623
+ // inference service nothing, so the notebook is genuinely
1624
+ // unaffected and the panel may say so.
1625
+ sandboxStillRuns: inPage, temporary: expiredKeyIsTemporary,
1626
+ // The trial key is spent once somebody signs in, and a session
1627
+ // left `expired` would keep this panel over a working chat.
1628
+ onSignedIn: () => useAnonymousSessionStore.getState().clear() }) })) : null] }));
1629
+ }
1630
+ /**
1631
+ * Which editor sits beside the conversation.
1632
+ *
1633
+ * Rendered only when a plugin contributed one — a chat on its own has nothing
1634
+ * to pick between, and a control with a single option is noise.
1635
+ */
1636
+ function SurfacePicker({ surfaces, active, onChange, workspace, }) {
1637
+ const ordered = [...surfaces].sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
1638
+ return (_jsxs(Box, { sx: {
1639
+ flex: '0 0 auto',
1640
+ display: 'flex',
1641
+ alignItems: 'center',
1642
+ gap: 2,
1643
+ px: 3,
1644
+ py: 2,
1645
+ borderBottom: '1px solid',
1646
+ borderColor: 'border.default',
1647
+ }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Beside the chat" }), _jsxs(SegmentedControl, { "aria-label": "Editor beside the chat", size: "small", children: [_jsx(SegmentedControl.Button, { selected: active === NO_SURFACE, onClick: () => onChange(NO_SURFACE), children: "Chat" }), ordered.map(surface => {
1648
+ const openable = canOpenView(surface, workspace);
1649
+ const reason = openable
1650
+ ? undefined
1651
+ : (surface.unavailableReason?.(workspace) ??
1652
+ 'Not available right now');
1653
+ return (_jsx(SegmentedControl.Button, { selected: active === surface.surfaceId, "aria-disabled": !openable,
1654
+ // The native attribute rather than Primer's `Tooltip`: that
1655
+ // component requires its child to *be* the interactive element,
1656
+ // and `SegmentedControl.Button` renders a list item around its
1657
+ // button — wrapping it throws before the workspace can paint.
1658
+ title: reason ?? surface.title,
1659
+ // Primer types this as its own icon shape; a contribution may
1660
+ // bring any component, which is the point of the extension point.
1661
+ leadingIcon: surface.icon, onClick: () => {
1662
+ if (openable) {
1663
+ onChange(surface.surfaceId);
1664
+ }
1665
+ }, children: surface.title }, surface.surfaceId));
1666
+ })] })] }));
1667
+ }
1668
+ function Centered({ children }) {
1669
+ return (_jsx(Box, { sx: {
1670
+ height: '100%',
1671
+ display: 'flex',
1672
+ alignItems: 'center',
1673
+ justifyContent: 'center',
1674
+ color: 'fg.muted',
1675
+ fontSize: 1,
1676
+ px: 3,
1677
+ textAlign: 'center',
1678
+ }, children: children }));
1679
+ }