@datalayer/agent-runtimes 0.0.7 → 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 (103) hide show
  1. package/README.md +9 -0
  2. package/lib/components/chat/components/AgentDetails.d.ts +14 -1
  3. package/lib/components/chat/components/AgentDetails.js +3 -2
  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 +20 -1
  7. package/lib/components/chat/components/Chat.js +16 -3
  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/base/ChatBase.d.ts +47 -1
  11. package/lib/components/chat/components/base/ChatBase.js +242 -63
  12. package/lib/components/chat/components/display/ToolCallDisplay.d.ts +16 -2
  13. package/lib/components/chat/components/display/ToolCallDisplay.js +148 -6
  14. package/lib/components/chat/components/display/index.d.ts +1 -1
  15. package/lib/components/chat/components/display/index.js +1 -1
  16. package/lib/components/chat/components/elements/ChatInputPrompt.d.ts +12 -1
  17. package/lib/components/chat/components/elements/ChatInputPrompt.js +8 -3
  18. package/lib/components/chat/components/index.d.ts +1 -0
  19. package/lib/components/chat/components/index.js +1 -0
  20. package/lib/components/chat/components/parts/ToolPart.d.ts +1 -1
  21. package/lib/components/chat/components/parts/ToolPart.js +142 -6
  22. package/lib/components/chat/index.d.ts +1 -1
  23. package/lib/components/chat/index.js +1 -1
  24. package/lib/components/chat/protocols/A2AAdapter.d.ts +9 -0
  25. package/lib/components/chat/protocols/A2AAdapter.js +13 -2
  26. package/lib/components/chat/protocols/ACPAdapter.d.ts +9 -0
  27. package/lib/components/chat/protocols/ACPAdapter.js +13 -2
  28. package/lib/components/chat/protocols/AGUIAdapter.d.ts +9 -0
  29. package/lib/components/chat/protocols/AGUIAdapter.js +19 -1
  30. package/lib/components/chat/protocols/VercelAIAdapter.d.ts +7 -0
  31. package/lib/components/chat/protocols/VercelAIAdapter.js +19 -0
  32. package/lib/components/chat/types/execution.d.ts +78 -0
  33. package/lib/components/chat/types/execution.js +64 -0
  34. package/lib/components/chat/types/index.d.ts +1 -0
  35. package/lib/components/chat/types/index.js +1 -0
  36. package/lib/components/chat/types/protocol.d.ts +9 -0
  37. package/lib/components/ui/pagination.d.ts +2 -2
  38. package/lib/components/ui/pagination.js +4 -4
  39. package/lib/components/ui/resizable.d.ts +4 -4
  40. package/lib/components/ui/resizable.js +4 -4
  41. package/lib/examples/A2UiRestaurantExample.js +2 -2
  42. package/lib/examples/AgUiAgenticExample.js +2 -2
  43. package/lib/examples/AgUiBackendToolRenderingExample.js +2 -2
  44. package/lib/examples/AgUiHaikuGenUIExample.js +2 -2
  45. package/lib/examples/AgUiHumanInTheLoopExample.js +2 -2
  46. package/lib/examples/AgUiSharedStateExample.js +2 -2
  47. package/lib/examples/AgUiToolsBasedGenUIExample.js +2 -2
  48. package/lib/examples/AgentRuntimeCustomExample.js +2 -2
  49. package/lib/examples/AgentRuntimeLexical2Example.js +2 -1
  50. package/lib/examples/AgentRuntimeLexicalExample.js +5 -2
  51. package/lib/examples/AgentRuntimeLexicalSidebarExample.js +4 -2
  52. package/lib/examples/AgentRuntimeNotebookExample.js +1 -1
  53. package/lib/examples/AgentRuntimeStandaloneExample.js +2 -2
  54. package/lib/examples/AgentSpaceFormExample.d.ts +70 -2
  55. package/lib/examples/AgentSpaceFormExample.js +177 -43
  56. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  57. package/lib/examples/components/AgentConfiguration.d.ts +17 -2
  58. package/lib/examples/components/AgentConfiguration.js +220 -16
  59. package/lib/examples/components/LexicalEditor.js +2 -1
  60. package/lib/examples/components/MockFileBrowser.js +6 -2
  61. package/lib/examples/components/index.d.ts +0 -1
  62. package/lib/examples/components/index.js +0 -1
  63. package/lib/examples/example-selector.js +0 -1
  64. package/lib/examples/index.d.ts +0 -1
  65. package/lib/examples/index.js +0 -1
  66. package/lib/examples/lexical/editorConfig.d.ts +3 -2
  67. package/lib/examples/lexical/editorConfig.js +7 -1
  68. package/lib/examples/lexical/initial-content.json +2210 -0
  69. package/lib/examples/main.js +15 -1
  70. package/lib/identity/IdentityConnect.d.ts +90 -0
  71. package/lib/identity/IdentityConnect.js +316 -0
  72. package/lib/identity/OAuthCallback.d.ts +58 -0
  73. package/lib/identity/OAuthCallback.js +223 -0
  74. package/lib/identity/dcr.d.ts +257 -0
  75. package/lib/identity/dcr.js +282 -0
  76. package/lib/identity/identityStore.d.ts +72 -0
  77. package/lib/identity/identityStore.js +529 -0
  78. package/lib/identity/index.d.ts +46 -0
  79. package/lib/identity/index.js +17 -0
  80. package/lib/identity/pkce.d.ts +30 -0
  81. package/lib/identity/pkce.js +65 -0
  82. package/lib/identity/types.d.ts +293 -0
  83. package/lib/identity/types.js +73 -0
  84. package/lib/identity/useIdentity.d.ts +108 -0
  85. package/lib/identity/useIdentity.js +323 -0
  86. package/lib/index.d.ts +1 -0
  87. package/lib/index.js +1 -0
  88. package/lib/lib/utils.js +1 -1
  89. package/lib/renderers/a2ui/lib/utils.js +1 -1
  90. package/lib/test-setup.d.ts +1 -1
  91. package/lib/test-setup.js +1 -0
  92. package/lib/tools/adapters/agent-runtimes/AgentRuntimesToolAdapter.js +32 -1
  93. package/lib/tools/adapters/agent-runtimes/lexicalHooks.d.ts +6 -0
  94. package/lib/tools/adapters/agent-runtimes/lexicalHooks.js +16 -17
  95. package/package.json +20 -7
  96. package/patches/@datalayer+jupyter-lexical+1.0.8.patch +11628 -0
  97. package/patches/@datalayer+jupyter-react+2.0.2.patch +5338 -0
  98. package/lib/examples/AgentSpaceHomeExample.d.ts +0 -8
  99. package/lib/examples/AgentSpaceHomeExample.js +0 -171
  100. package/lib/examples/components/AgentsDataTable.d.ts +0 -13
  101. package/lib/examples/components/AgentsDataTable.js +0 -74
  102. package/lib/examples/components/Rating.d.ts +0 -14
  103. package/lib/examples/components/Rating.js +0 -12
package/README.md CHANGED
@@ -132,6 +132,15 @@ app.add_agent(agent, name='my-agent', transport='ag-ui')
132
132
  app.run(port=8000)
133
133
  ```
134
134
 
135
+ ## 🧪 Run the examples
136
+
137
+ Run the Codemode + MCP example UI:
138
+
139
+ ```bash
140
+ cd src/ai/agent-runtimes
141
+ EXAMPLE=AgentCodemodeMcpExample npm run dev
142
+ ```
143
+
135
144
  ## 🔧 Key Concepts
136
145
 
137
146
  ### Protocols
@@ -1,3 +1,4 @@
1
+ import type { OAuthProvider, OAuthProviderConfig, Identity } from '../../../identity';
1
2
  export interface AgentDetailsProps {
2
3
  /** Agent name/title */
3
4
  name?: string;
@@ -9,11 +10,23 @@ export interface AgentDetailsProps {
9
10
  messageCount: number;
10
11
  /** Agent ID for context usage tracking */
11
12
  agentId?: string;
13
+ /** Identity provider configurations */
14
+ identityProviders?: {
15
+ [K in OAuthProvider]?: {
16
+ clientId: string;
17
+ scopes?: string[];
18
+ config?: Partial<OAuthProviderConfig>;
19
+ };
20
+ };
21
+ /** Callback when identity connects */
22
+ onIdentityConnect?: (identity: Identity) => void;
23
+ /** Callback when identity disconnects */
24
+ onIdentityDisconnect?: (provider: OAuthProvider) => void;
12
25
  /** Callback to go back to chat view */
13
26
  onBack: () => void;
14
27
  }
15
28
  /**
16
29
  * AgentDetails component displays comprehensive information about the agent.
17
30
  */
18
- export declare function AgentDetails({ name, protocol, url, messageCount, agentId, onBack, }: AgentDetailsProps): import("react/jsx-runtime").JSX.Element;
31
+ export declare function AgentDetails({ name, protocol, url, messageCount, agentId, identityProviders, onIdentityConnect, onIdentityDisconnect, onBack, }: AgentDetailsProps): import("react/jsx-runtime").JSX.Element;
19
32
  export default AgentDetails;
@@ -11,6 +11,7 @@ import { AiAgentIcon } from '@datalayer/icons-react';
11
11
  import { useQuery } from '@tanstack/react-query';
12
12
  import { ContextUsage } from './ContextUsage';
13
13
  import { ContextDistribution } from './ContextDistribution';
14
+ import { AgentIdentity } from './AgentIdentity';
14
15
  function getLocalApiBase() {
15
16
  if (typeof window === 'undefined') {
16
17
  return '';
@@ -23,7 +24,7 @@ function getLocalApiBase() {
23
24
  /**
24
25
  * AgentDetails component displays comprehensive information about the agent.
25
26
  */
26
- export function AgentDetails({ name = 'AI Agent', protocol, url, messageCount, agentId, onBack, }) {
27
+ export function AgentDetails({ name = 'AI Agent', protocol, url, messageCount, agentId, identityProviders, onIdentityConnect, onIdentityDisconnect, onBack, }) {
27
28
  // Fetch MCP toolsets status
28
29
  const { data: mcpStatus, isLoading: mcpLoading } = useQuery({
29
30
  queryKey: ['mcp-toolsets-status'],
@@ -143,6 +144,6 @@ export function AgentDetails({ name = 'AI Agent', protocol, url, messageCount, a
143
144
  borderRadius: 2,
144
145
  border: '1px solid',
145
146
  borderColor: 'border.default',
146
- }, children: _jsx(ContextDistribution, { agentId: agentId, height: "250px" }) })] })), _jsx(Box, { sx: { mt: 2 }, children: _jsx(Button, { variant: "primary", onClick: onBack, sx: { width: '100%' }, children: "Back to Chat" }) })] })] }));
147
+ }, children: _jsx(ContextDistribution, { agentId: agentId, height: "250px" }) })] })), _jsx(AgentIdentity, { providers: identityProviders, title: "Connected Accounts", showHeader: true, showDescription: true, description: "OAuth identities connected to this agent. Agents can use these to access external services like GitHub repositories on your behalf.", showExpirationDetails: true, allowReconnect: Boolean(identityProviders), onConnect: onIdentityConnect, onDisconnect: onIdentityDisconnect }), _jsx(Box, { sx: { mt: 2 }, children: _jsx(Button, { variant: "primary", onClick: onBack, sx: { width: '100%' }, children: "Back to Chat" }) })] })] }));
147
148
  }
148
149
  export default AgentDetails;
@@ -0,0 +1,92 @@
1
+ import type { OAuthProvider, OAuthProviderConfig, Identity } from '../../../identity';
2
+ /**
3
+ * Token status information
4
+ */
5
+ export interface TokenStatus {
6
+ isExpired: boolean;
7
+ isExpiringSoon: boolean;
8
+ expiresAt?: Date;
9
+ timeUntilExpiry?: number;
10
+ timeSinceExpiry?: number;
11
+ }
12
+ /**
13
+ * Calculate token status from expiration timestamp
14
+ */
15
+ export declare function getTokenStatus(expiresAt?: number): TokenStatus;
16
+ /**
17
+ * Format duration for display
18
+ */
19
+ export declare function formatDuration(ms: number): string;
20
+ /**
21
+ * Format expiration status text
22
+ */
23
+ export declare function formatExpirationStatus(status: TokenStatus): string;
24
+ /**
25
+ * Props for IdentityCard component
26
+ */
27
+ export interface IdentityCardProps {
28
+ /** Identity to display */
29
+ identity: Identity;
30
+ /** Provider configuration */
31
+ providerConfig?: {
32
+ clientId: string;
33
+ scopes?: string[];
34
+ config?: Partial<OAuthProviderConfig>;
35
+ };
36
+ /** Show detailed expiration info */
37
+ showExpirationDetails?: boolean;
38
+ /** Allow reconnection */
39
+ allowReconnect?: boolean;
40
+ /** Callback when connected */
41
+ onConnect?: (identity: Identity) => void;
42
+ /** Callback when disconnected */
43
+ onDisconnect?: (provider: OAuthProvider) => void;
44
+ /** Callback on error */
45
+ onError?: (error: Error) => void;
46
+ }
47
+ /**
48
+ * Single identity card with status and actions
49
+ */
50
+ export declare function IdentityCard({ identity, providerConfig, showExpirationDetails, allowReconnect, onConnect: _onConnect, onDisconnect, onError, }: IdentityCardProps): import("react/jsx-runtime").JSX.Element;
51
+ /**
52
+ * Props for AgentIdentity component
53
+ */
54
+ export interface AgentIdentityProps {
55
+ /** Provider configurations with client IDs */
56
+ providers?: {
57
+ [K in OAuthProvider]?: {
58
+ clientId: string;
59
+ scopes?: string[];
60
+ config?: Partial<OAuthProviderConfig>;
61
+ };
62
+ };
63
+ /** Title for the section */
64
+ title?: string;
65
+ /** Show header */
66
+ showHeader?: boolean;
67
+ /** Show description */
68
+ showDescription?: boolean;
69
+ /** Description text */
70
+ description?: string;
71
+ /** Show expiration details */
72
+ showExpirationDetails?: boolean;
73
+ /** Allow reconnection */
74
+ allowReconnect?: boolean;
75
+ /** Callback when identity connects */
76
+ onConnect?: (identity: Identity) => void;
77
+ /** Callback when identity disconnects */
78
+ onDisconnect?: (provider: OAuthProvider) => void;
79
+ /** Callback on error */
80
+ onError?: (provider: OAuthProvider, error: Error) => void;
81
+ }
82
+ /**
83
+ * AgentIdentity component - Displays all connected identities with status
84
+ *
85
+ * Features:
86
+ * - Shows connected OAuth identities (GitHub, Google, Kaggle, etc.)
87
+ * - Displays token expiration status with detailed timing
88
+ * - Allows reconnection if token expired
89
+ * - Reusable across AgentDetails, AgentConfiguration, etc.
90
+ */
91
+ export declare function AgentIdentity({ providers, title, showHeader, showDescription, description, showExpirationDetails, allowReconnect, onConnect, onDisconnect, onError, }: AgentIdentityProps): import("react/jsx-runtime").JSX.Element | null;
92
+ export default AgentIdentity;
@@ -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,10 +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;
52
57
  /** Initial model ID to select (e.g., 'openai:gpt-4o-mini') */
53
58
  initialModel?: string;
54
59
  /** Initial MCP server IDs to enable (others will be disabled) */
55
60
  initialMcpServers?: string[];
61
+ /** Initial skill IDs to enable */
62
+ initialSkills?: string[];
56
63
  /** Clear messages when component mounts or agentId changes */
57
64
  clearOnMount?: boolean;
58
65
  /** Suggestions to show in empty state */
@@ -63,6 +70,18 @@ export interface ChatProps {
63
70
  description?: string;
64
71
  /** Auto-focus the input on mount */
65
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;
66
85
  }
67
86
  /**
68
87
  * Chat Component
@@ -105,5 +124,5 @@ export interface ChatProps {
105
124
  * />
106
125
  * ```
107
126
  */
108
- 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, initialModel, initialMcpServers, 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;
109
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,12 +130,24 @@ 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, initialModel, initialMcpServers, 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);
137
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]);
138
151
  // Focus the input when returning from details view
139
152
  useEffect(() => {
140
153
  if (!showDetails) {
@@ -269,11 +282,11 @@ export function Chat({ transport, extensions: _extensions, baseUrl = 'http://loc
269
282
  display: showDetails ? 'flex' : 'none',
270
283
  flexDirection: 'column',
271
284
  height: '100%',
272
- }, children: _jsx(AgentDetails, { name: title || 'AI Agent', protocol: transport, url: protocolConfig?.endpoint || baseUrl, messageCount: messageCount, agentId: agentId, onBack: () => setShowDetails(false) }) }), _jsx(Box, { sx: {
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: {
273
286
  display: showDetails ? 'none' : 'flex',
274
287
  flexDirection: 'column',
275
288
  height: '100%',
276
- }, 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, initialModel: initialModel, initialMcpServers: initialMcpServers, onNewChat: handleNewChat, onMessagesChange: messages => setMessageCount(messages.length), headerButtons: {
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: {
277
290
  showNewChat: true,
278
291
  showClear: true,
279
292
  onNewChat: handleNewChat,
@@ -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;