@datalayer/agent-runtimes 1.3.88 → 1.3.90

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 (258) hide show
  1. package/lib/Agent.js +2 -18
  2. package/lib/AgentDocument.js +5 -55
  3. package/lib/AgentNode.js +31 -208
  4. package/lib/AgentNotebook.js +5 -59
  5. package/lib/agent-node/AgentNodeGallery.js +23 -111
  6. package/lib/agents/AgentDetails.js +36 -274
  7. package/lib/agents/AgentRuntimeChat.js +1 -8
  8. package/lib/chat/Chat.js +7 -46
  9. package/lib/chat/ChatFloating.js +15 -100
  10. package/lib/chat/ChatInline.js +5 -47
  11. package/lib/chat/ChatPopupStandalone.js +5 -53
  12. package/lib/chat/ChatSidebar.js +4 -51
  13. package/lib/chat/ChatStandalone.js +4 -45
  14. package/lib/chat/EphemeralSurfaceControl.js +5 -25
  15. package/lib/chat/assistant/AssistantContextMenu.d.ts +8 -0
  16. package/lib/chat/assistant/AssistantContextMenu.js +17 -1
  17. package/lib/chat/assistant/AssistantStage.js +6 -25
  18. package/lib/chat/assistant/BalloonParts.js +8 -40
  19. package/lib/chat/assistant/BalloonVisual.js +2 -2
  20. package/lib/chat/assistant/ConversationBalloon.js +2 -11
  21. package/lib/chat/assistant/DecisionAsk.js +3 -3
  22. package/lib/chat/assistant/SandboxDetailsDialog.js +3 -17
  23. package/lib/chat/assistant/SpeechBalloon.js +14 -78
  24. package/lib/chat/base/ChatBase.js +37 -172
  25. package/lib/chat/display/EmptyState.js +7 -35
  26. package/lib/chat/display/FloatingBrandButton.js +4 -34
  27. package/lib/chat/display/PoweredByTag.js +3 -14
  28. package/lib/chat/document/EphemeralDocument.js +3 -18
  29. package/lib/chat/header/ChatHeader.js +1 -17
  30. package/lib/chat/header/ChatHeaderBase.js +2 -24
  31. package/lib/chat/header/ChatViewModeToggle.js +7 -29
  32. package/lib/chat/indicators/McpStatusIndicator.js +3 -12
  33. package/lib/chat/indicators/SandboxStatusIndicator.js +6 -34
  34. package/lib/chat/indicators/SkillsStatusIndicator.js +3 -12
  35. package/lib/chat/indicators/TypingDots.js +1 -11
  36. package/lib/chat/messages/ChatMessageList.js +58 -185
  37. package/lib/chat/messages/ChatMessages.js +15 -79
  38. package/lib/chat/messages/FormSubmissionMessage.js +2 -33
  39. package/lib/chat/messages/NotebookToolSurfaces.js +12 -41
  40. package/lib/chat/messages/TurnFooter.js +4 -10
  41. package/lib/chat/notebook/EphemeralNotebook.js +2 -10
  42. package/lib/chat/parts/ReasoningPart.js +2 -16
  43. package/lib/chat/parts/TextPart.js +2 -12
  44. package/lib/chat/parts/ToolPart.js +16 -90
  45. package/lib/chat/presence/Presence.js +5 -33
  46. package/lib/chat/prompt/FloatingCard.js +1 -13
  47. package/lib/chat/prompt/InputPrompt.js +1 -6
  48. package/lib/chat/prompt/InputPromptBase.js +4 -17
  49. package/lib/chat/prompt/InputPromptLexical.js +44 -45
  50. package/lib/chat/prompt/InputPromptText.js +1 -1
  51. package/lib/chat/prompt/footer/InPromptFooter.js +1 -8
  52. package/lib/chat/prompt/menus/AgentsMenu.js +1 -1
  53. package/lib/chat/prompt/menus/InlineAgentsMenu.js +1 -1
  54. package/lib/chat/prompt/menus/ModelSelector.js +1 -5
  55. package/lib/chat/prompt/menus/SkillsMenu.js +3 -30
  56. package/lib/chat/prompt/menus/SuggestionsMenu.js +1 -1
  57. package/lib/chat/prompt/menus/ToolsMenu.js +4 -48
  58. package/lib/chat/prompt/plugins/AgentMentionPlugin.js +2 -9
  59. package/lib/chat/prompt/plugins/CommandPlugin.js +2 -9
  60. package/lib/chat/prompt/plugins/MentionChip.js +1 -15
  61. package/lib/chat/prompt/stack.js +1 -19
  62. package/lib/chat/tools/A2AAgentDialog.js +9 -42
  63. package/lib/chat/tools/SubagentActivity.js +5 -22
  64. package/lib/chat/tools/ToolApprovalBanner.js +4 -34
  65. package/lib/chat/tools/ToolApprovalDialog.js +4 -19
  66. package/lib/chat/tools/ToolCallDisplay.js +12 -97
  67. package/lib/chat/usage/ContextPie.js +4 -45
  68. package/lib/components/a2ui/datalayer/Chart.js +4 -10
  69. package/lib/components/a2ui/datalayer/Chat.js +2 -10
  70. package/lib/components/a2ui/datalayer/Download.js +1 -1
  71. package/lib/components/a2ui/datalayer/Evidence.js +1 -15
  72. package/lib/components/a2ui/datalayer/FileUpload.js +1 -12
  73. package/lib/components/a2ui/datalayer/Table.js +9 -17
  74. package/lib/components/a2ui/datalayer/formWidgets.js +3 -2
  75. package/lib/components/a2ui/datalayer/parts.js +1 -16
  76. package/lib/components/agents/AgentSummary.js +1 -51
  77. package/lib/components/cards/NotificationEventCard.js +6 -72
  78. package/lib/components/cards/OutputCard.js +4 -48
  79. package/lib/components/cards/ToolApprovalCard.js +2 -35
  80. package/lib/components/code-sandboxes/CodeSandboxCellVariables.js +1 -1
  81. package/lib/components/code-sandboxes/CodeSandboxEnvironmentSelect.js +4 -14
  82. package/lib/components/code-sandboxes/CodeSandboxPicker.js +16 -21
  83. package/lib/components/code-sandboxes/CodeSandboxVariables.js +2 -2
  84. package/lib/components/scheduler/ScheduleMenu.js +5 -10
  85. package/lib/components/specs/SpecPayloadPreview.js +1 -26
  86. package/lib/components/teams/A2ATeamGraph.d.ts +4 -3
  87. package/lib/components/teams/A2ATeamGraph.js +25 -71
  88. package/lib/components/teams/AnswerSurfacesView.js +2 -2
  89. package/lib/components/teams/NotebookPreview.js +1 -10
  90. package/lib/components/teams/NotebookPreviewView.js +2 -3
  91. package/lib/components/teams/SceneTranscript.js +3 -17
  92. package/lib/components/teams/SceneView.js +1 -8
  93. package/lib/components/teams/TeamNotebook.js +5 -13
  94. package/lib/components/teams/TeamNotebookView.js +2 -12
  95. package/lib/components/teams/index.d.ts +1 -0
  96. package/lib/components/teams/index.js +1 -0
  97. package/lib/components/teams/useBalloonRoom.d.ts +39 -0
  98. package/lib/components/teams/useBalloonRoom.js +89 -0
  99. package/lib/config/AgentConfiguration.js +22 -225
  100. package/lib/context/ContextDistribution.js +7 -41
  101. package/lib/context/ContextInspector.js +9 -107
  102. package/lib/context/ContextPanel.js +11 -86
  103. package/lib/context/ContextUsage.js +6 -34
  104. package/lib/context/CostTracker.js +7 -42
  105. package/lib/context/MessageHistory.js +3 -37
  106. package/lib/context/TurnGraphChart.js +9 -41
  107. package/lib/examples/A2UiAgentExample.js +6 -108
  108. package/lib/examples/A2UiComponentsGalleryExample.js +1 -1
  109. package/lib/examples/A2UiContactCardExample.js +1 -1
  110. package/lib/examples/A2UiJupyterOutputExample.js +25 -125
  111. package/lib/examples/A2UiRestaurantExample.js +1 -1
  112. package/lib/examples/A2UiViewerExample.js +1 -1
  113. package/lib/examples/AgUiAgenticExample.js +3 -35
  114. package/lib/examples/AgUiBackendToolRenderingExample.js +2 -25
  115. package/lib/examples/AgUiHaikuGenUiExample.js +2 -32
  116. package/lib/examples/AgUiHumanInTheLoopExample.js +5 -59
  117. package/lib/examples/AgUiSharedStateExample.js +6 -56
  118. package/lib/examples/AgUiToolsBasedGenUiExample.js +25 -120
  119. package/lib/examples/AgentA2AExample.js +1 -15
  120. package/lib/examples/AgentA2ATeamExample.js +6 -30
  121. package/lib/examples/AgentCheckpointsExample.js +11 -145
  122. package/lib/examples/AgentCodeSandboxExample.js +5 -108
  123. package/lib/examples/AgentCodeSandboxesExample.js +3 -30
  124. package/lib/examples/AgentCodemodeExample.js +9 -85
  125. package/lib/examples/AgentCompactionExample.js +3 -64
  126. package/lib/examples/AgentDecideExample.js +1 -13
  127. package/lib/examples/AgentEvalsExample.js +5 -60
  128. package/lib/examples/AgentGuardrailsExample.js +4 -28
  129. package/lib/examples/AgentHooksExample.js +1 -1
  130. package/lib/examples/AgentInferenceProviderExample.js +5 -96
  131. package/lib/examples/AgentMCPExample.js +7 -105
  132. package/lib/examples/AgentMemoryExample.js +5 -74
  133. package/lib/examples/AgentMonitoringExample.js +7 -53
  134. package/lib/examples/AgentNotificationsExample.js +3 -61
  135. package/lib/examples/AgentOtelExample.js +3 -72
  136. package/lib/examples/AgentOutputsExample.js +6 -81
  137. package/lib/examples/AgentParametersExample.js +2 -37
  138. package/lib/examples/AgentSkillsExample.js +3 -45
  139. package/lib/examples/AgentSubagentsExample.js +6 -56
  140. package/lib/examples/AgentToolApprovalsExample.js +1 -11
  141. package/lib/examples/AgentTriggersExample.js +29 -239
  142. package/lib/examples/AgentspecsExample.js +6 -60
  143. package/lib/examples/AssistantExample.js +5 -5
  144. package/lib/examples/AssistantGalleryExample.js +8 -65
  145. package/lib/examples/CellExample.js +1 -6
  146. package/lib/examples/ChatCustomExample.js +2 -31
  147. package/lib/examples/ChatExample.js +1 -9
  148. package/lib/examples/ChatStandaloneExample.js +3 -37
  149. package/lib/examples/CopilotKitLexicalExample.js +1 -24
  150. package/lib/examples/DecksAgent.js +1 -1
  151. package/lib/examples/DocumentPageAgent.js +1 -1
  152. package/lib/examples/HomeExample.js +14 -62
  153. package/lib/examples/Lexical2Example.js +3 -32
  154. package/lib/examples/LexicalAgentExample.js +2 -34
  155. package/lib/examples/LexicalAgentSidebarExample.js +3 -34
  156. package/lib/examples/LoopAppComputerExample.js +1 -1
  157. package/lib/examples/LoopShellExample.js +2 -2
  158. package/lib/examples/LoopStrategyExample.js +15 -87
  159. package/lib/examples/LoopWorkspaceEmbed.js +2 -18
  160. package/lib/examples/LoopWorkspaceExample.js +2 -2
  161. package/lib/examples/NotebookAgentExample.js +2 -33
  162. package/lib/examples/NotebookAgentSidebarExample.js +3 -46
  163. package/lib/examples/NotebookCollaborationExample.js +1 -31
  164. package/lib/examples/NotebookExample.js +1 -18
  165. package/lib/examples/NotebookPageAgent.js +1 -1
  166. package/lib/examples/ScenesExample.js +5 -24
  167. package/lib/examples/VoiceChatExample.js +1 -1
  168. package/lib/examples/components/AuthRequiredView.js +1 -7
  169. package/lib/examples/components/ErrorView.js +1 -8
  170. package/lib/examples/components/ExampleErrorBoundary.js +1 -14
  171. package/lib/examples/components/FooterMetrics.js +1 -7
  172. package/lib/examples/components/Header.js +12 -51
  173. package/lib/examples/components/HeaderControls.js +1 -9
  174. package/lib/examples/components/LexicalEditor.js +1 -8
  175. package/lib/examples/components/MainContent.js +1 -13
  176. package/lib/examples/components/MockFileBrowser.js +4 -40
  177. package/lib/examples/components/SessionTabs.js +1 -1
  178. package/lib/examples/components/TimeTravel.js +1 -13
  179. package/lib/examples/main.js +13 -81
  180. package/lib/examples/scenes/A2UiComponentsGalleryScene.js +3 -34
  181. package/lib/examples/scenes/A2UiContactCardScene.js +3 -35
  182. package/lib/examples/scenes/A2UiRestaurantScene.js +3 -53
  183. package/lib/examples/scenes/A2UiViewerScene.js +3 -34
  184. package/lib/examples/utils/AssistantGalleryGrid.js +6 -35
  185. package/lib/examples2/DatalayerNotebookExample.js +3 -33
  186. package/lib/examples2/NativeNavigationExample.js +4 -10
  187. package/lib/examples2/NotebookMutationsKernel.js +2 -1
  188. package/lib/examples2/NotebookMutationsServiceManager.js +2 -1
  189. package/lib/examples2/ReactRouterExample.js +14 -42
  190. package/lib/hooks/useToolApprovals.d.ts +2 -34
  191. package/lib/hooks/useToolApprovals.js +1 -64
  192. package/lib/identity/AgentIdentity.js +17 -62
  193. package/lib/identity/IdentityConnect.js +13 -59
  194. package/lib/identity/OAuthCallback.js +6 -44
  195. package/lib/lexical/ChatInlinePlugin.js +6 -14
  196. package/lib/loop/apps/AppRenderer.d.ts +4 -3
  197. package/lib/loop/apps/AppRenderer.js +11 -12
  198. package/lib/loop/apps/appspec.js +6 -3
  199. package/lib/loop/embed/AppEmbed.d.ts +1 -1
  200. package/lib/loop/embed/AppEmbed.js +2 -1
  201. package/lib/loop/embed/LoopEmbed.js +1 -7
  202. package/lib/loop/embed/embedConfig.d.ts +3 -3
  203. package/lib/loop/embed/embedConfig.js +2 -3
  204. package/lib/loop/embed/embedTheme.d.ts +4 -4
  205. package/lib/loop/embed/embedTheme.js +11 -7
  206. package/lib/loop/plugins/a2a-sidebar/A2ASidebar.js +7 -34
  207. package/lib/loop/plugins/a2ui/A2uiView.js +4 -12
  208. package/lib/loop/plugins/a2ui-surface/InlineSurface.js +1 -16
  209. package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +2 -12
  210. package/lib/loop/plugins/agents/AnonymousKeyBadge.js +3 -2
  211. package/lib/loop/plugins/agents/SandboxSelector.js +3 -7
  212. package/lib/loop/plugins/agents/SandboxView.js +4 -27
  213. package/lib/loop/plugins/agents/TeamMemberPicker.js +2 -2
  214. package/lib/loop/plugins/agentspecs/AgentspecPicker.js +3 -2
  215. package/lib/loop/plugins/app-activity/AppActivity.js +1 -1
  216. package/lib/loop/plugins/app-computer/AppComputer.js +5 -21
  217. package/lib/loop/plugins/app-elements/AppElements.js +2 -31
  218. package/lib/loop/plugins/app-page/AppPage.js +1 -1
  219. package/lib/loop/plugins/app-rules/AppRulesCard.js +4 -20
  220. package/lib/loop/plugins/assistant/index.js +1 -13
  221. package/lib/loop/plugins/chat/ChatView.js +40 -106
  222. package/lib/loop/plugins/checkpoints-sidebar/CheckpointsSidebar.js +2 -13
  223. package/lib/loop/plugins/decks/DeckSurface.js +2 -2
  224. package/lib/loop/plugins/document/DocumentView.js +4 -12
  225. package/lib/loop/plugins/document-toolbar/SandboxStatusItem.js +3 -8
  226. package/lib/loop/plugins/graph/GraphView.js +2 -2
  227. package/lib/loop/plugins/input-prompt/ComposerView.js +1 -1
  228. package/lib/loop/plugins/notebook/NotebookView.js +3 -11
  229. package/lib/loop/plugins/page-layout/SplitLayout.js +11 -34
  230. package/lib/loop/plugins/page-layout/TurnPanel.js +9 -35
  231. package/lib/loop/plugins/shell/useEditorPreviews.js +3 -2
  232. package/lib/loop/plugins/subagent-activity/SubagentPulse.js +1 -13
  233. package/lib/loop/plugins/window-frame/WindowFrame.js +10 -44
  234. package/lib/loop/plugins/window-frame/index.js +1 -1
  235. package/lib/loop/shell/LoopWorkspace.js +34 -77
  236. package/lib/loop/shell/PluginToggles.js +4 -15
  237. package/lib/loop/shell/SidebarRail.js +8 -40
  238. package/lib/loop/shell/ViewSwitcher.js +4 -24
  239. package/lib/loop-main.js +2 -3
  240. package/lib/mcp/McpServerManager.js +6 -37
  241. package/lib/portable/index.d.ts +11 -0
  242. package/lib/portable/index.js +20 -0
  243. package/lib/protocols/AGUIAdapter.js +2 -2
  244. package/lib/protocols/BaseProtocolAdapter.d.ts +2 -0
  245. package/lib/protocols/BaseProtocolAdapter.js +4 -0
  246. package/lib/specs/apps.js +6 -0
  247. package/lib/specs/appspecSchema.js +11 -4
  248. package/lib/stories/loop/ReferenceScreens.js +17 -91
  249. package/lib/stories/loop/pictures-main.js +1 -1
  250. package/lib/types/agentspecs.d.ts +5 -2
  251. package/lib/types/protocol.d.ts +2 -0
  252. package/lib/utils/approvals.d.ts +59 -0
  253. package/lib/utils/approvals.js +101 -0
  254. package/lib/voice/VoiceInput.js +3 -39
  255. package/package.json +2 -2
  256. package/scripts/codegen/generate_envvars.py +0 -1
  257. package/lib/shims/primerAddonsBox.d.ts +0 -1
  258. package/lib/shims/primerAddonsBox.js +0 -7
@@ -13,50 +13,16 @@ export function WindowFrame({ children, title, height, slotProps = {}, }) {
13
13
  * actions and fills them in when the workspace registers one.
14
14
  */
15
15
  const actions = useOptionalSlotComponents(WINDOW_ACTIONS_SLOT);
16
- return (_jsxs(Box, { sx: {
17
- position: 'relative',
18
- // Filling is the default: with no height given the frame takes the
19
- // space it was handed and the body gets the remainder.
20
- ...(height === undefined
21
- ? { height: '100%', display: 'flex', flexDirection: 'column' }
22
- : null),
23
- borderRadius: '14px',
24
- overflow: 'hidden',
25
- border: '1px solid',
26
- borderColor: 'border.default',
27
- bg: 'canvas.default',
28
- // Lifted off the field, so the workspace reads as a thing sitting on
29
- // the page rather than a hole cut into it.
30
- boxShadow: '0 24px 60px -28px rgba(0, 0, 0, 0.55)',
31
- }, children: [_jsxs(Box, { sx: {
32
- display: 'flex',
33
- alignItems: 'center',
34
- gap: 2,
35
- px: 3,
36
- py: 2,
37
- borderBottom: '1px solid',
38
- borderColor: 'border.default',
39
- bg: 'canvas.subtle',
40
- // The bar may be the page's first line of type as well as its
41
- // chrome, so it takes a second row on a narrow screen rather than
42
- // shedding its actions off the edge.
43
- flexWrap: 'wrap',
44
- rowGap: 2,
45
- // Never squeezed by the body beside it.
46
- flex: '0 0 auto',
47
- }, children: [_jsx(Box, { sx: {
48
- display: 'flex',
49
- alignItems: 'center',
50
- gap: '6px',
51
- mr: 1,
52
- flexShrink: 0,
53
- }, children: _jsx(ReactorSlot, { slot: WINDOW_CONTROLS_SLOT, props: slotProps }) }), _jsx(Box, { sx: { minWidth: 0, flex: '1 1 auto' }, children: title }), actions.length > 0 ? (_jsx(Box, { sx: {
54
- display: 'flex',
55
- alignItems: 'center',
56
- gap: 2,
57
- flexShrink: 0,
58
- ml: 'auto',
59
- }, children: _jsx(ReactorSlot, { slot: WINDOW_ACTIONS_SLOT, props: slotProps }) })) : null] }), _jsx(Box, { sx: height === undefined
16
+ return (_jsxs(Box, { position: "relative", borderRadius: "14px", overflow: "hidden", border: "1px solid", borderColor: "border.default", bg: "canvas.default",
17
+ // Lifted off the field, so the workspace reads as a thing sitting on
18
+ // the page rather than a hole cut into it.
19
+ boxShadow: "0 24px 60px -28px rgba(0, 0, 0, 0.55)", height: height === undefined ? '100%' : undefined, display: height === undefined ? 'flex' : undefined, flexDirection: height === undefined ? 'column' : undefined, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, px: 3, py: 2, borderBottom: "1px solid", borderColor: "border.default", bg: "canvas.subtle",
20
+ // The bar may be the page's first line of type as well as its
21
+ // chrome, so it takes a second row on a narrow screen rather than
22
+ // shedding its actions off the edge.
23
+ flexWrap: "wrap", rowGap: 2,
24
+ // Never squeezed by the body beside it.
25
+ flex: "0 0 auto", children: [_jsx(Box, { display: "flex", alignItems: "center", gap: "6px", mr: 1, flexShrink: 0, children: _jsx(ReactorSlot, { slot: WINDOW_CONTROLS_SLOT, props: slotProps }) }), _jsx(Box, { minWidth: 0, flex: "1 1 auto", children: title }), actions.length > 0 ? (_jsx(Box, { display: "flex", alignItems: "center", gap: 2, flexShrink: 0, ml: "auto", children: _jsx(ReactorSlot, { slot: WINDOW_ACTIONS_SLOT, props: slotProps }) })) : null] }), _jsx(Box, { sx: height === undefined
60
26
  ? { flex: '1 1 auto', minHeight: 0 }
61
27
  : { height, minHeight: 0 }, children: children })] }));
62
28
  }
@@ -48,7 +48,7 @@ export const WINDOW_FRAME_PLUGIN_NAME = '@datalayer/loop-plugin-window-frame';
48
48
  * controls can switch this plugin off and fill the slot itself.
49
49
  */
50
50
  function WindowControls() {
51
- return (_jsx(_Fragment, { children: ['danger.emphasis', 'attention.emphasis', 'success.emphasis'].map(token => (_jsx(Box, { sx: { width: 10, height: 10, borderRadius: '50%', bg: token } }, token))) }));
51
+ return (_jsx(_Fragment, { children: ['danger.emphasis', 'attention.emphasis', 'success.emphasis'].map(token => (_jsx(Box, { width: 10, height: 10, borderRadius: "50%", bg: token }, token))) }));
52
52
  }
53
53
  export const WindowFramePlugin = definePlugin({
54
54
  name: WINDOW_FRAME_PLUGIN_NAME,
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
- import { Box } from '@primer/react';
4
+ import { Box } from '@datalayer/primer-addons';
5
5
  import { buildReactorFromPlugins, onView, } from '@datalayer/reactor';
6
6
  import { ReactorSlot, ReactorViewHost, useContributions, useReactorEvent, useSlotComponents, useReactor, } from '@datalayer/reactor/react';
7
7
  import { LoopCommand, LoopSlots, LoopViewType, canOpenView, createPromptChannel, parseCommand, loopSurfaceId, } from '../core';
@@ -168,82 +168,39 @@ function WorkspaceBody({ serverUrl, agentId: initialAgentId, conversationId, mod
168
168
  through the context: the plugin needs a DOM node and nothing else, and
169
169
  the shell should not have to publish one to be found.
170
170
  */
171
- , { "data-loop-workspace": "", sx: {
172
- display: 'flex',
173
- flexDirection: 'column',
174
- height: '100%',
175
- minHeight: 0,
176
- // The containing block for anything in the root slot that positions
177
- // itself absolutely — the floating prompt does. Without this it would
178
- // resolve against whatever ancestor happens to be positioned, and
179
- // float over the host's page instead of over the workspace.
180
- position: 'relative',
181
- bg: 'canvas.default',
182
- color: 'fg.default',
183
- }, children: [showHeader && headerContainer
184
- ? createPortal(_jsx(Box, { sx: {
185
- display: 'flex',
186
- alignItems: 'center',
187
- flexWrap: 'wrap',
188
- gap: 2,
189
- }, children: headerControls }), headerContainer)
190
- : null, showHeader && headerContainer === undefined ? (_jsx(Box, { as: "header", sx: {
191
- display: 'flex',
192
- alignItems: 'center',
193
- // In a column there is no room for a row: the header wraps rather
194
- // than pushing the model chip off the edge.
195
- justifyContent: compact ? 'flex-start' : 'space-between',
196
- /*
197
- Allowed to wrap at any width.
198
-
199
- `nowrap` meant the row absorbed whatever a plugin put in it by
200
- squeezing everything else — a sandbox failure message, a long
201
- agent name — instead of taking a second line. The controls stayed
202
- on one row and got narrower, which is the wrong trade: a header
203
- two lines tall is legible, a compressed one is not.
204
- */
205
- flexWrap: 'wrap',
206
- gap: 2,
207
- px: compact ? 2 : 3,
208
- py: 2,
209
- borderBottom: '1px solid',
210
- borderColor: 'border.default',
211
- flex: '0 0 auto',
212
- }, children: headerControls })) : null, _jsxs(Box, { sx: { flex: '1 1 auto', minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: {
213
- flex: '1 1 auto',
214
- minWidth: 0,
215
- minHeight: 0,
216
- position: 'relative',
217
- }, children: _jsx(ReactorViewHost, { point: LoopViewType, active: effectiveViewType, props: { viewType: effectiveViewType, workspace }, fallback: _jsx(Centered, { children: "Loading\u2026" }), empty: _jsx(Centered, { children: "No view is available yet." }), errorFallback: error => (_jsxs(Centered, { children: ["This view failed to load: ", error.message] })) }) }), sidebar.length > 0 && sidebarRail && !compact ? (_jsx(SidebarRail, { components: sidebar, props: { workspace }, width: SIDEBAR_WIDTH })) : sidebar.length > 0 ? (_jsx(Box, { as: "aside", sx: {
218
- flex: '0 0 auto',
219
- // Wide enough for a plugin's name, its description and a switch
220
- // on one line. The panel truncates to the same number, and takes
221
- // it from `SIDEBAR_WIDTH` so the two cannot drift.
222
- width: compact ? '100%' : `${SIDEBAR_WIDTH}px`,
223
- minWidth: 0,
224
- // On the trailing edge: the work is what a person reads first,
225
- // and the switches belong beside it rather than in front of it.
226
- borderLeft: compact ? 'none' : '1px solid',
227
- borderTop: compact ? '1px solid' : 'none',
228
- borderColor: 'border.default',
229
- bg: 'canvas.subtle',
230
- overflowY: 'auto',
231
- // The shell's chrome, not the plugins': a contributed panel
232
- // should not have to guess how far its host keeps things from
233
- // the edge, and two of them would guess differently.
234
- px: 3,
235
- py: 3,
236
- }, children: _jsx(ReactorSlot, { slot: LoopSlots.sidebar, props: { workspace } }) })) : null] }), _jsxs(Box, { sx: { flex: '0 0 auto' }, children: [_jsx(ReactorSlot, { slot: LoopSlots.footer, props: { workspace } }), _jsx(ReactorSlot, { slot: LoopSlots.status, props: { workspace } })] }), _jsx(ReactorSlot, { slot: LoopSlots.root, props: { workspace } })] }));
171
+ , { "data-loop-workspace": "", display: "flex", flexDirection: "column", height: "100%", minHeight: 0,
172
+ // The containing block for anything in the root slot that positions
173
+ // itself absolutely — the floating prompt does. Without this it would
174
+ // resolve against whatever ancestor happens to be positioned, and
175
+ // float over the host's page instead of over the workspace.
176
+ position: "relative", bg: "canvas.default", color: "fg.default", children: [showHeader && headerContainer
177
+ ? createPortal(_jsx(Box, { display: "flex", alignItems: "center", flexWrap: "wrap", gap: 2, children: headerControls }), headerContainer)
178
+ : null, showHeader && headerContainer === undefined ? (_jsx(Box, { as: "header", display: "flex", alignItems: "center",
179
+ // In a column there is no room for a row: the header wraps rather
180
+ // than pushing the model chip off the edge.
181
+ justifyContent: compact ? 'flex-start' : 'space-between',
182
+ /*
183
+ Allowed to wrap at any width.
184
+
185
+ `nowrap` meant the row absorbed whatever a plugin put in it by
186
+ squeezing everything else — a sandbox failure message, a long
187
+ agent name — instead of taking a second line. The controls stayed
188
+ on one row and got narrower, which is the wrong trade: a header
189
+ two lines tall is legible, a compressed one is not.
190
+ */
191
+ flexWrap: "wrap", gap: 2, px: compact ? 2 : 3, py: 2, borderBottom: "1px solid", borderColor: "border.default", flex: "0 0 auto", children: headerControls })) : null, _jsxs(Box, { flex: "1 1 auto", minHeight: 0, display: "flex", children: [_jsx(Box, { flex: "1 1 auto", minWidth: 0, minHeight: 0, position: "relative", children: _jsx(ReactorViewHost, { point: LoopViewType, active: effectiveViewType, props: { viewType: effectiveViewType, workspace }, fallback: _jsx(Centered, { children: "Loading\u2026" }), empty: _jsx(Centered, { children: "No view is available yet." }), errorFallback: error => (_jsxs(Centered, { children: ["This view failed to load: ", error.message] })) }) }), sidebar.length > 0 && sidebarRail && !compact ? (_jsx(SidebarRail, { components: sidebar, props: { workspace }, width: SIDEBAR_WIDTH })) : sidebar.length > 0 ? (_jsx(Box, { as: "aside", flex: "0 0 auto",
192
+ // Wide enough for a plugin's name, its description and a switch
193
+ // on one line. The panel truncates to the same number, and takes
194
+ // it from `SIDEBAR_WIDTH` so the two cannot drift.
195
+ width: compact ? '100%' : `${SIDEBAR_WIDTH}px`, minWidth: 0,
196
+ // On the trailing edge: the work is what a person reads first,
197
+ // and the switches belong beside it rather than in front of it.
198
+ borderLeft: compact ? 'none' : '1px solid', borderTop: compact ? '1px solid' : 'none', borderColor: "border.default", bg: "canvas.subtle", overflowY: "auto",
199
+ // The shell's chrome, not the plugins': a contributed panel
200
+ // should not have to guess how far its host keeps things from
201
+ // the edge, and two of them would guess differently.
202
+ px: 3, py: 3, children: _jsx(ReactorSlot, { slot: LoopSlots.sidebar, props: { workspace } }) })) : null] }), _jsxs(Box, { flex: "0 0 auto", children: [_jsx(ReactorSlot, { slot: LoopSlots.footer, props: { workspace } }), _jsx(ReactorSlot, { slot: LoopSlots.status, props: { workspace } })] }), _jsx(ReactorSlot, { slot: LoopSlots.root, props: { workspace } })] }));
237
203
  }
238
204
  function Centered({ children }) {
239
- return (_jsx(Box, { sx: {
240
- height: '100%',
241
- display: 'flex',
242
- alignItems: 'center',
243
- justifyContent: 'center',
244
- color: 'fg.muted',
245
- fontSize: 1,
246
- px: 3,
247
- textAlign: 'center',
248
- }, children: children }));
205
+ return (_jsx(Box, { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "fg.muted", fontSize: 1, px: 3, textAlign: "center", children: children }));
249
206
  }
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useSyncExternalStore } from 'react';
3
- import { Box, Text } from '@primer/react';
3
+ import { Box } from '@datalayer/primer-addons';
4
+ import { Text } from '@primer/react';
4
5
  import { useReactorPlatform } from '@datalayer/reactor/react';
5
6
  /** Trim the package scope so the list reads as names rather than paths. */
6
7
  function shortName(name) {
@@ -21,21 +22,9 @@ export function PluginToggles({ locked = [], title = 'Plugins', }) {
21
22
  reactor.disable(name);
22
23
  }
23
24
  }, [reactor]);
24
- return (_jsxs(Box, { sx: {
25
- p: 3,
26
- borderBottom: '1px solid',
27
- borderColor: 'border.default',
28
- bg: 'canvas.subtle',
29
- }, children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.muted' }, children: title }), _jsx(Box, { sx: { display: 'flex', flexWrap: 'wrap', gap: 3, mt: 2 }, children: reactor.listPlugins().map(name => {
25
+ return (_jsxs(Box, { p: 3, borderBottom: "1px solid", borderColor: "border.default", bg: "canvas.subtle", children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.muted' }, children: title }), _jsx(Box, { display: "flex", flexWrap: "wrap", gap: 3, mt: 2, children: reactor.listPlugins().map(name => {
30
26
  const isLocked = locked.includes(name);
31
- return (_jsxs(Box, { as: "label", sx: {
32
- display: 'inline-flex',
33
- alignItems: 'center',
34
- gap: 1,
35
- fontSize: 1,
36
- cursor: isLocked ? 'not-allowed' : 'pointer',
37
- opacity: isLocked ? 0.6 : 1,
38
- }, children: [_jsx("input", { type: "checkbox", checked: reactor.isEnabled(name), disabled: isLocked, onChange: event => toggle(name, event.target.checked) }), shortName(name)] }, name));
27
+ return (_jsxs(Box, { as: "label", display: "inline-flex", alignItems: "center", gap: 1, fontSize: 1, cursor: isLocked ? 'not-allowed' : 'pointer', opacity: isLocked ? 0.6 : 1, children: [_jsx("input", { type: "checkbox", checked: reactor.isEnabled(name), disabled: isLocked, onChange: event => toggle(name, event.target.checked) }), shortName(name)] }, name));
39
28
  }) })] }));
40
29
  }
41
30
  export default PluginToggles;
@@ -23,51 +23,19 @@ export function SidebarRail({ components, props, width, }) {
23
23
  const [chosen, setChosen] = useState(items[0]?.id ?? null);
24
24
  // A panel that left the slot is no longer chosen.
25
25
  const open = items.some(item => item.id === chosen) ? chosen : null;
26
- return (_jsxs(Box, { as: "aside", "data-loop-rail-sidebar": "", sx: { flex: '0 0 auto', display: 'flex', minHeight: 0 }, children: [_jsx(Box, { "data-loop-rail-panel": open ?? undefined, sx: {
27
- display: open ? 'block' : 'none',
28
- width: `${width}px`,
29
- minWidth: 0,
30
- borderLeft: 'var(--theme-hairline, 1px) solid',
31
- borderColor: 'border.default',
32
- overflowY: 'auto',
33
- px: 3,
34
- py: 3,
35
- }, children: components.map(component => {
26
+ return (_jsxs(Box, { as: "aside", "data-loop-rail-sidebar": "", flex: "0 0 auto", display: "flex", minHeight: 0, children: [_jsx(Box, { "data-loop-rail-panel": open ?? undefined, display: open ? 'block' : 'none', width: `${width}px`, minWidth: 0, borderLeft: "var(--theme-hairline, 1px) solid", borderColor: "border.default", overflowY: "auto", px: 3, py: 3, children: components.map(component => {
36
27
  const { Component } = component;
37
28
  const shown = component.id === open;
38
29
  return (_jsx(Box, { id: `loop-rail-panel-${component.id}`, role: "region", "aria-label": railItemsOf([component])[0].label, hidden: !shown, sx: shown ? paneOpening('right') : undefined, children: _jsx(Component, { ...props }) }, component.id));
39
- }) }), _jsx(Box, { as: "nav", "aria-label": "Panels", sx: {
40
- width: `${RAIL_WIDTH}px`,
41
- flex: `0 0 ${RAIL_WIDTH}px`,
42
- display: 'flex',
43
- flexDirection: 'column',
44
- alignItems: 'center',
45
- gap: 1,
46
- py: 2,
47
- borderLeft: 'var(--theme-hairline, 1px) solid',
48
- borderColor: 'border.default',
49
- }, children: items.map(item => {
30
+ }) }), _jsx(Box, { as: "nav", "aria-label": "Panels", width: `${RAIL_WIDTH}px`, flex: `0 0 ${RAIL_WIDTH}px`, display: "flex", flexDirection: "column", alignItems: "center", gap: 1, py: 2, borderLeft: "var(--theme-hairline, 1px) solid", borderColor: "border.default", children: items.map(item => {
50
31
  const Icon = item.icon;
51
32
  const active = item.id === open;
52
- return (_jsx(Box, { as: "button", type: "button", "data-loop-rail-item": item.id, "aria-label": item.label, title: item.label, "aria-pressed": active, "aria-controls": `loop-rail-panel-${item.id}`, onClick: () => setChosen(railChoiceAfter(open, item.id)), sx: {
53
- width: 32,
54
- height: 32,
55
- display: 'inline-flex',
56
- alignItems: 'center',
57
- justifyContent: 'center',
58
- p: 0,
59
- border: 'none',
60
- borderRadius: 2,
61
- cursor: 'pointer',
62
- // One weight, no colour: the ink, quieter when not chosen.
63
- color: active ? 'fg.default' : 'fg.muted',
64
- bg: active ? 'neutral.muted' : 'transparent',
65
- '&:hover': { color: 'fg.default', bg: 'neutral.subtle' },
66
- '&:focus-visible': {
67
- outline: '2px solid',
68
- outlineColor: 'var(--focus-outlineColor, #0969da)',
69
- outlineOffset: '1px',
70
- },
33
+ return (_jsx(Box, { as: "button", type: "button", "data-loop-rail-item": item.id, "aria-label": item.label, title: item.label, "aria-pressed": active, "aria-controls": `loop-rail-panel-${item.id}`, onClick: () => setChosen(railChoiceAfter(open, item.id)), width: 32, height: 32, display: "inline-flex", alignItems: "center", justifyContent: "center", p: 0, border: "none", borderRadius: 2, cursor: "pointer",
34
+ // One weight, no colour: the ink, quieter when not chosen.
35
+ color: active ? 'fg.default' : 'fg.muted', bg: active ? 'neutral.muted' : 'transparent', hover: { color: 'fg.default', bg: 'neutral.subtle' }, focusVisible: {
36
+ outline: '2px solid',
37
+ outlineColor: 'var(--focus-outlineColor, #0969da)',
38
+ outlineOffset: '1px',
71
39
  }, children: _jsx(Icon, { size: 16 }) }, item.id));
72
40
  }) })] }));
73
41
  }
@@ -1,19 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Tooltip } from '@primer/react';
2
+ import { Box } from '@datalayer/primer-addons';
3
+ import { Tooltip } from '@primer/react';
3
4
  import { canOpenView, } from '../core';
4
5
  export function ViewSwitcher({ views, workspace, compact = false, }) {
5
6
  // One choice is not a choice.
6
7
  if (views.length < 2) {
7
8
  return null;
8
9
  }
9
- return (_jsx(Box, { role: "tablist", "aria-label": "Workspace views", sx: {
10
- display: 'inline-flex',
11
- alignItems: 'center',
12
- bg: 'neutral.muted',
13
- borderRadius: '6px',
14
- p: '2px',
15
- gap: '1px',
16
- }, children: views.map(entry => {
10
+ return (_jsx(Box, { role: "tablist", "aria-label": "Workspace views", display: "inline-flex", alignItems: "center", bg: "neutral.muted", borderRadius: "6px", p: "2px", gap: "1px", children: views.map(entry => {
17
11
  const view = entry.value;
18
12
  const open = canOpenView(view, workspace);
19
13
  const active = workspace.activeViewType === view.viewType;
@@ -24,20 +18,6 @@ export function ViewSwitcher({ views, workspace, compact = false, }) {
24
18
  const label = open
25
19
  ? view.title
26
20
  : `${view.title} — ${view.unavailableReason?.(workspace) ?? 'unavailable'}`;
27
- return (_jsx(Tooltip, { text: label, direction: "n", children: _jsxs(Box, { as: "button", role: "tab", "aria-selected": active, "aria-disabled": !open, onClick: () => open && workspace.setActiveViewType(view.viewType), sx: {
28
- display: 'inline-flex',
29
- alignItems: 'center',
30
- gap: 1,
31
- border: 'none',
32
- borderRadius: '5px',
33
- px: 2,
34
- py: '4px',
35
- fontSize: 0,
36
- cursor: open ? 'pointer' : 'not-allowed',
37
- opacity: open ? 1 : 0.5,
38
- bg: active ? 'canvas.default' : 'transparent',
39
- color: active ? 'fg.default' : 'fg.muted',
40
- boxShadow: active ? 'shadow.small' : 'none',
41
- }, children: [Icon ? _jsx(Icon, { size: 14 }) : null, compact && Icon ? null : view.title] }) }, view.viewType));
21
+ return (_jsx(Tooltip, { text: label, direction: "n", children: _jsxs(Box, { as: "button", role: "tab", "aria-selected": active, "aria-disabled": !open, onClick: () => open && workspace.setActiveViewType(view.viewType), display: "inline-flex", alignItems: "center", gap: 1, border: "none", borderRadius: "5px", px: 2, py: "4px", fontSize: 0, cursor: open ? 'pointer' : 'not-allowed', opacity: open ? 1 : 0.5, bg: active ? 'canvas.default' : 'transparent', color: active ? 'fg.default' : 'fg.muted', boxShadow: active ? 'shadow.small' : 'none', children: [Icon ? _jsx(Icon, { size: 14 }) : null, compact && Icon ? null : view.title] }) }, view.viewType));
42
22
  }) }));
43
23
  }
package/lib/loop-main.js CHANGED
@@ -31,8 +31,7 @@ if (globals['__webpack_public_path__'] === undefined) {
31
31
  import { StrictMode, useEffect, useState } from 'react';
32
32
  import { createRoot } from 'react-dom/client';
33
33
  import { QueryClientProvider } from '@tanstack/react-query';
34
- import { Box } from '@primer/react';
35
- import { DatalayerThemeProvider, setupPrimerPortals, themeConfigs, useSystemColorMode, useThemeStore, } from '@datalayer/primer-addons';
34
+ import { Box, DatalayerThemeProvider, setupPrimerPortals, themeConfigs, useSystemColorMode, useThemeStore, } from '@datalayer/primer-addons';
36
35
  import { configurePlugin } from '@datalayer/reactor';
37
36
  import { LoopWorkspace } from './loop/shell/LoopWorkspace';
38
37
  import { ChatPlugin } from './loop/plugins/chat';
@@ -113,7 +112,7 @@ function LoopPage() {
113
112
  const serverUrl = window.location.origin;
114
113
  void resolveSession(serverUrl).then(setSession);
115
114
  }, []);
116
- return (_jsx(DatalayerThemeProvider, { colorMode: resolvedMode, theme: themeConfig.primerTheme, themeStyles: themeConfig.themeStyles, children: _jsx(QueryClientProvider, { client: internalQueryClient, children: _jsx(Box, { sx: { height: '100vh', overflow: 'hidden' }, children: session ? (_jsx(LoopWorkspace, { serverUrl: session.serverUrl, agentId: session.agentId, conversationId: session.conversationId, model: session.model, sandbox: session.sandbox, initialViewType: session.view,
115
+ return (_jsx(DatalayerThemeProvider, { colorMode: resolvedMode, theme: themeConfig.primerTheme, themeStyles: themeConfig.themeStyles, children: _jsx(QueryClientProvider, { client: internalQueryClient, children: _jsx(Box, { height: "100vh", overflow: "hidden", children: session ? (_jsx(LoopWorkspace, { serverUrl: session.serverUrl, agentId: session.agentId, conversationId: session.conversationId, model: session.model, sandbox: session.sandbox, initialViewType: session.view,
117
116
  // The notebook and document plugins depend on the sandbox, so
118
117
  // the reactor pulls it in; it is named here only to be told which
119
118
  // server it talks to.
@@ -256,52 +256,21 @@ export function McpServerManager({ baseUrl, agentId, enableCodemode = false, sel
256
256
  }, [selectedServers, onSelectedServersChange]);
257
257
  const isLoading = catalogQuery.isLoading;
258
258
  const isMutating = enableMutation.isPending || disableMutation.isPending;
259
- return (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsxs(Box, { sx: {
260
- display: 'flex',
261
- alignItems: 'center',
262
- justifyContent: 'space-between',
263
- }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(ToolsIcon, { size: 16 }), _jsx(Text, { sx: { fontWeight: 'semibold' }, children: "MCP Server Management" })] }), _jsx(IconButton, { icon: SyncIcon, "aria-label": "Refresh server lists", size: "small", onClick: handleRefresh, disabled: isLoading || isMutating })] }), error && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: error }) })), enableCodemode && (_jsx(Flash, { variant: "default", children: _jsxs(Text, { sx: { fontSize: 0 }, children: [_jsx("strong", { children: "Codemode enabled:" }), " Adding or removing MCP servers will regenerate the Codemode tool registry."] }) })), isLoading && (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, py: 3 }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Loading MCP servers..." })] })), !isLoading && assignedConfigServers.length > 0 && (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Assigned Configured Servers (", assignedConfigServers.length, ")"] }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "These servers are from ~/.datalayer/mcp.json and assigned to this agent." }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: assignedConfigServers.map(server => (_jsx(ServerCard, { server: server, variant: "config", disabled: disabled, isSelected: true, onSelect: onSelectedServersChange ? handleServerSelect : undefined, onRemove: () => handleRemoveServer(server.id, true) }, server.id))) })] })), !isLoading && assignedCatalogServers.length > 0 && (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Assigned Catalog Servers (", assignedCatalogServers.length, ")"] }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: assignedCatalogServers.map(server => (_jsx(ServerCard, { server: server, variant: "active", disabled: disabled || isMutating, isSelected: true, onSelect: onSelectedServersChange ? handleServerSelect : undefined, onRemove: () => handleDisableServer(server.id), isRemoving: disableMutation.isPending &&
264
- disableMutation.variables === server.id }, server.id))) })] })), !isLoading && (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Available from Catalog (", availableCatalogServers.length, ")"] }), availableCatalogServers.length > 3 && (_jsx(TextInput, { leadingVisual: SearchIcon, placeholder: "Search servers...", value: searchQuery, onChange: e => setSearchQuery(e.target.value), sx: { width: '100%' } })), filteredCatalogServers.length === 0 ? (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: searchQuery
259
+ return (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 4, children: [_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(ToolsIcon, { size: 16 }), _jsx(Text, { sx: { fontWeight: 'semibold' }, children: "MCP Server Management" })] }), _jsx(IconButton, { icon: SyncIcon, "aria-label": "Refresh server lists", size: "small", onClick: handleRefresh, disabled: isLoading || isMutating })] }), error && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: error }) })), enableCodemode && (_jsx(Flash, { variant: "default", children: _jsxs(Text, { sx: { fontSize: 0 }, children: [_jsx("strong", { children: "Codemode enabled:" }), " Adding or removing MCP servers will regenerate the Codemode tool registry."] }) })), isLoading && (_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, py: 3, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Loading MCP servers..." })] })), !isLoading && assignedConfigServers.length > 0 && (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Assigned Configured Servers (", assignedConfigServers.length, ")"] }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "These servers are from ~/.datalayer/mcp.json and assigned to this agent." }), _jsx(Box, { display: "flex", flexDirection: "column", gap: 2, children: assignedConfigServers.map(server => (_jsx(ServerCard, { server: server, variant: "config", disabled: disabled, isSelected: true, onSelect: onSelectedServersChange ? handleServerSelect : undefined, onRemove: () => handleRemoveServer(server.id, true) }, server.id))) })] })), !isLoading && assignedCatalogServers.length > 0 && (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Assigned Catalog Servers (", assignedCatalogServers.length, ")"] }), _jsx(Box, { display: "flex", flexDirection: "column", gap: 2, children: assignedCatalogServers.map(server => (_jsx(ServerCard, { server: server, variant: "active", disabled: disabled || isMutating, isSelected: true, onSelect: onSelectedServersChange ? handleServerSelect : undefined, onRemove: () => handleDisableServer(server.id), isRemoving: disableMutation.isPending &&
260
+ disableMutation.variables === server.id }, server.id))) })] })), !isLoading && (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Available from Catalog (", availableCatalogServers.length, ")"] }), availableCatalogServers.length > 3 && (_jsx(TextInput, { leadingVisual: SearchIcon, placeholder: "Search servers...", value: searchQuery, onChange: e => setSearchQuery(e.target.value), sx: { width: '100%' } })), filteredCatalogServers.length === 0 ? (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: searchQuery
265
261
  ? 'No servers match your search.'
266
- : 'All catalog servers are already enabled.' })) : (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: filteredCatalogServers.map(server => (_jsx(ServerCard, { server: server, variant: "catalog", disabled: disabled || isMutating, onAdd: () => handleEnableServer(server.id), isAdding: enableMutation.isPending &&
262
+ : 'All catalog servers are already enabled.' })) : (_jsx(Box, { display: "flex", flexDirection: "column", gap: 2, children: filteredCatalogServers.map(server => (_jsx(ServerCard, { server: server, variant: "catalog", disabled: disabled || isMutating, onAdd: () => handleEnableServer(server.id), isAdding: enableMutation.isPending &&
267
263
  enableMutation.variables === server.id }, server.id))) }))] }))] }));
268
264
  }
269
265
  function ServerCard({ server, variant, disabled = false, isSelected = false, onSelect, onAdd, onRemove, isAdding = false, isRemoving = false, }) {
270
266
  // Check availability based on isAvailable field from the server
271
267
  const isAvailable = server.isAvailable !== false;
272
268
  const missingEnvVars = !isAvailable && server.requiredEnvVars && server.requiredEnvVars.length > 0;
273
- return (_jsxs(Box, { sx: {
274
- display: 'flex',
275
- alignItems: 'flex-start',
276
- gap: 2,
277
- padding: 2,
278
- borderRadius: 2,
279
- backgroundColor: 'canvas.subtle',
280
- border: '1px solid',
281
- borderColor: isSelected ? 'accent.emphasis' : 'border.default',
282
- opacity: disabled || !isAvailable ? 0.6 : 1,
283
- cursor: onSelect && !disabled && isAvailable ? 'pointer' : 'default',
284
- }, onClick: () => {
269
+ return (_jsxs(Box, { display: "flex", alignItems: "flex-start", gap: 2, padding: 2, borderRadius: 2, backgroundColor: "canvas.subtle", border: "1px solid", borderColor: isSelected ? 'accent.emphasis' : 'border.default', opacity: disabled || !isAvailable ? 0.6 : 1, cursor: onSelect && !disabled && isAvailable ? 'pointer' : 'default', onClick: () => {
285
270
  if (onSelect && !disabled && isAvailable) {
286
271
  onSelect(server, !isSelected);
287
272
  }
288
- }, children: [_jsx(Box, { sx: {
289
- display: 'flex',
290
- alignItems: 'center',
291
- justifyContent: 'center',
292
- width: 32,
293
- height: 32,
294
- borderRadius: 2,
295
- backgroundColor: variant === 'config' ? 'attention.subtle' : 'accent.subtle',
296
- flexShrink: 0,
297
- }, children: _jsx(ServerIcon, { size: 16 }) }), _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, mb: 1 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: server.name }), variant === 'active' && (_jsx(Label, { variant: "success", size: "small", children: "Active" })), variant === 'config' && (_jsx(Label, { variant: "attention", size: "small", children: "Config" })), !isAvailable && (_jsx(Label, { variant: "danger", size: "small", children: "Missing env vars" })), isSelected && (_jsxs(Label, { variant: "accent", size: "small", children: [_jsx(CheckIcon, { size: 12 }), " Selected"] }))] }), server.description && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', mb: 1 }, children: server.description })), missingEnvVars && (_jsxs(Box, { sx: {
298
- fontSize: 0,
299
- color: 'danger.fg',
300
- mb: 1,
301
- p: 1,
302
- bg: 'danger.subtle',
303
- borderRadius: 1,
304
- }, children: [_jsx(Text, { sx: { fontWeight: 'semibold', display: 'block', mb: 1 }, children: "Missing environment variables:" }), server.requiredEnvVars.map(envVar => (_jsx(Text, { as: "code", sx: {
273
+ }, children: [_jsx(Box, { display: "flex", alignItems: "center", justifyContent: "center", width: 32, height: 32, borderRadius: 2, backgroundColor: variant === 'config' ? 'attention.subtle' : 'accent.subtle', flexShrink: 0, children: _jsx(ServerIcon, { size: 16 }) }), _jsxs(Box, { flex: 1, minWidth: 0, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, mb: 1, children: [_jsx(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: server.name }), variant === 'active' && (_jsx(Label, { variant: "success", size: "small", children: "Active" })), variant === 'config' && (_jsx(Label, { variant: "attention", size: "small", children: "Config" })), !isAvailable && (_jsx(Label, { variant: "danger", size: "small", children: "Missing env vars" })), isSelected && (_jsxs(Label, { variant: "accent", size: "small", children: [_jsx(CheckIcon, { size: 12 }), " Selected"] }))] }), server.description && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', mb: 1 }, children: server.description })), missingEnvVars && (_jsxs(Box, { fontSize: 0, color: "danger.fg", mb: 1, p: 1, bg: "danger.subtle", borderRadius: 1, children: [_jsx(Text, { sx: { fontWeight: 'semibold', display: 'block', mb: 1 }, children: "Missing environment variables:" }), server.requiredEnvVars.map(envVar => (_jsx(Text, { as: "code", sx: {
305
274
  display: 'inline-block',
306
275
  mr: 1,
307
276
  mb: 1,
@@ -317,7 +286,7 @@ function ServerCard({ server, variant, disabled = false, isSelected = false, onS
317
286
  overflow: 'hidden',
318
287
  textOverflow: 'ellipsis',
319
288
  whiteSpace: 'nowrap',
320
- }, children: ["Tools: ", server.tools.map(t => t.name).join(', ')] })), server.transport && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Transport: ", server.transport] }))] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, flexShrink: 0 }, children: [variant === 'catalog' && onAdd && (_jsx(Button, { variant: "primary", size: "small", leadingVisual: isAdding ? Spinner : PlusIcon, onClick: e => {
289
+ }, children: ["Tools: ", server.tools.map(t => t.name).join(', ')] })), server.transport && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Transport: ", server.transport] }))] }), _jsxs(Box, { display: "flex", alignItems: "center", gap: 1, flexShrink: 0, children: [variant === 'catalog' && onAdd && (_jsx(Button, { variant: "primary", size: "small", leadingVisual: isAdding ? Spinner : PlusIcon, onClick: e => {
321
290
  e.stopPropagation();
322
291
  onAdd();
323
292
  }, disabled: disabled || !isAvailable || isAdding, children: "Add" })), variant === 'active' && onRemove && (_jsx(Button, { variant: "danger", size: "small", leadingVisual: isRemoving ? Spinner : TrashIcon, onClick: e => {
@@ -0,0 +1,11 @@
1
+ /**
2
+ * What any JavaScript runtime can import from Agent Runtimes: no React, DOM,
3
+ * Jupyter or Node built-in. Re-exports only; the mobile app reaches Agent
4
+ * Runtimes through this entry alone.
5
+ *
6
+ * @module portable
7
+ */
8
+ export { messageOfApproval, normalizeApproval, whyAsked, type ApprovalArgs, type ApprovalRecord, type MessageAsked, } from '../utils/approvals';
9
+ export { AGUIAdapter, type AGUIAdapterConfig } from '../protocols/AGUIAdapter';
10
+ export { createActivityMessage, createAssistantMessage, createUserMessage, generateMessageId, type ChatMessage, type ChatMessageDelta, type ChatMessageInput, type ChatThread, type ContentPart, type MessageRole, type ToolCallStatus, } from '../types/messages';
11
+ export type { ProtocolAdapterConfig, ProtocolConnectionState, ProtocolEvent, ProtocolEventHandler, } from '../types/protocol';
@@ -0,0 +1,20 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /*
6
+ * Copyright (c) 2026 Datalayer, Inc.
7
+ * Distributed under the terms of the Modified BSD License.
8
+ */
9
+ /**
10
+ * What any JavaScript runtime can import from Agent Runtimes: no React, DOM,
11
+ * Jupyter or Node built-in. Re-exports only; the mobile app reaches Agent
12
+ * Runtimes through this entry alone.
13
+ *
14
+ * @module portable
15
+ */
16
+ export { messageOfApproval, normalizeApproval, whyAsked, } from '../utils/approvals';
17
+ // The AG-UI stream client and the messages it builds; a phone passes a
18
+ // streaming `fetch` in the adapter's config.
19
+ export { AGUIAdapter } from '../protocols/AGUIAdapter';
20
+ export { createActivityMessage, createAssistantMessage, createUserMessage, generateMessageId, } from '../types/messages';
@@ -99,7 +99,7 @@ export class AGUIAdapter extends BaseProtocolAdapter {
99
99
  // e.g., http://localhost:8765/api/v1/ag-ui/default -> http://localhost:8765/api/v1/ag-ui/terminate
100
100
  const baseUrl = new URL(this.aguiConfig.baseUrl);
101
101
  const terminateUrl = `${baseUrl.origin}/api/v1/ag-ui/terminate`;
102
- const response = await fetch(terminateUrl, {
102
+ const response = await this.fetchOf()(terminateUrl, {
103
103
  method: 'POST',
104
104
  headers: {
105
105
  'Content-Type': 'application/json',
@@ -205,7 +205,7 @@ export class AGUIAdapter extends BaseProtocolAdapter {
205
205
  console.log('[AGUIAdapter] Sending with identities:', options.identities.map(i => i.provider));
206
206
  }
207
207
  try {
208
- const response = await fetch(this.aguiConfig.baseUrl, {
208
+ const response = await this.fetchOf()(this.aguiConfig.baseUrl, {
209
209
  method: 'POST',
210
210
  headers: this.buildHeaders({
211
211
  Accept: 'text/event-stream',
@@ -70,6 +70,8 @@ export declare abstract class BaseProtocolAdapter implements ProtocolAdapter {
70
70
  * Build headers for HTTP requests
71
71
  */
72
72
  protected buildHeaders(additionalHeaders?: Record<string, string>): Record<string, string>;
73
+ /** The fetch the caller gave, else the global one; read per call so a test can stub the global. */
74
+ protected fetchOf(): typeof fetch;
73
75
  /**
74
76
  * Create abort signal with timeout
75
77
  */
@@ -110,6 +110,10 @@ export class BaseProtocolAdapter {
110
110
  }
111
111
  return { ...headers, ...additionalHeaders };
112
112
  }
113
+ /** The fetch the caller gave, else the global one; read per call so a test can stub the global. */
114
+ fetchOf() {
115
+ return this.config.fetch ?? ((input, init) => fetch(input, init));
116
+ }
113
117
  /**
114
118
  * Create abort signal with timeout
115
119
  */
package/lib/specs/apps.js CHANGED
@@ -3515,6 +3515,7 @@ export const APP_SOURCES = {
3515
3515
  },
3516
3516
  ],
3517
3517
  interface: {
3518
+ accent: 'green',
3518
3519
  welcome: "Ask me for a report from the books: open invoices, aged balances, a trial balance or a customer's ledger. I read Odoo; I change nothing.",
3519
3520
  starters: [
3520
3521
  {
@@ -3869,6 +3870,7 @@ export const APP_SOURCES = {
3869
3870
  agent: 'jupyter-data-analyst:0.0.1',
3870
3871
  contents: ['The dataset under investigation'],
3871
3872
  interface: {
3873
+ accent: 'green',
3872
3874
  components: [
3873
3875
  'Card',
3874
3876
  'Column',
@@ -4230,6 +4232,7 @@ export const APP_SOURCES = {
4230
4232
  },
4231
4233
  ],
4232
4234
  interface: {
4235
+ accent: 'green',
4233
4236
  welcome: 'I sort your mail and draft the replies. I ask before I send, and I never delete.',
4234
4237
  starters: [
4235
4238
  {
@@ -4321,6 +4324,7 @@ export const APP_SOURCES = {
4321
4324
  'The Models page: how each model is billed (standard or credits) and who hosts it',
4322
4325
  ],
4323
4326
  interface: {
4327
+ accent: 'green',
4324
4328
  components: [
4325
4329
  'Card',
4326
4330
  'Column',
@@ -5247,6 +5251,7 @@ export const APP_SOURCES = {
5247
5251
  agent: 'jupyter-data-analyst:0.0.1',
5248
5252
  contents: ['The benchmark run: task results, traces, cost and latency'],
5249
5253
  interface: {
5254
+ accent: 'green',
5250
5255
  components: [
5251
5256
  'Card',
5252
5257
  'Column',
@@ -5354,6 +5359,7 @@ export const APP_SOURCES = {
5354
5359
  agent: 'jupyter-data-analyst:0.0.1',
5355
5360
  contents: ['Order history', 'Supplier price lists', 'Delivery records'],
5356
5361
  interface: {
5362
+ accent: 'green',
5357
5363
  components: [
5358
5364
  'Card',
5359
5365
  'Column',
@@ -435,9 +435,16 @@ export const APPSPEC_SCHEMA = {
435
435
  description: "`chat`, `page` or `split`; the kind's own when unsaid",
436
436
  },
437
437
  accent: {
438
- $ref: '#/$defs/Accent',
439
- default: 'green',
440
- description: "The application's one colour",
438
+ anyOf: [
439
+ {
440
+ $ref: '#/$defs/Accent',
441
+ },
442
+ {
443
+ type: 'null',
444
+ },
445
+ ],
446
+ default: null,
447
+ description: "The application's one colour, over whichever theme it runs in; unsaid, it wears the theme's own colours",
441
448
  },
442
449
  theme: {
443
450
  anyOf: [
@@ -449,7 +456,7 @@ export const APPSPEC_SCHEMA = {
449
456
  },
450
457
  ],
451
458
  default: null,
452
- description: "The theme it runs in by default, at its address, embedded, in the Studio's Preview and as an example: a `variant` and, optionally, a colour `mode`. The person's own when unsaid. Its `accent` colours the `loop` theme only",
459
+ description: "The theme it runs in by default, at its address, embedded, in the Studio's Preview and as an example: a `variant` and, optionally, a colour `mode`. The person's own when unsaid. Its `accent`, when it names one, colours it, whichever theme it is",
453
460
  },
454
461
  welcome: {
455
462
  default: '',