@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.
- package/README.md +9 -0
- package/lib/components/chat/components/AgentDetails.d.ts +14 -1
- package/lib/components/chat/components/AgentDetails.js +3 -2
- package/lib/components/chat/components/AgentIdentity.d.ts +92 -0
- package/lib/components/chat/components/AgentIdentity.js +318 -0
- package/lib/components/chat/components/Chat.d.ts +20 -1
- package/lib/components/chat/components/Chat.js +16 -3
- package/lib/components/chat/components/ChatFloating.d.ts +6 -1
- package/lib/components/chat/components/ChatFloating.js +12 -6
- package/lib/components/chat/components/base/ChatBase.d.ts +47 -1
- package/lib/components/chat/components/base/ChatBase.js +242 -63
- package/lib/components/chat/components/display/ToolCallDisplay.d.ts +16 -2
- package/lib/components/chat/components/display/ToolCallDisplay.js +148 -6
- package/lib/components/chat/components/display/index.d.ts +1 -1
- package/lib/components/chat/components/display/index.js +1 -1
- package/lib/components/chat/components/elements/ChatInputPrompt.d.ts +12 -1
- package/lib/components/chat/components/elements/ChatInputPrompt.js +8 -3
- package/lib/components/chat/components/index.d.ts +1 -0
- package/lib/components/chat/components/index.js +1 -0
- package/lib/components/chat/components/parts/ToolPart.d.ts +1 -1
- package/lib/components/chat/components/parts/ToolPart.js +142 -6
- package/lib/components/chat/index.d.ts +1 -1
- package/lib/components/chat/index.js +1 -1
- package/lib/components/chat/protocols/A2AAdapter.d.ts +9 -0
- package/lib/components/chat/protocols/A2AAdapter.js +13 -2
- package/lib/components/chat/protocols/ACPAdapter.d.ts +9 -0
- package/lib/components/chat/protocols/ACPAdapter.js +13 -2
- package/lib/components/chat/protocols/AGUIAdapter.d.ts +9 -0
- package/lib/components/chat/protocols/AGUIAdapter.js +19 -1
- package/lib/components/chat/protocols/VercelAIAdapter.d.ts +7 -0
- package/lib/components/chat/protocols/VercelAIAdapter.js +19 -0
- package/lib/components/chat/types/execution.d.ts +78 -0
- package/lib/components/chat/types/execution.js +64 -0
- package/lib/components/chat/types/index.d.ts +1 -0
- package/lib/components/chat/types/index.js +1 -0
- package/lib/components/chat/types/protocol.d.ts +9 -0
- package/lib/components/ui/pagination.d.ts +2 -2
- package/lib/components/ui/pagination.js +4 -4
- package/lib/components/ui/resizable.d.ts +4 -4
- package/lib/components/ui/resizable.js +4 -4
- package/lib/examples/A2UiRestaurantExample.js +2 -2
- package/lib/examples/AgUiAgenticExample.js +2 -2
- package/lib/examples/AgUiBackendToolRenderingExample.js +2 -2
- package/lib/examples/AgUiHaikuGenUIExample.js +2 -2
- package/lib/examples/AgUiHumanInTheLoopExample.js +2 -2
- package/lib/examples/AgUiSharedStateExample.js +2 -2
- package/lib/examples/AgUiToolsBasedGenUIExample.js +2 -2
- package/lib/examples/AgentRuntimeCustomExample.js +2 -2
- package/lib/examples/AgentRuntimeLexical2Example.js +2 -1
- package/lib/examples/AgentRuntimeLexicalExample.js +5 -2
- package/lib/examples/AgentRuntimeLexicalSidebarExample.js +4 -2
- package/lib/examples/AgentRuntimeNotebookExample.js +1 -1
- package/lib/examples/AgentRuntimeStandaloneExample.js +2 -2
- package/lib/examples/AgentSpaceFormExample.d.ts +70 -2
- package/lib/examples/AgentSpaceFormExample.js +177 -43
- package/lib/examples/CopilotKitLexicalExample.js +2 -1
- package/lib/examples/components/AgentConfiguration.d.ts +17 -2
- package/lib/examples/components/AgentConfiguration.js +220 -16
- package/lib/examples/components/LexicalEditor.js +2 -1
- package/lib/examples/components/MockFileBrowser.js +6 -2
- package/lib/examples/components/index.d.ts +0 -1
- package/lib/examples/components/index.js +0 -1
- package/lib/examples/example-selector.js +0 -1
- package/lib/examples/index.d.ts +0 -1
- package/lib/examples/index.js +0 -1
- package/lib/examples/lexical/editorConfig.d.ts +3 -2
- package/lib/examples/lexical/editorConfig.js +7 -1
- package/lib/examples/lexical/initial-content.json +2210 -0
- package/lib/examples/main.js +15 -1
- package/lib/identity/IdentityConnect.d.ts +90 -0
- package/lib/identity/IdentityConnect.js +316 -0
- package/lib/identity/OAuthCallback.d.ts +58 -0
- package/lib/identity/OAuthCallback.js +223 -0
- package/lib/identity/dcr.d.ts +257 -0
- package/lib/identity/dcr.js +282 -0
- package/lib/identity/identityStore.d.ts +72 -0
- package/lib/identity/identityStore.js +529 -0
- package/lib/identity/index.d.ts +46 -0
- package/lib/identity/index.js +17 -0
- package/lib/identity/pkce.d.ts +30 -0
- package/lib/identity/pkce.js +65 -0
- package/lib/identity/types.d.ts +293 -0
- package/lib/identity/types.js +73 -0
- package/lib/identity/useIdentity.d.ts +108 -0
- package/lib/identity/useIdentity.js +323 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/lib/utils.js +1 -1
- package/lib/renderers/a2ui/lib/utils.js +1 -1
- package/lib/test-setup.d.ts +1 -1
- package/lib/test-setup.js +1 -0
- package/lib/tools/adapters/agent-runtimes/AgentRuntimesToolAdapter.js +32 -1
- package/lib/tools/adapters/agent-runtimes/lexicalHooks.d.ts +6 -0
- package/lib/tools/adapters/agent-runtimes/lexicalHooks.js +16 -17
- package/package.json +20 -7
- package/patches/@datalayer+jupyter-lexical+1.0.8.patch +11628 -0
- package/patches/@datalayer+jupyter-react+2.0.2.patch +5338 -0
- package/lib/examples/AgentSpaceHomeExample.d.ts +0 -8
- package/lib/examples/AgentSpaceHomeExample.js +0 -171
- package/lib/examples/components/AgentsDataTable.d.ts +0 -13
- package/lib/examples/components/AgentsDataTable.js +0 -74
- package/lib/examples/components/Rating.d.ts +0 -14
- 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
|
|
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
|
-
}, [
|
|
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;
|