@datalayer/agent-runtimes 1.0.19 → 1.0.21

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 (141) hide show
  1. package/README.md +11 -3
  2. package/lib/AgentNode.js +141 -40
  3. package/lib/App.js +2 -2
  4. package/lib/agent-node/AgentNodeGallery.js +188 -25
  5. package/lib/api/runtimes/runtimes.d.ts +57 -0
  6. package/lib/api/runtimes/runtimes.js +55 -1
  7. package/lib/chat/Chat.d.ts +1 -1
  8. package/lib/chat/Chat.js +2 -2
  9. package/lib/chat/base/ChatBase.js +50 -13
  10. package/lib/chat/document/EphemeralDocument.d.ts +5 -1
  11. package/lib/chat/document/EphemeralDocument.js +7 -5
  12. package/lib/chat/header/ChatHeaderBase.d.ts +3 -1
  13. package/lib/chat/header/ChatHeaderBase.js +5 -3
  14. package/lib/chat/messages/ChatMessageList.d.ts +19 -0
  15. package/lib/chat/messages/ChatMessageList.js +180 -9
  16. package/lib/chat/notebook/EphemeralNotebook.d.ts +5 -1
  17. package/lib/chat/notebook/EphemeralNotebook.js +9 -6
  18. package/lib/chat/prompt/InputPrompt.js +1 -1
  19. package/lib/chat/tools/SubagentActivity.d.ts +20 -0
  20. package/lib/chat/tools/SubagentActivity.js +154 -0
  21. package/lib/chat/tools/index.d.ts +1 -0
  22. package/lib/chat/tools/index.js +1 -0
  23. package/lib/chat/usage/TokenUsageBar.js +3 -3
  24. package/lib/client/AgentRuntimesClient.d.ts +5 -0
  25. package/lib/client/AgentsMixin.js +3 -1
  26. package/lib/client/mixins/RuntimesMixin.d.ts +12 -0
  27. package/lib/client/mixins/RuntimesMixin.js +24 -1
  28. package/lib/components/AgentSummary.d.ts +1 -0
  29. package/lib/components/AgentSummary.js +66 -10
  30. package/lib/components/runtimes/RuntimePickerBase.js +64 -29
  31. package/lib/components/runtimes/RuntimePickerCell.js +1 -1
  32. package/lib/components/runtimes/RuntimePickerNotebook.js +1 -1
  33. package/lib/components/runtimes/RuntimeSimplePicker.js +3 -2
  34. package/lib/components/runtimes/RuntimeUtils.js +2 -2
  35. package/lib/context/GraphFlowChart.js +1 -1
  36. package/lib/context/TokenUsageChart.js +1 -1
  37. package/lib/context/TurnGraphChart.js +2 -2
  38. package/lib/examples/A2UiAgentExample.js +4 -4
  39. package/lib/examples/A2UiComponentsGalleryExample.js +3 -3
  40. package/lib/examples/A2UiContactCardExample.js +3 -3
  41. package/lib/examples/A2UiRestaurantExample.js +2 -2
  42. package/lib/examples/A2UiViewerExample.js +3 -3
  43. package/lib/examples/AgUiAgenticExample.js +3 -3
  44. package/lib/examples/AgUiBackendToolRenderingExample.js +2 -2
  45. package/lib/examples/AgUiHaikuGenUiExample.js +3 -3
  46. package/lib/examples/AgUiHumanInTheLoopExample.js +5 -5
  47. package/lib/examples/AgUiSharedStateExample.js +3 -3
  48. package/lib/examples/AgUiToolsBasedGenUiExample.js +7 -5
  49. package/lib/examples/AgentCheckpointsExample.js +6 -5
  50. package/lib/examples/{AgentSandboxExample.d.ts → AgentCodeSandboxExample.d.ts} +4 -4
  51. package/lib/examples/{AgentSandboxExample.js → AgentCodeSandboxExample.js} +10 -9
  52. package/lib/examples/AgentCodeSandboxesExample.js +2 -2
  53. package/lib/examples/AgentCodemodeExample.js +3 -3
  54. package/lib/examples/AgentCompactionExample.d.ts +12 -0
  55. package/lib/examples/AgentCompactionExample.js +244 -0
  56. package/lib/examples/AgentEvalsExample.js +10 -20
  57. package/lib/examples/AgentGuardrailsExample.js +2 -2
  58. package/lib/examples/AgentHooksExample.js +1 -1
  59. package/lib/examples/AgentInferenceProviderExample.js +6 -8
  60. package/lib/examples/AgentLoopExample.js +3 -3
  61. package/lib/examples/AgentMCPExample.js +3 -3
  62. package/lib/examples/AgentMemoryExample.d.ts +4 -2
  63. package/lib/examples/AgentMemoryExample.js +93 -10
  64. package/lib/examples/AgentMonitoringExample.js +2 -2
  65. package/lib/examples/AgentNotificationsExample.js +1 -1
  66. package/lib/examples/AgentOtelExample.d.ts +2 -3
  67. package/lib/examples/AgentOtelExample.js +7 -12
  68. package/lib/examples/AgentOutputsExample.js +5 -5
  69. package/lib/examples/AgentParametersExample.js +3 -3
  70. package/lib/examples/AgentSkillsExample.js +4 -4
  71. package/lib/examples/AgentSubagentsExample.d.ts +2 -2
  72. package/lib/examples/AgentSubagentsExample.js +43 -34
  73. package/lib/examples/AgentToolApprovalsExample.js +1 -1
  74. package/lib/examples/AgentTriggersExample.js +9 -11
  75. package/lib/examples/AgentspecsExample.js +13 -5
  76. package/lib/examples/CellExample.d.ts +6 -0
  77. package/lib/examples/CellExample.js +84 -0
  78. package/lib/examples/ChatCustomExample.js +1 -1
  79. package/lib/examples/ChatExample.js +1 -1
  80. package/lib/examples/ChatStandaloneExample.js +2 -2
  81. package/lib/examples/HomeExample.js +29 -28
  82. package/lib/examples/Lexical2Example.js +2 -2
  83. package/lib/examples/LexicalAgentExample.js +1 -1
  84. package/lib/examples/LexicalAgentSidebarExample.js +2 -2
  85. package/lib/examples/NotebookAgentSidebarExample.d.ts +1 -1
  86. package/lib/examples/NotebookAgentSidebarExample.js +5 -4
  87. package/lib/examples/NotebookCollaborationExample.js +3 -3
  88. package/lib/examples/NotebookExample.js +2 -2
  89. package/lib/examples/components/ExampleWrapper.js +5 -0
  90. package/lib/examples/components/Header.js +2 -2
  91. package/lib/examples/components/HeaderControls.js +1 -1
  92. package/lib/examples/components/LexicalEditor.js +1 -1
  93. package/lib/examples/components/MainContent.js +1 -1
  94. package/lib/examples/components/MockFileBrowser.js +8 -8
  95. package/lib/examples/components/TimeTravel.js +1 -1
  96. package/lib/examples/components/haiku/HaikuDisplay.js +2 -2
  97. package/lib/examples/example-selector.js +4 -3
  98. package/lib/examples/hooks/useExampleAgentRuntime.js +11 -1
  99. package/lib/examples/hooks/useExampleAgentRuntimes.d.ts +7 -0
  100. package/lib/examples/hooks/useExampleAgentRuntimes.js +82 -0
  101. package/lib/examples/index.d.ts +2 -1
  102. package/lib/examples/index.js +2 -1
  103. package/lib/examples/main.js +359 -46
  104. package/lib/examples/utils/agentSummaryStore.d.ts +17 -0
  105. package/lib/examples/utils/themedProvider.js +32 -2
  106. package/lib/examples/utils/useExampleAgentRuntimesUrl.js +2 -1
  107. package/lib/examples2/NotebookMutationsKernel.js +6 -3
  108. package/lib/examples2/NotebookMutationsServiceManager.js +6 -3
  109. package/lib/examples2/ReactRouterExample.js +5 -5
  110. package/lib/examples2/main.js +38 -0
  111. package/lib/hooks/useAIAgentsWebSocket.js +35 -0
  112. package/lib/hooks/useAgentRuntimes.d.ts +22 -0
  113. package/lib/hooks/useAgentRuntimes.js +192 -5
  114. package/lib/hooks/useCheckpoints.js +2 -2
  115. package/lib/hooks/useToolApprovals.d.ts +10 -4
  116. package/lib/hooks/useToolApprovals.js +11 -5
  117. package/lib/models/Runtime.d.ts +10 -8
  118. package/lib/models/RuntimeDTO.d.ts +5 -3
  119. package/lib/models/RuntimeDTO.js +3 -3
  120. package/lib/runtimes/actions.js +3 -1
  121. package/lib/runtimes/apis.d.ts +7 -0
  122. package/lib/specs/agents/agents.d.ts +51 -0
  123. package/lib/specs/agents/agents.js +3389 -94
  124. package/lib/specs/models.d.ts +2 -0
  125. package/lib/specs/models.js +37 -0
  126. package/lib/stores/agentRuntimeStore.d.ts +32 -1
  127. package/lib/stores/agentRuntimeStore.js +65 -1
  128. package/lib/stores/index.d.ts +1 -1
  129. package/lib/stores/index.js +1 -1
  130. package/lib/types/agents.d.ts +9 -3
  131. package/lib/types/agentspecs.d.ts +2 -0
  132. package/lib/types/chat.d.ts +12 -0
  133. package/lib/types/models.d.ts +5 -0
  134. package/lib/types/stream.d.ts +58 -1
  135. package/package.json +4 -4
  136. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
  137. package/scripts/codegen/generate_agents.py +7 -0
  138. package/scripts/codegen/generate_models.py +22 -9
  139. package/scripts/gen_workers.py +340 -0
  140. package/lib/examples/CellSimpleExample.d.ts +0 -6
  141. package/lib/examples/CellSimpleExample.js +0 -20
@@ -5,8 +5,76 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
5
5
  */
6
6
  import { useCallback, useEffect, useMemo, useState } from 'react';
7
7
  import { Box, Button, Heading, Label, Spinner, Text, TextInput, } from '@primer/react';
8
- import { CheckCircleFillIcon, RocketIcon } from '@primer/octicons-react';
9
- const GRID_TEMPLATE = ['1fr', '1fr 1fr', '1fr 1fr 1fr'];
8
+ import { AgentIcon, BeakerIcon, BookIcon, BugIcon, CheckCircleFillIcon, CloudIcon, CodeIcon, CopilotIcon, CpuIcon, DatabaseIcon, DependabotIcon, EyeIcon, FileIcon, GearIcon, GitBranchIcon, GlobeIcon, GraphIcon, HeartIcon, HomeIcon, IssueOpenedIcon, LightBulbIcon, LinkIcon, MailIcon, MegaphoneIcon, NoteIcon, PackageIcon, PencilIcon, PeopleIcon, PlayIcon, PlugIcon, PulseIcon, RocketIcon, SearchIcon, ShareIcon, ShieldCheckIcon, ShieldIcon, SparklesFillIcon, SquirrelIcon, StarIcon, SyncIcon, TableIcon, TagIcon, TelescopeIcon, TerminalIcon, ToolsIcon, ZapIcon, } from '@primer/octicons-react';
9
+ const GRID_TEMPLATE = ['1fr', '1fr 1fr', 'repeat(5, minmax(0, 1fr))'];
10
+ const ICON_REGISTRY = {
11
+ // Primary agentspec icon values.
12
+ agent: AgentIcon,
13
+ database: DatabaseIcon,
14
+ globe: GlobeIcon,
15
+ 'git-branch': GitBranchIcon,
16
+ 'trending-up': GraphIcon,
17
+ 'share-2': ShareIcon,
18
+ // Additional mappings used across the agentspec catalog.
19
+ rocket: RocketIcon,
20
+ sparkles: SparklesFillIcon,
21
+ search: SearchIcon,
22
+ code: CodeIcon,
23
+ cpu: CpuIcon,
24
+ beaker: BeakerIcon,
25
+ book: BookIcon,
26
+ bug: BugIcon,
27
+ cloud: CloudIcon,
28
+ copilot: CopilotIcon,
29
+ dependabot: DependabotIcon,
30
+ eye: EyeIcon,
31
+ file: FileIcon,
32
+ gear: GearIcon,
33
+ graph: GraphIcon,
34
+ heart: HeartIcon,
35
+ home: HomeIcon,
36
+ lightbulb: LightBulbIcon,
37
+ link: LinkIcon,
38
+ mail: MailIcon,
39
+ megaphone: MegaphoneIcon,
40
+ note: NoteIcon,
41
+ package: PackageIcon,
42
+ pencil: PencilIcon,
43
+ people: PeopleIcon,
44
+ play: PlayIcon,
45
+ plug: PlugIcon,
46
+ pulse: PulseIcon,
47
+ 'issue-opened': IssueOpenedIcon,
48
+ shield: ShieldIcon,
49
+ 'shield-check': ShieldCheckIcon,
50
+ squirrel: SquirrelIcon,
51
+ star: StarIcon,
52
+ sync: SyncIcon,
53
+ table: TableIcon,
54
+ tag: TagIcon,
55
+ telescope: TelescopeIcon,
56
+ terminal: TerminalIcon,
57
+ tools: ToolsIcon,
58
+ zap: ZapIcon,
59
+ };
60
+ const DEFAULT_ICON = SparklesFillIcon;
61
+ function hashString(value) {
62
+ let hash = 0;
63
+ for (let i = 0; i < value.length; i++) {
64
+ hash = ((hash << 5) - hash + value.charCodeAt(i)) | 0;
65
+ }
66
+ return Math.abs(hash);
67
+ }
68
+ function resolveAgentspecIcon(iconId, seed) {
69
+ if (iconId) {
70
+ return ICON_REGISTRY[iconId] || DEFAULT_ICON;
71
+ }
72
+ const iconValues = Object.values(ICON_REGISTRY);
73
+ if (iconValues.length === 0) {
74
+ return DEFAULT_ICON;
75
+ }
76
+ return iconValues[hashString(seed) % iconValues.length];
77
+ }
10
78
  /**
11
79
  * URL (optionally with `?token=`) of the LOCAL Jupyter server that the node's
12
80
  * agents should use as their `jupyter` code sandbox. This mirrors
@@ -136,6 +204,23 @@ export function AgentNodeGallery({ baseUrl, token, activeAgentId, onLaunched, on
136
204
  }, [running, normalizedActiveAgentId]);
137
205
  // An "active" agent must be both configured and currently running.
138
206
  const hasActiveAgent = Boolean(normalizedActiveAgentId && activeAgent);
207
+ const activeAgentSpec = useMemo(() => {
208
+ if (!activeAgent) {
209
+ return null;
210
+ }
211
+ const directSpecId = String(activeAgent.agent_spec_id || '').trim();
212
+ if (directSpecId) {
213
+ const directMatch = specs.find(spec => spec.id === directSpecId);
214
+ if (directMatch) {
215
+ return directMatch;
216
+ }
217
+ }
218
+ const nameCandidate = String(activeAgent.name || '').trim();
219
+ if (!nameCandidate) {
220
+ return null;
221
+ }
222
+ return specs.find(spec => spec.id === nameCandidate) || null;
223
+ }, [activeAgent, specs]);
139
224
  const normalizedSearch = search.trim().toLowerCase();
140
225
  const filteredSpecs = useMemo(() => {
141
226
  if (!normalizedSearch) {
@@ -251,7 +336,9 @@ export function AgentNodeGallery({ baseUrl, token, activeAgentId, onLaunched, on
251
336
  if (!recreateResponse.ok) {
252
337
  throw new Error(`Failed to launch agent (${recreateResponse.status})`);
253
338
  }
254
- const recreatePayload = await recreateResponse.json().catch(() => null);
339
+ const recreatePayload = await recreateResponse
340
+ .json()
341
+ .catch(() => null);
255
342
  agentId =
256
343
  recreatePayload?.agent_id ||
257
344
  recreatePayload?.id ||
@@ -298,7 +385,9 @@ export function AgentNodeGallery({ baseUrl, token, activeAgentId, onLaunched, on
298
385
  if (!recreateResponse.ok) {
299
386
  throw new Error(`Failed to recreate agent with spec (${recreateResponse.status})`);
300
387
  }
301
- const recreatePayload = await recreateResponse.json().catch(() => null);
388
+ const recreatePayload = await recreateResponse
389
+ .json()
390
+ .catch(() => null);
302
391
  agentId =
303
392
  recreatePayload?.agent_id ||
304
393
  recreatePayload?.id ||
@@ -364,11 +453,19 @@ export function AgentNodeGallery({ baseUrl, token, activeAgentId, onLaunched, on
364
453
  finally {
365
454
  setTerminating(false);
366
455
  }
367
- }, [authHeaders, baseUrl, normalizedActiveAgentId, onTerminated, onLaunchError]);
456
+ }, [
457
+ authHeaders,
458
+ baseUrl,
459
+ normalizedActiveAgentId,
460
+ onTerminated,
461
+ onLaunchError,
462
+ ]);
368
463
  if (loading) {
369
464
  return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 6 }, children: _jsx(Spinner, { size: "large" }) }));
370
465
  }
371
- return (_jsxs(Box, { children: [_jsxs(Box, { sx: { mb: 3 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Choose an agent" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Pick an agent from the gallery to launch it on this node. The active agent handles prompts sent directly to the node and through the Datalayer platform." })] }), error && (_jsx(Box, { sx: {
466
+ return (_jsxs(Box, { children: [_jsxs(Box, { sx: { mb: 3 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: hasActiveAgent ? 'Running agent' : 'Choose an agent' }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: hasActiveAgent
467
+ ? 'Your node is currently running an active agent. You can terminate it to go back to the gallery.'
468
+ : 'Pick an agent from the gallery to launch it on this node. The active agent handles prompts sent directly to the node and through the Datalayer platform.' })] }), error && (_jsx(Box, { sx: {
372
469
  mb: 3,
373
470
  p: 2,
374
471
  borderRadius: 2,
@@ -378,13 +475,31 @@ export function AgentNodeGallery({ baseUrl, token, activeAgentId, onLaunched, on
378
475
  }, children: error })), hasActiveAgent ? (_jsxs(Box, { sx: {
379
476
  p: 4,
380
477
  border: '1px solid',
381
- borderColor: 'success.muted',
478
+ borderColor: 'accent.muted',
382
479
  borderRadius: 2,
383
- bg: 'success.subtle',
480
+ bg: 'accent.subtle',
384
481
  display: 'flex',
385
482
  flexDirection: 'column',
386
483
  gap: 3,
387
- }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsxs(Label, { variant: "success", sx: { display: 'inline-flex', alignItems: 'center', gap: 1 }, children: [_jsx(CheckCircleFillIcon, { size: 12 }), "Active agent"] }), _jsx(Text, { sx: { fontWeight: 600 }, children: activeAgent?.name || activeAgent?.agent_spec_id || normalizedActiveAgentId })] }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Chat is enabled while an active agent is running on this node. Terminate it to return to the agent cards picker." }), _jsx(Box, { sx: { display: 'flex', justifyContent: 'flex-start' }, children: _jsx(Button, { variant: "danger", disabled: terminating, onClick: () => void terminateActiveAgent(), children: terminating ? 'Terminating…' : 'Terminate' }) })] })) : specs.length === 0 ? (_jsx(Box, { sx: {
484
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsxs(Label, { variant: "secondary", sx: {
485
+ display: 'inline-flex',
486
+ alignItems: 'center',
487
+ gap: 1,
488
+ color: 'accent.fg',
489
+ }, children: [_jsx(CheckCircleFillIcon, { size: 12 }), "Active agent"] }), _jsx(Text, { sx: { fontWeight: 600 }, children: activeAgent?.name ||
490
+ activeAgent?.agent_spec_id ||
491
+ normalizedActiveAgentId })] }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Chat is enabled while an active agent is running on this node. Terminate it to return to the agent cards picker." }), activeAgentSpec && (_jsxs(Box, { sx: {
492
+ p: 3,
493
+ border: '1px solid',
494
+ borderColor: 'border.default',
495
+ borderRadius: 2,
496
+ bg: 'canvas.default',
497
+ display: 'flex',
498
+ flexDirection: 'column',
499
+ gap: 2,
500
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Label, { size: "small", variant: "accent", children: "Spec" }), _jsx(Text, { sx: { fontWeight: 600 }, children: activeAgentSpec.name || activeAgentSpec.id }), activeAgentSpec.version && (_jsxs(Label, { size: "small", variant: "secondary", children: ["v", activeAgentSpec.version] }))] }), activeAgentSpec.description && (_jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: activeAgentSpec.description })), _jsxs(Box, { sx: { display: 'flex', gap: 1, flexWrap: 'wrap' }, children: [_jsxs(Label, { size: "small", variant: "secondary", children: ["id: ", activeAgentSpec.id] }), activeAgentSpec.model && (_jsxs(Label, { size: "small", variant: "secondary", children: ["model: ", activeAgentSpec.model] })), activeAgentSpec.inference_provider && (_jsxs(Label, { size: "small", variant: "secondary", children: ["provider: ", activeAgentSpec.inference_provider] })), Array.isArray(activeAgentSpec.tags)
501
+ ? activeAgentSpec.tags.map(tag => (_jsx(Label, { size: "small", variant: "secondary", children: tag }, tag)))
502
+ : null] })] })), _jsx(Box, { sx: { display: 'flex', justifyContent: 'flex-start' }, children: _jsx(Button, { variant: "danger", disabled: terminating, onClick: () => void terminateActiveAgent(), children: terminating ? 'Terminating…' : 'Terminate' }) })] })) : specs.length === 0 ? (_jsx(Box, { sx: {
388
503
  p: 4,
389
504
  textAlign: 'center',
390
505
  border: '1px solid',
@@ -396,22 +511,57 @@ export function AgentNodeGallery({ baseUrl, token, activeAgentId, onLaunched, on
396
511
  gridTemplateColumns: GRID_TEMPLATE,
397
512
  gap: 3,
398
513
  }, children: filteredSpecs.map(spec => {
399
- const isActive = !!activeAgentId &&
400
- runningBySpec.get(spec.id) === activeAgentId;
514
+ const IconComponent = resolveAgentspecIcon(spec.icon, spec.name || spec.id);
515
+ const isActive = !!activeAgentId && runningBySpec.get(spec.id) === activeAgentId;
401
516
  const isLaunching = launchingId === spec.id;
402
517
  const isRunning = runningBySpec.has(spec.id);
403
- return (_jsxs(Box, { sx: {
518
+ const launchLabel = isActive
519
+ ? 'Open chat'
520
+ : isLaunching
521
+ ? 'Launching…'
522
+ : isRunning
523
+ ? 'Set active'
524
+ : 'Launch';
525
+ return (_jsxs(Box, { role: "button", tabIndex: isLaunching ? -1 : 0, onClick: () => {
526
+ if (!isLaunching) {
527
+ void launch(spec);
528
+ }
529
+ }, onKeyDown: event => {
530
+ if ((event.key === 'Enter' || event.key === ' ') &&
531
+ !isLaunching) {
532
+ event.preventDefault();
533
+ void launch(spec);
534
+ }
535
+ }, "aria-label": `${launchLabel} ${spec.name || spec.id}`, "aria-busy": isLaunching, "aria-disabled": isLaunching, sx: {
404
536
  display: 'flex',
405
537
  flexDirection: 'column',
538
+ textAlign: 'left',
539
+ cursor: isLaunching ? 'progress' : 'pointer',
406
540
  p: 3,
407
541
  border: '1px solid',
408
- borderColor: isActive ? 'success.emphasis' : 'border.default',
542
+ borderColor: isActive
543
+ ? 'success.emphasis'
544
+ : 'border.default',
409
545
  borderRadius: 2,
410
546
  bg: 'canvas.default',
411
- transition: 'box-shadow 0.15s ease, transform 0.15s ease',
412
- ':hover': {
547
+ // Hover feedback is border + shadow only. No transform: a
548
+ // transform here promotes the card to its own layer and makes
549
+ // the line-clamped description re-rasterize (per-paragraph
550
+ // motion artifact).
551
+ transition: 'box-shadow 180ms ease, border-color 180ms ease',
552
+ '&:hover': {
413
553
  boxShadow: 'shadow.medium',
414
- transform: 'translateY(-2px)',
554
+ borderColor: isActive
555
+ ? 'success.emphasis'
556
+ : 'accent.emphasis',
557
+ },
558
+ '&:focus-visible': {
559
+ outline: '2px solid',
560
+ outlineColor: 'accent.fg',
561
+ outlineOffset: '2px',
562
+ },
563
+ '&[aria-disabled="true"]': {
564
+ opacity: 0.8,
415
565
  },
416
566
  }, children: [_jsxs(Box, { sx: {
417
567
  display: 'flex',
@@ -427,13 +577,17 @@ export function AgentNodeGallery({ baseUrl, token, activeAgentId, onLaunched, on
427
577
  borderRadius: 2,
428
578
  fontSize: 3,
429
579
  bg: 'canvas.subtle',
430
- }, children: spec.emoji || '🤖' }), _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsx(Heading, { as: "h3", sx: {
580
+ }, children: spec.emoji ? (_jsx(Text, { sx: { fontSize: 3, lineHeight: 1 }, children: spec.emoji })) : (_jsx(IconComponent, { size: 20 })) }), _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsx(Heading, { as: "h3", sx: {
431
581
  fontSize: 2,
432
582
  mb: 0,
433
583
  overflow: 'hidden',
434
584
  textOverflow: 'ellipsis',
435
585
  whiteSpace: 'nowrap',
436
- }, children: spec.name || spec.id }), spec.version && (_jsxs(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: ["v", spec.version] }))] }), isActive && (_jsxs(Label, { variant: "success", sx: { display: 'inline-flex', alignItems: 'center', gap: 1 }, children: [_jsx(CheckCircleFillIcon, { size: 12 }), "Active"] }))] }), spec.description && (_jsx(Text, { sx: {
586
+ }, children: spec.name || spec.id }), spec.version && (_jsxs(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: ["v", spec.version] }))] }), isActive && (_jsxs(Label, { variant: "success", sx: {
587
+ display: 'inline-flex',
588
+ alignItems: 'center',
589
+ gap: 1,
590
+ }, children: [_jsx(CheckCircleFillIcon, { size: 12 }), "Active"] }))] }), spec.description && (_jsx(Text, { sx: {
437
591
  color: 'fg.muted',
438
592
  fontSize: 1,
439
593
  mb: 2,
@@ -446,13 +600,22 @@ export function AgentNodeGallery({ baseUrl, token, activeAgentId, onLaunched, on
446
600
  flexWrap: 'wrap',
447
601
  gap: 1,
448
602
  mb: 3,
449
- }, children: spec.tags.slice(0, 4).map(tag => (_jsx(Label, { size: "small", variant: "secondary", children: tag }, tag))) })), _jsx(Box, { sx: { mt: 'auto', pt: 1 }, children: _jsx(Button, { variant: isActive ? 'default' : 'primary', leadingVisual: RocketIcon, block: true, disabled: isLaunching, onClick: () => void launch(spec), children: isActive
450
- ? 'Open chat'
451
- : isLaunching
452
- ? 'Launching…'
453
- : isRunning
454
- ? 'Set active'
455
- : 'Launch' }) })] }, spec.id));
603
+ }, children: spec.tags.slice(0, 4).map(tag => (_jsx(Label, { size: "small", variant: "secondary", children: tag }, tag))) })), _jsx(Box, { sx: { mt: 'auto', pt: 1 }, children: _jsxs(Box, { as: "span", sx: {
604
+ display: 'inline-flex',
605
+ alignItems: 'center',
606
+ gap: 1,
607
+ px: 2,
608
+ py: 1,
609
+ borderRadius: 2,
610
+ border: '1px solid',
611
+ borderColor: isActive
612
+ ? 'border.default'
613
+ : 'accent.muted',
614
+ bg: isActive ? 'canvas.subtle' : 'accent.subtle',
615
+ color: isActive ? 'fg.default' : 'accent.fg',
616
+ fontSize: 1,
617
+ fontWeight: 600,
618
+ }, children: [_jsx(IconComponent, { size: 14 }), launchLabel] }) })] }, spec.id));
456
619
  }) }), filteredSpecs.length === 0 && (_jsx(Box, { sx: {
457
620
  mt: 3,
458
621
  p: 3,
@@ -120,6 +120,63 @@ export interface ResumeRuntimeBody {
120
120
  /** Additional metadata */
121
121
  metadata?: Record<string, any>;
122
122
  }
123
+ /**
124
+ * Persisted memory record returned by ``/api/runtimes/v1/memories``.
125
+ */
126
+ export interface RuntimeMemory {
127
+ id: string;
128
+ memory?: string | null;
129
+ hash?: string | null;
130
+ user_id?: string | null;
131
+ agent_id?: string | null;
132
+ run_id?: string | null;
133
+ actor_id?: string | null;
134
+ role?: string | null;
135
+ scope?: string | null;
136
+ created_at?: string | null;
137
+ updated_at?: string | null;
138
+ metadata?: Record<string, any>;
139
+ }
140
+ /**
141
+ * Response payload for listing persisted runtime memories.
142
+ */
143
+ export interface ListRuntimeMemoriesResponse {
144
+ success: boolean;
145
+ message?: string;
146
+ count: number;
147
+ limit: number;
148
+ offset: number;
149
+ user_id?: string;
150
+ memories: RuntimeMemory[];
151
+ }
152
+ /**
153
+ * Response payload for fetching one persisted memory.
154
+ */
155
+ export interface GetRuntimeMemoryResponse {
156
+ success: boolean;
157
+ message?: string;
158
+ memory: RuntimeMemory;
159
+ }
160
+ /**
161
+ * Query options for listing persisted runtime memories.
162
+ */
163
+ export interface ListRuntimeMemoriesOptions {
164
+ userId?: string;
165
+ agentId?: string;
166
+ limit?: number;
167
+ offset?: number;
168
+ }
169
+ /**
170
+ * List persisted memories from the runtimes API.
171
+ *
172
+ * Non-admin callers are automatically scoped to their own personal account by
173
+ * the backend, even when ``userId`` is omitted.
174
+ */
175
+ export declare const listRuntimeMemories: (token: string, options?: ListRuntimeMemoriesOptions, baseUrl?: string) => Promise<ListRuntimeMemoriesResponse>;
176
+ /**
177
+ * Fetch one persisted memory by id from the runtimes API.
178
+ */
179
+ export declare const getRuntimeMemory: (token: string, memoryId: string, baseUrl?: string) => Promise<GetRuntimeMemoryResponse>;
123
180
  /**
124
181
  * Resume a paused runtime by restoring from a checkpoint (async).
125
182
  *
@@ -39,7 +39,7 @@ export const createRuntime = async (token, data, baseUrl = DEFAULT_SERVICE_URLS.
39
39
  const responseData = error.response.data || {};
40
40
  if (status === 404) {
41
41
  // Environment not found
42
- throw new Error(`Environment '${data.environment_name}' not found. ${responseData.message || 'Please check the environment name and try again.'}`);
42
+ throw new Error(`Environment '${data.environment?.name || ''}' not found. ${responseData.message || 'Please check the environment name and try again.'}`);
43
43
  }
44
44
  else if (status === 503) {
45
45
  // No runtime available
@@ -203,6 +203,60 @@ export const pauseRuntime = async (token, podName, baseUrl = DEFAULT_SERVICE_URL
203
203
  ...(body ? { body } : {}),
204
204
  });
205
205
  };
206
+ /**
207
+ * List persisted memories from the runtimes API.
208
+ *
209
+ * Non-admin callers are automatically scoped to their own personal account by
210
+ * the backend, even when ``userId`` is omitted.
211
+ */
212
+ export const listRuntimeMemories = async (token, options = {}, baseUrl = DEFAULT_SERVICE_URLS.RUNTIMES) => {
213
+ validateToken(token);
214
+ const query = new URLSearchParams();
215
+ if (options.userId?.trim()) {
216
+ query.set('user_id', options.userId.trim());
217
+ }
218
+ if (options.agentId?.trim()) {
219
+ query.set('agent_id', options.agentId.trim());
220
+ }
221
+ if (typeof options.limit === 'number' && Number.isFinite(options.limit)) {
222
+ query.set('limit', String(Math.max(1, Math.min(1000, Math.floor(options.limit)))));
223
+ }
224
+ if (typeof options.offset === 'number' && Number.isFinite(options.offset)) {
225
+ query.set('offset', String(Math.max(0, Math.floor(options.offset))));
226
+ }
227
+ const qs = query.toString();
228
+ const response = await requestDatalayerAPI({
229
+ url: `${baseUrl}${API_BASE_PATHS.RUNTIMES}/memories${qs ? `?${qs}` : ''}`,
230
+ method: 'GET',
231
+ token,
232
+ });
233
+ return {
234
+ success: Boolean(response?.success),
235
+ message: response?.message,
236
+ count: Number(response?.count ?? 0),
237
+ limit: Number(response?.limit ?? options.limit ?? 100),
238
+ offset: Number(response?.offset ?? options.offset ?? 0),
239
+ user_id: response?.user_id,
240
+ memories: Array.isArray(response?.memories) ? response.memories : [],
241
+ };
242
+ };
243
+ /**
244
+ * Fetch one persisted memory by id from the runtimes API.
245
+ */
246
+ export const getRuntimeMemory = async (token, memoryId, baseUrl = DEFAULT_SERVICE_URLS.RUNTIMES) => {
247
+ validateToken(token);
248
+ validateRequiredString(memoryId, 'Memory id');
249
+ const response = await requestDatalayerAPI({
250
+ url: `${baseUrl}${API_BASE_PATHS.RUNTIMES}/memories/${encodeURIComponent(memoryId)}`,
251
+ method: 'GET',
252
+ token,
253
+ });
254
+ return {
255
+ success: Boolean(response?.success),
256
+ message: response?.message,
257
+ memory: response?.memory,
258
+ };
259
+ };
206
260
  /**
207
261
  * Resume a paused runtime by restoring from a checkpoint (async).
208
262
  *
@@ -107,5 +107,5 @@ export interface ChatProps extends ChatCommonProps {
107
107
  * />
108
108
  * ```
109
109
  */
110
- export declare function Chat({ protocol: transport, extensions: _extensions, baseUrl, endpoint: endpointProp, configEndpoint: configEndpointProp, enableConfigQuery: enableConfigQueryProp, wsUrl, agentId, authToken: authTokenProp, placeholder, title, subtitle, brandIcon, autoConnect, streaming: _streaming, onMessageSent: _onMessageSent, onMessageReceived: _onMessageReceived, onDisconnect, onLogout: _onLogout, onCollapsePanel: _onCollapsePanel, className, height, showHeader, showNewChatButton, showClearButton, showModelSelector, showToolsMenu, showInput, disableInputPrompt, overlay, launching, launchingMessage, showSkillsMenu, codemodeEnabled, onToggleCodemode, showTokenUsage, initialModel, availableModels, mcpServers, initialSkills, clearOnMount: _clearOnMount, suggestions, submitOnSuggestionClick, description, headerContent, headerActions, autoFocus, identityProviders, onIdentityConnect, onIdentityDisconnect, runtimeId, historyEndpoint, pendingPrompt, errorBanner, showInformation, chatViewMode, onChatViewModeChange, frontendTools, enableEphemeralNotebook, initialEphemeralNotebookOpen, onEphemeralNotebookOpenChange, enableEphemeralDocument, initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange, collapsed, onExpandFromCollapsed, ephemeralNotebookToolbar, ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration, ephemeralRuntimeOverride, onToolCallStart, onToolCallComplete, renderToolResult, hideMessagesAfterToolUI, contextSnapshot, mcpStatusData, codemodeStatusData, sandboxStatusData, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, disableInternalJupyterTheme, }: ChatProps): import("react/jsx-runtime").JSX.Element;
110
+ export declare function Chat({ protocol: transport, extensions: _extensions, baseUrl, endpoint: endpointProp, configEndpoint: configEndpointProp, enableConfigQuery: enableConfigQueryProp, wsUrl, agentId, authToken: authTokenProp, placeholder, title, subtitle, brandIcon, autoConnect, streaming: _streaming, onMessageSent: _onMessageSent, onMessageReceived: _onMessageReceived, onDisconnect, onLogout: _onLogout, onCollapsePanel: _onCollapsePanel, className, height, showHeader, showNewChatButton, showClearButton, showModelSelector, showToolsMenu, showInput, disableInputPrompt, overlay, launching, launchingMessage, showSkillsMenu, codemodeEnabled, onToggleCodemode, showTokenUsage, initialModel, availableModels, mcpServers, initialSkills, clearOnMount: _clearOnMount, suggestions, submitOnSuggestionClick, description, headerContent, headerActions, autoFocus, identityProviders, onIdentityConnect, onIdentityDisconnect, runtimeId, historyEndpoint, pendingPrompt, errorBanner, showInformation, chatViewMode, onChatViewModeChange, frontendTools, enableEphemeralNotebook, initialEphemeralNotebookOpen, onEphemeralNotebookOpenChange, enableEphemeralDocument, initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange, collapsed, onExpandFromCollapsed, ephemeralNotebookToolbar, ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration, ephemeralRuntimeOverride, onToolCallStart, onToolCallComplete, renderToolResult, hideMessagesAfterToolUI, contextSnapshot, mcpStatusData, codemodeStatusData, sandboxStatusData, kernel, kernelIndicatorPlacement, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, themeVariant, colorMode, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, disableInternalJupyterTheme, }: ChatProps): import("react/jsx-runtime").JSX.Element;
111
111
  export default Chat;
package/lib/chat/Chat.js CHANGED
@@ -136,7 +136,7 @@ function getProtocolType(protocol) {
136
136
  * />
137
137
  * ```
138
138
  */
139
- export function Chat({ protocol: transport, extensions: _extensions, baseUrl = 'http://localhost:8765', endpoint: endpointProp, configEndpoint: configEndpointProp, enableConfigQuery: enableConfigQueryProp, wsUrl, agentId, authToken: authTokenProp, placeholder = 'Type your message...', title, subtitle, brandIcon, autoConnect = true, streaming: _streaming = true, onMessageSent: _onMessageSent, onMessageReceived: _onMessageReceived, onDisconnect, onLogout: _onLogout, onCollapsePanel: _onCollapsePanel, className, height = '600px', showHeader = true, showNewChatButton = true, showClearButton = true, showModelSelector = true, showToolsMenu = true, showInput = true, disableInputPrompt = false, overlay, launching = false, launchingMessage, showSkillsMenu = true, codemodeEnabled = false, onToggleCodemode, showTokenUsage = true, initialModel, availableModels, mcpServers, initialSkills, clearOnMount: _clearOnMount = true, suggestions, submitOnSuggestionClick = true, description, headerContent, headerActions, autoFocus = false, identityProviders, onIdentityConnect, onIdentityDisconnect, runtimeId, historyEndpoint, pendingPrompt, errorBanner, showInformation = true, chatViewMode, onChatViewModeChange, frontendTools, enableEphemeralNotebook, initialEphemeralNotebookOpen, onEphemeralNotebookOpenChange, enableEphemeralDocument, initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange, collapsed = false, onExpandFromCollapsed, ephemeralNotebookToolbar, ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration, ephemeralRuntimeOverride, onToolCallStart, onToolCallComplete, renderToolResult, hideMessagesAfterToolUI = false, contextSnapshot, mcpStatusData, codemodeStatusData, sandboxStatusData, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, disableInternalJupyterTheme = false, }) {
139
+ export function Chat({ protocol: transport, extensions: _extensions, baseUrl = 'http://localhost:8765', endpoint: endpointProp, configEndpoint: configEndpointProp, enableConfigQuery: enableConfigQueryProp, wsUrl, agentId, authToken: authTokenProp, placeholder = 'Type your message...', title, subtitle, brandIcon, autoConnect = true, streaming: _streaming = true, onMessageSent: _onMessageSent, onMessageReceived: _onMessageReceived, onDisconnect, onLogout: _onLogout, onCollapsePanel: _onCollapsePanel, className, height = '600px', showHeader = true, showNewChatButton = true, showClearButton = true, showModelSelector = true, showToolsMenu = true, showInput = true, disableInputPrompt = false, overlay, launching = false, launchingMessage, showSkillsMenu = true, codemodeEnabled = false, onToggleCodemode, showTokenUsage = true, initialModel, availableModels, mcpServers, initialSkills, clearOnMount: _clearOnMount = true, suggestions, submitOnSuggestionClick = true, description, headerContent, headerActions, autoFocus = false, identityProviders, onIdentityConnect, onIdentityDisconnect, runtimeId, historyEndpoint, pendingPrompt, errorBanner, showInformation = true, chatViewMode, onChatViewModeChange, frontendTools, enableEphemeralNotebook, initialEphemeralNotebookOpen, onEphemeralNotebookOpenChange, enableEphemeralDocument, initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange, collapsed = false, onExpandFromCollapsed, ephemeralNotebookToolbar, ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration, ephemeralRuntimeOverride, onToolCallStart, onToolCallComplete, renderToolResult, hideMessagesAfterToolUI = false, contextSnapshot, mcpStatusData, codemodeStatusData, sandboxStatusData, kernel, kernelIndicatorPlacement, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, themeVariant, colorMode, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, disableInternalJupyterTheme = false, }) {
140
140
  const resolvedAgentId = typeof agentId === 'string' && agentId.trim().length > 0
141
141
  ? agentId.trim()
142
142
  : 'default';
@@ -364,6 +364,6 @@ export function Chat({ protocol: transport, extensions: _extensions, baseUrl = '
364
364
  ? 'attention.fg'
365
365
  : 'danger.fg',
366
366
  flex: 1,
367
- }, children: errorBanner.message })] })), _jsx(ChatBase, { title: title, subtitle: subtitle, brandIcon: brandIcon, showHeader: showHeader, protocol: protocolConfig, placeholder: placeholder, description: description, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, autoFocus: autoFocus, runtimeId: runtimeId, historyEndpoint: historyEndpoint, pendingPrompt: pendingPrompt, showInformation: showInformation, onInformationClick: handleInformationClick, headerContent: headerContent, headerActions: headerActions, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showInput: showInput, disableInputPrompt: disableInputPrompt, overlay: overlay, launching: launching, launchingMessage: launchingMessage, autoConnect: autoConnect, showSkillsMenu: showSkillsMenu, showTokenUsage: showTokenUsage, codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, initialModel: initialModel, availableModels: availableModels, mcpServers: mcpServers, initialSkills: initialSkills, connectedIdentities: identitiesForChat, onNewChat: handleNewChat, onMessagesChange: handleMessagesChange, headerButtons: headerButtons, avatarConfig: avatarConfig, backgroundColor: "canvas.default", focusTrigger: focusTrigger, chatViewMode: chatViewMode, onChatViewModeChange: onChatViewModeChange, frontendTools: frontendTools, enableEphemeralNotebook: enableEphemeralNotebook, initialEphemeralNotebookOpen: initialEphemeralNotebookOpen, onEphemeralNotebookOpenChange: onEphemeralNotebookOpenChange, enableEphemeralDocument: enableEphemeralDocument, initialEphemeralSurfaceMode: initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange: onEphemeralSurfaceModeChange, collapsed: collapsed, onExpandFromCollapsed: onExpandFromCollapsed, ephemeralNotebookToolbar: ephemeralNotebookToolbar, ephemeralNotebookCollaborationProvider: ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId: ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration: ephemeralDocumentCollaboration, ephemeralRuntimeOverride: ephemeralRuntimeOverride, onToolCallStart: onToolCallStart, onToolCallComplete: onToolCallComplete, renderToolResult: renderToolResult, hideMessagesAfterToolUI: hideMessagesAfterToolUI, contextSnapshot: contextSnapshot, mcpStatusData: mcpStatusData, sandboxStatusData: sandboxStatusData, kernel: kernel, kernelEnvironmentName: kernelEnvironmentName, kernelCpu: kernelCpu, kernelMemory: kernelMemory, kernelGpu: kernelGpu, showToolApprovalBanner: showToolApprovalBanner, pendingApprovals: pendingApprovals, onApproveApproval: onApproveApproval, onRejectApproval: onRejectApproval, disableInternalJupyterTheme: disableInternalJupyterTheme })] })] }) }) }));
367
+ }, children: errorBanner.message })] })), _jsx(ChatBase, { title: title, subtitle: subtitle, brandIcon: brandIcon, showHeader: showHeader, protocol: protocolConfig, placeholder: placeholder, description: description, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, autoFocus: autoFocus, runtimeId: runtimeId, historyEndpoint: historyEndpoint, pendingPrompt: pendingPrompt, showInformation: showInformation, onInformationClick: handleInformationClick, headerContent: headerContent, headerActions: headerActions, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showInput: showInput, disableInputPrompt: disableInputPrompt, overlay: overlay, launching: launching, launchingMessage: launchingMessage, autoConnect: autoConnect, showSkillsMenu: showSkillsMenu, showTokenUsage: showTokenUsage, codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, initialModel: initialModel, availableModels: availableModels, mcpServers: mcpServers, initialSkills: initialSkills, connectedIdentities: identitiesForChat, onNewChat: handleNewChat, onMessagesChange: handleMessagesChange, headerButtons: headerButtons, avatarConfig: avatarConfig, backgroundColor: "canvas.default", focusTrigger: focusTrigger, chatViewMode: chatViewMode, onChatViewModeChange: onChatViewModeChange, frontendTools: frontendTools, enableEphemeralNotebook: enableEphemeralNotebook, initialEphemeralNotebookOpen: initialEphemeralNotebookOpen, onEphemeralNotebookOpenChange: onEphemeralNotebookOpenChange, enableEphemeralDocument: enableEphemeralDocument, initialEphemeralSurfaceMode: initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange: onEphemeralSurfaceModeChange, collapsed: collapsed, onExpandFromCollapsed: onExpandFromCollapsed, ephemeralNotebookToolbar: ephemeralNotebookToolbar, ephemeralNotebookCollaborationProvider: ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId: ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration: ephemeralDocumentCollaboration, ephemeralRuntimeOverride: ephemeralRuntimeOverride, onToolCallStart: onToolCallStart, onToolCallComplete: onToolCallComplete, renderToolResult: renderToolResult, hideMessagesAfterToolUI: hideMessagesAfterToolUI, contextSnapshot: contextSnapshot, mcpStatusData: mcpStatusData, sandboxStatusData: sandboxStatusData, kernel: kernel, kernelIndicatorPlacement: kernelIndicatorPlacement, kernelEnvironmentName: kernelEnvironmentName, kernelCpu: kernelCpu, kernelMemory: kernelMemory, kernelGpu: kernelGpu, themeVariant: themeVariant, colorMode: colorMode, showToolApprovalBanner: showToolApprovalBanner, pendingApprovals: pendingApprovals, onApproveApproval: onApproveApproval, onRejectApproval: onRejectApproval, disableInternalJupyterTheme: disableInternalJupyterTheme })] })] }) }) }));
368
368
  }
369
369
  export default Chat;
@@ -112,12 +112,14 @@ const ThemedJupyterReactTheme = JupyterReactTheme;
112
112
  * chat height/flex chain is preserved); CSS custom properties and inherited
113
113
  * properties (color, font) still cascade through it.
114
114
  */
115
- function ThemedChatBoundary({ children }) {
116
- const colorMode = useThemeStore(s => s.colorMode);
117
- const themeVariant = useThemeStore(s => s.theme);
115
+ function ThemedChatBoundary({ children, themeVariant, colorMode, }) {
116
+ const storeColorMode = useThemeStore(s => s.colorMode);
117
+ const storeThemeVariant = useThemeStore(s => s.theme);
118
+ const resolvedColorMode = colorMode ?? storeColorMode;
119
+ const resolvedThemeVariant = themeVariant ?? storeThemeVariant;
118
120
  const systemMode = useSystemColorMode();
119
- const themeConfig = getThemeConfig(themeVariant);
120
- const resolvedMode = colorMode === 'auto' ? systemMode : colorMode;
121
+ const themeConfig = getThemeConfig(resolvedThemeVariant);
122
+ const resolvedMode = resolvedColorMode === 'auto' ? systemMode : resolvedColorMode;
121
123
  const modeStyles = resolvedMode === 'dark'
122
124
  ? themeConfig.themeStyles.dark
123
125
  : themeConfig.themeStyles.light;
@@ -432,7 +434,7 @@ function parseApprovedMcpToolsByServer(mcpStatusData) {
432
434
  * ChatBase component — Universal chat panel supporting store, protocol, and custom modes.
433
435
  */
434
436
  export function ChatBase(props) {
435
- const { agentRuntimeConfig, protocol: protocolProp, useStore: useStoreMode = true, disableInternalJupyterTheme = false, } = props;
437
+ const { agentRuntimeConfig, protocol: protocolProp, useStore: useStoreMode = true, disableInternalJupyterTheme = false, themeVariant, colorMode, } = props;
436
438
  // Resolve protocol: string Protocol overrides type in agentRuntimeConfig or
437
439
  // is combined with a full ProtocolConfig object.
438
440
  const protocolType = typeof protocolProp === 'string' ? protocolProp : undefined;
@@ -459,7 +461,7 @@ export function ChatBase(props) {
459
461
  useStore: effectiveUseStoreMode,
460
462
  };
461
463
  const content = _jsx(ChatBaseInner, { ...innerProps });
462
- const wrappedContent = disableInternalJupyterTheme ? (content) : (_jsx(ThemedChatBoundary, { children: content }));
464
+ const wrappedContent = disableInternalJupyterTheme ? (content) : (_jsx(ThemedChatBoundary, { themeVariant: themeVariant, colorMode: colorMode, children: content }));
463
465
  if (!existingQueryClient) {
464
466
  return (_jsx(QueryClientProvider, { client: internalQueryClient, children: wrappedContent }));
465
467
  }
@@ -468,7 +470,7 @@ export function ChatBase(props) {
468
470
  // ---------------------------------------------------------------------------
469
471
  // ChatBaseInner — contains all actual logic
470
472
  // ---------------------------------------------------------------------------
471
- function ChatBaseInner({ title, subtitle, showHeader = false, showTokenUsage = true, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = true, showToolsMenu = true, showSkillsMenu = true, disableInputPrompt = false, overlay, launching = false, launchingMessage, autoConnect = true, codemodeEnabled = false, onToggleCodemode, initialModel, availableModels, mcpServers, initialSkills: _initialSkills, className, loadingState, headerActions, kernelIndicatorState, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, chatViewMode, onChatViewModeChange,
473
+ function ChatBaseInner({ title, subtitle, showHeader = false, showTokenUsage = true, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = true, showToolsMenu = true, showSkillsMenu = true, disableInputPrompt = false, overlay, launching = false, launchingMessage, autoConnect = true, codemodeEnabled = false, onToggleCodemode, initialModel, availableModels, mcpServers, initialSkills: _initialSkills, className, loadingState, headerActions, kernelIndicatorState, kernelIndicatorPlacement = 'left', kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, themeVariant, colorMode, chatViewMode, onChatViewModeChange,
472
474
  // Mode selection
473
475
  useStore: useStoreMode = true, protocol: protocolRaw, onSendMessage, enableStreaming = false,
474
476
  // Extended props
@@ -883,6 +885,10 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
883
885
  requestAiAgentsApprovalHistory,
884
886
  sendAiAgentsApprovalDecision,
885
887
  ]);
888
+ // Assigned once `applyServerApprovalDecision` is defined below. A ref lets the
889
+ // ai-agents approval WS effect (declared earlier) drive the deferred-run
890
+ // continuation without re-subscribing when the callback identity changes.
891
+ const applyServerApprovalDecisionRef = useRef(null);
886
892
  const reconcileResolvedApprovalInStore = useCallback((approval) => {
887
893
  const state = agentRuntimeStore.getState();
888
894
  const removalIds = new Set([approval.id]);
@@ -988,7 +994,24 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
988
994
  }
989
995
  const state = agentRuntimeStore.getState();
990
996
  for (const record of records) {
991
- const approval = normalizeApprovalPayload(record);
997
+ let approval = normalizeApprovalPayload(record);
998
+ // SaaS-resolved events (tool_approval_approved/rejected) can arrive
999
+ // with only {approvalId, status} and no tool_name/tool_args. Recover
1000
+ // the full envelope from the store (populated by the earlier pending
1001
+ // event) so the card clears AND the deferred run can resume.
1002
+ if (!approval) {
1003
+ const rawId = (typeof record.id === 'string' && record.id) ||
1004
+ (typeof record.approval_id === 'string' && record.approval_id) ||
1005
+ (typeof record.approvalId === 'string' && record.approvalId) ||
1006
+ '';
1007
+ const rawStatus = (typeof record.status === 'string' && record.status) || '';
1008
+ if (rawId && rawStatus && rawStatus !== 'pending') {
1009
+ const stored = state.approvals.find(a => a.id === rawId);
1010
+ if (stored) {
1011
+ approval = { ...stored, status: rawStatus };
1012
+ }
1013
+ }
1014
+ }
992
1015
  if (!approval) {
993
1016
  continue;
994
1017
  }
@@ -1018,6 +1041,11 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1018
1041
  }
1019
1042
  else {
1020
1043
  reconcileResolvedApprovalInStore(scopedApproval);
1044
+ // Resume the deferred tool call when the decision was made on
1045
+ // another surface (e.g. the SaaS Tool Approvals UI). Without this
1046
+ // the pending card clears but pydantic-ai never receives the
1047
+ // client continuation and the run stays parked.
1048
+ applyServerApprovalDecisionRef.current?.(scopedApproval, scopedApproval.status === 'approved', scopedApproval.note ?? undefined);
1021
1049
  }
1022
1050
  }
1023
1051
  }
@@ -1259,13 +1287,16 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1259
1287
  });
1260
1288
  return true;
1261
1289
  }, [activeAgentId]);
1290
+ applyServerApprovalDecisionRef.current = applyServerApprovalDecision;
1262
1291
  // ---- Agent-runtime WebSocket (monitoring stream) ----
1263
1292
  // Derive the bare base URL from configEndpoint or protocol.endpoint.
1293
+ const isAgentNodeTunnelAgUi = Boolean(protocol?.endpoint &&
1294
+ /\/api\/runtimes\/v1\/agent-nodes\/[^/]+\/ag-ui\/?$/.test(protocol.endpoint));
1264
1295
  const wsBaseUrl = protocol?.configEndpoint
1265
1296
  ? protocol.configEndpoint.replace(/\/api\/v1\/(config|configure)\/?$/, '')
1266
1297
  : (protocol?.endpoint?.replace(/\/api\/v1\/.*$/, '') ?? '');
1267
1298
  useAgentRuntimeWebSocket({
1268
- enabled: !!protocol && !!wsBaseUrl,
1299
+ enabled: !!protocol && !!wsBaseUrl && !isAgentNodeTunnelAgUi,
1269
1300
  baseUrl: wsBaseUrl,
1270
1301
  authToken: protocol?.authToken,
1271
1302
  agentId: protocol?.agentId,
@@ -2440,7 +2471,11 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
2440
2471
  pendingPromptSentRef.current = pendingPromptKey;
2441
2472
  return;
2442
2473
  }
2443
- if (!historyLoaded)
2474
+ // Do not block prompt replay forever when runtime history cannot be fetched
2475
+ // (e.g. websocket not connected yet/anymore). In that case we fall back to
2476
+ // submitting once the adapter is ready.
2477
+ const canProceedWithoutHistory = !historyScopeId || wsState !== 'connected';
2478
+ if (!historyLoaded && !canProceedWithoutHistory)
2444
2479
  return;
2445
2480
  if (!adapterReady && !onSendMessage)
2446
2481
  return;
@@ -2453,6 +2488,8 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
2453
2488
  pendingPrompt,
2454
2489
  pendingPromptKey,
2455
2490
  historyLoaded,
2491
+ historyScopeId,
2492
+ wsState,
2456
2493
  adapterReady,
2457
2494
  handleSend,
2458
2495
  onSendMessage,
@@ -2849,7 +2886,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
2849
2886
  // no ephemeral notebook is shown) or INSIDE the chat body column (when the
2850
2887
  // notebook is visible) so the header always follows the chat body across all
2851
2888
  // view modes (docked sidebar, floating popup, floating-small).
2852
- const chatHeaderElement = showHeader ? (_jsx(ChatBaseHeader, { title: title, subtitle: subtitle, brandIcon: brandIcon, headerContent: headerContent, headerActions: headerActions, showInformation: showInformation, onInformationClick: onInformationClick, padding: padding, kernelIndicatorState: kernelIndicatorState, runtimeStatus: sandboxStatusData ?? sandboxStatusQuery.data, kernel: notebookVisible
2889
+ const chatHeaderElement = showHeader ? (_jsx(ChatBaseHeader, { title: title, subtitle: subtitle, brandIcon: brandIcon, headerContent: headerContent, headerActions: headerActions, showInformation: showInformation, onInformationClick: onInformationClick, padding: padding, kernelIndicatorState: kernelIndicatorState, kernelIndicatorPlacement: kernelIndicatorPlacement, runtimeStatus: sandboxStatusData ?? sandboxStatusQuery.data, kernel: notebookVisible
2853
2890
  ? (notebookKernel ?? kernel)
2854
2891
  : documentVisible
2855
2892
  ? (documentKernel ?? kernel)
@@ -2900,7 +2937,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
2900
2937
  borderRight: '1px solid',
2901
2938
  borderColor: 'border.default',
2902
2939
  }),
2903
- }, children: launching || overlay ? (_jsx(CompanionSurfaceSkeleton, { mode: notebookVisible ? 'notebook' : 'document' })) : notebookVisible ? (_jsx(EphemeralNotebook, { notebookId: ephemeralNotebookId, runtimePodName: runtimeId || activeAgentId, runtimeOverride: ephemeralRuntimeOverride, nbformat: persistedEphemeralNbformat ?? undefined, onNbformatChange: handleEphemeralNotebookChange, toolbarComponent: ephemeralNotebookToolbar, collaborationProvider: ephemeralNotebookCollaborationProvider })) : (_jsx(React.Suspense, { fallback: null, children: _jsx(EphemeralDocument, { documentId: ephemeralDocumentId, runtimePodName: runtimeId || activeAgentId, runtimeOverride: ephemeralRuntimeOverride, content: persistedEphemeralDocument ?? undefined, onContentChange: handleEphemeralDocumentChange, onToolsReady: handleDocumentToolsReady, onKernelChange: handleDocumentKernelChange, collaboration: ephemeralDocumentCollaboration }) })) }), !surfaceCollapsed && (_jsxs(Box, { sx: surfaceChatFloating
2940
+ }, children: launching || overlay ? (_jsx(CompanionSurfaceSkeleton, { mode: notebookVisible ? 'notebook' : 'document' })) : notebookVisible ? (_jsx(EphemeralNotebook, { notebookId: ephemeralNotebookId, runtimePodName: runtimeId || activeAgentId, runtimeOverride: ephemeralRuntimeOverride, themeVariant: themeVariant, colorMode: colorMode, nbformat: persistedEphemeralNbformat ?? undefined, onNbformatChange: handleEphemeralNotebookChange, toolbarComponent: ephemeralNotebookToolbar, collaborationProvider: ephemeralNotebookCollaborationProvider })) : (_jsx(React.Suspense, { fallback: null, children: _jsx(EphemeralDocument, { documentId: ephemeralDocumentId, runtimePodName: runtimeId || activeAgentId, runtimeOverride: ephemeralRuntimeOverride, themeVariant: themeVariant, colorMode: colorMode, content: persistedEphemeralDocument ?? undefined, onContentChange: handleEphemeralDocumentChange, onToolsReady: handleDocumentToolsReady, onKernelChange: handleDocumentKernelChange, collaboration: ephemeralDocumentCollaboration }) })) }), !surfaceCollapsed && (_jsxs(Box, { sx: surfaceChatFloating
2904
2941
  ? {
2905
2942
  position: 'absolute',
2906
2943
  right: 16,