@datalayer/agent-runtimes 1.3.25 → 1.3.27

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 (38) hide show
  1. package/lib/chat/messages/ChatMessages.d.ts +4 -4
  2. package/lib/chat/messages/ChatMessages.js +17 -17
  3. package/lib/config/AgentConfiguration.js +1 -1
  4. package/lib/examples/example-selector.js +1 -1
  5. package/lib/loop/apps/AppRenderer.js +8 -1
  6. package/lib/loop/apps/checks.d.ts +6 -1
  7. package/lib/loop/apps/checks.js +17 -0
  8. package/lib/loop/embed/LoopEmbed.js +14 -1
  9. package/lib/loop/plugins/chat/ChatView.js +9 -6
  10. package/lib/loop/plugins/chat/index.d.ts +19 -0
  11. package/lib/loop/presets.d.ts +5 -1
  12. package/lib/loop/presets.js +4 -2
  13. package/lib/protocols/A2AAdapter.d.ts +1 -1
  14. package/lib/specs/uiPlugins.d.ts +6 -1
  15. package/lib/specs/uiPlugins.js +471 -0
  16. package/lib/stores/chatStore.d.ts +11 -11
  17. package/lib/stores/chatStore.js +34 -34
  18. package/lib/stores/index.d.ts +1 -1
  19. package/lib/stores/index.js +1 -1
  20. package/lib/types/agentspecs.d.ts +33 -0
  21. package/lib/types/index.d.ts +1 -1
  22. package/lib/types/index.js +1 -1
  23. package/lib/types/{extensions.d.ts → uiPlugins.d.ts} +49 -49
  24. package/lib/types/{extensions.js → uiPlugins.js} +8 -8
  25. package/lib/{extensions/A2UIExtension.d.ts → ui-plugins/A2UIPlugin.d.ts} +7 -7
  26. package/lib/{extensions/A2UIExtension.js → ui-plugins/A2UIPlugin.js} +4 -4
  27. package/lib/{extensions/MCPUIExtension.d.ts → ui-plugins/MCPUIPlugin.d.ts} +9 -9
  28. package/lib/{extensions/MCPUIExtension.js → ui-plugins/MCPUIPlugin.js} +6 -6
  29. package/lib/ui-plugins/UIPluginRegistry.d.ts +65 -0
  30. package/lib/{extensions/ExtensionRegistry.js → ui-plugins/UIPluginRegistry.js} +32 -32
  31. package/lib/ui-plugins/index.d.ts +9 -0
  32. package/lib/ui-plugins/index.js +12 -0
  33. package/package.json +1 -1
  34. package/scripts/codegen/generate_agents.py +6 -6
  35. package/scripts/codegen/generate_ui_plugins.py +64 -4
  36. package/lib/extensions/ExtensionRegistry.d.ts +0 -65
  37. package/lib/extensions/index.d.ts +0 -9
  38. package/lib/extensions/index.js +0 -12
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import React from 'react';
8
8
  import type { ChatMessage } from '../../types/messages';
9
- import type { ExtensionRegistry } from '../../extensions/ExtensionRegistry';
9
+ import type { UIPluginRegistry } from '../../ui-plugins/UIPluginRegistry';
10
10
  /**
11
11
  * ChatMessages props
12
12
  */
@@ -26,11 +26,11 @@ export interface ChatMessagesProps {
26
26
  autoScroll?: boolean;
27
27
  /** Custom class name */
28
28
  className?: string;
29
- /** Optional extension registry (defaults to store value) */
30
- extensionRegistry?: ExtensionRegistry;
29
+ /** Optional plugin registry (defaults to store value) */
30
+ uiPluginRegistry?: UIPluginRegistry;
31
31
  }
32
32
  /**
33
33
  * Chat Messages component
34
34
  */
35
- export declare function ChatMessages({ messageRenderer, activityRenderer, showTimestamps, showAvatars, autoScroll, className, extensionRegistry: extensionRegistryProp, }: ChatMessagesProps): React.JSX.Element;
35
+ export declare function ChatMessages({ messageRenderer, activityRenderer, showTimestamps, showAvatars, autoScroll, className, uiPluginRegistry: uiPluginRegistryProp, }: ChatMessagesProps): React.JSX.Element;
36
36
  export default ChatMessages;
@@ -14,14 +14,14 @@ import { Text, RelativeTime } from '@primer/react';
14
14
  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
- import { useChatMessages, useChatExtensionRegistry, } from '../../stores/chatStore';
17
+ import { useChatMessages, useChatUIPluginRegistry, } from '../../stores/chatStore';
18
18
  /**
19
19
  * Chat Messages component
20
20
  */
21
- export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps = true, showAvatars = true, autoScroll = true, className, extensionRegistry: extensionRegistryProp, }) {
21
+ export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps = true, showAvatars = true, autoScroll = true, className, uiPluginRegistry: uiPluginRegistryProp, }) {
22
22
  const messages = useChatMessages();
23
- const storeExtensionRegistry = useChatExtensionRegistry();
24
- const extensionRegistry = extensionRegistryProp ?? storeExtensionRegistry;
23
+ const storeUIPluginRegistry = useChatUIPluginRegistry();
24
+ const uiPluginRegistry = uiPluginRegistryProp ?? storeUIPluginRegistry;
25
25
  const containerRef = useRef(null);
26
26
  const lastMessageRef = useRef(null);
27
27
  // Resolve a light color from the current user theme palette so the
@@ -40,12 +40,12 @@ export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps
40
40
  if (messageRenderer) {
41
41
  return messageRenderer(message);
42
42
  }
43
- // Check extension registry for custom renderer
44
- if (extensionRegistry) {
45
- const messageRenderers = extensionRegistry.getMessageRenderers();
46
- const extensionRenderer = messageRenderers.find(r => r.canRender(message));
47
- if (extensionRenderer) {
48
- return extensionRenderer.render({
43
+ // Check plugin registry for custom renderer
44
+ if (uiPluginRegistry) {
45
+ const messageRenderers = uiPluginRegistry.getMessageRenderers();
46
+ const pluginRenderer = messageRenderers.find(r => r.canRender(message));
47
+ if (pluginRenderer) {
48
+ return pluginRenderer.render({
49
49
  message,
50
50
  isStreaming: false,
51
51
  });
@@ -86,7 +86,7 @@ export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps
86
86
  ? 'You'
87
87
  : message.role === 'assistant'
88
88
  ? 'Assistant'
89
- : 'System' }), showTimestamps && message.createdAt && (_jsx(RelativeTime, { datetime: message.createdAt.toISOString(), sx: { fontSize: 0, color: 'fg.muted' } }))] }), _jsx(Box, { sx: { fontSize: 1, lineHeight: 1.5 }, children: renderMessageContent(message, activityRenderer, extensionRegistry ?? undefined) })] })] }, message.id));
89
+ : 'System' }), showTimestamps && message.createdAt && (_jsx(RelativeTime, { datetime: message.createdAt.toISOString(), sx: { fontSize: 0, color: 'fg.muted' } }))] }), _jsx(Box, { sx: { fontSize: 1, lineHeight: 1.5 }, children: renderMessageContent(message, activityRenderer, uiPluginRegistry ?? undefined) })] })] }, message.id));
90
90
  };
91
91
  if (messages.length === 0) {
92
92
  return (_jsxs(Box, { className: className, sx: {
@@ -107,7 +107,7 @@ export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps
107
107
  /**
108
108
  * Render message content based on type
109
109
  */
110
- function renderMessageContent(message, activityRenderer, extensionRegistry) {
110
+ function renderMessageContent(message, activityRenderer, uiPluginRegistry) {
111
111
  const { content } = message;
112
112
  // Simple string content
113
113
  if (typeof content === 'string') {
@@ -115,14 +115,14 @@ function renderMessageContent(message, activityRenderer, extensionRegistry) {
115
115
  }
116
116
  // Array of content parts
117
117
  if (Array.isArray(content)) {
118
- return (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: content.map((part, index) => renderContentPart(part, index, activityRenderer, extensionRegistry)) }));
118
+ return (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: content.map((part, index) => renderContentPart(part, index, activityRenderer, uiPluginRegistry)) }));
119
119
  }
120
120
  return null;
121
121
  }
122
122
  /**
123
123
  * Render individual content part
124
124
  */
125
- function renderContentPart(part, index, activityRenderer, extensionRegistry) {
125
+ function renderContentPart(part, index, activityRenderer, uiPluginRegistry) {
126
126
  switch (part.type) {
127
127
  case 'text':
128
128
  return (_jsx(Text, { sx: { whiteSpace: 'pre-wrap' }, children: part.text }, index));
@@ -168,9 +168,9 @@ function renderContentPart(part, index, activityRenderer, extensionRegistry) {
168
168
  data: part.data,
169
169
  }) }, index));
170
170
  }
171
- // Check extension registry
172
- if (extensionRegistry) {
173
- const renderer = extensionRegistry.getActivityRenderer(part.activityType || 'unknown');
171
+ // Check plugin registry
172
+ if (uiPluginRegistry) {
173
+ const renderer = uiPluginRegistry.getActivityRenderer(part.activityType || 'unknown');
174
174
  if (renderer) {
175
175
  return (_jsx(Box, { children: renderer.render({
176
176
  activityType: part.activityType || 'unknown',
@@ -231,7 +231,7 @@ const EXTENSIONS = [
231
231
  {
232
232
  value: 'a2ui',
233
233
  label: 'A2UI',
234
- description: 'Agent-to-UI extension',
234
+ description: 'Agent-to-UI plugin',
235
235
  },
236
236
  {
237
237
  value: 'mcp-ui',
@@ -75,7 +75,7 @@ export const EXAMPLE_ENTRIES = [
75
75
  makeEntry('A2UiRestaurantExample', () => import('./A2UiRestaurantExample'), 'A2UI restaurant flow example.'),
76
76
  makeEntry('A2UiViewerExample', () => import('./A2UiViewerExample'), 'A2UI viewer integration example.'),
77
77
  makeEntry('A2UiJupyterOutputExample', () => import('./A2UiJupyterOutputExample'), 'One Jupyter execution shown twice: raw kernel outputs, and the A2UI surface the server converter makes of them.'),
78
- makeEntry('A2UiAgentExample', () => import('./A2UiAgentExample'), 'A2UI Agent with built-in chat component and Python A2UI extension surface.'),
78
+ makeEntry('A2UiAgentExample', () => import('./A2UiAgentExample'), 'A2UI Agent with built-in chat component and Python A2UI UI plugin surface.'),
79
79
  makeEntry('AgUiAgenticExample', () => import('./AgUiAgenticExample'), 'AG-UI agentic workflow example.'),
80
80
  makeEntry('AgUiBackendToolRenderingExample', () => import('./AgUiBackendToolRenderingExample'), 'AG-UI backend tool rendering example.'),
81
81
  makeEntry('AgUiHaikuGenUiExample', () => import('./AgUiHaikuGenUiExample'), 'AG-UI generative UI Haiku example.'),
@@ -135,6 +135,13 @@ export function AppRenderer({ app, plugins = NO_PLUGINS, instance, ...embed }) {
135
135
  targetFixed: true,
136
136
  // Applications first (LOOP T-12): an application's conversation wears
137
137
  // the `loop` theme; a host may say otherwise.
138
- themeVariant: "loop", datalayerCreatePayload: datalayerCreatePayload, ...embed, plugins: allPlugins }));
138
+ themeVariant: "loop",
139
+ // Its own face, name and welcome in the chat (T-08); no counters: a
140
+ // person using an application is not asking about tokens.
141
+ presence: {
142
+ name: app.name,
143
+ face: app.emoji,
144
+ welcome: app.interface.welcome || app.description,
145
+ }, showTokenUsage: false, datalayerCreatePayload: datalayerCreatePayload, ...embed, plugins: allPlugins }));
139
146
  }
140
147
  export default AppRenderer;
@@ -1,4 +1,4 @@
1
- import type { AppSpec } from '../../types/agentspecs';
1
+ import type { AppSpec, ComponentSpec } from '../../types/agentspecs';
2
2
  export declare const NOT_READY = "Not ready";
3
3
  export declare const NEEDS_ATTENTION = "Needs attention";
4
4
  export declare const PASSES = "Passes the instant checks";
@@ -9,6 +9,11 @@ export interface AppCheck {
9
9
  attention: string[];
10
10
  setup: string[];
11
11
  }
12
+ /**
13
+ * The component a layout names (LOOP C-13), from the catalogs of the enabled
14
+ * UI plugins, by the name a surface gives it.
15
+ */
16
+ export declare const componentNamed: (name: string) => ComponentSpec | undefined;
12
17
  /** The Guards the runtime executes (agent-runtimes `loop/apps/guards.py`). */
13
18
  export declare const EXECUTED_GUARDS: readonly string[];
14
19
  /** The instant checks of an application as an editor holds it. */
@@ -29,6 +29,7 @@ import { getCog } from '../../specs/cogs';
29
29
  import { getFrame } from '../../specs/frames';
30
30
  import { getGate } from '../../specs/gates';
31
31
  import { GUARD_CATALOGUE } from '../../specs/guards';
32
+ import { getComponent } from '../../specs/uiPlugins';
32
33
  import { MCP_SERVER_LIBRARY } from '../../specs/mcpServers';
33
34
  import { getMemory } from '../../specs/memory';
34
35
  import { getModel } from '../../specs/models';
@@ -47,6 +48,11 @@ const idOf = (ref) => {
47
48
  const at = ref.lastIndexOf(':');
48
49
  return at > 0 && ref.slice(at + 1).includes('.') ? ref.slice(0, at) : ref;
49
50
  };
51
+ /**
52
+ * The component a layout names (LOOP C-13), from the catalogs of the enabled
53
+ * UI plugins, by the name a surface gives it.
54
+ */
55
+ export const componentNamed = (name) => getComponent(name);
50
56
  const own = (record, key) => Object.prototype.hasOwnProperty.call(record, key) ? record[key] : undefined;
51
57
  const ACTIONS = {
52
58
  write: 'create or change things',
@@ -175,6 +181,17 @@ function referenceProblems(app) {
175
181
  if (app.model && !getModel(app.model)) {
176
182
  missing('model', app.model);
177
183
  }
184
+ // The components it may use, and those its surface uses, are the catalog's (C-13).
185
+ for (const name of app.interface.components ?? []) {
186
+ if (!componentNamed(name)) {
187
+ problems.push(`There is no component named “${name}” in the catalog.`);
188
+ }
189
+ }
190
+ for (const node of app.interface.surface?.components ?? []) {
191
+ if (!componentNamed(String(node.component))) {
192
+ problems.push(`The surface's “${String(node.id)}” is a “${String(node.component)}”, which the catalog does not have.`);
193
+ }
194
+ }
178
195
  const judge = app.decision?.judgmentModel;
179
196
  if (judge) {
180
197
  const model = getModel(judge);
@@ -66,6 +66,10 @@ export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions,
66
66
  preset.datalayerAgentSpecId,
67
67
  preset.targetFixed,
68
68
  preset.themeVariant,
69
+ preset.presence?.name,
70
+ preset.presence?.face,
71
+ preset.presence?.welcome,
72
+ preset.showTokenUsage,
69
73
  // By its content: an application's spec written inline is a new object each render.
70
74
  JSON.stringify(preset.datalayerCreatePayload ?? null),
71
75
  preset.defaultEditor,
@@ -126,6 +130,15 @@ export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions,
126
130
  */
127
131
  useReactor(reactor);
128
132
  const shell = (_jsx(LoopWorkspace, { serverUrl: preset.serverUrl ?? '', agentId: agentId, reactor: reactor, manageReactor: false, showViewSelector: preset.showViewSelector, showHeader: showHeader, chatHeaderActions: chatHeaderActions, headerActions: headerActions, headerContainer: headerContainer }));
129
- return (_jsx(QueryClientProvider, { client: internalQueryClient, children: frameTitle !== undefined ? (_jsx(WindowFrame, { title: frameTitle, height: frameHeight, children: shell })) : (_jsx(Box, { sx: { height: '100%', minHeight: 0 }, children: shell })) }));
133
+ return (_jsx(QueryClientProvider, { client: internalQueryClient, children: frameTitle !== undefined ? (_jsx(WindowFrame, { title: frameTitle, height: frameHeight, children: shell })) : (
134
+ // Filling its host, whatever the host is: in a flex row it used to
135
+ // shrink to the chat's own width and leave the rest of the frame bare.
136
+ _jsx(Box, { sx: {
137
+ height: '100%',
138
+ minHeight: 0,
139
+ width: '100%',
140
+ flex: '1 1 auto',
141
+ minWidth: 0,
142
+ }, children: shell })) }));
130
143
  }
131
144
  export default LoopEmbed;
@@ -722,6 +722,7 @@ export default function ChatView({ workspace }) {
722
722
  })), [suggestions]);
723
723
  /* The icon its spec asked for, at the size the empty state draws. */
724
724
  const BrandIcon = agentIcon(spec?.icon);
725
+ const presence = config?.presence;
725
726
  /* And the team's, for the level above it. */
726
727
  const TeamIcon = agentIcon(team?.team.icon);
727
728
  /*
@@ -1290,7 +1291,7 @@ export default function ChatView({ workspace }) {
1290
1291
  onSelectAgent: id => team?.select(id),
1291
1292
  // On unless the host said otherwise: a public page turns the counters
1292
1293
  // off, because they answer questions a visitor is not asking.
1293
- showTokenUsage: chatExtras.showTokenUsage ?? true,
1294
+ showTokenUsage: chatExtras.showTokenUsage ?? config?.showTokenUsage ?? true,
1294
1295
  showContextRing: true,
1295
1296
  agentUsage: contextUsage ?? undefined,
1296
1297
  showModelSelector: true,
@@ -1423,7 +1424,7 @@ export default function ChatView({ workspace }) {
1423
1424
  nothing about this one. The empty state is the first thing a
1424
1425
  person sees and the only place the agent introduces itself.
1425
1426
  */
1426
- title: member?.name ?? spec?.name ?? agentId, description: spec?.description,
1427
+ title: presence?.name ?? member?.name ?? spec?.name ?? agentId, description: presence?.welcome ?? spec?.description,
1427
1428
  /*
1428
1429
  Two sizes, because it is drawn in two places.
1429
1430
 
@@ -1435,13 +1436,15 @@ export default function ChatView({ workspace }) {
1435
1436
  // Big enough to read as the agent's mark rather than as
1436
1437
  // punctuation before its name, and still short enough not to
1437
1438
  // set the header's height.
1438
- brandIcon: _jsx(BrandIcon, { size: 20 }), emptyState: {
1439
- icon: _jsx(BrandIcon, { size: 48 }),
1439
+ brandIcon: presence?.face ? (_jsx("span", { "aria-hidden": true, style: { fontSize: 20, lineHeight: 1 }, children: presence.face })) : (_jsx(BrandIcon, { size: 20 })), emptyState: {
1440
+ // An application's own face, large: the thing a person's eye
1441
+ // lands on first (LOOP T-08).
1442
+ icon: presence?.face ? (_jsx("span", { "aria-hidden": true, style: { fontSize: 48, lineHeight: 1 }, children: presence.face })) : (_jsx(BrandIcon, { size: 48 })),
1440
1443
  // `ChatEmptyState` reads its heading from here and nowhere
1441
1444
  // else — the `title` above reaches the header only — so
1442
1445
  // without this the agent introduced itself as "Start a
1443
1446
  // conversation".
1444
- title: member?.name ?? spec?.name ?? agentId,
1447
+ title: presence?.name ?? member?.name ?? spec?.name ?? agentId,
1445
1448
  /*
1446
1449
  Two levels when there is a team: the team first — its name,
1447
1450
  what it is for, what it can be asked — and under it the
@@ -1600,7 +1603,7 @@ export default function ChatView({ workspace }) {
1600
1603
  // A host example's own tool-result renderer, when it feeds one
1601
1604
  // through the extras channel — the A2UI examples draw their
1602
1605
  // surface here. Wins over the notebook surfaces in ChatBase.
1603
- renderToolResult: chatExtras.renderToolResult, showTurnFooter: chatExtras.showTokenUsage ?? true, onContextSnapshot: handleContextSnapshot, onLoadingChange: handleLoadingChange, onItemsChange: handleMessagesChange, onDisplayItemsChange: handleDisplayItemsChange, enableStreaming: true }) }), besideChat && !promptHidden ? prompt : null] }) }));
1606
+ renderToolResult: chatExtras.renderToolResult, showTurnFooter: chatExtras.showTokenUsage ?? config?.showTokenUsage ?? true, onContextSnapshot: handleContextSnapshot, onLoadingChange: handleLoadingChange, onItemsChange: handleMessagesChange, onDisplayItemsChange: handleDisplayItemsChange, enableStreaming: true }) }), besideChat && !promptHidden ? prompt : null] }) }));
1604
1607
  // Off when the host asked for the menu alone: the composer's suggestions
1605
1608
  // control carries the same openers (`suggestions` above).
1606
1609
  const chipsEl = config?.suggestionLabels !== false && chatSuggestions.length > 0 ? (_jsx(Box, { "aria-label": "Suggested prompts", sx: { display: 'flex', flexWrap: 'wrap', gap: 2, px: 2, pb: '6px' }, children: chatSuggestions.map(item => (_jsx(Box, { as: "button", type: "button",
@@ -1,4 +1,12 @@
1
1
  /** What a host may set on the chat. */
2
+ /** Who is answering, as the person meets them. */
3
+ export interface ChatPresence {
4
+ name: string;
5
+ /** An emoji: its face where only an icon goes. */
6
+ face?: string;
7
+ /** What it says before anyone writes. */
8
+ welcome?: string;
9
+ }
2
10
  export type ChatPluginConfig = {
3
11
  /**
4
12
  * The theme the conversation wears, by name: the person's unless said. An
@@ -6,6 +14,17 @@ export type ChatPluginConfig = {
6
14
  * the theme's bubbles, pills and colours whatever the page around it wears.
7
15
  */
8
16
  themeVariant?: string;
17
+ /**
18
+ * Who is answering, as the person meets them (LOOP T-08): a name, a face
19
+ * and a welcome, in place of the agent's id and a generic icon. An
20
+ * application sets its own.
21
+ */
22
+ presence?: ChatPresence;
23
+ /**
24
+ * Whether the counters — tokens, context — are shown under the prompt.
25
+ * On unless said: a person using an application is not asking.
26
+ */
27
+ showTokenUsage?: boolean;
9
28
  /**
10
29
  * What the empty prompt says.
11
30
  *
@@ -21,7 +21,7 @@
21
21
  */
22
22
  import { type PluginRef } from '@datalayer/reactor';
23
23
  import { type AgentsConfig, type SandboxTarget } from './plugins/agents';
24
- import { type ChatPluginConfig } from './plugins/chat';
24
+ import { type ChatPluginConfig, type ChatPresence } from './plugins/chat';
25
25
  import { type PageSize } from './plugins/page-layout';
26
26
  export type LoopPresetOptions = {
27
27
  /** Where the agent runtimes service is. */
@@ -128,6 +128,10 @@ export type LoopPresetOptions = {
128
128
  targetFixed?: boolean;
129
129
  /** The theme the conversation wears, by name; the person's unless said. */
130
130
  themeVariant?: string;
131
+ /** Who is answering, as the person meets them (LOOP T-08). */
132
+ presence?: ChatPresence;
133
+ /** Whether the counters are shown under the prompt; on unless said. */
134
+ showTokenUsage?: boolean;
131
135
  /**
132
136
  * The agentspec the local agent is created from, by id.
133
137
  *
@@ -31,7 +31,7 @@ import { ShellPlugin } from './plugins/shell';
31
31
  import { PromptPlugin } from './plugins/prompt';
32
32
  import { AgentspecsPlugin } from './plugins/agentspecs';
33
33
  import { AgentsPlugin, } from './plugins/agents';
34
- import { ChatPlugin } from './plugins/chat';
34
+ import { ChatPlugin, } from './plugins/chat';
35
35
  import { ChatHeaderPlugin } from './plugins/chat-header';
36
36
  import { A2uiSurfacePlugin } from './plugins/a2ui-surface';
37
37
  import { ChatViewPlugin } from './plugins/chat-view';
@@ -52,7 +52,7 @@ import { LoopPageLayoutPlugin } from './plugins/page-layout';
52
52
  * be installed or signed into, and the switches a demonstration wants are off.
53
53
  */
54
54
  export function loopPlugins(options = {}) {
55
- const { serverUrl, target = 'browser', defaultEditor = 'notebook', showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, datalayerAgentSpecId, datalayerCreatePayload, targetFixed, themeVariant, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
55
+ const { serverUrl, target = 'browser', defaultEditor = 'notebook', showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, datalayerAgentSpecId, datalayerCreatePayload, targetFixed, themeVariant, presence, showTokenUsage, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
56
56
  return [
57
57
  // The chat owns the editor beside it, so which one opens is its
58
58
  // configuration rather than the workspace's.
@@ -69,6 +69,8 @@ export function loopPlugins(options = {}) {
69
69
  suggestionLabels,
70
70
  fullScreenTopOffset,
71
71
  themeVariant,
72
+ presence,
73
+ showTokenUsage,
72
74
  }),
73
75
  // The composer and the title bar are plugins of their own: the chat
74
76
  // assembles their props, these render them. In the preset by default —
@@ -8,7 +8,7 @@ import { BaseProtocolAdapter } from './BaseProtocolAdapter';
8
8
  export interface A2AAdapterConfig extends ProtocolAdapterConfig {
9
9
  /** Agent URL for .well-known/agent-card.json discovery */
10
10
  agentUrl?: string;
11
- /** Enable A2UI extension */
11
+ /** Enable the A2UI UI plugin */
12
12
  enableA2UI?: boolean;
13
13
  }
14
14
  /**
@@ -6,7 +6,7 @@
6
6
  * This file is AUTO-GENERATED from YAML specifications.
7
7
  * DO NOT EDIT MANUALLY - run 'make specs' to regenerate.
8
8
  */
9
- import type { UIPluginSpec } from '../types/agentspecs';
9
+ import type { ComponentSpec, UIPluginSpec } from '../types/agentspecs';
10
10
  export declare const A2UI_UI_PLUGIN_0_0_1: UIPluginSpec;
11
11
  export declare const MCP_APPS_UI_PLUGIN_0_0_1: UIPluginSpec;
12
12
  export declare const MCP_UI_UI_PLUGIN_0_0_1: UIPluginSpec;
@@ -14,3 +14,8 @@ export declare const UI_PLUGIN_CATALOGUE: Record<string, UIPluginSpec>;
14
14
  /** The plugin an agent spec's `uiPlugin` names, or undefined. */
15
15
  export declare function getUIPlugin(pluginId: string): UIPluginSpec | undefined;
16
16
  export declare function listUIPlugins(): UIPluginSpec[];
17
+ /** The visual components the enabled UI plugins render, by the name a surface gives them (LOOP C-13). */
18
+ export declare const COMPONENT_CATALOGUE: Record<string, ComponentSpec>;
19
+ /** The component a layout names, or undefined. */
20
+ export declare function getComponent(name: string): ComponentSpec | undefined;
21
+ export declare function listComponents(): ComponentSpec[];