@datalayer/agent-runtimes 0.0.5 → 0.0.8

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 (119) hide show
  1. package/README.md +150 -22
  2. package/lib/components/chat/components/AgentDetails.d.ts +15 -2
  3. package/lib/components/chat/components/AgentDetails.js +9 -93
  4. package/lib/components/chat/components/AgentIdentity.d.ts +92 -0
  5. package/lib/components/chat/components/AgentIdentity.js +318 -0
  6. package/lib/components/chat/components/Chat.d.ts +24 -1
  7. package/lib/components/chat/components/Chat.js +41 -19
  8. package/lib/components/chat/components/ChatFloating.d.ts +6 -1
  9. package/lib/components/chat/components/ChatFloating.js +12 -6
  10. package/lib/components/chat/components/ContextDistribution.d.ts +47 -0
  11. package/lib/components/chat/components/ContextDistribution.js +146 -0
  12. package/lib/components/chat/components/ContextUsage.d.ts +33 -0
  13. package/lib/components/chat/components/ContextUsage.js +127 -0
  14. package/lib/components/chat/components/base/ChatBase.d.ts +51 -1
  15. package/lib/components/chat/components/base/ChatBase.js +278 -74
  16. package/lib/components/chat/components/display/ToolCallDisplay.d.ts +16 -2
  17. package/lib/components/chat/components/display/ToolCallDisplay.js +148 -6
  18. package/lib/components/chat/components/display/index.d.ts +1 -1
  19. package/lib/components/chat/components/display/index.js +1 -1
  20. package/lib/components/chat/components/elements/ChatInputPrompt.d.ts +12 -1
  21. package/lib/components/chat/components/elements/ChatInputPrompt.js +8 -3
  22. package/lib/components/chat/components/index.d.ts +3 -0
  23. package/lib/components/chat/components/index.js +3 -0
  24. package/lib/components/chat/components/parts/ToolPart.d.ts +1 -1
  25. package/lib/components/chat/components/parts/ToolPart.js +142 -6
  26. package/lib/components/chat/index.d.ts +1 -1
  27. package/lib/components/chat/index.js +1 -1
  28. package/lib/components/chat/protocols/A2AAdapter.d.ts +9 -0
  29. package/lib/components/chat/protocols/A2AAdapter.js +13 -2
  30. package/lib/components/chat/protocols/ACPAdapter.d.ts +9 -0
  31. package/lib/components/chat/protocols/ACPAdapter.js +13 -2
  32. package/lib/components/chat/protocols/AGUIAdapter.d.ts +9 -0
  33. package/lib/components/chat/protocols/AGUIAdapter.js +19 -1
  34. package/lib/components/chat/protocols/VercelAIAdapter.d.ts +7 -0
  35. package/lib/components/chat/protocols/VercelAIAdapter.js +19 -0
  36. package/lib/components/chat/types/execution.d.ts +78 -0
  37. package/lib/components/chat/types/execution.js +64 -0
  38. package/lib/components/chat/types/index.d.ts +1 -0
  39. package/lib/components/chat/types/index.js +1 -0
  40. package/lib/components/chat/types/protocol.d.ts +9 -0
  41. package/lib/components/ui/pagination.d.ts +2 -2
  42. package/lib/components/ui/pagination.js +4 -4
  43. package/lib/components/ui/resizable.d.ts +4 -4
  44. package/lib/components/ui/resizable.js +4 -4
  45. package/lib/examples/A2UiRestaurantExample.js +2 -2
  46. package/lib/examples/AgUiAgenticExample.js +2 -2
  47. package/lib/examples/AgUiBackendToolRenderingExample.js +2 -2
  48. package/lib/examples/AgUiHaikuGenUIExample.js +2 -2
  49. package/lib/examples/AgUiHumanInTheLoopExample.js +2 -2
  50. package/lib/examples/AgUiSharedStateExample.js +2 -2
  51. package/lib/examples/AgUiToolsBasedGenUIExample.js +2 -2
  52. package/lib/examples/AgentRuntimeCustomExample.js +2 -2
  53. package/lib/examples/AgentRuntimeLexical2Example.js +2 -1
  54. package/lib/examples/AgentRuntimeLexicalExample.js +5 -2
  55. package/lib/examples/AgentRuntimeLexicalSidebarExample.js +4 -2
  56. package/lib/examples/AgentRuntimeNotebookExample.js +1 -1
  57. package/lib/examples/AgentRuntimeStandaloneExample.js +2 -2
  58. package/lib/examples/AgentSpaceFormExample.d.ts +70 -2
  59. package/lib/examples/AgentSpaceFormExample.js +204 -35
  60. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  61. package/lib/examples/components/AgentConfiguration.d.ts +37 -0
  62. package/lib/examples/components/AgentConfiguration.js +239 -8
  63. package/lib/examples/components/Header.d.ts +0 -2
  64. package/lib/examples/components/Header.js +2 -16
  65. package/lib/examples/components/LexicalEditor.js +2 -1
  66. package/lib/examples/components/MockFileBrowser.js +6 -2
  67. package/lib/examples/components/index.d.ts +0 -1
  68. package/lib/examples/components/index.js +0 -1
  69. package/lib/examples/example-selector.js +0 -1
  70. package/lib/examples/index.d.ts +0 -1
  71. package/lib/examples/index.js +0 -1
  72. package/lib/examples/lexical/editorConfig.d.ts +3 -2
  73. package/lib/examples/lexical/editorConfig.js +7 -1
  74. package/lib/examples/lexical/initial-content.json +2210 -0
  75. package/lib/examples/main.js +15 -1
  76. package/lib/identity/IdentityConnect.d.ts +90 -0
  77. package/lib/identity/IdentityConnect.js +316 -0
  78. package/lib/identity/OAuthCallback.d.ts +58 -0
  79. package/lib/identity/OAuthCallback.js +223 -0
  80. package/lib/identity/dcr.d.ts +257 -0
  81. package/lib/identity/dcr.js +282 -0
  82. package/lib/identity/identityStore.d.ts +72 -0
  83. package/lib/identity/identityStore.js +529 -0
  84. package/lib/identity/index.d.ts +46 -0
  85. package/lib/identity/index.js +17 -0
  86. package/lib/identity/pkce.d.ts +30 -0
  87. package/lib/identity/pkce.js +65 -0
  88. package/lib/identity/types.d.ts +293 -0
  89. package/lib/identity/types.js +73 -0
  90. package/lib/identity/useIdentity.d.ts +108 -0
  91. package/lib/identity/useIdentity.js +323 -0
  92. package/lib/index.d.ts +2 -0
  93. package/lib/index.js +2 -0
  94. package/lib/lib/utils.js +1 -1
  95. package/lib/renderers/a2ui/lib/utils.js +1 -1
  96. package/lib/runtime/index.d.ts +35 -0
  97. package/lib/runtime/index.js +40 -0
  98. package/lib/runtime/runtimeStore.d.ts +77 -0
  99. package/lib/runtime/runtimeStore.js +184 -0
  100. package/lib/runtime/types.d.ts +84 -0
  101. package/lib/runtime/types.js +15 -0
  102. package/lib/runtime/useAgentConnection.d.ts +46 -0
  103. package/lib/runtime/useAgentConnection.js +112 -0
  104. package/lib/runtime/useAgentRuntime.d.ts +94 -0
  105. package/lib/runtime/useAgentRuntime.js +125 -0
  106. package/lib/test-setup.d.ts +1 -1
  107. package/lib/test-setup.js +1 -0
  108. package/lib/tools/adapters/agent-runtimes/AgentRuntimesToolAdapter.js +32 -1
  109. package/lib/tools/adapters/agent-runtimes/lexicalHooks.d.ts +6 -0
  110. package/lib/tools/adapters/agent-runtimes/lexicalHooks.js +16 -17
  111. package/package.json +20 -7
  112. package/patches/@datalayer+jupyter-lexical+1.0.8.patch +11628 -0
  113. package/patches/@datalayer+jupyter-react+2.0.2.patch +5338 -0
  114. package/lib/examples/AgentSpaceHomeExample.d.ts +0 -8
  115. package/lib/examples/AgentSpaceHomeExample.js +0 -171
  116. package/lib/examples/components/AgentsDataTable.d.ts +0 -13
  117. package/lib/examples/components/AgentsDataTable.js +0 -74
  118. package/lib/examples/components/Rating.d.ts +0 -14
  119. package/lib/examples/components/Rating.js +0 -12
@@ -0,0 +1,318 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * AgentIdentity component - Shows connected identity providers with token status.
8
+ * Displays expiration details and allows reconnection if token expired.
9
+ *
10
+ * @module components/chat/components/AgentIdentity
11
+ */
12
+ import React, { useCallback, useMemo } from 'react';
13
+ import { Box, Text, Button, Label, Avatar, Flash, Tooltip, } from '@primer/react';
14
+ import { MarkGithubIcon, LinkIcon, UnlinkIcon, CheckCircleFillIcon, AlertIcon, ClockIcon, SyncIcon, KeyIcon, } from '@primer/octicons-react';
15
+ import { useIdentity, IdentityButton } from '../../../identity';
16
+ import { GITHUB_PROVIDER, GOOGLE_PROVIDER, KAGGLE_PROVIDER, } from '../../../identity';
17
+ const PROVIDER_DISPLAY = {
18
+ github: {
19
+ name: 'GitHub',
20
+ icon: MarkGithubIcon,
21
+ color: '#24292f',
22
+ description: 'Access GitHub repositories and APIs',
23
+ },
24
+ google: {
25
+ name: 'Google',
26
+ icon: KeyIcon,
27
+ color: '#4285f4',
28
+ description: 'Access Google services and APIs',
29
+ },
30
+ kaggle: {
31
+ name: 'Kaggle',
32
+ icon: KeyIcon,
33
+ color: '#20beff',
34
+ description: 'Access Kaggle datasets and notebooks',
35
+ },
36
+ linkedin: {
37
+ name: 'LinkedIn',
38
+ icon: KeyIcon,
39
+ color: '#0077b5',
40
+ description: 'Access LinkedIn profile',
41
+ },
42
+ slack: {
43
+ name: 'Slack',
44
+ icon: KeyIcon,
45
+ color: '#4a154b',
46
+ description: 'Access Slack workspaces',
47
+ },
48
+ notion: {
49
+ name: 'Notion',
50
+ icon: KeyIcon,
51
+ color: '#000000',
52
+ description: 'Access Notion pages',
53
+ },
54
+ custom: {
55
+ name: 'Custom',
56
+ icon: LinkIcon,
57
+ color: '#6f42c1',
58
+ description: 'Custom OAuth provider',
59
+ },
60
+ };
61
+ const DEFAULT_SCOPES = {
62
+ github: ['read:user', 'user:email', 'repo'],
63
+ google: ['openid', 'profile', 'email'],
64
+ kaggle: ['read'],
65
+ linkedin: ['r_liteprofile', 'r_emailaddress'],
66
+ slack: ['users:read', 'channels:read'],
67
+ notion: ['read_content'],
68
+ custom: [],
69
+ };
70
+ /**
71
+ * Calculate token status from expiration timestamp
72
+ */
73
+ export function getTokenStatus(expiresAt) {
74
+ if (!expiresAt) {
75
+ return { isExpired: false, isExpiringSoon: false };
76
+ }
77
+ const now = Date.now();
78
+ const expiresAtDate = new Date(expiresAt);
79
+ const timeUntilExpiry = expiresAt - now;
80
+ const timeSinceExpiry = now - expiresAt;
81
+ // Token expires within 5 minutes - considered "expiring soon"
82
+ const EXPIRING_SOON_THRESHOLD = 5 * 60 * 1000;
83
+ return {
84
+ isExpired: timeUntilExpiry < 0,
85
+ isExpiringSoon: timeUntilExpiry > 0 && timeUntilExpiry < EXPIRING_SOON_THRESHOLD,
86
+ expiresAt: expiresAtDate,
87
+ timeUntilExpiry: timeUntilExpiry > 0 ? timeUntilExpiry : undefined,
88
+ timeSinceExpiry: timeUntilExpiry < 0 ? timeSinceExpiry : undefined,
89
+ };
90
+ }
91
+ /**
92
+ * Format duration for display
93
+ */
94
+ export function formatDuration(ms) {
95
+ const seconds = Math.floor(ms / 1000);
96
+ const minutes = Math.floor(seconds / 60);
97
+ const hours = Math.floor(minutes / 60);
98
+ const days = Math.floor(hours / 24);
99
+ if (days > 0) {
100
+ return days === 1 ? '1 day' : `${days} days`;
101
+ }
102
+ if (hours > 0) {
103
+ return hours === 1 ? '1 hour' : `${hours} hours`;
104
+ }
105
+ if (minutes > 0) {
106
+ return minutes === 1 ? '1 minute' : `${minutes} minutes`;
107
+ }
108
+ return seconds === 1 ? '1 second' : `${seconds} seconds`;
109
+ }
110
+ /**
111
+ * Format expiration status text
112
+ */
113
+ export function formatExpirationStatus(status) {
114
+ if (status.isExpired && status.timeSinceExpiry) {
115
+ return `Expired ${formatDuration(status.timeSinceExpiry)} ago`;
116
+ }
117
+ if (status.timeUntilExpiry) {
118
+ if (status.isExpiringSoon) {
119
+ return `Expires in ${formatDuration(status.timeUntilExpiry)}`;
120
+ }
121
+ return `Expires in ${formatDuration(status.timeUntilExpiry)}`;
122
+ }
123
+ // Token has no expiration info - likely a long-lived token
124
+ return 'Token does not expire';
125
+ }
126
+ /**
127
+ * Single identity card with status and actions
128
+ */
129
+ export function IdentityCard({ identity, providerConfig, showExpirationDetails = true, allowReconnect = true, onConnect: _onConnect, onDisconnect, onError, }) {
130
+ const { connect, disconnect, configureProvider, isAuthorizing } = useIdentity();
131
+ const provider = identity.provider;
132
+ const display = PROVIDER_DISPLAY[provider] || PROVIDER_DISPLAY.custom;
133
+ const tokenStatus = useMemo(() => getTokenStatus(identity.token?.expiresAt), [identity.token?.expiresAt]);
134
+ // Configure provider on mount if config is provided
135
+ React.useEffect(() => {
136
+ if (providerConfig?.clientId) {
137
+ const baseConfig = provider === 'github'
138
+ ? GITHUB_PROVIDER
139
+ : provider === 'google'
140
+ ? GOOGLE_PROVIDER
141
+ : provider === 'kaggle'
142
+ ? KAGGLE_PROVIDER
143
+ : undefined;
144
+ if (baseConfig) {
145
+ const redirectUri = providerConfig.config?.redirectUri ||
146
+ (typeof window !== 'undefined'
147
+ ? `${window.location.origin}${window.location.pathname}`
148
+ : '');
149
+ configureProvider({
150
+ ...baseConfig,
151
+ ...providerConfig.config,
152
+ provider,
153
+ clientId: providerConfig.clientId,
154
+ redirectUri,
155
+ });
156
+ }
157
+ }
158
+ }, [providerConfig, provider, configureProvider]);
159
+ const handleReconnect = useCallback(async () => {
160
+ try {
161
+ const scopes = providerConfig?.scopes || DEFAULT_SCOPES[provider];
162
+ await connect(provider, scopes);
163
+ }
164
+ catch (err) {
165
+ onError?.(err instanceof Error ? err : new Error(String(err)));
166
+ }
167
+ }, [provider, providerConfig?.scopes, connect, onError]);
168
+ const handleDisconnect = useCallback(async () => {
169
+ try {
170
+ await disconnect(provider);
171
+ onDisconnect?.(provider);
172
+ }
173
+ catch (err) {
174
+ onError?.(err instanceof Error ? err : new Error(String(err)));
175
+ }
176
+ }, [provider, disconnect, onDisconnect, onError]);
177
+ return (_jsxs(Box, { sx: {
178
+ display: 'flex',
179
+ flexDirection: 'column',
180
+ gap: 2,
181
+ p: 3,
182
+ border: '1px solid',
183
+ borderColor: tokenStatus.isExpired
184
+ ? 'danger.muted'
185
+ : tokenStatus.isExpiringSoon
186
+ ? 'attention.muted'
187
+ : 'success.muted',
188
+ borderRadius: 2,
189
+ backgroundColor: tokenStatus.isExpired
190
+ ? 'danger.subtle'
191
+ : tokenStatus.isExpiringSoon
192
+ ? 'attention.subtle'
193
+ : 'success.subtle',
194
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Box, { sx: {
195
+ width: 32,
196
+ height: 32,
197
+ borderRadius: 2,
198
+ display: 'flex',
199
+ alignItems: 'center',
200
+ justifyContent: 'center',
201
+ backgroundColor: display.color,
202
+ color: 'white',
203
+ flexShrink: 0,
204
+ }, children: _jsx(display.icon, { size: 16 }) }), _jsxs(Box, { sx: { flex: 1, display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: display.name }), _jsx(Label, { size: "small", variant: "secondary", children: identity.authType === 'token' ? 'API Key' : 'OAuth' })] }), tokenStatus.isExpired ? (_jsx(Label, { variant: "danger", size: "small", children: _jsxs(Box, { sx: { display: 'inline-flex', alignItems: 'center', gap: 1 }, children: [_jsx(AlertIcon, { size: 12 }), " Expired"] }) })) : tokenStatus.isExpiringSoon ? (_jsx(Label, { variant: "attention", size: "small", children: _jsxs(Box, { sx: { display: 'inline-flex', alignItems: 'center', gap: 1 }, children: [_jsx(ClockIcon, { size: 12 }), " Expiring Soon"] }) })) : (_jsx(Label, { variant: "success", size: "small", children: _jsxs(Box, { sx: { display: 'inline-flex', alignItems: 'center', gap: 1 }, children: [_jsx(CheckCircleFillIcon, { size: 12 }), " Connected"] }) }))] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [identity.userInfo?.avatarUrl && (_jsx("a", { href: identity.userInfo.profileUrl ||
205
+ (provider === 'github'
206
+ ? `https://github.com/${identity.userInfo.username}`
207
+ : undefined), target: "_blank", rel: "noopener noreferrer", style: { display: 'block', lineHeight: 0, flexShrink: 0 }, children: _jsx(Avatar, { src: identity.userInfo.avatarUrl, size: 32, alt: identity.userInfo.name ||
208
+ identity.userInfo.username ||
209
+ display.name, sx: { cursor: 'pointer' } }) })), _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [identity.userInfo?.name && (_jsx(Text, { sx: { fontWeight: 'semibold', fontSize: 1, display: 'block' }, children: identity.userInfo.name })), (identity.userInfo?.username || identity.userInfo?.email) && (_jsx("a", { href: identity.userInfo.profileUrl ||
210
+ (provider === 'github'
211
+ ? `https://github.com/${identity.userInfo.username}`
212
+ : undefined), target: "_blank", rel: "noopener noreferrer", style: { textDecoration: 'none' }, children: _jsx(Text, { sx: {
213
+ fontSize: 0,
214
+ color: 'fg.muted',
215
+ display: 'block',
216
+ ':hover': { textDecoration: 'underline' },
217
+ }, children: identity.userInfo.username
218
+ ? `@${identity.userInfo.username}`
219
+ : identity.userInfo.email }) }))] })] }), showExpirationDetails && (_jsxs(Box, { sx: {
220
+ display: 'flex',
221
+ alignItems: 'center',
222
+ gap: 1,
223
+ }, children: [_jsx(ClockIcon, { size: 12 }), _jsx(Text, { sx: {
224
+ fontSize: 0,
225
+ color: tokenStatus.isExpired
226
+ ? 'danger.fg'
227
+ : tokenStatus.isExpiringSoon
228
+ ? 'attention.fg'
229
+ : 'fg.muted',
230
+ }, children: formatExpirationStatus(tokenStatus) })] })), identity.scopes && identity.scopes.length > 0 && (_jsx(Box, { sx: {
231
+ display: 'flex',
232
+ flexWrap: 'wrap',
233
+ gap: 1,
234
+ }, children: identity.scopes.map(scope => (_jsx(Label, { size: "small", variant: "secondary", children: scope }, scope))) })), _jsxs(Box, { sx: {
235
+ display: 'flex',
236
+ gap: 2,
237
+ mt: 1,
238
+ }, children: [tokenStatus.isExpired &&
239
+ allowReconnect &&
240
+ providerConfig?.clientId && (_jsx(Tooltip, { text: "Reconnect to refresh token", children: _jsx(Button, { variant: "primary", size: "small", leadingVisual: SyncIcon, onClick: handleReconnect, disabled: isAuthorizing, children: isAuthorizing ? 'Connecting...' : 'Reconnect' }) })), _jsxs(Button, { variant: tokenStatus.isExpired ? 'invisible' : 'danger', size: "small", leadingVisual: UnlinkIcon, onClick: handleDisconnect, disabled: isAuthorizing, children: ["Disconnect from ", display.name] })] })] }));
241
+ }
242
+ /**
243
+ * AgentIdentity component - Displays all connected identities with status
244
+ *
245
+ * Features:
246
+ * - Shows connected OAuth identities (GitHub, Google, Kaggle, etc.)
247
+ * - Displays token expiration status with detailed timing
248
+ * - Allows reconnection if token expired
249
+ * - Reusable across AgentDetails, AgentConfiguration, etc.
250
+ */
251
+ export function AgentIdentity({ providers, title = 'Connected Accounts', showHeader = true, showDescription = true, description = 'Connected identities for this agent. Agents can use these to access external services on your behalf.', showExpirationDetails = true, allowReconnect = true, onConnect, onDisconnect, onError, }) {
252
+ const { identities, error } = useIdentity();
253
+ // Filter to show configured providers AND any token-based connected identities
254
+ // Token-based identities (like Kaggle) should always be shown if connected
255
+ const displayIdentities = useMemo(() => {
256
+ if (providers) {
257
+ const providerKeys = Object.keys(providers);
258
+ // Include configured providers AND any token-based connected identities
259
+ return identities.filter(id => providerKeys.includes(id.provider) ||
260
+ (id.authType === 'token' && id.isConnected));
261
+ }
262
+ return identities;
263
+ }, [identities, providers]);
264
+ // Get list of connected provider names
265
+ const connectedProviderNames = useMemo(() => new Set(identities.map(id => id.provider)), [identities]);
266
+ // Get providers that are NOT yet connected (to show connect buttons)
267
+ const unconnectedProviders = useMemo(() => {
268
+ if (!providers)
269
+ return {};
270
+ const providerKeys = Object.keys(providers);
271
+ const unconnected = {};
272
+ for (const provider of providerKeys) {
273
+ if (!connectedProviderNames.has(provider)) {
274
+ unconnected[provider] = providers[provider];
275
+ }
276
+ }
277
+ return unconnected;
278
+ }, [providers, connectedProviderNames]);
279
+ const hasUnconnected = Object.keys(unconnectedProviders).length > 0;
280
+ const handleError = useCallback((provider) => (err) => {
281
+ onError?.(provider, err);
282
+ }, [onError]);
283
+ // No identities and no providers configured
284
+ if (displayIdentities.length === 0 && !providers) {
285
+ return null;
286
+ }
287
+ return (_jsxs(Box, { children: [showHeader && (_jsxs(Box, { sx: {
288
+ display: 'flex',
289
+ alignItems: 'center',
290
+ gap: 2,
291
+ mb: 2,
292
+ }, children: [_jsx(KeyIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold', color: 'fg.muted' }, children: title })] })), _jsxs(Box, { sx: {
293
+ p: 3,
294
+ bg: 'canvas.subtle',
295
+ borderRadius: 2,
296
+ border: '1px solid',
297
+ borderColor: 'border.default',
298
+ }, children: [showDescription && (_jsx(Text, { sx: {
299
+ fontSize: 0,
300
+ color: 'fg.muted',
301
+ display: 'block',
302
+ mb: displayIdentities.length > 0 ? 3 : 0,
303
+ }, children: description })), error && (_jsx(Flash, { variant: "danger", sx: { mb: 3 }, children: error instanceof Error ? error.message : String(error) })), displayIdentities.length > 0 ? (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: displayIdentities.map(identity => (_jsx(IdentityCard, { identity: identity, providerConfig: providers?.[identity.provider], showExpirationDetails: showExpirationDetails, allowReconnect: allowReconnect, onConnect: onConnect, onDisconnect: onDisconnect, onError: handleError(identity.provider) }, identity.provider))) })) : !hasUnconnected ? (_jsxs(Box, { sx: {
304
+ display: 'flex',
305
+ alignItems: 'center',
306
+ gap: 2,
307
+ color: 'fg.muted',
308
+ }, children: [_jsx(LinkIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1 }, children: "No connected accounts" })] })) : null, hasUnconnected && (_jsxs(Box, { sx: { mt: displayIdentities.length > 0 ? 3 : 0 }, children: [displayIdentities.length > 0 && (_jsx(Text, { sx: {
309
+ fontSize: 0,
310
+ color: 'fg.muted',
311
+ display: 'block',
312
+ mb: 2,
313
+ }, children: "Connect additional accounts:" })), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: Object.keys(unconnectedProviders).map(provider => {
314
+ const config = unconnectedProviders[provider];
315
+ return (_jsx(IdentityButton, { provider: provider, clientId: config.clientId, scopes: config.scopes, providerConfig: config.config, size: "medium", variant: "full", onConnect: onConnect, onDisconnect: onDisconnect, onError: handleError(provider) }, provider));
316
+ }) })] }))] })] }));
317
+ }
318
+ export default AgentIdentity;
@@ -1,4 +1,5 @@
1
1
  import { type Suggestion } from './base/ChatBase';
2
+ import type { OAuthProvider, OAuthProviderConfig, Identity } from '../../../identity';
2
3
  /**
3
4
  * Supported transports (communication transports)
4
5
  */
@@ -49,6 +50,16 @@ export interface ChatProps {
49
50
  showModelSelector?: boolean;
50
51
  /** Show tools menu (fetched from /configure endpoint) */
51
52
  showToolsMenu?: boolean;
53
+ /** Show skills menu (fetched from /skills endpoint) */
54
+ showSkillsMenu?: boolean;
55
+ /** Indicate tools are accessed via Codemode meta-tools */
56
+ codemodeEnabled?: boolean;
57
+ /** Initial model ID to select (e.g., 'openai:gpt-4o-mini') */
58
+ initialModel?: string;
59
+ /** Initial MCP server IDs to enable (others will be disabled) */
60
+ initialMcpServers?: string[];
61
+ /** Initial skill IDs to enable */
62
+ initialSkills?: string[];
52
63
  /** Clear messages when component mounts or agentId changes */
53
64
  clearOnMount?: boolean;
54
65
  /** Suggestions to show in empty state */
@@ -59,6 +70,18 @@ export interface ChatProps {
59
70
  description?: string;
60
71
  /** Auto-focus the input on mount */
61
72
  autoFocus?: boolean;
73
+ /** Identity providers configuration for OAuth */
74
+ identityProviders?: {
75
+ [K in OAuthProvider]?: {
76
+ clientId: string;
77
+ scopes?: string[];
78
+ config?: Partial<OAuthProviderConfig>;
79
+ };
80
+ };
81
+ /** Callback when identity connects */
82
+ onIdentityConnect?: (identity: Identity) => void;
83
+ /** Callback when identity disconnects */
84
+ onIdentityDisconnect?: (provider: OAuthProvider) => void;
62
85
  }
63
86
  /**
64
87
  * Chat Component
@@ -101,5 +124,5 @@ export interface ChatProps {
101
124
  * />
102
125
  * ```
103
126
  */
104
- export declare function Chat({ transport, extensions: _extensions, baseUrl, wsUrl, agentId, placeholder, title, autoConnect: _autoConnect, streaming: _streaming, onMessageSent: _onMessageSent, onMessageReceived: _onMessageReceived, onDisconnect, onLogout: _onLogout, onCollapsePanel: _onCollapsePanel, className, height, showHeader, showModelSelector, showToolsMenu, clearOnMount: _clearOnMount, suggestions, submitOnSuggestionClick, description, autoFocus, }: ChatProps): import("react/jsx-runtime").JSX.Element;
127
+ export declare function Chat({ transport, extensions: _extensions, baseUrl, wsUrl, agentId, placeholder, title, autoConnect: _autoConnect, streaming: _streaming, onMessageSent: _onMessageSent, onMessageReceived: _onMessageReceived, onDisconnect, onLogout: _onLogout, onCollapsePanel: _onCollapsePanel, className, height, showHeader, showModelSelector, showToolsMenu, showSkillsMenu, codemodeEnabled, initialModel, initialMcpServers, initialSkills, clearOnMount: _clearOnMount, suggestions, submitOnSuggestionClick, description, autoFocus, identityProviders, onIdentityConnect, onIdentityDisconnect, }: ChatProps): import("react/jsx-runtime").JSX.Element;
105
128
  export default Chat;
@@ -24,6 +24,7 @@ import { AlertIcon, SyncIcon, InfoIcon } from '@primer/octicons-react';
24
24
  import { Box } from '@datalayer/primer-addons';
25
25
  import { ChatBase } from './base/ChatBase';
26
26
  import { AgentDetails } from './AgentDetails';
27
+ import { useConnectedIdentities } from '../../../identity';
27
28
  // Try to get Jupyter settings if available
28
29
  let getJupyterSettings;
29
30
  try {
@@ -129,11 +130,34 @@ function getProtocolType(transport) {
129
130
  * />
130
131
  * ```
131
132
  */
132
- export function Chat({ transport, extensions: _extensions, baseUrl = 'http://localhost:8765', wsUrl, agentId, placeholder = 'Type your message...', title, autoConnect: _autoConnect = true, streaming: _streaming = true, onMessageSent: _onMessageSent, onMessageReceived: _onMessageReceived, onDisconnect, onLogout: _onLogout, onCollapsePanel: _onCollapsePanel, className, height = '600px', showHeader = true, showModelSelector = true, showToolsMenu = true, clearOnMount: _clearOnMount = true, suggestions, submitOnSuggestionClick = true, description, autoFocus = false, }) {
133
+ export function Chat({ transport, extensions: _extensions, baseUrl = 'http://localhost:8765', wsUrl, agentId, placeholder = 'Type your message...', title, autoConnect: _autoConnect = true, streaming: _streaming = true, onMessageSent: _onMessageSent, onMessageReceived: _onMessageReceived, onDisconnect, onLogout: _onLogout, onCollapsePanel: _onCollapsePanel, className, height = '600px', showHeader = true, showModelSelector = true, showToolsMenu = true, showSkillsMenu = false, codemodeEnabled = false, initialModel, initialMcpServers, initialSkills, clearOnMount: _clearOnMount = true, suggestions, submitOnSuggestionClick = true, description, autoFocus = false, identityProviders, onIdentityConnect, onIdentityDisconnect, }) {
133
134
  const [error, setError] = useState(null);
134
135
  const [isInitializing, setIsInitializing] = useState(true);
135
136
  const [showDetails, setShowDetails] = useState(false);
136
137
  const [messageCount, setMessageCount] = useState(0);
138
+ const [focusTrigger, setFocusTrigger] = useState(0);
139
+ // Get connected identities to pass to backend for skill execution
140
+ const connectedIdentities = useConnectedIdentities();
141
+ // Map identities to the format expected by ChatBase
142
+ // Filter out identities without tokens (not fully connected)
143
+ const identitiesForChat = useMemo(() => {
144
+ return connectedIdentities
145
+ .filter(identity => identity.token?.accessToken)
146
+ .map(identity => ({
147
+ provider: identity.provider,
148
+ accessToken: identity.token.accessToken,
149
+ }));
150
+ }, [connectedIdentities]);
151
+ // Focus the input when returning from details view
152
+ useEffect(() => {
153
+ if (!showDetails) {
154
+ // Small delay to ensure the chat view is visible before focusing
155
+ const timer = setTimeout(() => {
156
+ setFocusTrigger(prev => prev + 1);
157
+ }, 100);
158
+ return () => clearTimeout(timer);
159
+ }
160
+ }, [showDetails]);
137
161
  // Build protocol config based on transport
138
162
  const protocolConfig = useMemo(() => {
139
163
  try {
@@ -248,28 +272,26 @@ export function Chat({ transport, extensions: _extensions, baseUrl = 'http://loc
248
272
  bg: 'canvas.default',
249
273
  }, children: [_jsx(Spinner, { size: "large" }), _jsxs(Text, { sx: { mt: 3, color: 'fg.muted' }, children: ["Connecting to ", transport.toUpperCase().replace('-', ' '), " agent..."] })] }));
250
274
  }
251
- // Show agent details view
252
- if (showDetails) {
253
- return (_jsx(Box, { className: className, sx: {
254
- position: 'relative',
255
- height,
256
- bg: 'canvas.default',
257
- display: 'flex',
258
- flexDirection: 'column',
259
- }, children: _jsx(AgentDetails, { name: title || 'AI Agent', protocol: transport, url: protocolConfig?.endpoint || baseUrl, messageCount: messageCount, agentId: agentId, onBack: () => setShowDetails(false) }) }));
260
- }
261
- return (_jsx(Box, { className: className, sx: {
275
+ return (_jsxs(Box, { className: className, sx: {
262
276
  position: 'relative',
263
277
  height,
264
278
  bg: 'canvas.default',
265
279
  display: 'flex',
266
280
  flexDirection: 'column',
267
- }, children: _jsx(ChatBase, { title: title, showHeader: showHeader, useStore: false, protocol: protocolConfig, placeholder: placeholder, description: description, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, autoFocus: autoFocus, headerContent: _jsx(IconButton, { icon: InfoIcon, "aria-label": "Agent details", variant: "invisible", size: "small", onClick: () => setShowDetails(true) }), showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, onNewChat: handleNewChat, onMessagesChange: messages => setMessageCount(messages.length), headerButtons: {
268
- showNewChat: true,
269
- showClear: true,
270
- onNewChat: handleNewChat,
271
- }, avatarConfig: {
272
- showAvatars: true,
273
- }, backgroundColor: "canvas.default" }) }));
281
+ }, children: [_jsx(Box, { sx: {
282
+ display: showDetails ? 'flex' : 'none',
283
+ flexDirection: 'column',
284
+ height: '100%',
285
+ }, children: _jsx(AgentDetails, { name: title || 'AI Agent', protocol: transport, url: protocolConfig?.endpoint || baseUrl, messageCount: messageCount, agentId: agentId, identityProviders: identityProviders, onIdentityConnect: onIdentityConnect, onIdentityDisconnect: onIdentityDisconnect, onBack: () => setShowDetails(false) }) }), _jsx(Box, { sx: {
286
+ display: showDetails ? 'none' : 'flex',
287
+ flexDirection: 'column',
288
+ height: '100%',
289
+ }, children: _jsx(ChatBase, { title: title, showHeader: showHeader, protocol: protocolConfig, placeholder: placeholder, description: description, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, autoFocus: autoFocus, headerContent: _jsx(IconButton, { icon: InfoIcon, "aria-label": "Agent details", variant: "invisible", size: "small", onClick: () => setShowDetails(true) }), showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, codemodeEnabled: codemodeEnabled, initialModel: initialModel, initialMcpServers: initialMcpServers, initialSkills: initialSkills, connectedIdentities: identitiesForChat, onNewChat: handleNewChat, onMessagesChange: messages => setMessageCount(messages.length), headerButtons: {
290
+ showNewChat: true,
291
+ showClear: true,
292
+ onNewChat: handleNewChat,
293
+ }, avatarConfig: {
294
+ showAvatars: true,
295
+ }, backgroundColor: "canvas.default", focusTrigger: focusTrigger }) })] }));
274
296
  }
275
297
  export default Chat;
@@ -145,6 +145,11 @@ export interface ChatFloatingProps {
145
145
  * @default false
146
146
  */
147
147
  showToolsMenu?: boolean;
148
+ /**
149
+ * Show skills menu in footer.
150
+ * @default false
151
+ */
152
+ showSkillsMenu?: boolean;
148
153
  /** Additional ChatBase props */
149
154
  panelProps?: Partial<ChatBaseProps>;
150
155
  }
@@ -152,5 +157,5 @@ export interface ChatFloatingProps {
152
157
  * ChatFloating component
153
158
  * A floating chat window built on ChatBase
154
159
  */
155
- export declare function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode, title, description, position, defaultOpen, width, height, showHeader, showButton, showNewChatButton, showClearButton, showSettingsButton, enableKeyboardShortcuts, toggleShortcut, showPoweredBy, poweredByProps, clickOutsideToClose, escapeToClose, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip, brandColor, offset, animationDuration, renderToolResult, tools: _tools, initialState: _initialState, suggestions, submitOnSuggestionClick, hideMessagesAfterToolUI, defaultViewMode, showPanelBackdrop, showModelSelector, showToolsMenu, panelProps, }: ChatFloatingProps): import("react/jsx-runtime").JSX.Element;
160
+ export declare function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode, title, description, position, defaultOpen, width, height, showHeader, showButton, showNewChatButton, showClearButton, showSettingsButton, enableKeyboardShortcuts, toggleShortcut, showPoweredBy, poweredByProps, clickOutsideToClose, escapeToClose, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip, brandColor, offset, animationDuration, renderToolResult, tools: _tools, initialState: _initialState, suggestions, submitOnSuggestionClick, hideMessagesAfterToolUI, defaultViewMode, showPanelBackdrop, showModelSelector, showToolsMenu, showSkillsMenu, panelProps, }: ChatFloatingProps): import("react/jsx-runtime").JSX.Element;
156
161
  export default ChatFloating;
@@ -43,7 +43,7 @@ function useIsMobile(breakpoint = 640) {
43
43
  * ChatFloating component
44
44
  * A floating chat window built on ChatBase
45
45
  */
46
- export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode = true, title = 'Chat', description = 'Start a conversation with the AI agent.', position = 'bottom-right', defaultOpen = false, width = 400, height = 550, showHeader = true, showButton = true, showNewChatButton = true, showClearButton = true, showSettingsButton = false, enableKeyboardShortcuts = true, toggleShortcut = '/', showPoweredBy = true, poweredByProps, clickOutsideToClose = true, escapeToClose = true, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip = 'Chat with AI', brandColor = '#7c3aed', offset = 20, animationDuration = 200, renderToolResult, tools: _tools, initialState: _initialState, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, defaultViewMode = 'floating', showPanelBackdrop = false, showModelSelector = false, showToolsMenu = false, panelProps, }) {
46
+ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode = true, title = 'Chat', description = 'Start a conversation with the AI agent.', position = 'bottom-right', defaultOpen = false, width = 400, height = 550, showHeader = true, showButton = true, showNewChatButton = true, showClearButton = true, showSettingsButton = false, enableKeyboardShortcuts = true, toggleShortcut = '/', showPoweredBy = true, poweredByProps, clickOutsideToClose = true, escapeToClose = true, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip = 'Chat with AI', brandColor = '#7c3aed', offset = 20, animationDuration = 200, renderToolResult, tools: _tools, initialState: _initialState, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, defaultViewMode = 'floating', showPanelBackdrop = false, showModelSelector = false, showToolsMenu = false, showSkillsMenu = false, panelProps, }) {
47
47
  // Store-based state
48
48
  const storeIsOpen = useChatOpen();
49
49
  const storeMessages = useChatMessages();
@@ -73,14 +73,20 @@ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useSt
73
73
  return {
74
74
  type: 'ag-ui',
75
75
  endpoint,
76
- // Enable config query for model/tools selector when showModelSelector or showToolsMenu is true
77
- enableConfigQuery: showModelSelector || showToolsMenu,
76
+ // Enable config query for model/tools/skills selector
77
+ enableConfigQuery: showModelSelector || showToolsMenu || showSkillsMenu,
78
78
  // Config endpoint is at /api/v1/configure (global, not per-agent)
79
- configEndpoint: showModelSelector || showToolsMenu
79
+ configEndpoint: showModelSelector || showToolsMenu || showSkillsMenu
80
80
  ? `${baseUrl}/api/v1/configure`
81
81
  : undefined,
82
82
  };
83
- }, [protocolProp, endpoint, showModelSelector, showToolsMenu]);
83
+ }, [
84
+ protocolProp,
85
+ endpoint,
86
+ showModelSelector,
87
+ showToolsMenu,
88
+ showSkillsMenu,
89
+ ]);
84
90
  // Clear messages when endpoint/protocol changes (e.g., switching examples)
85
91
  useEffect(() => {
86
92
  clearStoreMessages();
@@ -387,6 +393,6 @@ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useSt
387
393
  ...poweredByProps,
388
394
  }, renderToolResult: renderToolResult, description: description, onStateUpdate: onStateUpdate, onNewChat: onNewChat, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, hideMessagesAfterToolUI: hideMessagesAfterToolUI, avatarConfig: {
389
395
  showAvatars: true,
390
- }, placeholder: "Type a message...", backgroundColor: "canvas.subtle", frontendTools: _tools, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, ...panelProps, children: children }) })] }));
396
+ }, placeholder: "Type a message...", backgroundColor: "canvas.subtle", frontendTools: _tools, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, ...panelProps, children: children }) })] }));
391
397
  }
392
398
  export default ChatFloating;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Distribution child item
3
+ */
4
+ interface DistributionChild {
5
+ name: string;
6
+ value: number;
7
+ children?: DistributionChild[];
8
+ }
9
+ /**
10
+ * Distribution data for treemap
11
+ */
12
+ interface Distribution {
13
+ name: string;
14
+ value: number;
15
+ children: DistributionChild[];
16
+ }
17
+ /**
18
+ * Context snapshot response from API
19
+ */
20
+ export interface ContextSnapshotResponse {
21
+ agentId: string;
22
+ systemPrompts: string[];
23
+ systemPromptTokens: number;
24
+ messages: Array<{
25
+ role: string;
26
+ content: string;
27
+ estimatedTokens: number;
28
+ timestamp: string | null;
29
+ }>;
30
+ userMessageTokens: number;
31
+ assistantMessageTokens: number;
32
+ totalTokens: number;
33
+ contextWindow: number;
34
+ distribution: Distribution;
35
+ error?: string;
36
+ }
37
+ export interface ContextDistributionProps {
38
+ /** Agent ID for fetching context snapshot */
39
+ agentId: string;
40
+ /** Height of the chart */
41
+ height?: string;
42
+ }
43
+ /**
44
+ * ContextDistribution component displays context distribution as a treemap.
45
+ */
46
+ export declare function ContextDistribution({ agentId, height, }: ContextDistributionProps): import("react/jsx-runtime").JSX.Element;
47
+ export default ContextDistribution;