@datalayer/agent-runtimes 1.0.5 → 1.0.6

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 (111) hide show
  1. package/README.md +157 -10
  2. package/lib/AgentNode.d.ts +3 -0
  3. package/lib/AgentNode.js +676 -0
  4. package/lib/agent-node/themeStore.d.ts +3 -0
  5. package/lib/agent-node/themeStore.js +156 -0
  6. package/lib/agent-node-main.d.ts +1 -0
  7. package/lib/agent-node-main.js +14 -0
  8. package/lib/chat/Chat.js +16 -10
  9. package/lib/chat/ChatFloating.js +1 -1
  10. package/lib/chat/ChatSidebar.js +81 -49
  11. package/lib/chat/base/ChatBase.js +388 -74
  12. package/lib/chat/display/FloatingBrandButton.js +8 -1
  13. package/lib/chat/header/ChatHeader.d.ts +3 -1
  14. package/lib/chat/header/ChatHeader.js +15 -12
  15. package/lib/chat/header/ChatHeaderBase.d.ts +29 -9
  16. package/lib/chat/header/ChatHeaderBase.js +26 -3
  17. package/lib/chat/indicators/SandboxStatusIndicator.js +82 -47
  18. package/lib/chat/messages/ChatMessageList.js +46 -1
  19. package/lib/chat/messages/ChatMessages.js +6 -2
  20. package/lib/chat/prompt/InputFooter.d.ts +3 -1
  21. package/lib/chat/prompt/InputFooter.js +8 -5
  22. package/lib/chat/prompt/InputPrompt.d.ts +3 -1
  23. package/lib/chat/prompt/InputPrompt.js +2 -2
  24. package/lib/chat/prompt/InputPromptFooter.d.ts +3 -1
  25. package/lib/chat/prompt/InputPromptFooter.js +3 -3
  26. package/lib/client/AgentsMixin.js +14 -0
  27. package/lib/config/AgentConfiguration.d.ts +22 -0
  28. package/lib/config/AgentConfiguration.js +319 -64
  29. package/lib/examples/AgUiSharedStateExample.js +2 -1
  30. package/lib/examples/AgentCheckpointsExample.js +3 -3
  31. package/lib/examples/AgentCodemodeExample.d.ts +3 -3
  32. package/lib/examples/AgentCodemodeExample.js +24 -12
  33. package/lib/examples/AgentEvalsExample.js +330 -40
  34. package/lib/examples/AgentGuardrailsExample.js +16 -5
  35. package/lib/examples/AgentHooksExample.js +27 -9
  36. package/lib/examples/AgentInferenceProviderExample.d.ts +3 -0
  37. package/lib/examples/AgentInferenceProviderExample.js +329 -0
  38. package/lib/examples/AgentMCPExample.js +6 -5
  39. package/lib/examples/AgentMemoryExample.d.ts +1 -2
  40. package/lib/examples/AgentMemoryExample.js +71 -22
  41. package/lib/examples/AgentMonitoringExample.js +5 -5
  42. package/lib/examples/AgentNotificationsExample.d.ts +1 -2
  43. package/lib/examples/AgentNotificationsExample.js +71 -22
  44. package/lib/examples/AgentOtelExample.js +31 -40
  45. package/lib/examples/AgentOutputsExample.d.ts +1 -1
  46. package/lib/examples/AgentOutputsExample.js +67 -16
  47. package/lib/examples/AgentParametersExample.js +10 -8
  48. package/lib/examples/AgentSandboxExample.d.ts +1 -1
  49. package/lib/examples/AgentSandboxExample.js +7 -6
  50. package/lib/examples/AgentSkillsExample.js +6 -6
  51. package/lib/examples/AgentSubagentsExample.d.ts +1 -1
  52. package/lib/examples/AgentSubagentsExample.js +6 -6
  53. package/lib/examples/AgentToolApprovalsExample.js +27 -11
  54. package/lib/examples/AgentTriggersExample.js +5 -5
  55. package/lib/examples/{AgentSpecsExample.d.ts → AgentspecsExample.d.ts} +2 -2
  56. package/lib/examples/AgentspecsExample.js +1096 -0
  57. package/lib/examples/ChatCustomExample.js +6 -5
  58. package/lib/examples/ChatExample.js +6 -5
  59. package/lib/examples/Lexical2Example.js +1 -1
  60. package/lib/examples/LexicalAgentExample.js +1 -1
  61. package/lib/examples/NotebookAgentExample.js +3 -3
  62. package/lib/examples/components/ExampleWrapper.d.ts +6 -7
  63. package/lib/examples/components/ExampleWrapper.js +27 -10
  64. package/lib/examples/example-selector.js +2 -1
  65. package/lib/examples/index.d.ts +2 -1
  66. package/lib/examples/index.js +2 -1
  67. package/lib/examples/lexical/initial-content.json +6 -6
  68. package/lib/examples/main.js +56 -16
  69. package/lib/examples/utils/agentId.d.ts +1 -1
  70. package/lib/examples/utils/agentId.js +1 -1
  71. package/lib/examples/utils/useExampleAgentRuntimesUrl.d.ts +5 -0
  72. package/lib/examples/utils/useExampleAgentRuntimesUrl.js +19 -0
  73. package/lib/hooks/useAIAgentsWebSocket.js +35 -0
  74. package/lib/hooks/useAgentRuntimes.d.ts +32 -3
  75. package/lib/hooks/useAgentRuntimes.js +114 -19
  76. package/lib/index.d.ts +1 -1
  77. package/lib/specs/agents/agents.d.ts +20 -13
  78. package/lib/specs/agents/agents.js +1267 -581
  79. package/lib/specs/benchmarks.d.ts +20 -0
  80. package/lib/specs/benchmarks.js +205 -0
  81. package/lib/specs/envvars.d.ts +0 -1
  82. package/lib/specs/envvars.js +0 -11
  83. package/lib/specs/evals.d.ts +10 -9
  84. package/lib/specs/evals.js +128 -88
  85. package/lib/specs/index.d.ts +0 -1
  86. package/lib/specs/index.js +0 -1
  87. package/lib/specs/models.d.ts +0 -2
  88. package/lib/specs/models.js +0 -15
  89. package/lib/specs/skills.d.ts +0 -1
  90. package/lib/specs/skills.js +0 -18
  91. package/lib/stores/agentRuntimeStore.d.ts +5 -1
  92. package/lib/stores/agentRuntimeStore.js +22 -8
  93. package/lib/stores/conversationStore.js +2 -2
  94. package/lib/types/agents-lifecycle.d.ts +18 -0
  95. package/lib/types/agents.d.ts +6 -0
  96. package/lib/types/agentspecs.d.ts +4 -0
  97. package/lib/types/benchmarks.d.ts +43 -0
  98. package/lib/types/benchmarks.js +5 -0
  99. package/lib/types/chat.d.ts +16 -0
  100. package/lib/types/evals.d.ts +26 -17
  101. package/lib/types/index.d.ts +1 -0
  102. package/lib/types/index.js +1 -0
  103. package/package.json +9 -5
  104. package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
  105. package/scripts/codegen/__pycache__/generate_benchmarks.cpython-313.pyc +0 -0
  106. package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
  107. package/scripts/codegen/generate_agents.py +89 -43
  108. package/scripts/codegen/generate_benchmarks.py +441 -0
  109. package/scripts/codegen/generate_evals.py +94 -16
  110. package/scripts/codegen/generate_events.py +0 -1
  111. package/lib/examples/AgentSpecsExample.js +0 -694
@@ -18,7 +18,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
18
18
  import { useContext } from 'react';
19
19
  import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
20
20
  import { Text, Spinner } from '@primer/react';
21
- import { Box, setupPrimerPortals } from '@datalayer/primer-addons';
21
+ import { Box, setupPrimerPortals, useThemeStore, getColorPalette, } from '@datalayer/primer-addons';
22
22
  import { AlertIcon, PersonIcon } from '@primer/octicons-react';
23
23
  import { AiAgentIcon } from '@datalayer/icons-react';
24
24
  import { QueryClientProvider, QueryClientContext } from '@tanstack/react-query';
@@ -41,6 +41,20 @@ import { ToolApprovalBanner, ToolApprovalDialog, } from '../tools';
41
41
  // This prevents layout-driven unmount/remount cycles from re-sending prompts.
42
42
  const sentPendingPromptKeys = new Set();
43
43
  const AI_AGENTS_API_PREFIX = '/api/ai-agents/v1';
44
+ const isDevTraceEnabled = () => {
45
+ try {
46
+ return Boolean(import.meta.env?.DEV);
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ };
52
+ const logApprovalTrace = (label, details) => {
53
+ if (!isDevTraceEnabled()) {
54
+ return;
55
+ }
56
+ console.debug(`[approval-trace] ${label}`, details);
57
+ };
44
58
  const normalizeAgentId = (value) => (value ?? '').trim().toLowerCase();
45
59
  const normalizeToolName = (value) => value.replace(/[-_]/g, '').toLowerCase();
46
60
  const normalizeSkillApprovalId = (value) => {
@@ -124,6 +138,22 @@ const normalizeApprovalPayload = (input) => {
124
138
  undefined,
125
139
  };
126
140
  };
141
+ const statusFromApprovalEvent = (eventName) => {
142
+ if (eventName === 'tool_approval_created') {
143
+ return 'pending';
144
+ }
145
+ if (eventName === 'tool_approval_approved') {
146
+ return 'approved';
147
+ }
148
+ if (eventName === 'tool_approval_rejected') {
149
+ return 'rejected';
150
+ }
151
+ if (eventName === 'tool_approval_deleted') {
152
+ return 'deleted';
153
+ }
154
+ return undefined;
155
+ };
156
+ const RESOLVED_TOOL_CALL_SUPPRESSION_MS = 15_000;
127
157
  const isApprovalForAgent = (approval, activeAgentId) => {
128
158
  if (!activeAgentId) {
129
159
  return true;
@@ -173,10 +203,9 @@ function extractChatMessagesFromFullContext(fullContext) {
173
203
  return rawMessages
174
204
  .map((msg, index) => {
175
205
  const role = String(msg.role || '').toLowerCase();
176
- if (role !== 'user' &&
177
- role !== 'assistant' &&
178
- role !== 'system' &&
179
- role !== 'tool') {
206
+ // Only hydrate conversational turns in the visible history.
207
+ // System/tool messages may contain internal prompts and metadata.
208
+ if (role !== 'user' && role !== 'assistant') {
180
209
  return null;
181
210
  }
182
211
  const timestampValue = typeof msg.timestamp === 'string' && msg.timestamp.length > 0
@@ -286,7 +315,7 @@ export function ChatBase(props) {
286
315
  // ---------------------------------------------------------------------------
287
316
  // ChatBaseInner — contains all actual logic
288
317
  // ---------------------------------------------------------------------------
289
- function ChatBaseInner({ title, subtitle, showHeader = false, showTokenUsage = true, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = true, showToolsMenu = true, showSkillsMenu = true, disableInputPrompt = false, codemodeEnabled = false, onToggleCodemode, initialModel, availableModels, mcpServers, initialSkills: _initialSkills, className, loadingState, headerActions, chatViewMode, onChatViewModeChange,
318
+ function ChatBaseInner({ title, subtitle, showHeader = false, showTokenUsage = true, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = true, showToolsMenu = true, showSkillsMenu = true, disableInputPrompt = false, codemodeEnabled = false, onToggleCodemode, initialModel, availableModels, mcpServers, initialSkills: _initialSkills, className, loadingState, headerActions, kernelIndicatorState, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, chatViewMode, onChatViewModeChange,
290
319
  // Mode selection
291
320
  useStore: useStoreMode = true, protocol: protocolRaw, onSendMessage, enableStreaming = false,
292
321
  // Extended props
@@ -304,18 +333,113 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
304
333
  useEffect(() => {
305
334
  setupPrimerPortals();
306
335
  }, []);
336
+ const { theme } = useThemeStore();
337
+ const assistantIconColor = getColorPalette(theme, 'dark').textLight;
307
338
  // ── Built-in pending approvals from the agent-runtime Zustand store ──
308
339
  // When the parent doesn't supply the `pendingApprovals` prop, derive them
309
340
  // from the shared store so the banner works out-of-the-box.
310
341
  const storeApprovals = useAgentRuntimeStore(s => s.approvals);
342
+ const storeMcpStatus = useAgentRuntimeStore(s => s.mcpStatus);
343
+ const effectiveMcpStatusData = mcpStatusData ?? storeMcpStatus;
311
344
  const protocolConfig = typeof protocolRaw === 'object'
312
345
  ? protocolRaw
313
346
  : undefined;
314
347
  const configuredAiAgentsBaseUrl = useCoreStore((s) => s.configuration?.aiagentsRunUrl);
315
348
  const activeAgentId = protocolConfig?.agentId || runtimeId;
349
+ const historyScopeId = runtimeId || activeAgentId;
316
350
  const aiAgentsAuthToken = protocolConfig?.authToken;
317
351
  const aiAgentsBaseUrl = useMemo(() => normalizeAiAgentsBaseUrl(configuredAiAgentsBaseUrl || DEFAULT_SERVICE_URLS.AI_AGENTS), [configuredAiAgentsBaseUrl]);
318
352
  const aiAgentsApprovalWsRef = useRef(null);
353
+ const resolvedToolCallSuppressionsRef = useRef(new Map());
354
+ const sendAiAgentsApprovalDecision = useCallback((approvalId, approved, note) => {
355
+ const ws = aiAgentsApprovalWsRef.current;
356
+ if (!ws || ws.readyState !== WebSocket.OPEN) {
357
+ logApprovalTrace('send_decision_skipped_ws_not_ready', {
358
+ approvalId,
359
+ approved,
360
+ wsReadyState: ws?.readyState,
361
+ });
362
+ return false;
363
+ }
364
+ try {
365
+ logApprovalTrace('send_decision', {
366
+ approvalId,
367
+ approved,
368
+ hasNote: Boolean(note),
369
+ });
370
+ ws.send(JSON.stringify({
371
+ type: 'tool_approval_decision',
372
+ approvalId,
373
+ approved,
374
+ ...(note ? { note } : {}),
375
+ }));
376
+ return true;
377
+ }
378
+ catch {
379
+ logApprovalTrace('send_decision_failed', {
380
+ approvalId,
381
+ approved,
382
+ });
383
+ return false;
384
+ }
385
+ }, []);
386
+ const requestAiAgentsApprovalHistory = useCallback(() => {
387
+ const ws = aiAgentsApprovalWsRef.current;
388
+ if (!ws || ws.readyState !== WebSocket.OPEN) {
389
+ return false;
390
+ }
391
+ try {
392
+ ws.send(JSON.stringify({ type: 'tool-approvals-history' }));
393
+ logApprovalTrace('request_history', {});
394
+ return true;
395
+ }
396
+ catch {
397
+ return false;
398
+ }
399
+ }, []);
400
+ const rememberResolvedToolCall = useCallback((toolCallId) => {
401
+ if (!toolCallId) {
402
+ return;
403
+ }
404
+ resolvedToolCallSuppressionsRef.current.set(toolCallId, Date.now() + RESOLVED_TOOL_CALL_SUPPRESSION_MS);
405
+ logApprovalTrace('suppress_pending_for_tool_call', {
406
+ toolCallId,
407
+ windowMs: RESOLVED_TOOL_CALL_SUPPRESSION_MS,
408
+ });
409
+ }, []);
410
+ const isSuppressedPending = useCallback((toolCallId) => {
411
+ if (!toolCallId) {
412
+ return false;
413
+ }
414
+ const expiresAt = resolvedToolCallSuppressionsRef.current.get(toolCallId);
415
+ if (!expiresAt) {
416
+ return false;
417
+ }
418
+ if (expiresAt < Date.now()) {
419
+ resolvedToolCallSuppressionsRef.current.delete(toolCallId);
420
+ return false;
421
+ }
422
+ return true;
423
+ }, []);
424
+ const queueApprovalDecisionRetry = useCallback((approvalId, approved, note) => {
425
+ window.setTimeout(() => {
426
+ const stillPending = agentRuntimeStore
427
+ .getState()
428
+ .approvals.some(approval => approval.id === approvalId && approval.status === 'pending');
429
+ if (!stillPending) {
430
+ return;
431
+ }
432
+ const resent = sendAiAgentsApprovalDecision(approvalId, approved, note);
433
+ logApprovalTrace('retry_send_decision', {
434
+ approvalId,
435
+ approved,
436
+ sent: resent,
437
+ });
438
+ if (resent) {
439
+ requestAiAgentsApprovalHistory();
440
+ }
441
+ }, 500);
442
+ }, [requestAiAgentsApprovalHistory, sendAiAgentsApprovalDecision]);
319
443
  const storePendingApprovals = useMemo(() => {
320
444
  if (pendingApprovalsProp)
321
445
  return pendingApprovalsProp;
@@ -408,36 +532,78 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
408
532
  const approval = agentRuntimeStore
409
533
  .getState()
410
534
  .approvals.find(a => a.id === approvalId);
411
- const ok = agentRuntimeStore
535
+ const resolvedToolCallId = approval?.tool_call_id ?? toolCallId;
536
+ rememberResolvedToolCall(resolvedToolCallId);
537
+ // Persist approval decision to the ai-agents backend WS (single source
538
+ // of truth). This drives SaaS Tool Approvals state and broadcast events.
539
+ const persistedViaBackend = sendAiAgentsApprovalDecision(approvalId, true, note);
540
+ if (!persistedViaBackend) {
541
+ console.warn('[ChatBase] ai-agents tool_approval_decision not sent: websocket not ready');
542
+ }
543
+ // Keep the runtime decision path so the in-flight tool call can resume.
544
+ const runtimeOk = agentRuntimeStore
412
545
  .getState()
413
- .sendDecision(approvalId, true, note, approval?.tool_call_id ?? toolCallId, activeAgentId);
414
- if (ok) {
415
- // Persist decision and clear pending approval only after a successful
416
- // WS send so failed sends keep the retryable pending UI visible.
546
+ .sendDecision(approvalId, true, note, resolvedToolCallId, activeAgentId);
547
+ if (runtimeOk) {
548
+ // Persist non-approval decisions locally; tool approvals are reconciled
549
+ // from ai-agents WS events/history to keep a single source of truth.
417
550
  persistApprovalDecision(approvalId, true);
418
- agentRuntimeStore.getState().removeApproval(approvalId);
419
551
  }
420
552
  else {
421
553
  console.warn('[ChatBase] tool_approval_decision dropped: websocket not ready');
422
554
  }
555
+ if (persistedViaBackend || runtimeOk) {
556
+ // Optimistically clear local pending UI so completed tool calls do not
557
+ // stay pinned when websocket reconciliation is delayed.
558
+ agentRuntimeStore.getState().removeApproval(approvalId);
559
+ }
560
+ requestAiAgentsApprovalHistory();
561
+ queueApprovalDecisionRetry(approvalId, true, note);
423
562
  await onApproveApprovalProp?.(approvalId, note);
424
- }, [activeAgentId, onApproveApprovalProp, persistApprovalDecision]);
563
+ }, [
564
+ activeAgentId,
565
+ rememberResolvedToolCall,
566
+ onApproveApprovalProp,
567
+ persistApprovalDecision,
568
+ queueApprovalDecisionRetry,
569
+ requestAiAgentsApprovalHistory,
570
+ sendAiAgentsApprovalDecision,
571
+ ]);
425
572
  const onRejectApproval = useCallback(async (approvalId, note, toolCallId) => {
426
573
  const approval = agentRuntimeStore
427
574
  .getState()
428
575
  .approvals.find(a => a.id === approvalId);
429
- const ok = agentRuntimeStore
576
+ const resolvedToolCallId = approval?.tool_call_id ?? toolCallId;
577
+ rememberResolvedToolCall(resolvedToolCallId);
578
+ const persistedViaBackend = sendAiAgentsApprovalDecision(approvalId, false, note);
579
+ if (!persistedViaBackend) {
580
+ console.warn('[ChatBase] ai-agents tool_approval_decision not sent: websocket not ready');
581
+ }
582
+ const runtimeOk = agentRuntimeStore
430
583
  .getState()
431
- .sendDecision(approvalId, false, note, approval?.tool_call_id ?? toolCallId, activeAgentId);
432
- if (ok) {
433
- // Keep pending approval visible when send fails, so the user can retry.
434
- agentRuntimeStore.getState().removeApproval(approvalId);
584
+ .sendDecision(approvalId, false, note, resolvedToolCallId, activeAgentId);
585
+ if (runtimeOk) {
586
+ // Tool approval list is reconciled from ai-agents WS updates/history.
435
587
  }
436
588
  else {
437
589
  console.warn('[ChatBase] tool_approval_decision dropped: websocket not ready');
438
590
  }
591
+ if (persistedViaBackend || runtimeOk) {
592
+ // Optimistically clear local pending UI so completed tool calls do not
593
+ // stay pinned when websocket reconciliation is delayed.
594
+ agentRuntimeStore.getState().removeApproval(approvalId);
595
+ }
596
+ requestAiAgentsApprovalHistory();
597
+ queueApprovalDecisionRetry(approvalId, false, note);
439
598
  await onRejectApprovalProp?.(approvalId, note);
440
- }, [activeAgentId, onRejectApprovalProp]);
599
+ }, [
600
+ activeAgentId,
601
+ rememberResolvedToolCall,
602
+ onRejectApprovalProp,
603
+ queueApprovalDecisionRetry,
604
+ requestAiAgentsApprovalHistory,
605
+ sendAiAgentsApprovalDecision,
606
+ ]);
441
607
  // Optional ai-agents bridge for server-mode visibility.
442
608
  // This keeps approval synchronization in ChatBase so examples do not need
443
609
  // their own approval websocket plumbing.
@@ -456,6 +622,9 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
456
622
  const ws = new WebSocket(wsUrl);
457
623
  aiAgentsApprovalWsRef.current = ws;
458
624
  ws.onopen = () => {
625
+ logApprovalTrace('ws_open_request_history', {
626
+ activeAgentId,
627
+ });
459
628
  ws.send(JSON.stringify({ type: 'tool-approvals-history' }));
460
629
  };
461
630
  ws.onmessage = (event) => {
@@ -484,7 +653,19 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
484
653
  ? raw.payload
485
654
  : null;
486
655
  if (data) {
487
- records.push(data);
656
+ const eventStatus = statusFromApprovalEvent(msgEvent);
657
+ logApprovalTrace('recv_tool_approval_event', {
658
+ event: msgEvent,
659
+ approvalId: typeof data.id === 'string'
660
+ ? data.id
661
+ : typeof data.approval_id === 'string'
662
+ ? data.approval_id
663
+ : undefined,
664
+ status: typeof data.status === 'string' ? data.status : eventStatus,
665
+ });
666
+ records.push(eventStatus && typeof data.status !== 'string'
667
+ ? { ...data, status: eventStatus }
668
+ : data);
488
669
  }
489
670
  }
490
671
  if (records.length === 0) {
@@ -502,6 +683,21 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
502
683
  if (!isApprovalForAgent(scopedApproval, activeAgentId)) {
503
684
  continue;
504
685
  }
686
+ if (scopedApproval.status === 'pending' &&
687
+ isSuppressedPending(scopedApproval.tool_call_id)) {
688
+ logApprovalTrace('drop_transient_pending', {
689
+ approvalId: scopedApproval.id,
690
+ toolCallId: scopedApproval.tool_call_id,
691
+ status: scopedApproval.status,
692
+ });
693
+ continue;
694
+ }
695
+ logApprovalTrace('apply_tool_approval_update', {
696
+ approvalId: scopedApproval.id,
697
+ status: scopedApproval.status,
698
+ agentId: scopedApproval.agent_id,
699
+ activeAgentId,
700
+ });
505
701
  if (scopedApproval.status === 'pending') {
506
702
  state.upsertApproval(scopedApproval);
507
703
  }
@@ -533,6 +729,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
533
729
  aiAgentsBaseUrl,
534
730
  aiAgentsAuthToken,
535
731
  activeAgentId,
732
+ isSuppressedPending,
536
733
  ]);
537
734
  // The outer ChatBase wrapper always resolves a string Protocol to a full
538
735
  // ProtocolConfig (or undefined). Narrow the type for internal use.
@@ -548,11 +745,28 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
548
745
  // ---- Component state ----
549
746
  const [displayItems, setDisplayItems] = useState([]);
550
747
  const [isLoading, setIsLoading] = useState(false);
748
+ const [liveKernelStatus, setLiveKernelStatus] = useState();
551
749
  const [isStreaming, setIsStreaming] = useState(false);
552
750
  const [error, setError] = useState(null);
553
751
  const [input, setInput] = useState('');
752
+ useEffect(() => {
753
+ if (!kernel) {
754
+ setLiveKernelStatus(undefined);
755
+ return;
756
+ }
757
+ setLiveKernelStatus(kernel.status);
758
+ const handleStatusChange = (_, nextStatus) => {
759
+ setLiveKernelStatus(nextStatus);
760
+ };
761
+ kernel.statusChanged.connect(handleStatusChange);
762
+ return () => {
763
+ kernel.statusChanged.disconnect(handleStatusChange);
764
+ };
765
+ }, [kernel]);
554
766
  // History-loaded flag — true immediately when there is nothing to fetch
555
- const [historyLoaded, setHistoryLoaded] = useState(!runtimeId);
767
+ const [historyLoaded, setHistoryLoaded] = useState(!historyScopeId);
768
+ const [historyRefreshTick, setHistoryRefreshTick] = useState(0);
769
+ const historyRetryAttemptsRef = useRef(new Map());
556
770
  // Adapter-ready flag — flipped to true once the protocol adapter is initialised
557
771
  const [adapterReady, setAdapterReady] = useState(false);
558
772
  // Guard so the pending prompt is sent at most once
@@ -660,6 +874,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
660
874
  const hideMessagesAfterToolUIRef = useRef(hideMessagesAfterToolUI);
661
875
  hideMessagesAfterToolUIRef.current = hideMessagesAfterToolUI;
662
876
  const threadIdRef = useRef(generateMessageId());
877
+ const messagesContainerRef = useRef(null);
663
878
  const messagesEndRef = useRef(null);
664
879
  const inputRef = useRef(null);
665
880
  const abortControllerRef = useRef(null);
@@ -732,7 +947,8 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
732
947
  authToken: protocol?.authToken,
733
948
  agentId: protocol?.agentId,
734
949
  onMessage: msg => {
735
- if (msg.type !== 'tool_approval_approved' &&
950
+ if (msg.type !== 'tool_approval_created' &&
951
+ msg.type !== 'tool_approval_approved' &&
736
952
  msg.type !== 'tool_approval_rejected') {
737
953
  return;
738
954
  }
@@ -740,6 +956,14 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
740
956
  if (!payload) {
741
957
  return;
742
958
  }
959
+ // Keep the top approval banner populated in local/no-token flows by
960
+ // ingesting pending approval events from the runtime stream.
961
+ if (msg.type === 'tool_approval_created') {
962
+ if (isApprovalForAgent(payload, activeAgentId)) {
963
+ agentRuntimeStore.getState().upsertApproval(payload);
964
+ }
965
+ return;
966
+ }
743
967
  applyServerApprovalDecision(payload, msg.type === 'tool_approval_approved', payload.note ?? undefined);
744
968
  },
745
969
  });
@@ -875,7 +1099,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
875
1099
  const mcpServersRef = useRef(mcpServers);
876
1100
  mcpServersRef.current = mcpServers;
877
1101
  useEffect(() => {
878
- const wsEnabledMcpTools = parseEnabledMcpToolsByServer(mcpStatusData);
1102
+ const wsEnabledMcpTools = parseEnabledMcpToolsByServer(effectiveMcpStatusData);
879
1103
  if (!wsEnabledMcpTools) {
880
1104
  return;
881
1105
  }
@@ -941,10 +1165,15 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
941
1165
  }
942
1166
  return next;
943
1167
  });
944
- }, [mcpStatusData, activeAgentId, configQuery.data?.mcpServers, wsState]);
1168
+ }, [
1169
+ effectiveMcpStatusData,
1170
+ activeAgentId,
1171
+ configQuery.data?.mcpServers,
1172
+ wsState,
1173
+ ]);
945
1174
  // Keep MCP tool *approval* synchronized with backend WS snapshots.
946
1175
  useEffect(() => {
947
- const wsApprovedMcpTools = parseApprovedMcpToolsByServer(mcpStatusData);
1176
+ const wsApprovedMcpTools = parseApprovedMcpToolsByServer(effectiveMcpStatusData);
948
1177
  if (!wsApprovedMcpTools) {
949
1178
  return;
950
1179
  }
@@ -959,12 +1188,12 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
959
1188
  });
960
1189
  return next;
961
1190
  });
962
- }, [mcpStatusData]);
1191
+ }, [effectiveMcpStatusData]);
963
1192
  // Refetch configQuery when WS reports MCP servers as started but the
964
1193
  // cached config response has missing servers or empty tools.
965
1194
  const lastConfigMcpKeyRef = useRef('');
966
1195
  useEffect(() => {
967
- const wsServers = mcpStatusData?.servers;
1196
+ const wsServers = effectiveMcpStatusData?.servers;
968
1197
  if (!wsServers || wsServers.length === 0)
969
1198
  return;
970
1199
  const startedIds = wsServers
@@ -986,7 +1215,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
986
1215
  lastConfigMcpKeyRef.current = key;
987
1216
  configQuery.refetch();
988
1217
  }
989
- }, [mcpStatusData, configQuery]);
1218
+ }, [effectiveMcpStatusData, configQuery]);
990
1219
  // initialSkills are now handled server-side during agent creation.
991
1220
  // ---- Toggle helpers ----
992
1221
  const toggleMcpTool = useCallback((serverId, toolName) => {
@@ -1122,38 +1351,46 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1122
1351
  }
1123
1352
  }, [useStoreMode, runtimeId]);
1124
1353
  // ---- Conversation history loading ----
1125
- const prevRuntimeIdRef = useRef(undefined);
1354
+ const prevHistoryScopeRef = useRef(undefined);
1126
1355
  useEffect(() => {
1127
- if (runtimeId !== prevRuntimeIdRef.current) {
1128
- prevRuntimeIdRef.current = runtimeId;
1356
+ if (historyScopeId !== prevHistoryScopeRef.current) {
1357
+ if (historyScopeId) {
1358
+ historyRetryAttemptsRef.current.set(historyScopeId, 0);
1359
+ }
1360
+ prevHistoryScopeRef.current = historyScopeId;
1129
1361
  setDisplayItems([]);
1130
1362
  toolCallsRef.current.clear();
1131
- if (!runtimeId)
1363
+ if (!historyScopeId)
1132
1364
  return;
1133
1365
  }
1134
- if (!runtimeId)
1366
+ if (!historyScopeId)
1135
1367
  return;
1136
1368
  const store = useConversationStore.getState();
1137
- const currentlyFetching = store.isFetching(runtimeId);
1138
- if (!store.needsFetch(runtimeId)) {
1139
- if (currentlyFetching) {
1140
- return;
1141
- }
1142
- const storedMessages = store.getMessages(runtimeId);
1143
- if (storedMessages.length > 0) {
1144
- setDisplayItems(storedMessages);
1145
- }
1369
+ const currentlyFetching = store.isFetching(historyScopeId);
1370
+ const storedMessages = store.getMessages(historyScopeId);
1371
+ // 1) Fast local hydration for view switches in the same browser session.
1372
+ if (storedMessages.length > 0) {
1373
+ setDisplayItems(storedMessages);
1146
1374
  setHistoryLoaded(true);
1375
+ }
1376
+ if (currentlyFetching) {
1377
+ return;
1378
+ }
1379
+ // 2) On refresh/mount, prefer websocket refresh from the runtime.
1380
+ // If the socket is not connected yet, keep retryable fetch state and wait.
1381
+ if (wsState !== 'connected') {
1382
+ if (storedMessages.length === 0) {
1383
+ setHistoryLoaded(false);
1384
+ }
1147
1385
  return;
1148
1386
  }
1149
- store.setFetching(runtimeId, true);
1387
+ store.setFetching(historyScopeId, true);
1150
1388
  const fullContextToMessages = () => extractChatMessagesFromFullContext(agentRuntimeStore.getState().fullContext);
1151
1389
  const applyMessages = (messages) => {
1152
- if (messages.length > 0) {
1153
- store.setMessages(runtimeId, messages);
1154
- setDisplayItems(convertHistoryToDisplayItems(messages));
1155
- }
1156
- store.markFetched(runtimeId);
1390
+ store.setMessages(historyScopeId, messages);
1391
+ setDisplayItems(convertHistoryToDisplayItems(messages));
1392
+ historyRetryAttemptsRef.current.set(historyScopeId, 0);
1393
+ store.markFetched(historyScopeId);
1157
1394
  setHistoryLoaded(true);
1158
1395
  };
1159
1396
  const existingMessages = fullContextToMessages();
@@ -1161,23 +1398,54 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1161
1398
  applyMessages(existingMessages);
1162
1399
  return;
1163
1400
  }
1401
+ const requestSnapshotRefresh = () => {
1402
+ const candidates = [
1403
+ activeAgentId,
1404
+ protocolConfig?.agentId,
1405
+ runtimeId,
1406
+ historyScopeId,
1407
+ 'default',
1408
+ undefined,
1409
+ ];
1410
+ const tried = new Set();
1411
+ for (const candidate of candidates) {
1412
+ const normalized = typeof candidate === 'string' ? candidate.trim() : undefined;
1413
+ const key = normalized || '__global__';
1414
+ if (tried.has(key)) {
1415
+ continue;
1416
+ }
1417
+ tried.add(key);
1418
+ const ok = agentRuntimeStore.getState().requestRefresh(normalized);
1419
+ if (ok) {
1420
+ return true;
1421
+ }
1422
+ }
1423
+ return false;
1424
+ };
1164
1425
  // Ask the monitoring websocket for a fresh snapshot and wait briefly
1165
1426
  // for `fullContext.messages` to arrive.
1166
- const refreshRequested = agentRuntimeStore.getState().requestRefresh();
1427
+ const refreshRequested = requestSnapshotRefresh();
1167
1428
  if (!refreshRequested) {
1168
1429
  // Socket not ready yet; allow a later retry (e.g. when wsState changes).
1169
- store.setFetching(runtimeId, false);
1170
- setHistoryLoaded(true);
1430
+ store.setFetching(historyScopeId, false);
1431
+ if (storedMessages.length === 0) {
1432
+ setHistoryLoaded(false);
1433
+ }
1171
1434
  return;
1172
1435
  }
1173
1436
  let resolved = false;
1437
+ const retryRefreshTimeout = window.setTimeout(() => {
1438
+ if (!resolved) {
1439
+ requestSnapshotRefresh();
1440
+ }
1441
+ }, 500);
1174
1442
  const unsubscribe = agentRuntimeStore.subscribe(state => state.fullContext, nextFullContext => {
1175
1443
  if (resolved || !nextFullContext) {
1176
1444
  return;
1177
1445
  }
1446
+ const messages = extractChatMessagesFromFullContext(nextFullContext);
1178
1447
  resolved = true;
1179
1448
  unsubscribe();
1180
- const messages = extractChatMessagesFromFullContext(nextFullContext);
1181
1449
  applyMessages(messages);
1182
1450
  });
1183
1451
  const timeout = window.setTimeout(() => {
@@ -1187,23 +1455,45 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1187
1455
  resolved = true;
1188
1456
  unsubscribe();
1189
1457
  // Do not mark as fetched on timeout; keep it retryable for late WS snapshots.
1190
- store.setFetching(runtimeId, false);
1191
- setHistoryLoaded(true);
1458
+ store.setFetching(historyScopeId, false);
1459
+ setHistoryLoaded(storedMessages.length > 0);
1460
+ const attempts = historyRetryAttemptsRef.current.get(historyScopeId) ?? 0;
1461
+ const canRetry = wsState === 'connected' && attempts < 3;
1462
+ if (canRetry) {
1463
+ historyRetryAttemptsRef.current.set(historyScopeId, attempts + 1);
1464
+ requestSnapshotRefresh();
1465
+ setHistoryRefreshTick(tick => tick + 1);
1466
+ }
1467
+ else {
1468
+ // After retries are exhausted, treat the conversation as loaded-empty
1469
+ // so pending prompts are not blocked forever on fresh runtimes.
1470
+ setHistoryLoaded(true);
1471
+ }
1192
1472
  }, 2000);
1193
1473
  return () => {
1474
+ window.clearTimeout(retryRefreshTimeout);
1194
1475
  window.clearTimeout(timeout);
1195
1476
  unsubscribe();
1196
1477
  };
1197
- }, [runtimeId, historyEndpoint, protocol?.agentId, wsState]);
1478
+ }, [
1479
+ historyScopeId,
1480
+ historyEndpoint,
1481
+ protocol?.agentId,
1482
+ wsState,
1483
+ activeAgentId,
1484
+ historyRefreshTick,
1485
+ ]);
1198
1486
  // Keep in-memory store in sync with displayItems
1199
1487
  useEffect(() => {
1200
- if (runtimeId && displayItems.length > 0) {
1488
+ if (historyScopeId && displayItems.length > 0) {
1201
1489
  const messagesToSave = displayItems.filter((item) => !isToolCallMessage(item));
1202
1490
  if (messagesToSave.length > 0) {
1203
- useConversationStore.getState().setMessages(runtimeId, messagesToSave);
1491
+ useConversationStore
1492
+ .getState()
1493
+ .setMessages(historyScopeId, messagesToSave);
1204
1494
  }
1205
1495
  }
1206
- }, [runtimeId, displayItems]);
1496
+ }, [historyScopeId, displayItems]);
1207
1497
  // ---- Derived state ----
1208
1498
  const messages = displayItems.filter((item) => !isToolCallMessage(item));
1209
1499
  const ready = true;
@@ -1227,7 +1517,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1227
1517
  }, [protocol?.configEndpoint, protocol?.authToken]);
1228
1518
  const defaultAvatarConfig = {
1229
1519
  userAvatar: _jsx(PersonIcon, { size: 16 }),
1230
- assistantAvatar: _jsx(AiAgentIcon, { size: 16 }),
1520
+ assistantAvatar: _jsx(AiAgentIcon, { size: 16, color: assistantIconColor }),
1231
1521
  showAvatars: true,
1232
1522
  avatarSize: 32,
1233
1523
  userAvatarBg: 'neutral.muted',
@@ -1552,7 +1842,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1552
1842
  pendingToolExecutionsRef.current = 0;
1553
1843
  setIsLoading(false);
1554
1844
  setIsStreaming(false);
1555
- agentRuntimeStore.getState().requestRefresh();
1845
+ agentRuntimeStore.getState().requestRefresh(activeAgentId);
1556
1846
  break;
1557
1847
  case 'error':
1558
1848
  console.error('[ChatBase] Protocol error:', event.error);
@@ -1587,7 +1877,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1587
1877
  pendingToolExecutionsRef.current = 0;
1588
1878
  setIsLoading(false);
1589
1879
  setIsStreaming(false);
1590
- agentRuntimeStore.getState().requestRefresh();
1880
+ agentRuntimeStore.getState().requestRefresh(activeAgentId);
1591
1881
  break;
1592
1882
  }
1593
1883
  });
@@ -1654,6 +1944,14 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1654
1944
  }
1655
1945
  // ---- Auto-scroll to bottom ----
1656
1946
  useEffect(() => {
1947
+ const container = messagesContainerRef.current;
1948
+ if (container) {
1949
+ container.scrollTo({
1950
+ top: container.scrollHeight,
1951
+ behavior: 'smooth',
1952
+ });
1953
+ return;
1954
+ }
1657
1955
  messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
1658
1956
  }, [displayItems]);
1659
1957
  // ========================================================================
@@ -1782,7 +2080,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1782
2080
  if (!adapterRef.current) {
1783
2081
  setIsLoading(false);
1784
2082
  setIsStreaming(false);
1785
- agentRuntimeStore.getState().requestRefresh();
2083
+ agentRuntimeStore.getState().requestRefresh(activeAgentId);
1786
2084
  }
1787
2085
  suppressAssistantTextForToolOnlyRef.current = false;
1788
2086
  currentAssistantMessageRef.current = null;
@@ -1796,6 +2094,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1796
2094
  frontendTools,
1797
2095
  useStoreMode,
1798
2096
  onSendMessage,
2097
+ activeAgentId,
1799
2098
  enableStreaming,
1800
2099
  getEnabledMcpToolNames,
1801
2100
  getEnabledSkillIds,
@@ -1879,13 +2178,20 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1879
2178
  pendingToolExecutionsRef.current = 0;
1880
2179
  setIsLoading(false);
1881
2180
  setIsStreaming(false);
1882
- agentRuntimeStore.getState().requestRefresh();
2181
+ agentRuntimeStore.getState().requestRefresh(activeAgentId);
1883
2182
  suppressAssistantTextForToolOnlyRef.current = false;
1884
2183
  currentAssistantMessageRef.current = null;
1885
2184
  // Also interrupt any code running in the sandbox (best-effort).
1886
2185
  sandboxStatusQuery.interrupt();
2186
+ // Interrupt the connected notebook kernel as well (best-effort),
2187
+ // matching the toolbar's stop/interrupt behavior.
2188
+ if (kernel && kernel.status === 'busy') {
2189
+ void kernel.interrupt().catch(() => { });
2190
+ }
1887
2191
  }, [
2192
+ kernel,
1888
2193
  useStoreMode,
2194
+ activeAgentId,
1889
2195
  protocol?.configEndpoint,
1890
2196
  protocol?.authToken,
1891
2197
  protocol?.agentId,
@@ -1899,8 +2205,8 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1899
2205
  threadIdRef.current = generateMessageId();
1900
2206
  if (useStoreMode)
1901
2207
  clearStoreMessages();
1902
- if (runtimeId)
1903
- useConversationStore.getState().clearMessages(runtimeId);
2208
+ if (historyScopeId)
2209
+ useConversationStore.getState().clearMessages(historyScopeId);
1904
2210
  onNewChat?.();
1905
2211
  headerButtons?.onNewChat?.();
1906
2212
  }, [clearStoreMessages, onNewChat, headerButtons, useStoreMode, runtimeId]);
@@ -1911,8 +2217,8 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1911
2217
  toolCallsRef.current.clear();
1912
2218
  if (useStoreMode)
1913
2219
  clearStoreMessages();
1914
- if (runtimeId)
1915
- useConversationStore.getState().clearMessages(runtimeId);
2220
+ if (historyScopeId)
2221
+ useConversationStore.getState().clearMessages(historyScopeId);
1916
2222
  onClear?.();
1917
2223
  headerButtons?.onClear?.();
1918
2224
  }
@@ -2108,7 +2414,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
2108
2414
  const configMcpServers = (configQuery.data?.mcpServers || []).filter(server => !mcpServers || isServerSelected(server));
2109
2415
  const filteredMcpServers = useMemo(() => {
2110
2416
  const merged = configMcpServers.map(server => {
2111
- const wsServer = mcpStatusData?.servers?.find(s => s.id === server.id);
2417
+ const wsServer = effectiveMcpStatusData?.servers?.find(s => s.id === server.id);
2112
2418
  if (wsServer && wsServer.status === 'started') {
2113
2419
  const updates = {};
2114
2420
  if (!server.isAvailable) {
@@ -2133,7 +2439,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
2133
2439
  // Include WS-only servers that are started but missing from the config
2134
2440
  // query (e.g. config was fetched before the MCP server finished starting).
2135
2441
  const configIds = new Set(configMcpServers.map(s => s.id));
2136
- for (const wsServer of mcpStatusData?.servers ?? []) {
2442
+ for (const wsServer of effectiveMcpStatusData?.servers ?? []) {
2137
2443
  if (wsServer.status === 'started' &&
2138
2444
  !configIds.has(wsServer.id) &&
2139
2445
  wsServer.tools &&
@@ -2162,7 +2468,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
2162
2468
  }
2163
2469
  }
2164
2470
  return merged;
2165
- }, [configMcpServers, mcpStatusData, mcpServers]);
2471
+ }, [configMcpServers, effectiveMcpStatusData, mcpServers]);
2166
2472
  // ---- Not ready ----
2167
2473
  if (!ready) {
2168
2474
  return (_jsx(Box, { className: className, sx: {
@@ -2196,12 +2502,14 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
2196
2502
  display: 'flex',
2197
2503
  flexDirection: 'column',
2198
2504
  height: '100%',
2505
+ maxHeight: '100%',
2506
+ minHeight: 0,
2199
2507
  bg: backgroundColor || 'canvas.default',
2200
2508
  borderRadius,
2201
2509
  border,
2202
2510
  boxShadow,
2203
2511
  overflow: 'hidden',
2204
- }, children: [showHeader && (_jsx(ChatBaseHeader, { title: title, subtitle: subtitle, brandIcon: brandIcon, headerContent: headerContent, headerActions: headerActions, showInformation: showInformation, onInformationClick: onInformationClick, padding: padding, sandboxApiBase: indicatorApiBase, sandboxAuthToken: protocol?.authToken, sandboxAgentId: protocol?.agentId, sandboxStatusData: sandboxStatusData, headerButtons: headerButtons, messageCount: messages.length, onNewChat: handleNewChat, onClear: handleClear, chatViewMode: chatViewMode, onChatViewModeChange: onChatViewModeChange })), showToolApprovalBanner &&
2512
+ }, children: [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: kernel, kernelEnvironmentName: kernelEnvironmentName, kernelCpu: kernelCpu, kernelMemory: kernelMemory, kernelGpu: kernelGpu, headerButtons: headerButtons, messageCount: messages.length, onNewChat: handleNewChat, onClear: handleClear, chatViewMode: chatViewMode, onChatViewModeChange: onChatViewModeChange })), showToolApprovalBanner &&
2205
2513
  pendingApprovals &&
2206
2514
  pendingApprovals.length > 0 && (_jsx(ToolApprovalBannerSection, { pendingApprovals: pendingApprovals, onApprove: handleBannerApprove, onReject: handleBannerReject })), showErrors && error && (_jsxs(Box, { sx: {
2207
2515
  display: 'flex',
@@ -2211,12 +2519,18 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
2211
2519
  bg: 'danger.subtle',
2212
2520
  borderBottom: '1px solid',
2213
2521
  borderColor: 'danger.muted',
2214
- }, children: [_jsx(AlertIcon, { size: 16 }), _jsx(Text, { sx: { color: 'danger.fg', fontSize: 1 }, children: error.message })] })), _jsx(Box, { sx: { flex: 1, flexGrow: 1, overflow: 'auto', bg: 'canvas.default' }, children: children ? (children) : (_jsx(Box, { sx: {
2522
+ }, children: [_jsx(AlertIcon, { size: 16 }), _jsx(Text, { sx: { color: 'danger.fg', fontSize: 1 }, children: error.message })] })), _jsx(Box, { ref: messagesContainerRef, sx: {
2523
+ flex: 1,
2524
+ flexGrow: 1,
2525
+ minHeight: 0,
2526
+ overflow: 'auto',
2527
+ bg: 'canvas.default',
2528
+ }, children: children ? (children) : (_jsx(Box, { sx: {
2215
2529
  display: 'flex',
2216
2530
  flexDirection: 'column',
2217
- minHeight: '100%',
2531
+ minHeight: 0,
2218
2532
  bg: 'canvas.default',
2219
- }, children: _jsx(ChatMessageList, { displayItems: displayItems, isLoading: isLoading, isStreaming: isStreaming, showLoadingIndicator: showLoadingIndicator, hideMessagesAfterToolUI: hideMessagesAfterToolUI, avatarConfig: defaultAvatarConfig, padding: padding, renderToolResult: renderToolResult, approvalConfig: approvalConfig, messagesEndRef: messagesEndRef, onRespond: handleRespond, emptyContent: _jsx(ChatEmptyState, { emptyState: emptyState, brandIcon: brandIcon, description: description, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, onSuggestionSubmit: handleSuggestionSubmit, onSuggestionFill: handleSuggestionFill }) }) })) }), footerContent, showInput && (_jsx(InputToolbar, { input: input, setInput: setInput, isLoading: isLoading, connectionConfirmed: connectionConfirmed, placeholder: placeholder, autoFocus: autoFocus, focusTrigger: focusTrigger, padding: padding, onSend: () => handleSend(), onStop: handleStop, disableInputPrompt: disableInputPrompt, showTokenUsage: showTokenUsage, agentUsage: agentUsage, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, isA2AProtocol: isA2AProtocol, hasConfigData: !!configQuery.data, hasSkillsData: !!skillsQuery.data, models: availableModels || configQuery.data?.models || [], selectedModel: selectedModel, onModelSelect: setSelectedModel, availableTools: configQuery.data?.builtinTools || [], mcpServers: filteredMcpServers, enabledMcpTools: enabledMcpTools, enabledMcpToolCount: getEnabledMcpToolNames().length, onToggleMcpTool: toggleMcpTool, onToggleAllMcpServerTools: toggleAllMcpServerTools, approvedMcpTools: approvedMcpTools, onToggleMcpToolApproval: toggleMcpToolApproval, skills: skillsQuery.data?.skills || [], skillsLoading: !!skillsQuery.isLoading, enabledSkills: enabledSkills, onToggleSkill: toggleSkill, onToggleAllSkills: toggleAllSkills, approvedSkills: approvedSkills, onToggleSkillApproval: toggleSkillApproval, apiBase: indicatorApiBase, authToken: protocol?.authToken, mcpStatusData: mcpStatusData })), showPoweredBy && _jsx(PoweredByTag, { ...poweredByProps })] }));
2533
+ }, children: _jsx(ChatMessageList, { displayItems: displayItems, isLoading: isLoading, isStreaming: isStreaming, showLoadingIndicator: showLoadingIndicator, hideMessagesAfterToolUI: hideMessagesAfterToolUI, avatarConfig: defaultAvatarConfig, padding: padding, renderToolResult: renderToolResult, approvalConfig: approvalConfig, messagesEndRef: messagesEndRef, onRespond: handleRespond, emptyContent: _jsx(ChatEmptyState, { emptyState: emptyState, brandIcon: brandIcon, description: description, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, onSuggestionSubmit: handleSuggestionSubmit, onSuggestionFill: handleSuggestionFill }) }) })) }), footerContent, showInput && (_jsx(InputToolbar, { input: input, setInput: setInput, isLoading: isLoading, kernelStatus: liveKernelStatus, connectionConfirmed: connectionConfirmed, placeholder: placeholder, autoFocus: autoFocus, focusTrigger: focusTrigger, padding: padding, onSend: () => handleSend(), onStop: handleStop, disableInputPrompt: disableInputPrompt, showTokenUsage: showTokenUsage, agentUsage: agentUsage, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, isA2AProtocol: isA2AProtocol, hasConfigData: !!configQuery.data, hasSkillsData: !!skillsQuery.data, models: availableModels || configQuery.data?.models || [], selectedModel: selectedModel, onModelSelect: setSelectedModel, availableTools: configQuery.data?.builtinTools || [], mcpServers: filteredMcpServers, enabledMcpTools: enabledMcpTools, enabledMcpToolCount: getEnabledMcpToolNames().length, onToggleMcpTool: toggleMcpTool, onToggleAllMcpServerTools: toggleAllMcpServerTools, approvedMcpTools: approvedMcpTools, onToggleMcpToolApproval: toggleMcpToolApproval, skills: skillsQuery.data?.skills || [], skillsLoading: !!skillsQuery.isLoading, enabledSkills: enabledSkills, onToggleSkill: toggleSkill, onToggleAllSkills: toggleAllSkills, approvedSkills: approvedSkills, onToggleSkillApproval: toggleSkillApproval, apiBase: indicatorApiBase, authToken: protocol?.authToken, mcpStatusData: effectiveMcpStatusData })), showPoweredBy && _jsx(PoweredByTag, { ...poweredByProps })] }));
2220
2534
  }
2221
2535
  /**
2222
2536
  * Internal component rendering the top-of-chat approval banner + review dialog.