@datalayer/agent-runtimes 1.3.83 → 1.3.85

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 (168) hide show
  1. package/lib/chat/ChatFloating.d.ts +5 -4
  2. package/lib/chat/ChatFloating.js +8 -6
  3. package/lib/chat/ChatLanguage.d.ts +17 -0
  4. package/lib/chat/ChatLanguage.js +29 -0
  5. package/lib/chat/assistant/AssistantStage.d.ts +4 -4
  6. package/lib/chat/assistant/AssistantStage.js +4 -2
  7. package/lib/chat/assistant/BalloonParts.d.ts +2 -1
  8. package/lib/chat/assistant/BalloonParts.js +12 -5
  9. package/lib/chat/assistant/BalloonVisual.js +3 -1
  10. package/lib/chat/assistant/ConversationBalloon.d.ts +1 -1
  11. package/lib/chat/assistant/ConversationBalloon.js +3 -2
  12. package/lib/chat/assistant/SpeechBalloon.d.ts +1 -1
  13. package/lib/chat/assistant/SpeechBalloon.js +10 -7
  14. package/lib/chat/assistant/toolLine.d.ts +8 -4
  15. package/lib/chat/assistant/toolLine.js +13 -9
  16. package/lib/chat/base/ChatBase.js +30 -11
  17. package/lib/chat/base/loopElement.d.ts +53 -0
  18. package/lib/chat/base/loopElement.js +78 -0
  19. package/lib/chat/base/loopWindow.d.ts +31 -0
  20. package/lib/chat/base/loopWindow.js +74 -0
  21. package/lib/chat/display/EmptyState.js +3 -1
  22. package/lib/chat/index.d.ts +2 -0
  23. package/lib/chat/index.js +3 -0
  24. package/lib/chat/messages/ChatMessages.js +3 -1
  25. package/lib/chat/messages/TurnFooter.js +3 -1
  26. package/lib/chat/prompt/InputPrompt.js +3 -1
  27. package/lib/chat/prompt/InputPromptLexical.js +3 -1
  28. package/lib/chat/prompt/footer/InPromptFooter.js +3 -1
  29. package/lib/chat/tools/ToolApprovalBanner.js +5 -3
  30. package/lib/chat/tools/ToolApprovalDialog.js +5 -3
  31. package/lib/chat/tools/ToolCallDisplay.js +29 -25
  32. package/lib/chat/words.d.ts +102 -0
  33. package/lib/chat/words.js +460 -0
  34. package/lib/components/a2ui/custom/index.d.ts +39 -0
  35. package/lib/components/a2ui/custom/index.js +95 -0
  36. package/lib/components/a2ui/custom/sandbox.d.ts +73 -0
  37. package/lib/components/a2ui/custom/sandbox.js +188 -0
  38. package/lib/components/a2ui/datalayer/FileUpload.d.ts +10 -2
  39. package/lib/components/a2ui/datalayer/FileUpload.js +10 -7
  40. package/lib/components/a2ui/datalayer/Form.d.ts +2 -0
  41. package/lib/components/a2ui/datalayer/Form.js +9 -2
  42. package/lib/components/a2ui/datalayer/formWidgets.d.ts +19 -0
  43. package/lib/components/a2ui/datalayer/formWidgets.js +51 -0
  44. package/lib/components/a2ui/datalayer/index.d.ts +1 -1
  45. package/lib/components/a2ui/datalayer/index.js +1 -1
  46. package/lib/components/a2ui/datalayer/schema.d.ts +10 -0
  47. package/lib/components/a2ui/datalayer/schema.js +12 -1
  48. package/lib/components/a2ui/index.d.ts +1 -0
  49. package/lib/components/a2ui/index.js +1 -0
  50. package/lib/components/a2ui/visibleWhen.d.ts +12 -1
  51. package/lib/components/a2ui/visibleWhen.js +24 -3
  52. package/lib/components/inspector/AgentInspector.d.ts +25 -44
  53. package/lib/components/inspector/AgentInspector.js +27 -215
  54. package/lib/components/inspector/AgentInspectorDialog.d.ts +6 -6
  55. package/lib/components/inspector/AgentInspectorDialog.js +2 -2
  56. package/lib/components/inspector/a2aSpans.d.ts +51 -0
  57. package/lib/components/inspector/a2aSpans.js +373 -0
  58. package/lib/components/inspector/agentSpans.d.ts +120 -0
  59. package/lib/components/inspector/agentSpans.js +253 -0
  60. package/lib/components/inspector/chatSpans.d.ts +36 -0
  61. package/lib/components/inspector/chatSpans.js +50 -0
  62. package/lib/components/inspector/classify.d.ts +11 -8
  63. package/lib/components/inspector/classify.js +23 -24
  64. package/lib/components/inspector/index.d.ts +7 -7
  65. package/lib/components/inspector/index.js +7 -7
  66. package/lib/components/teams/A2ATeamGraph.d.ts +4 -4
  67. package/lib/components/teams/SceneTranscript.d.ts +29 -0
  68. package/lib/components/teams/SceneTranscript.js +54 -0
  69. package/lib/components/teams/SceneView.d.ts +41 -0
  70. package/lib/components/teams/SceneView.js +28 -0
  71. package/lib/components/teams/index.d.ts +3 -0
  72. package/lib/components/teams/index.js +3 -0
  73. package/lib/components/teams/sceneTranscript.d.ts +74 -0
  74. package/lib/components/teams/sceneTranscript.js +294 -0
  75. package/lib/components/teams/teamConnections.d.ts +2 -2
  76. package/lib/components/teams/teamConnections.js +3 -3
  77. package/lib/components/teams/useA2ATeam.d.ts +4 -4
  78. package/lib/components/teams/useA2ATeam.js +50 -46
  79. package/lib/examples/AgentA2ATeamExample.d.ts +10 -9
  80. package/lib/examples/AgentA2ATeamExample.js +70 -60
  81. package/lib/examples/AssistantExample.js +2 -2
  82. package/lib/loop/apps/AppComposer.d.ts +40 -4
  83. package/lib/loop/apps/AppComposer.js +194 -19
  84. package/lib/loop/apps/AppRenderer.d.ts +21 -3
  85. package/lib/loop/apps/AppRenderer.js +88 -48
  86. package/lib/loop/apps/appspec.d.ts +13 -1
  87. package/lib/loop/apps/appspec.js +305 -12
  88. package/lib/loop/apps/checks.d.ts +14 -0
  89. package/lib/loop/apps/checks.js +276 -5
  90. package/lib/loop/apps/composer.d.ts +21 -10
  91. package/lib/loop/apps/composer.js +41 -8
  92. package/lib/loop/apps/customComponents.d.ts +41 -0
  93. package/lib/loop/apps/customComponents.js +260 -0
  94. package/lib/loop/apps/hostTools.d.ts +8 -1
  95. package/lib/loop/apps/hostTools.js +9 -0
  96. package/lib/loop/apps/index.d.ts +1 -0
  97. package/lib/loop/apps/index.js +1 -0
  98. package/lib/loop/apps/language.d.ts +68 -0
  99. package/lib/loop/apps/language.js +275 -0
  100. package/lib/loop/apps/settingsInputs.d.ts +29 -0
  101. package/lib/loop/apps/settingsInputs.js +147 -0
  102. package/lib/loop/apps/uploads.d.ts +43 -0
  103. package/lib/loop/apps/uploads.js +66 -0
  104. package/lib/loop/core/a2uiComponents.d.ts +10 -0
  105. package/lib/loop/core/a2uiComponents.js +15 -0
  106. package/lib/loop/core/index.d.ts +15 -1
  107. package/lib/loop/core/index.js +3 -0
  108. package/lib/loop/embed/AppEmbed.d.ts +25 -3
  109. package/lib/loop/embed/AppEmbed.js +106 -18
  110. package/lib/loop/embed/element.d.ts +5 -0
  111. package/lib/loop/embed/element.js +84 -5
  112. package/lib/loop/embed/embedConfig.d.ts +8 -0
  113. package/lib/loop/embed/embedConfig.js +18 -0
  114. package/lib/loop/embed/embedSession.d.ts +14 -0
  115. package/lib/loop/embed/embedSession.js +34 -0
  116. package/lib/loop/embed/hostBridge.d.ts +50 -3
  117. package/lib/loop/embed/hostBridge.js +22 -2
  118. package/lib/loop/index.d.ts +4 -3
  119. package/lib/loop/index.js +4 -3
  120. package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +7 -1
  121. package/lib/loop/plugins/a2ui-surface/InlineSurface.js +7 -2
  122. package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +4 -1
  123. package/lib/loop/plugins/a2ui-surface/ownComponents.d.ts +6 -0
  124. package/lib/loop/plugins/a2ui-surface/ownComponents.js +30 -0
  125. package/lib/loop/plugins/app-components/index.d.ts +26 -0
  126. package/lib/loop/plugins/app-components/index.js +56 -0
  127. package/lib/loop/plugins/app-elements/AppElements.d.ts +27 -0
  128. package/lib/loop/plugins/app-elements/AppElements.js +79 -0
  129. package/lib/loop/plugins/app-elements/index.d.ts +20 -0
  130. package/lib/loop/plugins/app-elements/index.js +43 -0
  131. package/lib/loop/plugins/app-page/AppPage.d.ts +9 -0
  132. package/lib/loop/plugins/app-page/AppPage.js +106 -8
  133. package/lib/loop/plugins/app-page/appPageModel.d.ts +17 -1
  134. package/lib/loop/plugins/app-page/appPageModel.js +122 -4
  135. package/lib/loop/plugins/app-page/index.d.ts +1 -0
  136. package/lib/loop/plugins/app-page/index.js +1 -0
  137. package/lib/loop/plugins/app-page/pageRun.d.ts +46 -0
  138. package/lib/loop/plugins/app-page/pageRun.js +111 -0
  139. package/lib/loop/plugins/assistant/index.js +8 -6
  140. package/lib/loop/plugins/chat/ChatView.js +16 -6
  141. package/lib/loop/plugins/chat/index.d.ts +1 -1
  142. package/lib/loop/plugins/chat/index.js +2 -1
  143. package/lib/models/PageTag.d.ts +1 -1
  144. package/lib/protocols/BrowserAgentAdapter.d.ts +8 -6
  145. package/lib/protocols/BrowserAgentAdapter.js +13 -10
  146. package/lib/runtimes/browser/a2aPeer.d.ts +2 -1
  147. package/lib/runtimes/browser/protocol.d.ts +1 -1
  148. package/lib/specs/actions.js +78 -1
  149. package/lib/specs/apps.d.ts +5 -0
  150. package/lib/specs/apps.js +1518 -216
  151. package/lib/specs/appspecSchema.js +630 -3
  152. package/lib/specs/teams/teams.d.ts +3 -0
  153. package/lib/specs/teams/teams.js +249 -0
  154. package/lib/specs/uiPlugins.js +6 -1
  155. package/lib/types/agentspecs.d.ts +200 -1
  156. package/lib/types/chat.d.ts +6 -5
  157. package/lib/types/teams.d.ts +13 -2
  158. package/package.json +2 -2
  159. package/scripts/codegen/generate_apps.py +20 -2
  160. package/scripts/codegen/generate_teams.py +15 -2
  161. package/lib/components/inspector/JsonTree.d.ts +0 -24
  162. package/lib/components/inspector/JsonTree.js +0 -89
  163. package/lib/components/inspector/a2aInspect.d.ts +0 -56
  164. package/lib/components/inspector/a2aInspect.js +0 -478
  165. package/lib/components/inspector/agentInspector.d.ts +0 -179
  166. package/lib/components/inspector/agentInspector.js +0 -267
  167. package/lib/components/inspector/chatInspect.d.ts +0 -38
  168. package/lib/components/inspector/chatInspect.js +0 -65
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Where an element of an application's code shows (LOOP P-18): in a side
3
+ * panel beside the conversation, or on a page of its own over it. Inline,
4
+ * an element is a message, and the chat draws it as one (P-04).
5
+ *
6
+ * The session API says it with a `CUSTOM` event named `loop.element` —
7
+ * `{id, where, title, shows}`, `shows` the A2UI surface an answer's
8
+ * components are drawn from, or `{id, closed: true}` — which the AG-UI
9
+ * adapter hands on as an `activity`. The chat does not draw it: it keeps
10
+ * what is open here, and whoever draws panels and pages (the `app-elements`
11
+ * plugin) listens. One store per page, as there is one chat per
12
+ * application on it.
13
+ */
14
+ /** The `CUSTOM` event's name. */
15
+ export declare const LOOP_ELEMENT = "loop.element";
16
+ /** Where an element opens, besides inline. */
17
+ export type LoopElementWhere = 'panel' | 'page';
18
+ /** An element open in a side panel or on a page. */
19
+ export type LoopElement = {
20
+ id: string;
21
+ where: LoopElementWhere;
22
+ title: string;
23
+ /** The surface it draws: what `render_a2ui_surface` returns. */
24
+ shows: unknown;
25
+ };
26
+ /** What an event says: an element opened or changed, or one closed. */
27
+ export type LoopElementChange = LoopElement | {
28
+ id: string;
29
+ closed: true;
30
+ };
31
+ /** The change an activity says, or `null` when it says none. */
32
+ export declare function loopElementChange(activity: {
33
+ type: string;
34
+ data: unknown;
35
+ } | undefined): LoopElementChange | null;
36
+ /**
37
+ * The elements open with a change applied: one opened is added last, one
38
+ * changed is replaced where it is, one closed is taken away.
39
+ */
40
+ export declare function withLoopElement(open: readonly LoopElement[], change: LoopElementChange): LoopElement[];
41
+ /** The elements open now, in the order they opened. */
42
+ export declare const openLoopElements: () => LoopElement[];
43
+ /** Apply a change, and tell whoever draws them. */
44
+ export declare function applyLoopElement(change: LoopElementChange): void;
45
+ /**
46
+ * Close an element on this screen: the person closed it. The code is not
47
+ * told; it may show it again.
48
+ */
49
+ export declare const closeLoopElement: (id: string) => void;
50
+ /** Close every element: a new conversation starts with none. */
51
+ export declare function clearLoopElements(): void;
52
+ /** Listen for what is open. Returns the unsubscribe. */
53
+ export declare function onLoopElements(listener: (open: LoopElement[]) => void): () => void;
@@ -0,0 +1,78 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * Where an element of an application's code shows (LOOP P-18): in a side
7
+ * panel beside the conversation, or on a page of its own over it. Inline,
8
+ * an element is a message, and the chat draws it as one (P-04).
9
+ *
10
+ * The session API says it with a `CUSTOM` event named `loop.element` —
11
+ * `{id, where, title, shows}`, `shows` the A2UI surface an answer's
12
+ * components are drawn from, or `{id, closed: true}` — which the AG-UI
13
+ * adapter hands on as an `activity`. The chat does not draw it: it keeps
14
+ * what is open here, and whoever draws panels and pages (the `app-elements`
15
+ * plugin) listens. One store per page, as there is one chat per
16
+ * application on it.
17
+ */
18
+ /** The `CUSTOM` event's name. */
19
+ export const LOOP_ELEMENT = 'loop.element';
20
+ /** The change an activity says, or `null` when it says none. */
21
+ export function loopElementChange(activity) {
22
+ if (!activity || activity.type !== LOOP_ELEMENT)
23
+ return null;
24
+ const data = activity.data;
25
+ if (!data || typeof data.id !== 'string' || !data.id)
26
+ return null;
27
+ if (data.closed === true)
28
+ return { id: data.id, closed: true };
29
+ if (data.where !== 'panel' && data.where !== 'page')
30
+ return null;
31
+ return {
32
+ id: data.id,
33
+ where: data.where,
34
+ title: typeof data.title === 'string' ? data.title : '',
35
+ shows: data.shows ?? null,
36
+ };
37
+ }
38
+ /**
39
+ * The elements open with a change applied: one opened is added last, one
40
+ * changed is replaced where it is, one closed is taken away.
41
+ */
42
+ export function withLoopElement(open, change) {
43
+ if ('closed' in change) {
44
+ return open.filter(element => element.id !== change.id);
45
+ }
46
+ const at = open.findIndex(element => element.id === change.id);
47
+ if (at < 0)
48
+ return [...open, change];
49
+ return open.map((element, index) => (index === at ? change : element));
50
+ }
51
+ let opened = [];
52
+ const listeners = new Set();
53
+ /** The elements open now, in the order they opened. */
54
+ export const openLoopElements = () => opened;
55
+ /** Apply a change, and tell whoever draws them. */
56
+ export function applyLoopElement(change) {
57
+ opened = withLoopElement(opened, change);
58
+ for (const listener of [...listeners])
59
+ listener(opened);
60
+ }
61
+ /**
62
+ * Close an element on this screen: the person closed it. The code is not
63
+ * told; it may show it again.
64
+ */
65
+ export const closeLoopElement = (id) => applyLoopElement({ id, closed: true });
66
+ /** Close every element: a new conversation starts with none. */
67
+ export function clearLoopElements() {
68
+ opened = [];
69
+ for (const listener of [...listeners])
70
+ listener(opened);
71
+ }
72
+ /** Listen for what is open. Returns the unsubscribe. */
73
+ export function onLoopElements(listener) {
74
+ listeners.add(listener);
75
+ return () => {
76
+ listeners.delete(listener);
77
+ };
78
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Window messages (LOOP P-25): what an application's code tells the page it
3
+ * sits in, outside the conversation — ``session.send_window_message(data)``.
4
+ *
5
+ * The session API says it with a `CUSTOM` event named `loop.window` —
6
+ * `{data}` — which the AG-UI adapter hands on as an `activity`. The chat
7
+ * does not draw it: it hands it to whoever listens here — the embed's host
8
+ * bridge, which raises it on the element as its `window-message` event.
9
+ */
10
+ /** The `CUSTOM` event's name. */
11
+ export declare const LOOP_WINDOW = "loop.window";
12
+ /** What an activity says to the page, or `null` when it is not a window message. */
13
+ export declare function loopWindowMessage(activity: {
14
+ type: string;
15
+ data: unknown;
16
+ } | undefined): {
17
+ data: unknown;
18
+ } | null;
19
+ /** Hand a window message to whoever listens. */
20
+ export declare function tellLoopWindow(data: unknown): void;
21
+ /** Listen for window messages. Returns the unsubscribe. */
22
+ export declare function onLoopWindowMessage(listener: (data: unknown) => void): () => void;
23
+ /**
24
+ * What a turn's stream says to the page — the `loop.window` events of the
25
+ * turn a window message posted by the page ran — and the sentence its
26
+ * `RUN_ERROR` said, if it failed.
27
+ */
28
+ export declare function windowTurnOf(stream: string): {
29
+ said: unknown[];
30
+ error: string | null;
31
+ };
@@ -0,0 +1,74 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * Window messages (LOOP P-25): what an application's code tells the page it
7
+ * sits in, outside the conversation — ``session.send_window_message(data)``.
8
+ *
9
+ * The session API says it with a `CUSTOM` event named `loop.window` —
10
+ * `{data}` — which the AG-UI adapter hands on as an `activity`. The chat
11
+ * does not draw it: it hands it to whoever listens here — the embed's host
12
+ * bridge, which raises it on the element as its `window-message` event.
13
+ */
14
+ /** The `CUSTOM` event's name. */
15
+ export const LOOP_WINDOW = 'loop.window';
16
+ /** What an activity says to the page, or `null` when it is not a window message. */
17
+ export function loopWindowMessage(activity) {
18
+ if (!activity || activity.type !== LOOP_WINDOW)
19
+ return null;
20
+ const said = activity.data;
21
+ if (!said || typeof said !== 'object' || !('data' in said))
22
+ return null;
23
+ return { data: said.data };
24
+ }
25
+ const listeners = new Set();
26
+ /** Hand a window message to whoever listens. */
27
+ export function tellLoopWindow(data) {
28
+ for (const listener of [...listeners])
29
+ listener(data);
30
+ }
31
+ /** Listen for window messages. Returns the unsubscribe. */
32
+ export function onLoopWindowMessage(listener) {
33
+ listeners.add(listener);
34
+ return () => {
35
+ listeners.delete(listener);
36
+ };
37
+ }
38
+ /**
39
+ * What a turn's stream says to the page — the `loop.window` events of the
40
+ * turn a window message posted by the page ran — and the sentence its
41
+ * `RUN_ERROR` said, if it failed.
42
+ */
43
+ export function windowTurnOf(stream) {
44
+ const said = [];
45
+ let error = null;
46
+ for (const block of stream.split('\n\n')) {
47
+ const data = block
48
+ .split('\n')
49
+ .filter(line => line.startsWith('data:'))
50
+ .map(line => line.slice('data:'.length).trim())
51
+ .join('');
52
+ if (!data)
53
+ continue;
54
+ let event;
55
+ try {
56
+ event = JSON.parse(data);
57
+ }
58
+ catch {
59
+ continue;
60
+ }
61
+ if (event.type === 'RUN_ERROR') {
62
+ error = String(event.message ?? 'The turn failed.');
63
+ }
64
+ else if (event.type === 'CUSTOM') {
65
+ const message = loopWindowMessage({
66
+ type: String(event.name),
67
+ data: event.value,
68
+ });
69
+ if (message)
70
+ said.push(message.data);
71
+ }
72
+ }
73
+ return { said, error };
74
+ }
@@ -3,6 +3,7 @@ import { Text, LabelGroup, Label, Truncate, ThemeProvider, } from '@primer/react
3
3
  import { Box } from '@datalayer/primer-addons';
4
4
  import { AiAgentIcon } from '@datalayer/icons-react';
5
5
  import { groupSuggestions } from './groupSuggestions';
6
+ import { useChatWords } from '../ChatLanguage';
6
7
  export { groupSuggestions } from './groupSuggestions';
7
8
  /**
8
9
  * How wide a suggestion chip stands, in pixels — fixed, not a cap.
@@ -21,6 +22,7 @@ export const SUGGESTION_CHIP_WIDTH = 140;
21
22
  // Component
22
23
  // ---------------------------------------------------------------------------
23
24
  export function ChatEmptyState({ emptyState, brandIcon, description, suggestions, onSuggestionSubmit, onSuggestionFill, submitOnSuggestionClick = true, }) {
25
+ const chatText = useChatWords();
24
26
  // Custom render takes precedence
25
27
  if (emptyState?.render) {
26
28
  return _jsx(_Fragment, { children: emptyState.render() });
@@ -107,7 +109,7 @@ export function ChatEmptyState({ emptyState, brandIcon, description, suggestions
107
109
  }, children: section.title }), section.subtitle ? (_jsx(Text, { sx: { fontSize: level === 0 ? 1 : 0 }, children: section.subtitle })) : null, (() => {
108
110
  const own = (suggestions ?? []).filter(suggestion => suggestion.group === section.group);
109
111
  return own.length > 0 ? chips(own) : null;
110
- })()] }, section.group)))) : (_jsxs(_Fragment, { children: [emptyState?.icon || brandIcon || _jsx(AiAgentIcon, { colored: true, size: 48 }), _jsx(Text, { sx: { fontSize: 2 }, children: emptyState?.title || 'Start a conversation' }), (emptyState?.subtitle || description) && (_jsx(Text, { sx: { fontSize: 1 }, children: emptyState?.subtitle || description }))] })), blocks.map(block => (_jsxs(Box, { sx: {
112
+ })()] }, section.group)))) : (_jsxs(_Fragment, { children: [emptyState?.icon || brandIcon || _jsx(AiAgentIcon, { colored: true, size: 48 }), _jsx(Text, { sx: { fontSize: 2 }, children: emptyState?.title || chatText.startConversation }), (emptyState?.subtitle || description) && (_jsx(Text, { sx: { fontSize: 1 }, children: emptyState?.subtitle || description }))] })), blocks.map(block => (_jsxs(Box, { sx: {
111
113
  display: 'flex',
112
114
  flexDirection: 'column',
113
115
  alignItems: 'center',
@@ -70,3 +70,5 @@ export { ChatFloating, type ChatFloatingProps } from './ChatFloating';
70
70
  export { ChatInline, type ChatInlineProps, type ChatInlineProtocolConfig, } from './ChatInline';
71
71
  export { AgentRuntimeChat, type AgentRuntimeChatProps, } from '../agents/AgentRuntimeChat';
72
72
  export * from './EphemeralSurfaceControl';
73
+ export { ChatLanguage, useChatLanguage, useChatWords } from './ChatLanguage';
74
+ export { CHAT_WORDS, ENGLISH_CHAT_WORDS, chatLanguage, chatWords, type AroundName, type ChatWords, } from './words';
package/lib/chat/index.js CHANGED
@@ -85,3 +85,6 @@ export { ChatInline, } from './ChatInline';
85
85
  // Generic full-page chat surface for a live agent runtime (with launching state)
86
86
  export { AgentRuntimeChat, } from '../agents/AgentRuntimeChat';
87
87
  export * from './EphemeralSurfaceControl';
88
+ // The chat's own words in the person's language (LOOP P-26)
89
+ export { ChatLanguage, useChatLanguage, useChatWords } from './ChatLanguage';
90
+ export { CHAT_WORDS, ENGLISH_CHAT_WORDS, chatLanguage, chatWords, } from './words';
@@ -15,10 +15,12 @@ import { Box, useThemeStore, getColorPalette } from '@datalayer/primer-addons';
15
15
  import { PersonIcon, ToolsIcon } from '@primer/octicons-react';
16
16
  import { AiAgentIcon } from '@datalayer/icons-react';
17
17
  import { useChatMessages, useChatUIPluginRegistry, } from '../../stores/chatStore';
18
+ import { useChatWords } from '../ChatLanguage';
18
19
  /**
19
20
  * Chat Messages component
20
21
  */
21
22
  export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps = true, showAvatars = true, autoScroll = true, className, uiPluginRegistry: uiPluginRegistryProp, }) {
23
+ const chatText = useChatWords();
22
24
  const messages = useChatMessages();
23
25
  const storeUIPluginRegistry = useChatUIPluginRegistry();
24
26
  const uiPluginRegistry = uiPluginRegistryProp ?? storeUIPluginRegistry;
@@ -97,7 +99,7 @@ export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps
97
99
  height: '100%',
98
100
  p: 4,
99
101
  color: 'fg.muted',
100
- }, children: [_jsx(AiAgentIcon, { colored: true, size: 48 }), _jsx(Text, { sx: { mt: 3, fontSize: 2 }, children: "Start a conversation" }), _jsx(Text, { sx: { mt: 1, fontSize: 1 }, children: "Send a message to begin chatting" })] }));
102
+ }, children: [_jsx(AiAgentIcon, { colored: true, size: 48 }), _jsx(Text, { sx: { mt: 3, fontSize: 2 }, children: chatText.startConversation }), _jsx(Text, { sx: { mt: 1, fontSize: 1 }, children: chatText.sendToBegin })] }));
101
103
  }
102
104
  return (_jsx(Box, { ref: containerRef, className: className, sx: {
103
105
  flex: 1,
@@ -4,7 +4,9 @@ import { IconButton, Text } from '@primer/react';
4
4
  import { Box } from '@datalayer/primer-addons';
5
5
  import { CheckIcon, CopyIcon, TrashIcon } from '@primer/octicons-react';
6
6
  import { formatTokenCount } from '../../utils';
7
+ import { useChatWords } from '../ChatLanguage';
7
8
  export function TurnFooter({ usage, live, latest, padding, onCopy, onRemove, }) {
9
+ const chatText = useChatWords();
8
10
  const [copied, setCopied] = useState(false);
9
11
  const copiedTimer = useRef(null);
10
12
  useEffect(() => () => {
@@ -70,6 +72,6 @@ export function TurnFooter({ usage, live, latest, padding, onCopy, onRemove, })
70
72
  '&:hover [data-turn-actions], &:focus-within [data-turn-actions]': {
71
73
  opacity: 1,
72
74
  },
73
- }, children: [record && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default', fontSize: 0 }, children: formatTokenCount(record.contextTokens) }), ' / ', formatTokenCount(record.contextWindow), ' ctx · turn ', formatTokenCount(record.inputTokens), _jsx(Text, { as: "span", sx: { color: 'success.fg', fontSize: 0 }, children: '▲' }), ' ', formatTokenCount(record.outputTokens), _jsx(Text, { as: "span", sx: { color: 'attention.fg', fontSize: 0 }, children: '▼' })] })), !live && (_jsxs(Box, { "data-turn-actions": "", sx: { display: 'flex', alignItems: 'center' }, children: [_jsx(IconButton, { "data-turn-copy": "", icon: copied ? CheckIcon : CopyIcon, "aria-label": "Copy this turn", size: "small", variant: "invisible", onClick: () => void handleCopy() }), _jsx(IconButton, { "data-turn-remove": "", icon: TrashIcon, "aria-label": "Remove this turn", size: "small", variant: "invisible", onClick: onRemove })] }))] }));
75
+ }, children: [record && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default', fontSize: 0 }, children: formatTokenCount(record.contextTokens) }), ' / ', formatTokenCount(record.contextWindow), ' ctx · turn ', formatTokenCount(record.inputTokens), _jsx(Text, { as: "span", sx: { color: 'success.fg', fontSize: 0 }, children: '▲' }), ' ', formatTokenCount(record.outputTokens), _jsx(Text, { as: "span", sx: { color: 'attention.fg', fontSize: 0 }, children: '▼' })] })), !live && (_jsxs(Box, { "data-turn-actions": "", sx: { display: 'flex', alignItems: 'center' }, children: [_jsx(IconButton, { "data-turn-copy": "", icon: copied ? CheckIcon : CopyIcon, "aria-label": chatText.copyTurn, size: "small", variant: "invisible", onClick: () => void handleCopy() }), _jsx(IconButton, { "data-turn-remove": "", icon: TrashIcon, "aria-label": chatText.removeTurn, size: "small", variant: "invisible", onClick: onRemove })] }))] }));
74
76
  }
75
77
  export default TurnFooter;
@@ -22,6 +22,7 @@ import { SkillsStatusIndicator } from '../indicators/SkillsStatusIndicator';
22
22
  const ContextPie = lazy(() => import('../usage/ContextPie').then(module => ({
23
23
  default: module.ContextPie,
24
24
  })));
25
+ import { useChatWords } from '../ChatLanguage';
25
26
  /* Shared, so a defaulted prop is the same object on every render: a fresh
26
27
  `new Map()` per render is a new dependency for anything memoising on it. */
27
28
  const EMPTY_TOOL_MAP = new Map();
@@ -30,6 +31,7 @@ const EMPTY_ID_SET = new Set();
30
31
  // Component
31
32
  // ---------------------------------------------------------------------------
32
33
  export function InputPrompt({ input, setInput, isLoading, kernelStatus, connectionConfirmed, placeholder, typingSuggestions, autoFocus, focusTrigger, padding, placement, floatingAnchor = 'bottom', draggable = false, container, footerExtras, leadingPanel, trailingPanel, onSend, onStop, disableInputPrompt = false, promptVariant, mentionableAgents, promptCommands, promptHistory, headerContent, showAgentsMenu = false, showInlineAgentsMenu = true, agents = [], selectedAgentId, onSelectAgent, showTokenUsage, showContextRing = false, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, showSuggestionsMenu = true, suggestions, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, configLoading = false, models = [], decisions, selectedModel = '', onModelSelect = () => { }, availableTools = [], availableFrontendTools = [], mcpServers = [], enabledMcpTools = EMPTY_TOOL_MAP, enabledMcpToolCount = 0, onToggleMcpTool = () => { }, onToggleAllMcpServerTools = () => { }, approvedMcpTools = EMPTY_TOOL_MAP, onToggleMcpToolApproval = () => { }, skills = [], skillsLoading = false, enabledSkills = EMPTY_ID_SET, onToggleSkill = () => { }, onToggleAllSkills = () => { }, approvedSkills = EMPTY_ID_SET, onToggleSkillApproval = () => { }, apiBase, authToken, mcpStatusData, }) {
34
+ const chatText = useChatWords();
33
35
  const isKernelBusy = kernelStatus === 'busy';
34
36
  /*
35
37
  * Each menu is offered when it is switched on *and* has something behind it.
@@ -111,7 +113,7 @@ export function InputPrompt({ input, setInput, isLoading, kernelStatus, connecti
111
113
  it. Both, rather than either: a host contributing its own controls
112
114
  should not lose the one control that says who is being addressed.
113
115
  */
114
- headerContent: inlineAgents || headerContent ? (_jsxs(_Fragment, { children: [inlineAgents ? (_jsx(InlineAgentsMenu, { agents: agents, selectedAgentId: selectedAgentId, onSelectAgent: onSelectAgent })) : null, headerContent] })) : undefined, placeholder: placeholder || 'Type a message...', typingSuggestions: typingSuggestions, isLoading: isLoading, isKernelBusy: isKernelBusy, disabled: disableInputPrompt, readOnly: !connectionConfirmed, onSend: onSend, onStop: onStop, autoFocus: autoFocus, focusTrigger: (focusTrigger ?? 0) + pickedFocus, padding: padding, value: input, onChange: setInput, footerRightContent: _jsxs(_Fragment, { children: [_jsx(McpStatusIndicator, { apiBase: apiBase, authToken: authToken, data: mcpStatusData }), _jsx(SkillsStatusIndicator, { skillsCount: skills.length, enabledCount: enabledSkills.size, loading: skillsLoading })] }) }), _jsx(BelowPromptHeader, { disabled: disableInputPrompt, stacks: showTokenUsage
116
+ headerContent: inlineAgents || headerContent ? (_jsxs(_Fragment, { children: [inlineAgents ? (_jsx(InlineAgentsMenu, { agents: agents, selectedAgentId: selectedAgentId, onSelectAgent: onSelectAgent })) : null, headerContent] })) : undefined, placeholder: placeholder || chatText.typeMessage, typingSuggestions: typingSuggestions, isLoading: isLoading, isKernelBusy: isKernelBusy, disabled: disableInputPrompt, readOnly: !connectionConfirmed, onSend: onSend, onStop: onStop, autoFocus: autoFocus, focusTrigger: (focusTrigger ?? 0) + pickedFocus, padding: padding, value: input, onChange: setInput, footerRightContent: _jsxs(_Fragment, { children: [_jsx(McpStatusIndicator, { apiBase: apiBase, authToken: authToken, data: mcpStatusData }), _jsx(SkillsStatusIndicator, { skillsCount: skills.length, enabledCount: enabledSkills.size, loading: skillsLoading })] }) }), _jsx(BelowPromptHeader, { disabled: disableInputPrompt, stacks: showTokenUsage
115
117
  ? [
116
118
  {
117
119
  id: 'context-usage',
@@ -29,6 +29,7 @@ import { MentionNode } from './plugins/MentionNode';
29
29
  import { Box } from '@datalayer/primer-addons';
30
30
  import { AgentMentionPlugin, } from './plugins/AgentMentionPlugin';
31
31
  import { CommandPlugin, PROMPT_COMMANDS, } from './plugins/CommandPlugin';
32
+ import { useChatWords } from '../ChatLanguage';
32
33
  // ---- Lexical extension (plain-text only) ---------------------------------
33
34
  // Module scope on purpose: the composer rebuilds the editor whenever this
34
35
  // reference changes.
@@ -266,6 +267,7 @@ function ReadOnlyPlugin({ readOnly }) {
266
267
  return null;
267
268
  }
268
269
  export function InputPromptLexical({ value, onChange, placeholder = 'Ask anything…', disabled = false, readOnly = false, onSubmit, onHistory, autoFocus = false, focusSignal, mentionableAgents, promptCommands, }) {
270
+ const chatText = useChatWords();
269
271
  return (_jsx(Box, { sx: {
270
272
  /*
271
273
  The placeholder is positioned against this box.
@@ -313,7 +315,7 @@ export function InputPromptLexical({ value, onChange, placeholder = 'Ask anythin
313
315
  fontSize: 1,
314
316
  pointerEvents: 'none',
315
317
  userSelect: 'none',
316
- }, children: placeholder }), "aria-placeholder": placeholder, className: "input-prompt-lexical-content", "aria-label": "Message input", style: {
318
+ }, children: placeholder }), "aria-placeholder": placeholder, className: "input-prompt-lexical-content", "aria-label": chatText.messageInput, style: {
317
319
  outline: 'none',
318
320
  minHeight: 32,
319
321
  maxHeight: 120,
@@ -3,7 +3,9 @@ import { IconButton } from '@primer/react';
3
3
  import { Box } from '@datalayer/primer-addons';
4
4
  import { PaperAirplaneIcon, SquareCircleIcon, PauseIcon, } from '@primer/octicons-react';
5
5
  import { PromptStacks } from '../stack';
6
+ import { useChatWords } from '../../ChatLanguage';
6
7
  export function InPromptFooter({ stacks, children, rightContent, isLoading = false, sendDisabled = false, isKernelBusy = false, onSend, onStop, }) {
8
+ const chatText = useChatWords();
7
9
  return (_jsxs(_Fragment, { children: [stacks && stacks.length > 0 ? _jsx(PromptStacks, { stacks: stacks }) : null, _jsxs(Box, { "data-prompt-stack": "controls", sx: {
8
10
  display: 'flex',
9
11
  alignItems: 'center',
@@ -11,6 +13,6 @@ export function InPromptFooter({ stacks, children, rightContent, isLoading = fal
11
13
  px: 2,
12
14
  pt: 1,
13
15
  pb: 2,
14
- }, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, flex: 1 }, children: children }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [rightContent, isLoading ? (_jsx(IconButton, { icon: SquareCircleIcon, "aria-label": "Stop", "data-prompt-action": "stop", onClick: onStop, size: "small", variant: "invisible" })) : isKernelBusy ? (_jsx(IconButton, { icon: PauseIcon, "aria-label": "Pause (kernel busy)", onClick: onStop, size: "small", variant: "invisible", disabled: !onStop })) : (_jsx(IconButton, { icon: PaperAirplaneIcon, "aria-label": "Send", onClick: onSend, disabled: sendDisabled, size: "small", variant: "invisible" }))] })] })] }));
16
+ }, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, flex: 1 }, children: children }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [rightContent, isLoading ? (_jsx(IconButton, { icon: SquareCircleIcon, "aria-label": chatText.stop, "data-prompt-action": "stop", onClick: onStop, size: "small", variant: "invisible" })) : isKernelBusy ? (_jsx(IconButton, { icon: PauseIcon, "aria-label": chatText.pauseKernelBusy, onClick: onStop, size: "small", variant: "invisible", disabled: !onStop })) : (_jsx(IconButton, { icon: PaperAirplaneIcon, "aria-label": chatText.send, onClick: onSend, disabled: sendDisabled, size: "small", variant: "invisible" }))] })] })] }));
15
17
  }
16
18
  export default InPromptFooter;
@@ -15,10 +15,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  import { useState } from 'react';
16
16
  import { Box, Button, Text, CounterLabel } from '@primer/react';
17
17
  import { ShieldCheckIcon, AlertIcon, CheckIcon, XIcon, } from '@primer/octicons-react';
18
+ import { useChatWords } from '../ChatLanguage';
18
19
  /**
19
20
  * Persistent banner for tool approval notifications.
20
21
  */
21
22
  export function ToolApprovalBanner({ pendingApprovals, onReview, onApproveAll, onDismiss, collapsible = true, }) {
23
+ const chatText = useChatWords();
22
24
  const [collapsed, setCollapsed] = useState(false);
23
25
  if (pendingApprovals.length === 0) {
24
26
  return null;
@@ -34,7 +36,7 @@ export function ToolApprovalBanner({ pendingApprovals, onReview, onApproveAll, o
34
36
  borderBottom: '1px solid',
35
37
  borderColor: 'attention.muted',
36
38
  cursor: 'pointer',
37
- }, onClick: () => setCollapsed(false), children: [_jsx(ShieldCheckIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: "Tool approvals pending" }), _jsx(CounterLabel, { children: pendingApprovals.length })] }));
39
+ }, onClick: () => setCollapsed(false), children: [_jsx(ShieldCheckIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: chatText.toolApprovalsPending }), _jsx(CounterLabel, { children: pendingApprovals.length })] }));
38
40
  }
39
41
  return (_jsxs(Box, { sx: {
40
42
  borderBottom: '1px solid',
@@ -46,7 +48,7 @@ export function ToolApprovalBanner({ pendingApprovals, onReview, onApproveAll, o
46
48
  justifyContent: 'space-between',
47
49
  px: 3,
48
50
  py: 2,
49
- }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(AlertIcon, { size: 16 }), _jsxs(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: [pendingApprovals.length, " tool", ' ', pendingApprovals.length === 1 ? 'approval' : 'approvals', " pending"] })] }), _jsxs(Box, { sx: { display: 'flex', gap: 1 }, children: [onApproveAll && pendingApprovals.length >= 1 && (_jsx(Button, { size: "small", variant: "primary", leadingVisual: CheckIcon, onClick: onApproveAll, children: "Approve all" })), collapsible && (_jsx(Button, { size: "small", variant: "invisible", onClick: () => setCollapsed(true), children: "Collapse" })), onDismiss && (_jsx(Button, { size: "small", variant: "invisible", leadingVisual: XIcon, onClick: onDismiss, "aria-label": "Dismiss" }))] })] }), _jsx(Box, { sx: { px: 3, pb: 2 }, children: pendingApprovals.map(approval => (_jsxs(Box, { sx: {
51
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(AlertIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: chatText.approvalsPending(pendingApprovals.length) })] }), _jsxs(Box, { sx: { display: 'flex', gap: 1 }, children: [onApproveAll && pendingApprovals.length >= 1 && (_jsx(Button, { size: "small", variant: "primary", leadingVisual: CheckIcon, onClick: onApproveAll, children: chatText.approveAll })), collapsible && (_jsx(Button, { size: "small", variant: "invisible", onClick: () => setCollapsed(true), children: chatText.collapse })), onDismiss && (_jsx(Button, { size: "small", variant: "invisible", leadingVisual: XIcon, onClick: onDismiss, "aria-label": chatText.dismiss }))] })] }), _jsx(Box, { sx: { px: 3, pb: 2 }, children: pendingApprovals.map(approval => (_jsxs(Box, { sx: {
50
52
  display: 'flex',
51
53
  alignItems: 'center',
52
54
  justifyContent: 'space-between',
@@ -57,6 +59,6 @@ export function ToolApprovalBanner({ pendingApprovals, onReview, onApproveAll, o
57
59
  borderRadius: 2,
58
60
  border: '1px solid',
59
61
  borderColor: 'border.default',
60
- }, children: [_jsxs(Box, { children: [_jsx(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: approval.toolName }), approval.toolDescription && (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 0, ml: 2 }, children: approval.toolDescription }))] }), _jsx(Button, { size: "small", variant: "default", onClick: () => onReview(approval), children: "Review" })] }, approval.id))) })] }));
62
+ }, children: [_jsxs(Box, { children: [_jsx(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: approval.toolName }), approval.toolDescription && (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 0, ml: 2 }, children: approval.toolDescription }))] }), _jsx(Button, { size: "small", variant: "default", onClick: () => onReview(approval), children: chatText.review })] }, approval.id))) })] }));
61
63
  }
62
64
  export default ToolApprovalBanner;
@@ -13,10 +13,12 @@ import { useCallback, useState } from 'react';
13
13
  import { Box, Button, Text, Heading, FormControl, Checkbox, } from '@primer/react';
14
14
  import { Dialog } from '@primer/react/experimental';
15
15
  import { AlertIcon, ToolsIcon, CheckIcon, XIcon } from '@primer/octicons-react';
16
+ import { useChatWords } from '../ChatLanguage';
16
17
  /**
17
18
  * Tool Approval Dialog component
18
19
  */
19
20
  export function ToolApprovalDialog({ isOpen, toolName, toolDescription, args, onApprove, onDeny, onClose, showRememberChoice = true, }) {
21
+ const chatText = useChatWords();
20
22
  const [rememberChoice, setRememberChoice] = useState(false);
21
23
  const handleApprove = useCallback(() => {
22
24
  onApprove(rememberChoice);
@@ -27,12 +29,12 @@ export function ToolApprovalDialog({ isOpen, toolName, toolDescription, args, on
27
29
  if (!isOpen) {
28
30
  return null;
29
31
  }
30
- return (_jsxs(Dialog, { onClose: onClose, "aria-labelledby": "tool-approval-title", children: [_jsx(Dialog.Header, { children: _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(AlertIcon, { size: 16 }), "Tool Approval Required"] }) }), _jsxs(Box, { sx: { p: 3 }, children: [_jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, mb: 2 }, children: [_jsx(ToolsIcon, { size: 20 }), _jsx(Heading, { as: "h4", sx: { fontSize: 2 }, children: toolName })] }), toolDescription && (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: toolDescription }))] }), _jsxs(Box, { sx: { mb: 3 }, children: [_jsx(Text, { sx: {
32
+ return (_jsxs(Dialog, { onClose: onClose, "aria-labelledby": "tool-approval-title", children: [_jsx(Dialog.Header, { children: _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(AlertIcon, { size: 16 }), chatText.approvalTitle] }) }), _jsxs(Box, { sx: { p: 3 }, children: [_jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, mb: 2 }, children: [_jsx(ToolsIcon, { size: 20 }), _jsx(Heading, { as: "h4", sx: { fontSize: 2 }, children: toolName })] }), toolDescription && (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: toolDescription }))] }), _jsxs(Box, { sx: { mb: 3 }, children: [_jsx(Text, { sx: {
31
33
  fontWeight: 'semibold',
32
34
  fontSize: 1,
33
35
  mb: 2,
34
36
  display: 'block',
35
- }, children: "Arguments:" }), _jsx(Box, { as: "pre", sx: {
37
+ }, children: chatText.arguments }), _jsx(Box, { as: "pre", sx: {
36
38
  p: 3,
37
39
  bg: 'canvas.subtle',
38
40
  borderRadius: 2,
@@ -48,7 +50,7 @@ export function ToolApprovalDialog({ isOpen, toolName, toolDescription, args, on
48
50
  border: '1px solid',
49
51
  borderColor: 'attention.muted',
50
52
  mb: 3,
51
- }, children: _jsx(Text, { sx: { fontSize: 1 }, children: "This tool will perform an action on your behalf. Please review the arguments before approving." }) }), showRememberChoice && (_jsxs(FormControl, { sx: { mb: 3 }, children: [_jsx(Checkbox, { checked: rememberChoice, onChange: e => setRememberChoice(e.target.checked) }), _jsx(FormControl.Label, { children: "Remember my choice for this tool" })] })), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end', gap: 2 }, children: [_jsx(Button, { variant: "danger", onClick: handleDeny, leadingVisual: XIcon, children: "Deny" }), _jsx(Button, { variant: "primary", onClick: handleApprove, leadingVisual: CheckIcon, children: "Approve" })] })] })] }));
53
+ }, children: _jsx(Text, { sx: { fontSize: 1 }, children: chatText.approvalWarning }) }), showRememberChoice && (_jsxs(FormControl, { sx: { mb: 3 }, children: [_jsx(Checkbox, { checked: rememberChoice, onChange: e => setRememberChoice(e.target.checked) }), _jsx(FormControl.Label, { children: chatText.rememberChoice })] })), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end', gap: 2 }, children: [_jsx(Button, { variant: "danger", onClick: handleDeny, leadingVisual: XIcon, children: chatText.deny }), _jsx(Button, { variant: "primary", onClick: handleApprove, leadingVisual: CheckIcon, children: chatText.approve })] })] })] }));
52
54
  }
53
55
  /**
54
56
  * Hook to manage tool approval dialog state