@datalayer/agent-runtimes 1.3.89 → 1.3.91

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 (265) 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/chat/words.d.ts +7 -0
  69. package/lib/chat/words.js +12 -0
  70. package/lib/components/a2ui/datalayer/Chart.js +4 -10
  71. package/lib/components/a2ui/datalayer/Chat.js +2 -10
  72. package/lib/components/a2ui/datalayer/Download.js +1 -1
  73. package/lib/components/a2ui/datalayer/Evidence.js +1 -15
  74. package/lib/components/a2ui/datalayer/FileUpload.js +1 -12
  75. package/lib/components/a2ui/datalayer/Table.js +9 -17
  76. package/lib/components/a2ui/datalayer/formWidgets.js +3 -2
  77. package/lib/components/a2ui/datalayer/parts.js +1 -16
  78. package/lib/components/a2ui/styles.js +16 -5
  79. package/lib/components/agents/AgentSummary.js +1 -51
  80. package/lib/components/cards/NotificationEventCard.js +6 -72
  81. package/lib/components/cards/OutputCard.js +4 -48
  82. package/lib/components/cards/ToolApprovalCard.js +2 -35
  83. package/lib/components/code-sandboxes/CodeSandboxCellVariables.js +1 -1
  84. package/lib/components/code-sandboxes/CodeSandboxEnvironmentSelect.js +4 -14
  85. package/lib/components/code-sandboxes/CodeSandboxPicker.js +16 -21
  86. package/lib/components/code-sandboxes/CodeSandboxVariables.js +2 -2
  87. package/lib/components/scheduler/ScheduleMenu.js +5 -10
  88. package/lib/components/specs/SpecPayloadPreview.js +1 -26
  89. package/lib/components/teams/A2ATeamGraph.d.ts +4 -3
  90. package/lib/components/teams/A2ATeamGraph.js +25 -71
  91. package/lib/components/teams/AnswerSurfacesView.js +2 -2
  92. package/lib/components/teams/NotebookPreview.js +1 -10
  93. package/lib/components/teams/NotebookPreviewView.js +2 -3
  94. package/lib/components/teams/SceneTranscript.js +3 -17
  95. package/lib/components/teams/SceneView.js +1 -8
  96. package/lib/components/teams/TeamNotebook.js +5 -13
  97. package/lib/components/teams/TeamNotebookView.js +2 -12
  98. package/lib/components/teams/index.d.ts +1 -0
  99. package/lib/components/teams/index.js +1 -0
  100. package/lib/components/teams/useBalloonRoom.d.ts +39 -0
  101. package/lib/components/teams/useBalloonRoom.js +89 -0
  102. package/lib/config/AgentConfiguration.js +22 -225
  103. package/lib/context/ContextDistribution.js +7 -41
  104. package/lib/context/ContextInspector.js +9 -107
  105. package/lib/context/ContextPanel.js +11 -86
  106. package/lib/context/ContextUsage.js +6 -34
  107. package/lib/context/CostTracker.js +7 -42
  108. package/lib/context/MessageHistory.js +3 -37
  109. package/lib/context/TurnGraphChart.js +9 -41
  110. package/lib/examples/A2UiAgentExample.js +6 -108
  111. package/lib/examples/A2UiComponentsGalleryExample.js +1 -1
  112. package/lib/examples/A2UiContactCardExample.js +1 -1
  113. package/lib/examples/A2UiJupyterOutputExample.js +25 -125
  114. package/lib/examples/A2UiRestaurantExample.js +1 -1
  115. package/lib/examples/A2UiViewerExample.js +1 -1
  116. package/lib/examples/AgUiAgenticExample.js +3 -35
  117. package/lib/examples/AgUiBackendToolRenderingExample.js +2 -25
  118. package/lib/examples/AgUiHaikuGenUiExample.js +2 -32
  119. package/lib/examples/AgUiHumanInTheLoopExample.js +5 -59
  120. package/lib/examples/AgUiSharedStateExample.js +6 -56
  121. package/lib/examples/AgUiToolsBasedGenUiExample.js +25 -120
  122. package/lib/examples/AgentA2AExample.js +1 -15
  123. package/lib/examples/AgentA2ATeamExample.js +6 -30
  124. package/lib/examples/AgentCheckpointsExample.js +11 -145
  125. package/lib/examples/AgentCodeSandboxExample.js +5 -108
  126. package/lib/examples/AgentCodeSandboxesExample.js +3 -30
  127. package/lib/examples/AgentCodemodeExample.js +9 -85
  128. package/lib/examples/AgentCompactionExample.js +3 -64
  129. package/lib/examples/AgentDecideExample.js +1 -13
  130. package/lib/examples/AgentEvalsExample.js +5 -60
  131. package/lib/examples/AgentGuardrailsExample.js +4 -28
  132. package/lib/examples/AgentHooksExample.js +1 -1
  133. package/lib/examples/AgentInferenceProviderExample.js +5 -96
  134. package/lib/examples/AgentMCPExample.js +7 -105
  135. package/lib/examples/AgentMemoryExample.js +5 -74
  136. package/lib/examples/AgentMonitoringExample.js +7 -53
  137. package/lib/examples/AgentNotificationsExample.js +3 -61
  138. package/lib/examples/AgentOtelExample.js +3 -72
  139. package/lib/examples/AgentOutputsExample.js +6 -81
  140. package/lib/examples/AgentParametersExample.js +2 -37
  141. package/lib/examples/AgentSkillsExample.js +3 -45
  142. package/lib/examples/AgentSubagentsExample.js +6 -56
  143. package/lib/examples/AgentToolApprovalsExample.js +1 -11
  144. package/lib/examples/AgentTriggersExample.js +29 -239
  145. package/lib/examples/AgentspecsExample.js +6 -60
  146. package/lib/examples/AssistantExample.js +5 -5
  147. package/lib/examples/AssistantGalleryExample.js +8 -65
  148. package/lib/examples/CellExample.js +1 -6
  149. package/lib/examples/ChatCustomExample.js +2 -31
  150. package/lib/examples/ChatExample.js +1 -9
  151. package/lib/examples/ChatStandaloneExample.js +3 -37
  152. package/lib/examples/CopilotKitLexicalExample.js +1 -24
  153. package/lib/examples/DecksAgent.js +1 -1
  154. package/lib/examples/DocumentPageAgent.js +1 -1
  155. package/lib/examples/HomeExample.js +14 -62
  156. package/lib/examples/Lexical2Example.js +3 -32
  157. package/lib/examples/LexicalAgentExample.js +2 -34
  158. package/lib/examples/LexicalAgentSidebarExample.js +3 -34
  159. package/lib/examples/LoopAppComputerExample.js +1 -1
  160. package/lib/examples/LoopShellExample.js +2 -2
  161. package/lib/examples/LoopStrategyExample.js +15 -87
  162. package/lib/examples/LoopWorkspaceEmbed.js +2 -18
  163. package/lib/examples/LoopWorkspaceExample.js +2 -2
  164. package/lib/examples/NotebookAgentExample.js +2 -33
  165. package/lib/examples/NotebookAgentSidebarExample.js +3 -46
  166. package/lib/examples/NotebookCollaborationExample.js +1 -31
  167. package/lib/examples/NotebookExample.js +1 -18
  168. package/lib/examples/NotebookPageAgent.js +1 -1
  169. package/lib/examples/ScenesExample.js +5 -24
  170. package/lib/examples/VoiceChatExample.js +1 -1
  171. package/lib/examples/components/AuthRequiredView.js +1 -7
  172. package/lib/examples/components/ErrorView.js +1 -8
  173. package/lib/examples/components/ExampleErrorBoundary.js +1 -14
  174. package/lib/examples/components/FooterMetrics.js +1 -7
  175. package/lib/examples/components/Header.js +12 -51
  176. package/lib/examples/components/HeaderControls.js +1 -9
  177. package/lib/examples/components/LexicalEditor.js +1 -8
  178. package/lib/examples/components/MainContent.js +1 -13
  179. package/lib/examples/components/MockFileBrowser.js +4 -40
  180. package/lib/examples/components/SessionTabs.js +1 -1
  181. package/lib/examples/components/TimeTravel.js +1 -13
  182. package/lib/examples/main.js +13 -81
  183. package/lib/examples/scenes/A2UiComponentsGalleryScene.js +3 -34
  184. package/lib/examples/scenes/A2UiContactCardScene.js +3 -35
  185. package/lib/examples/scenes/A2UiRestaurantScene.js +3 -53
  186. package/lib/examples/scenes/A2UiViewerScene.js +3 -34
  187. package/lib/examples/utils/AssistantGalleryGrid.js +6 -35
  188. package/lib/examples2/DatalayerNotebookExample.js +3 -33
  189. package/lib/examples2/NativeNavigationExample.js +4 -10
  190. package/lib/examples2/NotebookMutationsKernel.js +2 -1
  191. package/lib/examples2/NotebookMutationsServiceManager.js +2 -1
  192. package/lib/examples2/ReactRouterExample.js +14 -42
  193. package/lib/hooks/useToolApprovals.d.ts +2 -34
  194. package/lib/hooks/useToolApprovals.js +1 -64
  195. package/lib/identity/AgentIdentity.js +17 -62
  196. package/lib/identity/IdentityConnect.js +13 -59
  197. package/lib/identity/OAuthCallback.js +6 -44
  198. package/lib/lexical/ChatInlinePlugin.js +6 -14
  199. package/lib/loop/apps/AppFeedback.js +12 -3
  200. package/lib/loop/apps/AppRenderer.d.ts +4 -3
  201. package/lib/loop/apps/AppRenderer.js +11 -12
  202. package/lib/loop/apps/appspec.js +6 -3
  203. package/lib/loop/apps/feedback.d.ts +3 -2
  204. package/lib/loop/apps/feedback.js +4 -2
  205. package/lib/loop/core/index.d.ts +19 -0
  206. package/lib/loop/core/index.js +27 -0
  207. package/lib/loop/embed/AppEmbed.d.ts +1 -1
  208. package/lib/loop/embed/AppEmbed.js +2 -1
  209. package/lib/loop/embed/LoopEmbed.js +1 -7
  210. package/lib/loop/embed/embedConfig.d.ts +3 -3
  211. package/lib/loop/embed/embedConfig.js +2 -3
  212. package/lib/loop/embed/embedTheme.d.ts +4 -4
  213. package/lib/loop/embed/embedTheme.js +11 -7
  214. package/lib/loop/plugins/a2a-sidebar/A2ASidebar.js +7 -34
  215. package/lib/loop/plugins/a2ui/A2uiView.js +4 -12
  216. package/lib/loop/plugins/a2ui-surface/InlineSurface.js +1 -16
  217. package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +2 -12
  218. package/lib/loop/plugins/agents/AnonymousKeyBadge.js +3 -2
  219. package/lib/loop/plugins/agents/SandboxSelector.js +3 -7
  220. package/lib/loop/plugins/agents/SandboxView.js +4 -27
  221. package/lib/loop/plugins/agents/TeamMemberPicker.js +2 -2
  222. package/lib/loop/plugins/agentspecs/AgentspecPicker.js +3 -2
  223. package/lib/loop/plugins/app-activity/AppActivity.js +1 -1
  224. package/lib/loop/plugins/app-computer/AppComputer.js +32 -27
  225. package/lib/loop/plugins/app-elements/AppElements.js +2 -31
  226. package/lib/loop/plugins/app-page/AppPage.js +26 -8
  227. package/lib/loop/plugins/app-rules/AppRulesCard.js +4 -20
  228. package/lib/loop/plugins/assistant/index.js +1 -13
  229. package/lib/loop/plugins/chat/ChatView.js +89 -130
  230. package/lib/loop/plugins/checkpoints-sidebar/CheckpointsSidebar.js +2 -13
  231. package/lib/loop/plugins/decks/DeckSurface.js +2 -2
  232. package/lib/loop/plugins/document/DocumentView.js +4 -12
  233. package/lib/loop/plugins/document-toolbar/SandboxStatusItem.js +3 -8
  234. package/lib/loop/plugins/graph/GraphView.js +2 -2
  235. package/lib/loop/plugins/input-prompt/ComposerView.js +1 -1
  236. package/lib/loop/plugins/notebook/NotebookView.js +3 -11
  237. package/lib/loop/plugins/page-layout/SplitLayout.js +11 -34
  238. package/lib/loop/plugins/page-layout/TurnPanel.js +9 -35
  239. package/lib/loop/plugins/shell/useEditorPreviews.js +3 -2
  240. package/lib/loop/plugins/subagent-activity/SubagentPulse.js +1 -13
  241. package/lib/loop/plugins/window-frame/WindowFrame.js +10 -44
  242. package/lib/loop/plugins/window-frame/index.js +1 -1
  243. package/lib/loop/shell/LoopWorkspace.js +34 -77
  244. package/lib/loop/shell/PluginToggles.js +4 -15
  245. package/lib/loop/shell/SidebarRail.js +8 -40
  246. package/lib/loop/shell/ViewSwitcher.js +4 -24
  247. package/lib/loop-main.js +2 -3
  248. package/lib/mcp/McpServerManager.js +6 -37
  249. package/lib/portable/index.d.ts +11 -0
  250. package/lib/portable/index.js +20 -0
  251. package/lib/protocols/AGUIAdapter.js +2 -2
  252. package/lib/protocols/BaseProtocolAdapter.d.ts +2 -0
  253. package/lib/protocols/BaseProtocolAdapter.js +4 -0
  254. package/lib/specs/apps.js +6 -0
  255. package/lib/specs/appspecSchema.js +11 -4
  256. package/lib/stories/loop/ReferenceScreens.js +17 -91
  257. package/lib/stories/loop/pictures-main.js +1 -1
  258. package/lib/types/agentspecs.d.ts +5 -2
  259. package/lib/types/protocol.d.ts +2 -0
  260. package/lib/utils/approvals.d.ts +59 -0
  261. package/lib/utils/approvals.js +101 -0
  262. package/lib/voice/VoiceInput.js +3 -39
  263. package/package.json +2 -2
  264. package/lib/shims/primerAddonsBox.d.ts +0 -1
  265. package/lib/shims/primerAddonsBox.js +0 -7
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- import { getThemeConfig, loopAccentStyles, loopFontFamily, scopeThemeCss, THEME_SCOPE_ATTRIBUTE, } from '@datalayer/primer-addons';
5
+ import { getThemeConfig, loopFontFamily, scopeThemeCss, THEME_SCOPE_ATTRIBUTE, themeAccentVars, } from '@datalayer/primer-addons';
6
6
  /** One mode's properties, with the theme's face replaced by the host's. */
7
7
  function withFace(styles, font, own) {
8
8
  if (!font || !own) {
@@ -20,18 +20,22 @@ function faceOf(variant, styles) {
20
20
  }
21
21
  return typeof styles.fontFamily === 'string' ? styles.fontFamily : '';
22
22
  }
23
- /** The accent over a theme: `loop`'s colours, over `loop` only (T-30). */
24
- const accentOver = (variant, accent, which) => variant === 'loop' ? loopAccentStyles(accent, which) : {};
23
+ /**
24
+ * The accent over a theme, whichever theme (T-05, T-30; every theme since
25
+ * 2026-10-07); none when neither the application nor the host names one —
26
+ * the theme's own colours.
27
+ */
28
+ const accentOver = (accent, which) => (accent ? themeAccentVars(accent, which) : {});
25
29
  /**
26
30
  * The theme as the embed wears it — `loop`, or the one the application
27
- * names (T-30) — with the application's accent over `loop` in both modes
31
+ * names (T-30) — with the application's accent over it in both modes
28
32
  * (T-05), and the host's face when it named one.
29
33
  */
30
34
  export function embedThemeStyles({ accent, font = '', variant = 'loop', }) {
31
35
  const base = getThemeConfig(variant).themeStyles;
32
36
  const mode = (which) => {
33
37
  const theme = base[which];
34
- return withFace({ ...theme, ...accentOver(variant, accent, which) }, font, faceOf(variant, theme));
38
+ return withFace({ ...theme, ...accentOver(accent, which) }, font, faceOf(variant, theme));
35
39
  };
36
40
  return {
37
41
  ...base,
@@ -43,7 +47,7 @@ export function embedThemeStyles({ accent, font = '', variant = 'loop', }) {
43
47
  * The same accent and face laid over the conversation's own theme (T-05,
44
48
  * D-11): the chat sets its theme again inside it, which would put the
45
49
  * theme's colours and face back over what the embed set around it. The
46
- * accent's properties in each mode (over `loop` only), and, when the host
50
+ * accent's properties in each mode (over any theme), and, when the host
47
51
  * named a face, every property of the theme that names its own.
48
52
  */
49
53
  export function embedThemeOverrides({ accent, font = '', variant = 'loop', }) {
@@ -51,7 +55,7 @@ export function embedThemeOverrides({ accent, font = '', variant = 'loop', }) {
51
55
  const mode = (which) => {
52
56
  const theme = base[which];
53
57
  const faced = Object.fromEntries(Object.entries(withFace(theme, font, faceOf(variant, theme))).filter(([name, value]) => typeof value === 'string' && value !== theme[name]));
54
- return { ...faced, ...accentOver(variant, accent, which) };
58
+ return { ...faced, ...accentOver(accent, which) };
55
59
  };
56
60
  return { light: mode('light'), dark: mode('dark') };
57
61
  }
@@ -100,14 +100,14 @@ const STATUS_LABEL = {
100
100
  function StatusBadge({ status, }) {
101
101
  switch (status) {
102
102
  case 'done':
103
- return (_jsx(Box, { sx: { color: 'success.fg', display: 'flex' }, children: _jsx(CheckCircleIcon, {}) }));
103
+ return (_jsx(Box, { color: "success.fg", display: "flex", children: _jsx(CheckCircleIcon, {}) }));
104
104
  case 'stopped':
105
- return (_jsx(Box, { sx: { color: 'attention.fg', display: 'flex' }, children: _jsx(AlertIcon, {}) }));
105
+ return (_jsx(Box, { color: "attention.fg", display: "flex", children: _jsx(AlertIcon, {}) }));
106
106
  case 'failed':
107
- return (_jsx(Box, { sx: { color: 'danger.fg', display: 'flex' }, children: _jsx(AlertIcon, {}) }));
107
+ return (_jsx(Box, { color: "danger.fg", display: "flex", children: _jsx(AlertIcon, {}) }));
108
108
  case 'launching':
109
109
  case 'working':
110
- return (_jsx(Box, { sx: { color: 'accent.fg', display: 'flex' }, children: _jsx(DependabotIcon, {}) }));
110
+ return (_jsx(Box, { color: "accent.fg", display: "flex", children: _jsx(DependabotIcon, {}) }));
111
111
  default:
112
112
  return _jsx(BroadcastIcon, {});
113
113
  }
@@ -120,13 +120,7 @@ function LaunchChip({ launch, }) {
120
120
  : launch === 'local'
121
121
  ? DeviceDesktopIcon
122
122
  : BroadcastIcon;
123
- return (_jsxs(Box, { sx: {
124
- display: 'inline-flex',
125
- alignItems: 'center',
126
- gap: 1,
127
- fontSize: 0,
128
- color: 'fg.muted',
129
- }, children: [_jsx(Icon, { size: 12 }), _jsx(Text, { sx: { fontSize: 0 }, children: launch })] }));
123
+ return (_jsxs(Box, { display: "inline-flex", alignItems: "center", gap: 1, fontSize: 0, color: "fg.muted", children: [_jsx(Icon, { size: 12 }), _jsx(Text, { sx: { fontSize: 0 }, children: launch })] }));
130
124
  }
131
125
  function SectionHeading({ children }) {
132
126
  return (_jsx(Heading, { as: "h4", sx: { fontSize: 1, mb: 2 }, children: children }));
@@ -142,30 +136,9 @@ export function A2ASidebar(_props) {
142
136
  const [shown, setShown] = useState(null);
143
137
  const activeOverA2A = activeKey !== null &&
144
138
  (activity[activeKey] ?? []).some(event => event.transport === 'a2a');
145
- return (_jsxs(Box, { "data-a2a-sidebar": true, sx: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(BroadcastIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2, m: 0, flex: 1 }, children: "A2A agents" }), _jsx(Label, { variant: "accent", children: agents.length })] }), _jsxs(Box, { children: [_jsx(SectionHeading, { children: "Running now" }), activeKey && activeOverA2A ? (_jsx(SubagentChatPanel, { toolCallId: activeKey, height: A2A_ACTIVE_PANEL_HEIGHT })) : (_jsx(Box, { "data-a2a-sidebar-idle": true, sx: {
146
- height: A2A_ACTIVE_PANEL_HEIGHT,
147
- display: 'flex',
148
- alignItems: 'center',
149
- justifyContent: 'center',
150
- border: '1px solid',
151
- borderColor: 'border.muted',
152
- borderRadius: 2,
153
- bg: 'canvas.default',
154
- }, children: _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', m: 0 }, children: "No A2A agent running" }) }))] }), _jsxs(Box, { children: [_jsx(SectionHeading, { children: "Remote agents" }), agents.length === 0 ? (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0 }, children: "This agent declares no A2A agents." })) : (_jsx(Timeline, { children: agents.map(agent => {
139
+ return (_jsxs(Box, { "data-a2a-sidebar": true, display: "flex", flexDirection: "column", gap: 4, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(BroadcastIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2, m: 0, flex: 1 }, children: "A2A agents" }), _jsx(Label, { variant: "accent", children: agents.length })] }), _jsxs(Box, { children: [_jsx(SectionHeading, { children: "Running now" }), activeKey && activeOverA2A ? (_jsx(SubagentChatPanel, { toolCallId: activeKey, height: A2A_ACTIVE_PANEL_HEIGHT })) : (_jsx(Box, { "data-a2a-sidebar-idle": true, height: A2A_ACTIVE_PANEL_HEIGHT, display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid", borderColor: "border.muted", borderRadius: 2, bg: "canvas.default", children: _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', m: 0 }, children: "No A2A agent running" }) }))] }), _jsxs(Box, { children: [_jsx(SectionHeading, { children: "Remote agents" }), agents.length === 0 ? (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0 }, children: "This agent declares no A2A agents." })) : (_jsx(Timeline, { children: agents.map(agent => {
155
140
  const status = STATUS_LABEL[agent.status];
156
- return (_jsxs(Timeline.Item, { "data-a2a-agent": agent.name, children: [_jsx(Timeline.Badge, { children: _jsx(StatusBadge, { status: agent.status }) }), _jsxs(Timeline.Body, { children: [_jsxs(Box, { sx: {
157
- display: 'flex',
158
- alignItems: 'center',
159
- gap: 2,
160
- mb: 1,
161
- flexWrap: 'wrap',
162
- }, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: agent.name }), _jsx(Label, { variant: status.variant, size: "small", children: status.text }), _jsx(LaunchChip, { launch: agent.launch })] }), agent.description ? (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mt: 0, mb: 1 }, children: agent.description })) : null, _jsxs(Box, { sx: {
163
- display: 'flex',
164
- flexDirection: 'column',
165
- gap: '2px',
166
- fontSize: 0,
167
- color: 'fg.muted',
168
- }, children: [agent.card?.name ? (_jsxs(Text, { sx: { fontSize: 0 }, children: ["Card: ", agent.card.name, agent.card.version ? ` v${agent.card.version}` : ''] })) : agent.ref ? (_jsx(Text, { sx: { fontSize: 0, fontFamily: 'mono' }, children: agent.ref })) : null, agent.url ? (_jsx(Link, { as: "button", type: "button", "data-a2a-card-button": agent.name, onClick: () => setShown(agent), sx: {
141
+ return (_jsxs(Timeline.Item, { "data-a2a-agent": agent.name, children: [_jsx(Timeline.Badge, { children: _jsx(StatusBadge, { status: agent.status }) }), _jsxs(Timeline.Body, { children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, mb: 1, flexWrap: "wrap", children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: agent.name }), _jsx(Label, { variant: status.variant, size: "small", children: status.text }), _jsx(LaunchChip, { launch: agent.launch })] }), agent.description ? (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mt: 0, mb: 1 }, children: agent.description })) : null, _jsxs(Box, { display: "flex", flexDirection: "column", gap: "2px", fontSize: 0, color: "fg.muted", children: [agent.card?.name ? (_jsxs(Text, { sx: { fontSize: 0 }, children: ["Card: ", agent.card.name, agent.card.version ? ` v${agent.card.version}` : ''] })) : agent.ref ? (_jsx(Text, { sx: { fontSize: 0, fontFamily: 'mono' }, children: agent.ref })) : null, agent.url ? (_jsx(Link, { as: "button", type: "button", "data-a2a-card-button": agent.name, onClick: () => setShown(agent), sx: {
169
142
  fontSize: 0,
170
143
  fontFamily: 'mono',
171
144
  overflow: 'hidden',
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } 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 { MessageProcessor, } from '@a2ui/web_core/v0_9';
5
6
  import { useSignalValue } from '@datalayer/reactor/react';
6
7
  import { A2uiSurfaceComposed, datalayerCatalog, } from '../../../components/a2ui';
@@ -80,17 +81,8 @@ export default function A2uiView({ workspace }) {
80
81
  if (surfaces.length === 0) {
81
82
  return (_jsx(Centered, { children: _jsx(Text, { children: "Nothing has run yet. Ask for something that executes code and the result appears here as a surface rather than as output." }) }));
82
83
  }
83
- return (_jsx(Box, { sx: { height: '100%', overflowY: 'auto', px: 4, py: 3 }, style: INHERIT_THEME, children: surfaces.map(surface => (_jsx(A2uiSurfaceComposed, { surface: surface }, surface.id))) }));
84
+ return (_jsx(Box, { height: "100%", overflowY: "auto", px: 4, py: 3, style: INHERIT_THEME, children: surfaces.map(surface => (_jsx(A2uiSurfaceComposed, { surface: surface }, surface.id))) }));
84
85
  }
85
86
  function Centered({ children }) {
86
- return (_jsx(Box, { sx: {
87
- height: '100%',
88
- display: 'flex',
89
- alignItems: 'center',
90
- justifyContent: 'center',
91
- color: 'fg.muted',
92
- fontSize: 1,
93
- px: 4,
94
- textAlign: 'center',
95
- }, children: children }));
87
+ return (_jsx(Box, { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "fg.muted", fontSize: 1, px: 4, textAlign: "center", children: children }));
96
88
  }
@@ -79,21 +79,6 @@ export function InlineSurface({ messages, onAction, validationError, data, catal
79
79
  display: 'flex',
80
80
  flexDirection: 'column',
81
81
  gap: 2,
82
- }, children: [notice ? (_jsx(Box, { role: "alert", sx: {
83
- px: 3,
84
- py: 2,
85
- borderRadius: 2,
86
- bg: 'danger.subtle',
87
- border: '1px solid',
88
- borderColor: 'danger.muted',
89
- color: 'danger.fg',
90
- fontSize: 1,
91
- }, children: notice })) : null, surfaces.map(surface => (_jsx(Box, { sx: {
92
- border: '1px solid',
93
- borderColor: 'border.default',
94
- borderRadius: 2,
95
- p: 3,
96
- bg: 'canvas.default',
97
- }, children: _jsx(A2uiSurfaceComposed, { surface: surface }) }, surface.id)))] }) }));
82
+ }, children: [notice ? (_jsx(Box, { role: "alert", px: 3, py: 2, borderRadius: 2, bg: "danger.subtle", border: "1px solid", borderColor: "danger.muted", color: "danger.fg", fontSize: 1, children: notice })) : null, surfaces.map(surface => (_jsx(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, p: 3, bg: "canvas.default", children: _jsx(A2uiSurfaceComposed, { surface: surface }) }, surface.id)))] }) }));
98
83
  }
99
84
  export default InlineSurface;
@@ -56,18 +56,8 @@ export function SurfaceToolResult({ context, send, onRendered, onSubmitted, }) {
56
56
  }
57
57
  }, [parsed, title, send, onSubmitted]);
58
58
  if (!parsed?.messages || parsed.messages.length === 0) {
59
- return (_jsxs(Box, { sx: {
60
- border: '1px solid',
61
- borderColor: 'border.default',
62
- borderRadius: 2,
63
- px: 3,
64
- py: 2,
65
- bg: 'canvas.default',
66
- display: 'flex',
67
- alignItems: 'center',
68
- gap: 2,
69
- }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Rendering A2UI surface..." })] }));
59
+ return (_jsxs(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, px: 3, py: 2, bg: "canvas.default", display: "flex", alignItems: "center", gap: 2, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Rendering A2UI surface..." })] }));
70
60
  }
71
- return (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsx(Text, { sx: { fontWeight: 'bold' }, children: title }), _jsx(InlineSurface, { messages: parsed.messages, onAction: handleAction, validationError: validationError, catalog: catalog })] }));
61
+ return (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [_jsx(Text, { sx: { fontWeight: 'bold' }, children: title }), _jsx(InlineSurface, { messages: parsed.messages, onAction: handleAction, validationError: validationError, catalog: catalog })] }));
72
62
  }
73
63
  export default SurfaceToolResult;
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, Text } from '@primer/react';
2
+ import { Box } from '@datalayer/primer-addons';
3
+ import { Text } from '@primer/react';
3
4
  /* Both by file rather than through their barrels. The barrels reach the
4
5
  sign-in form and the whole in-page harness, and a plugin that only draws a
5
6
  countdown should not put either in the graph of every host that mounts
@@ -18,7 +19,7 @@ import { targetRunsAgentInPage } from './switchable';
18
19
  * that lives.
19
20
  */
20
21
  export function AnonymousKeyHeaderBadge({ workspace, }) {
21
- return (_jsx(Box, { sx: { order: 1, display: 'inline-flex', alignItems: 'center' }, children: _jsx(AnonymousKeyBadge, { workspace: workspace }) }));
22
+ return (_jsx(Box, { order: 1, display: "inline-flex", alignItems: "center", children: _jsx(AnonymousKeyBadge, { workspace: workspace }) }));
22
23
  }
23
24
  export function AnonymousKeyBadge({ workspace, }) {
24
25
  const session = useAnonymousSession();
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useState } from 'react';
3
- import { Box, SegmentedControl, Spinner, Text } from '@primer/react';
3
+ import { Box } from '@datalayer/primer-addons';
4
+ import { SegmentedControl, Spinner, Text } from '@primer/react';
4
5
  import { KernelIndicator } from '@datalayer/jupyter-react/kernel-indicator';
5
6
  import { useSignalValue } from '@datalayer/reactor/react';
6
7
  import { IDLE_SANDBOX_SNAPSHOT_SIGNAL, sandboxIndicatorState, } from '../../core';
@@ -37,12 +38,7 @@ export function SandboxSelector(_props) {
37
38
  return null;
38
39
  }
39
40
  const index = Math.max(0, SANDBOX_TARGETS.indexOf(target));
40
- return (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, minWidth: 0 }, children: [_jsx(Box, { sx: {
41
- width: 16,
42
- flexShrink: 0,
43
- display: 'flex',
44
- justifyContent: 'center',
45
- }, children: moving ? _jsx(Spinner, { size: "small" }) : null }), _jsx(KernelIndicator, { state: sandboxIndicatorState(snapshot.state, status?.execution_state, status?.is_executing), kernelId: snapshot.kernelId ?? status?.kernel_id, kernelName: status?.kernel_name, serverUrl: snapshot.jupyterUrl ?? status?.jupyter_url, websocketUrl: toWebsocketUrl(snapshot.jupyterUrl ?? status?.jupyter_url),
41
+ return (_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, minWidth: 0, children: [_jsx(Box, { width: 16, flexShrink: 0, display: "flex", justifyContent: "center", children: moving ? _jsx(Spinner, { size: "small" }) : null }), _jsx(KernelIndicator, { state: sandboxIndicatorState(snapshot.state, status?.execution_state, status?.is_executing), kernelId: snapshot.kernelId ?? status?.kernel_id, kernelName: status?.kernel_name, serverUrl: snapshot.jupyterUrl ?? status?.jupyter_url, websocketUrl: toWebsocketUrl(snapshot.jupyterUrl ?? status?.jupyter_url),
46
42
  /*
47
43
  The target that was chosen, not the variant last reported.
48
44
 
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Label, Text } from '@primer/react';
2
+ import { Box } from '@datalayer/primer-addons';
3
+ import { Label, Text } from '@primer/react';
3
4
  import { useSignalValue } from '@datalayer/reactor/react';
4
5
  import { useSandboxService } from './useSandboxService';
5
6
  function Row({ label, value }) {
6
- return (_jsxs(Box, { sx: { display: 'flex', gap: 3, py: 1, fontSize: 1 }, children: [_jsx(Text, { sx: { color: 'fg.muted', minWidth: '140px' }, children: label }), _jsx(Text, { sx: { fontFamily: 'mono', wordBreak: 'break-all' }, children: value || _jsx(Text, { sx: { color: 'fg.subtle' }, children: "\u2014" }) })] }));
7
+ return (_jsxs(Box, { display: "flex", gap: 3, py: 1, fontSize: 1, children: [_jsx(Text, { sx: { color: 'fg.muted', minWidth: '140px' }, children: label }), _jsx(Text, { sx: { fontFamily: 'mono', wordBreak: 'break-all' }, children: value || _jsx(Text, { sx: { color: 'fg.subtle' }, children: "\u2014" }) })] }));
7
8
  }
8
9
  export default function SandboxView(_props) {
9
10
  const service = useSandboxService();
@@ -15,29 +16,5 @@ export default function SandboxView(_props) {
15
16
  : snapshot.state === 'error'
16
17
  ? 'danger'
17
18
  : 'attention';
18
- return (_jsxs(Box, { sx: { height: '100%', overflowY: 'auto', px: 4, py: 3 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, mb: 3 }, children: [_jsx(Text, { sx: { fontSize: 3, fontWeight: 'bold' }, children: "Code Sandbox" }), _jsx(Label, { variant: tone, children: snapshot.state })] }), _jsx(Row, { label: "Variant", value: snapshot.variant }), _jsx(Row, { label: "Kernel", value: status?.kernel_name ?? snapshot.kernelId }), _jsx(Row, { label: "Kernel id", value: snapshot.kernelId }), _jsx(Row, { label: "Jupyter server", value: snapshot.jupyterUrl }), _jsx(Row, { label: "Execution state", value: status?.execution_state }), snapshot.state !== 'running' ? (_jsx(Box, { sx: {
19
- mt: 3,
20
- p: 3,
21
- bg: 'attention.subtle',
22
- borderRadius: 2,
23
- fontSize: 1,
24
- }, children: "No sandbox is attached. Views that need one \u2014 the notebook, the document \u2014 stay unavailable until there is a kernel to bind to." })) : null, lastExecution ? (_jsxs(Box, { sx: { mt: 4 }, children: [_jsx(Text, { sx: { fontSize: 2, fontWeight: 'bold' }, children: "Last execution" }), _jsx(Box, { sx: {
25
- mt: 2,
26
- p: 2,
27
- bg: 'canvas.subtle',
28
- borderRadius: 2,
29
- fontFamily: 'mono',
30
- fontSize: 0,
31
- whiteSpace: 'pre-wrap',
32
- }, children: lastExecution.code }), lastExecution.stdout ? (_jsx(Box, { sx: {
33
- mt: 1,
34
- fontFamily: 'mono',
35
- fontSize: 0,
36
- whiteSpace: 'pre-wrap',
37
- }, children: lastExecution.stdout })) : null, lastExecution.error ? (_jsx(Box, { sx: {
38
- mt: 1,
39
- color: 'danger.fg',
40
- fontFamily: 'mono',
41
- fontSize: 0,
42
- }, children: lastExecution.error })) : null] })) : null] }));
19
+ return (_jsxs(Box, { height: "100%", overflowY: "auto", px: 4, py: 3, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, mb: 3, children: [_jsx(Text, { sx: { fontSize: 3, fontWeight: 'bold' }, children: "Code Sandbox" }), _jsx(Label, { variant: tone, children: snapshot.state })] }), _jsx(Row, { label: "Variant", value: snapshot.variant }), _jsx(Row, { label: "Kernel", value: status?.kernel_name ?? snapshot.kernelId }), _jsx(Row, { label: "Kernel id", value: snapshot.kernelId }), _jsx(Row, { label: "Jupyter server", value: snapshot.jupyterUrl }), _jsx(Row, { label: "Execution state", value: status?.execution_state }), snapshot.state !== 'running' ? (_jsx(Box, { mt: 3, p: 3, bg: "attention.subtle", borderRadius: 2, fontSize: 1, children: "No sandbox is attached. Views that need one \u2014 the notebook, the document \u2014 stay unavailable until there is a kernel to bind to." })) : null, lastExecution ? (_jsxs(Box, { mt: 4, children: [_jsx(Text, { sx: { fontSize: 2, fontWeight: 'bold' }, children: "Last execution" }), _jsx(Box, { mt: 2, p: 2, bg: "canvas.subtle", borderRadius: 2, fontFamily: "mono", fontSize: 0, whiteSpace: "pre-wrap", children: lastExecution.code }), lastExecution.stdout ? (_jsx(Box, { mt: 1, fontFamily: "mono", fontSize: 0, whiteSpace: "pre-wrap", children: lastExecution.stdout })) : null, lastExecution.error ? (_jsx(Box, { mt: 1, color: "danger.fg", fontFamily: "mono", fontSize: 0, children: lastExecution.error })) : null] })) : null] }));
43
20
  }
@@ -42,10 +42,10 @@ function TeamMemberMenu({ selection, }) {
42
42
  // than an id pointing at an icon would.
43
43
  , { ...anchorProps, "aria-labelledby": undefined, icon: AiAgentIcon, "aria-label": active
44
44
  ? `Talking to ${active.name}. Choose another agent.`
45
- : 'Choose an agent', variant: "invisible", size: "small" })), children: _jsx(Box, { sx: { width: 440, maxWidth: 'calc(100vw - 16px)' }, children: _jsxs(ActionList, { selectionVariant: "single", role: "listbox", "aria-label": `Agents in ${selection.team.name}`, children: [_jsx(ActionList.GroupHeading, { children: selection.team.name }), selection.members.map(member => (_jsxs(ActionList.Item, { selected: member.id === selectedId, onSelect: () => {
45
+ : 'Choose an agent', variant: "invisible", size: "small" })), children: _jsx(Box, { width: 440, maxWidth: "calc(100vw - 16px)", children: _jsxs(ActionList, { selectionVariant: "single", role: "listbox", "aria-label": `Agents in ${selection.team.name}`, children: [_jsx(ActionList.GroupHeading, { children: selection.team.name }), selection.members.map(member => (_jsxs(ActionList.Item, { selected: member.id === selectedId, onSelect: () => {
46
46
  selection.select(member.id);
47
47
  setOpen(false);
48
- }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(MemberIcon, { icon: member.icon }) }), _jsxs(Box, { as: "span", sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [member.name, member.isSupervisor ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "supervisor" })) : null] }), member.description ? (_jsx(ActionList.Description, { variant: "block",
48
+ }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(MemberIcon, { icon: member.icon }) }), _jsxs(Box, { as: "span", display: "flex", alignItems: "center", gap: 2, children: [member.name, member.isSupervisor ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "supervisor" })) : null] }), member.description ? (_jsx(ActionList.Description, { variant: "block",
49
49
  /* Wrapped and breakable. `minWidth: 0` is the load-bearing
50
50
  one: a grid item defaults to `min-content`, so without it
51
51
  the description refuses to be narrower than its longest
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useState } from 'react';
3
- import { ActionList, ActionMenu, Box, Spinner } from '@primer/react';
3
+ import { Box } from '@datalayer/primer-addons';
4
+ import { ActionList, ActionMenu, Spinner } from '@primer/react';
4
5
  import { getAgentspecs } from '../../../specs/agents';
5
6
  export function AgentspecPicker({ workspace, }) {
6
7
  const [agents, setAgents] = useState([]);
@@ -85,6 +86,6 @@ export function AgentspecPicker({ workspace, }) {
85
86
  */
86
87
  return null;
87
88
  }
88
- return (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [switching ? _jsx(Spinner, { size: "small" }) : null, _jsxs(ActionMenu, { children: [_jsxs(ActionMenu.Button, { variant: "invisible", size: "small", children: [current?.emoji ? `${current.emoji} ` : '', label] }), _jsx(ActionMenu.Overlay, { width: "medium", children: _jsx(ActionList, { selectionVariant: "single", children: agents.map(agent => (_jsxs(ActionList.Item, { selected: agent.id === active, onSelect: () => void choose(agent.id), children: [agent.emoji ? `${agent.emoji} ` : '', agent.name || agent.id, agent.description ? (_jsx(ActionList.Description, { variant: "block", children: agent.description })) : null] }, agent.id))) }) })] })] }));
89
+ return (_jsxs(Box, { display: "flex", alignItems: "center", gap: 1, children: [switching ? _jsx(Spinner, { size: "small" }) : null, _jsxs(ActionMenu, { children: [_jsxs(ActionMenu.Button, { variant: "invisible", size: "small", children: [current?.emoji ? `${current.emoji} ` : '', label] }), _jsx(ActionMenu.Overlay, { width: "medium", children: _jsx(ActionList, { selectionVariant: "single", children: agents.map(agent => (_jsxs(ActionList.Item, { selected: agent.id === active, onSelect: () => void choose(agent.id), children: [agent.emoji ? `${agent.emoji} ` : '', agent.name || agent.id, agent.description ? (_jsx(ActionList.Description, { variant: "block", children: agent.description })) : null] }, agent.id))) }) })] })] }));
89
90
  }
90
91
  export default AgentspecPicker;
@@ -40,7 +40,7 @@ function useRead(load, key, when = true) {
40
40
  function Session({ context, session, }) {
41
41
  const [open, setOpen] = useState(false);
42
42
  const entries = useRead(() => readSession(context, session.sessionUid), `${session.sessionUid}:${context.token}`, open);
43
- return (_jsxs(Box, { as: "details", onToggle: (event) => setOpen(event.currentTarget.open), sx: { borderTop: '1px solid', borderColor: 'border.muted', py: 2 }, children: [_jsxs(Box, { as: "summary", sx: { cursor: 'pointer', fontSize: 1 }, children: [session.createdAt ? (_jsx(RelativeTime, { datetime: session.createdAt })) : ('A session'), session.version ? ` · version ${session.version}` : '', entries.value ? ` · ${sessionSentence(entries.value)}` : ''] }), !open ? null : entries.loading ? (_jsx(Spinner, { size: "small" })) : entries.error ? (_jsx(Text, { role: "alert", sx: { fontSize: 1 }, children: entries.error })) : (_jsx(Box, { as: "ol", sx: { pl: 3, mt: 2, mb: 0, fontSize: 1 }, children: (entries.value ?? []).map(entry => (_jsxs(Box, { as: "li", sx: { mb: 1 }, children: [_jsx(Label, { sx: { mr: 2 }, children: RECORD_KIND_WORDS[entry.kind] ?? entry.kind }), entry.summary] }, entry.uid))) }))] }));
43
+ return (_jsxs(Box, { as: "details", onToggle: (event) => setOpen(event.currentTarget.open), borderTop: "1px solid", borderColor: "border.muted", py: 2, children: [_jsxs(Box, { as: "summary", cursor: "pointer", fontSize: 1, children: [session.createdAt ? (_jsx(RelativeTime, { datetime: session.createdAt })) : ('A session'), session.version ? ` · version ${session.version}` : '', entries.value ? ` · ${sessionSentence(entries.value)}` : ''] }), !open ? null : entries.loading ? (_jsx(Spinner, { size: "small" })) : entries.error ? (_jsx(Text, { role: "alert", sx: { fontSize: 1 }, children: entries.error })) : (_jsx(Box, { as: "ol", pl: 3, mt: 2, mb: 0, fontSize: 1, children: (entries.value ?? []).map(entry => (_jsxs(Box, { as: "li", mb: 1, children: [_jsx(Label, { sx: { mr: 2 }, children: RECORD_KIND_WORDS[entry.kind] ?? entry.kind }), entry.summary] }, entry.uid))) }))] }));
44
44
  }
45
45
  export function AppActivity({ appUid }) {
46
46
  const token = useIAMStore(state => state.token) ?? '';
@@ -5,7 +5,8 @@ import { Box } from '@datalayer/primer-addons';
5
5
  import { signal } from '@datalayer/reactor';
6
6
  import { useContributions, useSignalValue } from '@datalayer/reactor/react';
7
7
  import { useIAMStore } from '@datalayer/core/lib/state/substates/IAMState';
8
- import { LoopChatTurn, } from '../../core';
8
+ import { useChatWords } from '../../../chat/ChatLanguage';
9
+ import { LoopChatTurn, agentServerOf, noRuntimeSaid, IDLE_SANDBOX_SNAPSHOT, } from '../../core';
9
10
  import { COMPUTER_PARTS, COMPUTER_WORDS, computerParts, downloadComputerFile, handBackComputer, hasComputer, listComputerFiles, parentOf, partsOff, readComputer, runOnComputer, takeOverComputer, terminalEntries, } from '../../apps/computer';
10
11
  const NO_TURN = signal({ id: 0, status: 'idle' });
11
12
  const NO_CONVERSATION = signal([]);
@@ -20,19 +21,11 @@ const MONO = {
20
21
  function Parts({ app }) {
21
22
  const parts = computerParts(app);
22
23
  const off = partsOff(parts);
23
- return (_jsxs(Box, { "data-testid": "computer-parts", children: [_jsx(Box, { sx: { display: 'flex', gap: 1, flexWrap: 'wrap', mb: 1 }, children: COMPUTER_PARTS.map(part => (_jsxs(Label, { variant: parts[part] ? 'success' : 'secondary', "data-part": part, "data-on": parts[part] ? 'true' : 'false', children: [COMPUTER_WORDS.part[part], ":", ' ', parts[part] ? COMPUTER_WORDS.on : COMPUTER_WORDS.off] }, part))) }), off.length > 0 && off.length < COMPUTER_PARTS.length ? (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0 }, children: COMPUTER_WORDS.offSaid(off) })) : null] }));
24
+ return (_jsxs(Box, { "data-testid": "computer-parts", children: [_jsx(Box, { display: "flex", gap: 1, flexWrap: "wrap", mb: 1, children: COMPUTER_PARTS.map(part => (_jsxs(Label, { variant: parts[part] ? 'success' : 'secondary', "data-part": part, "data-on": parts[part] ? 'true' : 'false', children: [COMPUTER_WORDS.part[part], ":", ' ', parts[part] ? COMPUTER_WORDS.on : COMPUTER_WORDS.off] }, part))) }), off.length > 0 && off.length < COMPUTER_PARTS.length ? (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0 }, children: COMPUTER_WORDS.offSaid(off) })) : null] }));
24
25
  }
25
26
  function Terminal({ conversation, }) {
26
27
  const entries = terminalEntries(conversation);
27
- return (_jsxs(Box, { "data-testid": "computer-terminal", children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 1 }, children: COMPUTER_WORDS.terminal }), entries.length === 0 ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: COMPUTER_WORDS.nothingRan })) : (_jsx(Box, { sx: {
28
- bg: 'canvas.inset',
29
- border: '1px solid',
30
- borderColor: 'border.muted',
31
- borderRadius: 2,
32
- p: 2,
33
- maxHeight: 320,
34
- overflow: 'auto',
35
- }, children: entries.map((entry, index) => (_jsxs(Box, { sx: { mb: 2 }, children: [_jsx(Text, { as: "p", sx: { ...MONO, fontWeight: 'semibold' }, children: `$ ${entry.tool}${entry.command ? `: ${entry.command}` : ''}` }), entry.output === undefined ? (_jsx(Spinner, { size: "small" })) : (_jsx(Text, { as: "pre", sx: { ...MONO, color: 'fg.muted' }, children: entry.output }))] }, index))) }))] }));
28
+ return (_jsxs(Box, { "data-testid": "computer-terminal", children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 1 }, children: COMPUTER_WORDS.terminal }), entries.length === 0 ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: COMPUTER_WORDS.nothingRan })) : (_jsx(Box, { bg: "canvas.inset", border: "1px solid", borderColor: "border.muted", borderRadius: 2, p: 2, maxHeight: 320, overflow: "auto", children: entries.map((entry, index) => (_jsxs(Box, { mb: 2, children: [_jsx(Text, { as: "p", sx: { ...MONO, fontWeight: 'semibold' }, children: `$ ${entry.tool}${entry.command ? `: ${entry.command}` : ''}` }), entry.output === undefined ? (_jsx(Spinner, { size: "small" })) : (_jsx(Text, { as: "pre", sx: { ...MONO, color: 'fg.muted' }, children: entry.output }))] }, index))) }))] }));
36
29
  }
37
30
  function save(blob, name) {
38
31
  const url = URL.createObjectURL(blob);
@@ -70,15 +63,7 @@ function Files({ context, state, ended, }) {
70
63
  state.started,
71
64
  ended,
72
65
  ]);
73
- return (_jsxs(Box, { "data-testid": "computer-files", children: [_jsxs(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 1 }, children: [COMPUTER_WORDS.files, path !== '.' ? (_jsx(Text, { sx: { fontWeight: 'normal', color: 'fg.muted' }, children: ` · ${path}` })) : null] }), !state.started ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: COMPUTER_WORDS.notStarted })) : error ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'danger.fg', m: 0 }, children: error })) : !entries ? (_jsx(Spinner, { size: "small" })) : (_jsxs(Box, { as: "ul", sx: { listStyle: 'none', p: 0, m: 0 }, children: [path !== '.' ? (_jsx(Box, { as: "li", children: _jsx(Button, { size: "small", variant: "invisible", onClick: () => setPath(parentOf(path)), children: COMPUTER_WORDS.up }) })) : null, entries.length === 0 ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: COMPUTER_WORDS.emptyDirectory })) : null, entries.map(entry => (_jsx(Box, { as: "li", "data-path": entry.path, sx: {
74
- display: 'flex',
75
- alignItems: 'center',
76
- gap: 2,
77
- fontSize: 1,
78
- py: 1,
79
- borderTop: '1px solid',
80
- borderColor: 'border.muted',
81
- }, children: entry.type === 'directory' ? (_jsx(Button, { size: "small", variant: "invisible", onClick: () => setPath(entry.path), children: `${entry.name}/` })) : (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { flex: 1, fontFamily: MONO_FACE, fontSize: 0 }, children: entry.name }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: `${entry.size.toLocaleString('en')} bytes` }), _jsx(Button, { size: "small", "aria-label": `${COMPUTER_WORDS.download} ${entry.name}`, onClick: () => {
66
+ return (_jsxs(Box, { "data-testid": "computer-files", children: [_jsxs(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 1 }, children: [COMPUTER_WORDS.files, path !== '.' ? (_jsx(Text, { sx: { fontWeight: 'normal', color: 'fg.muted' }, children: ` · ${path}` })) : null] }), !state.started ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: COMPUTER_WORDS.notStarted })) : error ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'danger.fg', m: 0 }, children: error })) : !entries ? (_jsx(Spinner, { size: "small" })) : (_jsxs(Box, { as: "ul", listStyle: "none", p: 0, m: 0, children: [path !== '.' ? (_jsx(Box, { as: "li", children: _jsx(Button, { size: "small", variant: "invisible", onClick: () => setPath(parentOf(path)), children: COMPUTER_WORDS.up }) })) : null, entries.length === 0 ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: COMPUTER_WORDS.emptyDirectory })) : null, entries.map(entry => (_jsx(Box, { as: "li", "data-path": entry.path, display: "flex", alignItems: "center", gap: 2, fontSize: 1, py: 1, borderTop: "1px solid", borderColor: "border.muted", children: entry.type === 'directory' ? (_jsx(Button, { size: "small", variant: "invisible", onClick: () => setPath(entry.path), children: `${entry.name}/` })) : (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { flex: 1, fontFamily: MONO_FACE, fontSize: 0 }, children: entry.name }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: `${entry.size.toLocaleString('en')} bytes` }), _jsx(Button, { size: "small", "aria-label": `${COMPUTER_WORDS.download} ${entry.name}`, onClick: () => {
82
67
  downloadComputerFile(context, entry.path).then(blob => save(blob, entry.name), (failed) => setError(failed instanceof Error
83
68
  ? failed.message
84
69
  : String(failed)));
@@ -109,7 +94,7 @@ function YourTerminal({ context }) {
109
94
  setRunning(false);
110
95
  });
111
96
  };
112
- return (_jsxs(Box, { "data-testid": "computer-your-terminal", sx: { mt: 2 }, children: [outputs.map((each, index) => (_jsxs(Box, { sx: { mb: 2 }, children: [_jsx(Text, { as: "p", sx: { ...MONO, fontWeight: 'semibold' }, children: `>>> ${each.code}` }), _jsx(Text, { as: "pre", sx: { ...MONO, color: 'fg.muted' }, children: [each.output.stdout, each.output.stderr, each.output.error]
97
+ return (_jsxs(Box, { "data-testid": "computer-your-terminal", mt: 2, children: [outputs.map((each, index) => (_jsxs(Box, { mb: 2, children: [_jsx(Text, { as: "p", sx: { ...MONO, fontWeight: 'semibold' }, children: `>>> ${each.code}` }), _jsx(Text, { as: "pre", sx: { ...MONO, color: 'fg.muted' }, children: [each.output.stdout, each.output.stderr, each.output.error]
113
98
  .filter(Boolean)
114
99
  .join('\n') })] }, index))), _jsx(Textarea, { "aria-label": COMPUTER_WORDS.yourCode, placeholder: COMPUTER_WORDS.yourCode, value: code, onChange: event => setCode(event.target.value), sx: { width: '100%', fontFamily: MONO_FACE }, rows: 3 }), _jsx(Button, { size: "small", sx: { mt: 1 }, disabled: running || !code.trim(), onClick: run, children: COMPUTER_WORDS.run })] }));
115
100
  }
@@ -125,9 +110,23 @@ export function AppComputer({ app, workspace, }) {
125
110
  setEnded(turn.id);
126
111
  }
127
112
  }, [settled, turn.id]);
128
- const serverUrl = workspace?.sandbox.agentBaseUrl || workspace?.serverUrl || '';
113
+ const chatText = useChatWords();
114
+ /*
115
+ * The runtime its agent is on, or nothing while none is assigned: then the
116
+ * computer is asked nothing and says why, rather than asking the host's
117
+ * server (on a hosted page, the page's own origin). Outside a workspace
118
+ * there is none either.
119
+ */
120
+ const serverUrl = workspace
121
+ ? agentServerOf(workspace.sandbox, workspace.serverUrl)
122
+ : undefined;
123
+ const noRuntime = serverUrl === undefined
124
+ ? noRuntimeSaid(workspace?.sandbox ?? IDLE_SANDBOX_SNAPSHOT, chatText)
125
+ : undefined;
129
126
  const agentId = workspace?.agentId || app.id;
130
- const context = useMemo(() => ({ serverUrl, agentId, token: token || undefined }), [serverUrl, agentId, token]);
127
+ const context = useMemo(() => serverUrl === undefined
128
+ ? undefined
129
+ : { serverUrl, agentId, token: token || undefined }, [serverUrl, agentId, token]);
131
130
  const parts = computerParts(app);
132
131
  const [state, setState] = useState();
133
132
  const [error, setError] = useState('');
@@ -141,6 +140,9 @@ export function AppComputer({ app, workspace, }) {
141
140
  };
142
141
  }, []);
143
142
  const read = useCallback(() => {
143
+ if (!context) {
144
+ return;
145
+ }
144
146
  readComputer(context).then(found => {
145
147
  if (alive.current) {
146
148
  setState(found);
@@ -151,11 +153,14 @@ export function AppComputer({ app, workspace, }) {
151
153
  }, [context]);
152
154
  const used = hasComputer(parts);
153
155
  useEffect(() => {
154
- if (used && serverUrl) {
156
+ if (used && context) {
155
157
  read();
156
158
  }
157
- }, [used, serverUrl, read, ended]);
159
+ }, [used, context, read, ended]);
158
160
  const act = (step) => {
161
+ if (!context) {
162
+ return;
163
+ }
159
164
  setBusy(true);
160
165
  step(context).then(() => {
161
166
  if (alive.current) {
@@ -170,10 +175,10 @@ export function AppComputer({ app, workspace, }) {
170
175
  });
171
176
  };
172
177
  const me = Boolean(state?.yours);
173
- return (_jsxs(Box, { "data-testid": "app-computer", children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 2 }, children: COMPUTER_WORDS.title }), _jsx(Parts, { app: app }), !used ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0, mt: 1 }, children: COMPUTER_WORDS.none })) : (_jsxs(_Fragment, { children: [_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0, mt: 1 }, children: COMPUTER_WORDS.noBrowser }), error ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'danger.fg', m: 0, mt: 2 }, children: error })) : null, state ? (_jsxs(Box, { sx: { mt: 2 }, "data-testid": "computer-holder", children: [_jsx(Text, { as: "p", sx: { fontSize: 1, m: 0, mb: 1 }, children: !state.held
178
+ return (_jsxs(Box, { "data-testid": "app-computer", children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 2 }, children: COMPUTER_WORDS.title }), _jsx(Parts, { app: app }), !used ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0, mt: 1 }, children: COMPUTER_WORDS.none })) : (_jsxs(_Fragment, { children: [_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0, mt: 1 }, children: COMPUTER_WORDS.noBrowser }), noRuntime ? (_jsx(Text, { as: "p", role: "status", sx: { fontSize: 1, color: 'fg.default', m: 0, mt: 2 }, children: noRuntime })) : null, error ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'danger.fg', m: 0, mt: 2 }, children: error })) : null, state && context ? (_jsxs(Box, { mt: 2, "data-testid": "computer-holder", children: [_jsx(Text, { as: "p", sx: { fontSize: 1, m: 0, mb: 1 }, children: !state.held
174
179
  ? COMPUTER_WORDS.agentHas
175
180
  : me
176
181
  ? COMPUTER_WORDS.youHave
177
- : COMPUTER_WORDS.someoneHas }), !state.held ? (_jsx(Button, { size: "small", disabled: busy, onClick: () => act(takeOverComputer), children: COMPUTER_WORDS.takeOver })) : me ? (_jsxs(_Fragment, { children: [_jsx(Button, { size: "small", variant: "primary", disabled: busy, onClick: () => act(handBackComputer), children: COMPUTER_WORDS.handBack }), _jsx(YourTerminal, { context: context })] })) : null] })) : null, _jsx(Terminal, { conversation: conversation }), state ? (_jsx(Files, { context: context, state: state, ended: ended })) : null] }))] }));
182
+ : COMPUTER_WORDS.someoneHas }), !state.held ? (_jsx(Button, { size: "small", disabled: busy, onClick: () => act(takeOverComputer), children: COMPUTER_WORDS.takeOver })) : me ? (_jsxs(_Fragment, { children: [_jsx(Button, { size: "small", variant: "primary", disabled: busy, onClick: () => act(handBackComputer), children: COMPUTER_WORDS.handBack }), _jsx(YourTerminal, { context: context })] })) : null] })) : null, _jsx(Terminal, { conversation: conversation }), state && context ? (_jsx(Files, { context: context, state: state, ended: ended })) : null] }))] }));
178
183
  }
179
184
  export default AppComputer;
@@ -36,13 +36,7 @@ function ElementBody({ element, send, }) {
36
36
  , { messages: parsed.messages, onAction: onAction, catalog: catalog }, JSON.stringify(element.shows)));
37
37
  }
38
38
  function ElementHeader({ element }) {
39
- return (_jsxs(Box, { sx: {
40
- display: 'flex',
41
- alignItems: 'center',
42
- justifyContent: 'space-between',
43
- gap: 2,
44
- mb: 2,
45
- }, children: [_jsx(Text, { as: "h2", sx: { fontSize: 2, fontWeight: 'bold', m: 0 }, children: element.title }), _jsx(IconButton, { icon: XIcon, variant: "invisible", size: "small", "aria-label": `Close ${element.title}`, onClick: () => closeLoopElement(element.id) })] }));
39
+ return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 2, mb: 2, children: [_jsx(Text, { as: "h2", sx: { fontSize: 2, fontWeight: 'bold', m: 0 }, children: element.title }), _jsx(IconButton, { icon: XIcon, variant: "invisible", size: "small", "aria-label": `Close ${element.title}`, onClick: () => closeLoopElement(element.id) })] }));
46
40
  }
47
41
  /** Every element open in a side panel or on a page, drawn where it belongs. */
48
42
  export function AppElements({ workspace, }) {
@@ -51,29 +45,6 @@ export function AppElements({ workspace, }) {
51
45
  const send = workspace?.viewControls.send;
52
46
  if (panels.length === 0 && page === null)
53
47
  return null;
54
- return (_jsxs(_Fragment, { children: [panels.length > 0 ? (_jsx(Box, { as: "aside", "aria-label": "Side panel", sx: {
55
- position: 'absolute',
56
- top: 0,
57
- right: 0,
58
- bottom: 0,
59
- width: `min(${APP_PANEL_WIDTH}px, 100%)`,
60
- zIndex: 1,
61
- overflowY: 'auto',
62
- borderLeft: '1px solid',
63
- borderColor: 'border.default',
64
- bg: 'canvas.subtle',
65
- boxShadow: 'shadow.medium',
66
- p: 3,
67
- display: 'flex',
68
- flexDirection: 'column',
69
- gap: 3,
70
- }, children: panels.map(element => (_jsxs(Box, { as: "section", "aria-label": element.title, children: [_jsx(ElementHeader, { element: element }), _jsx(ElementBody, { element: element, send: send })] }, element.id))) })) : null, page !== null ? (_jsxs(Box, { role: "dialog", "aria-label": page.title, sx: {
71
- position: 'absolute',
72
- inset: 0,
73
- zIndex: 2,
74
- overflowY: 'auto',
75
- bg: 'canvas.default',
76
- p: [3, 4],
77
- }, children: [_jsx(ElementHeader, { element: page }), _jsx(ElementBody, { element: page, send: send })] })) : null] }));
48
+ return (_jsxs(_Fragment, { children: [panels.length > 0 ? (_jsx(Box, { as: "aside", "aria-label": "Side panel", position: "absolute", top: 0, right: 0, bottom: 0, width: `min(${APP_PANEL_WIDTH}px, 100%)`, zIndex: 1, overflowY: "auto", borderLeft: "1px solid", borderColor: "border.default", bg: "canvas.subtle", boxShadow: "shadow.medium", p: 3, display: "flex", flexDirection: "column", gap: 3, children: panels.map(element => (_jsxs(Box, { as: "section", "aria-label": element.title, children: [_jsx(ElementHeader, { element: element }), _jsx(ElementBody, { element: element, send: send })] }, element.id))) })) : null, page !== null ? (_jsxs(Box, { role: "dialog", "aria-label": page.title, position: "absolute", inset: 0, zIndex: 2, overflowY: "auto", bg: "canvas.default", p: [3, 4], children: [_jsx(ElementHeader, { element: page }), _jsx(ElementBody, { element: page, send: send })] })) : null] }));
78
49
  }
79
50
  export default AppElements;
@@ -5,7 +5,8 @@ import { Box } from '@datalayer/primer-addons';
5
5
  import { signal } from '@datalayer/reactor';
6
6
  import { useContributions, useSignalValue } from '@datalayer/reactor/react';
7
7
  import { catalogOfBlocks } from '../../../components/a2ui';
8
- import { LoopCanvasBlock, LoopChatTurn, } from '../../core';
8
+ import { useChatWords } from '../../../chat/ChatLanguage';
9
+ import { LoopCanvasBlock, LoopChatTurn, agentServerOf, noRuntimeSaid, } from '../../core';
9
10
  import { InlineSurface, SURFACE_CATALOG_ID, } from '../a2ui-surface/InlineSurface';
10
11
  import { useOwnComponents } from '../a2ui-surface/ownComponents';
11
12
  import { appPageAction, appPageData, appPageMessages, pageInputsOf, pageOf, } from './appPageModel';
@@ -73,12 +74,25 @@ export function AppPage({ app, workspace }) {
73
74
  ...outputsData(outputs),
74
75
  }), [app, turn, conversation, outputs]);
75
76
  const token = useIAMStore(state => state.token) ?? '';
76
- const pageContext = useRef({ serverUrl: '', agentId: '' });
77
- pageContext.current = {
78
- serverUrl: workspace.sandbox?.agentBaseUrl || workspace.serverUrl || '',
79
- agentId: workspace.agentId || app.id,
80
- token: token || undefined,
81
- };
77
+ const chatText = useChatWords();
78
+ /*
79
+ * Where its page runs: the runtime its agent is on, or nothing while none
80
+ * is assigned — then a run is not sent and the page says why, rather than
81
+ * reaching for the host's server (on a hosted page, the page's own origin).
82
+ */
83
+ const pageServer = agentServerOf(workspace.sandbox, workspace.serverUrl);
84
+ const pageContext = useRef(null);
85
+ pageContext.current =
86
+ pageServer === undefined
87
+ ? null
88
+ : {
89
+ serverUrl: pageServer,
90
+ agentId: workspace.agentId || app.id,
91
+ token: token || undefined,
92
+ };
93
+ const noRuntime = useRef('');
94
+ noRuntime.current =
95
+ pageServer === undefined ? noRuntimeSaid(workspace.sandbox, chatText) : '';
82
96
  const pageSession = useRef(null);
83
97
  const pageRunning = useRef(false);
84
98
  const pageNext = useRef(null);
@@ -96,6 +110,10 @@ export function AppPage({ app, workspace }) {
96
110
  const next = pageNext.current;
97
111
  pageNext.current = null;
98
112
  const context = pageContext.current;
113
+ if (!context) {
114
+ setRefusal(noRuntime.current);
115
+ continue;
116
+ }
99
117
  try {
100
118
  pageSession.current ??= openPageSession(context);
101
119
  const turn = await runPageIn(context, await pageSession.current, next);
@@ -201,7 +219,7 @@ export function AppPage({ app, workspace }) {
201
219
  }
202
220
  }
203
221
  }, [app, runPage]);
204
- return (_jsx(Box, { "data-testid": "app-page", sx: { height: '100%', minHeight: 0, overflow: 'auto', p: 3 }, children: 'problem' in drawing ? (_jsx(Box, { role: "status", sx: { color: 'fg.muted', fontSize: 1 }, children: drawing.problem })) : (_jsx(InlineSurface
222
+ return (_jsx(Box, { "data-testid": "app-page", height: "100%", minHeight: 0, overflow: "auto", p: 3, children: 'problem' in drawing ? (_jsx(Box, { role: "status", color: "fg.muted", fontSize: 1, children: drawing.problem })) : (_jsx(InlineSurface
205
223
  // A new list of blocks is a new surface, drawn with it.
206
224
  , { messages: messages, data: data, onAction: onAction, validationError: refusal, catalog: drawing.catalog, ...(page ? { onSurface } : {}) }, drawnWith)) }));
207
225
  }