@datalayer/agent-runtimes 1.3.55 → 1.3.57
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/lib/chat/base/ChatBase.js +8 -3
- package/lib/chat/index.d.ts +1 -0
- package/lib/chat/index.js +3 -0
- package/lib/chat/marks/SpecMark.d.ts +46 -0
- package/lib/chat/marks/SpecMark.js +90 -0
- package/lib/chat/marks/iconRef.d.ts +29 -0
- package/lib/chat/marks/iconRef.js +40 -0
- package/lib/chat/marks/index.d.ts +8 -0
- package/lib/chat/marks/index.js +12 -0
- package/lib/chat/marks/toolMarks.d.ts +43 -0
- package/lib/chat/marks/toolMarks.js +110 -0
- package/lib/chat/messages/ChatMessageList.d.ts +8 -1
- package/lib/chat/messages/ChatMessageList.js +11 -18
- package/lib/chat/parts/ToolPart.js +6 -1
- package/lib/chat/prompt/InputPrompt.d.ts +4 -1
- package/lib/chat/prompt/InputPrompt.js +2 -2
- package/lib/chat/prompt/menus/SkillsMenu.js +2 -1
- package/lib/chat/prompt/menus/ToolsMenu.d.ts +3 -1
- package/lib/chat/prompt/menus/ToolsMenu.js +25 -9
- package/lib/chat/tools/ToolCallDisplay.d.ts +8 -1
- package/lib/chat/tools/ToolCallDisplay.js +3 -2
- package/lib/loop/apps/appspec.js +17 -2
- package/lib/loop/plugins/chat/ChatView.js +7 -5
- package/lib/specs/actions.js +56 -0
- package/lib/specs/apps.js +239 -4
- package/lib/specs/appspecSchema.js +37 -1
- package/lib/specs/frontendTools.js +6 -6
- package/lib/specs/mcpServers.d.ts +1 -0
- package/lib/specs/mcpServers.js +39 -16
- package/lib/specs/skills.js +7 -7
- package/lib/specs/tools.js +21 -21
- package/lib/types/agentspecs.d.ts +11 -0
- package/lib/types/chat.d.ts +4 -0
- package/lib/types/mcp.d.ts +2 -2
- package/lib/types/skills.d.ts +2 -2
- package/lib/types/tools.d.ts +4 -4
- package/package.json +1 -1
- package/scripts/codegen/agentspecs_clone.py +5 -3
- package/scripts/codegen/generate_frontend_tools.py +2 -0
- package/scripts/codegen/generate_mcp_servers.py +2 -0
- package/scripts/codegen/generate_skills.py +2 -0
- package/scripts/codegen/generate_tools.py +2 -0
- package/scripts/codegen/marks.py +33 -0
|
@@ -1342,7 +1342,12 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
1342
1342
|
return true;
|
|
1343
1343
|
})
|
|
1344
1344
|
.map(tool => ({ id: tool.name, name: tool.name }));
|
|
1345
|
-
|
|
1345
|
+
/*
|
|
1346
|
+
* Kept apart: the runtime's tools, and the frontend tools — the ones this
|
|
1347
|
+
* page runs. The menu names them under two headings, so a person can tell
|
|
1348
|
+
* what the agent does on the server from what it does here.
|
|
1349
|
+
*/
|
|
1350
|
+
return { runtime: fromConfig, frontend: fromPage };
|
|
1346
1351
|
}, [configQuery.data?.builtinTools, frontendTools, protocol?.options]);
|
|
1347
1352
|
/*
|
|
1348
1353
|
* Who is answering, as one row when that is all there is.
|
|
@@ -3489,7 +3494,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3489
3494
|
maxWidth: 920,
|
|
3490
3495
|
mx: 'auto',
|
|
3491
3496
|
bg: 'canvas.default',
|
|
3492
|
-
}, children: _jsx(ChatMessageList, { displayItems: displayItems, isLoading: isLoading, isStreaming: isStreaming, showLoadingIndicator: showLoadingIndicator, hideMessagesAfterToolUI: hideMessagesAfterToolUI, avatarConfig: defaultAvatarConfig, padding: padding, renderToolResult: effectiveRenderToolResult, approvalConfig: approvalConfig, messagesEndRef: messagesEndRef, onRespond: handleRespond, showTurnFooters: showTurnFooter, agentUsage: agentUsage, onRemoveItems: handleRemoveItems, emptyContent: launching ? null : (_jsx(ChatEmptyState, { emptyState: emptyState, brandIcon: brandIcon, description: resolvedDescription, suggestions: resolvedSuggestions, submitOnSuggestionClick: submitOnSuggestionClick, onSuggestionSubmit: handleSuggestionSubmit, onSuggestionFill: handleSuggestionFill })) }) }));
|
|
3497
|
+
}, children: _jsx(ChatMessageList, { displayItems: displayItems, isLoading: isLoading, isStreaming: isStreaming, showLoadingIndicator: showLoadingIndicator, hideMessagesAfterToolUI: hideMessagesAfterToolUI, avatarConfig: defaultAvatarConfig, padding: padding, renderToolResult: effectiveRenderToolResult, approvalConfig: approvalConfig, messagesEndRef: messagesEndRef, onRespond: handleRespond, showTurnFooters: showTurnFooter, agentUsage: agentUsage, onRemoveItems: handleRemoveItems, mcpServers: filteredMcpServers, emptyContent: launching ? null : (_jsx(ChatEmptyState, { emptyState: emptyState, brandIcon: brandIcon, description: resolvedDescription, suggestions: resolvedSuggestions, submitOnSuggestionClick: submitOnSuggestionClick, onSuggestionSubmit: handleSuggestionSubmit, onSuggestionFill: handleSuggestionFill })) }) }));
|
|
3493
3498
|
const inputToolbar = showInput ? (_jsx(InputPrompt, { input: input, setInput: setInput, isLoading: isLoading, kernelStatus: liveKernelStatus, connectionConfirmed: connectionConfirmed, placeholder: placeholder,
|
|
3494
3499
|
/*
|
|
3495
3500
|
The same openers the empty state offers, typed into the box they would
|
|
@@ -3513,7 +3518,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3513
3518
|
models: configQuery.data.decisionModels,
|
|
3514
3519
|
note: configQuery.data.decisionsNote,
|
|
3515
3520
|
}
|
|
3516
|
-
: undefined, selectedModel: selectedModel, onModelSelect: setSelectedModel, availableTools: builtinTools, mcpServers: filteredMcpServers, enabledMcpTools: enabledMcpTools, enabledMcpToolCount: getEnabledMcpToolNames().length, onToggleMcpTool: toggleMcpTool, onToggleAllMcpServerTools: toggleAllMcpServerTools, approvedMcpTools: approvedMcpTools, onToggleMcpToolApproval: toggleMcpToolApproval, skills: skillsQuery.data?.skills || [], skillsLoading: !!skillsQuery.isLoading, enabledSkills: enabledSkills, onToggleSkill: toggleSkill, onToggleAllSkills: toggleAllSkills, approvedSkills: approvedSkills, onToggleSkillApproval: toggleSkillApproval, apiBase: indicatorApiBase, authToken: protocol?.authToken, mcpStatusData: effectiveMcpStatusData })) : null;
|
|
3521
|
+
: undefined, selectedModel: selectedModel, onModelSelect: setSelectedModel, availableTools: builtinTools.runtime, availableFrontendTools: builtinTools.frontend, mcpServers: filteredMcpServers, enabledMcpTools: enabledMcpTools, enabledMcpToolCount: getEnabledMcpToolNames().length, onToggleMcpTool: toggleMcpTool, onToggleAllMcpServerTools: toggleAllMcpServerTools, approvedMcpTools: approvedMcpTools, onToggleMcpToolApproval: toggleMcpToolApproval, skills: skillsQuery.data?.skills || [], skillsLoading: !!skillsQuery.isLoading, enabledSkills: enabledSkills, onToggleSkill: toggleSkill, onToggleAllSkills: toggleAllSkills, approvedSkills: approvedSkills, onToggleSkillApproval: toggleSkillApproval, apiBase: indicatorApiBase, authToken: protocol?.authToken, mcpStatusData: effectiveMcpStatusData })) : null;
|
|
3517
3522
|
// Shared header element. It is rendered either at the top of the chat (when
|
|
3518
3523
|
// no ephemeral notebook is shown) or INSIDE the chat body column (when the
|
|
3519
3524
|
// notebook is visible) so the header always follows the chat body across all
|
package/lib/chat/index.d.ts
CHANGED
|
@@ -62,6 +62,7 @@ export { ChatHeader, type ChatHeaderProps } from './header/ChatHeader';
|
|
|
62
62
|
export { ChatViewModeToggle, VIEW_MODE_ICONS, type ChatViewModeToggleProps, } from './header/ChatViewModeToggle';
|
|
63
63
|
export { MessagePart, type MessagePartProps, TextPart, type TextPartProps, ReasoningPart, type ReasoningPartProps, ToolPart, type ToolPartProps, DynamicToolPart, type DynamicToolPartProps, } from './parts';
|
|
64
64
|
export { ToolCallDisplay, type ToolCallDisplayProps } from './tools';
|
|
65
|
+
export { ICON_PACKAGES, SpecMark, hasMark, marksOfFrontendTool, marksOfMcpServer, marksOfRuntimeTool, marksOfSkill, marksOfToolCall, parseIconRef, useMarkIcon, type IconPackage, type IconRef, type MarkIcon, type MarkedMcpServer, type Marks, type SpecMarkProps, type ToolKind, type ToolMarks, } from './marks';
|
|
65
66
|
export { ToolApprovalDialog, useToolApprovalDialog, type ToolApprovalDialogProps, } from './tools/ToolApprovalDialog';
|
|
66
67
|
export { ToolApprovalBanner, type ToolApprovalBannerProps, type PendingApproval, } from './tools/ToolApprovalBanner';
|
|
67
68
|
export { Chat, type ChatProps } from './Chat';
|
package/lib/chat/index.js
CHANGED
|
@@ -73,6 +73,9 @@ export { ChatViewModeToggle, VIEW_MODE_ICONS, } from './header/ChatViewModeToggl
|
|
|
73
73
|
export { MessagePart, TextPart, ReasoningPart, ToolPart, DynamicToolPart, } from './parts';
|
|
74
74
|
// Components - Tool UI
|
|
75
75
|
export { ToolCallDisplay } from './tools';
|
|
76
|
+
// Marks: the icon (by package), else the emoji, of an MCP server, a skill, a
|
|
77
|
+
// frontend tool set or a runtime tool — and whose a tool call is.
|
|
78
|
+
export { ICON_PACKAGES, SpecMark, hasMark, marksOfFrontendTool, marksOfMcpServer, marksOfRuntimeTool, marksOfSkill, marksOfToolCall, parseIconRef, useMarkIcon, } from './marks';
|
|
76
79
|
export { ToolApprovalDialog, useToolApprovalDialog, } from './tools/ToolApprovalDialog';
|
|
77
80
|
export { ToolApprovalBanner, } from './tools/ToolApprovalBanner';
|
|
78
81
|
// Components - Transport-agnostic chat
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mark of a catalogue entry: its icon, else its emoji, else nothing.
|
|
3
|
+
*
|
|
4
|
+
* An MCP server, a skill, a tool and a frontend tool set each name an icon by
|
|
5
|
+
* its package (`@datalayer/icons-react:odoo`). Each package is imported when
|
|
6
|
+
* the first icon from it is drawn, once, and never before: the Datalayer icons
|
|
7
|
+
* are five hundred components, and a chat that names three of them should not
|
|
8
|
+
* have to carry the rest up front.
|
|
9
|
+
*
|
|
10
|
+
* @module chat/marks/SpecMark
|
|
11
|
+
*/
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { type IconPackage, type IconRef } from './iconRef';
|
|
14
|
+
/** What either package exports an icon as. */
|
|
15
|
+
export type MarkIcon = React.ComponentType<{
|
|
16
|
+
size?: number;
|
|
17
|
+
'aria-hidden'?: boolean | 'true' | 'false';
|
|
18
|
+
}>;
|
|
19
|
+
type IconModule = Record<string, unknown>;
|
|
20
|
+
/** Import a package of icons, once. */
|
|
21
|
+
export declare function loadIconPackage(pkg: IconPackage): Promise<IconModule>;
|
|
22
|
+
/** The icon a reference names, from its loaded package; refused when it has none. */
|
|
23
|
+
export declare function iconOf(ref: IconRef, module: IconModule): MarkIcon;
|
|
24
|
+
/** An icon reference resolved: the component, `undefined` while its package loads. */
|
|
25
|
+
export declare function useMarkIcon(icon: string | undefined): MarkIcon | undefined;
|
|
26
|
+
export interface SpecMarkProps {
|
|
27
|
+
/** `<package>:<name>`, as the catalogue says it. */
|
|
28
|
+
icon?: string;
|
|
29
|
+
/** Drawn where there is no icon. */
|
|
30
|
+
emoji?: string;
|
|
31
|
+
/** In pixels. */
|
|
32
|
+
size?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The icon, else the emoji, else nothing.
|
|
36
|
+
*
|
|
37
|
+
* While the icon's package loads, a blank of the icon's size holds its place,
|
|
38
|
+
* so the line it sits in does not move when it arrives.
|
|
39
|
+
*/
|
|
40
|
+
export declare function SpecMark({ icon, emoji, size }: SpecMarkProps): React.JSX.Element | null;
|
|
41
|
+
/** Whether an entry has a mark to draw. */
|
|
42
|
+
export declare function hasMark(marks: {
|
|
43
|
+
icon?: string;
|
|
44
|
+
emoji?: string;
|
|
45
|
+
} | null | undefined): boolean;
|
|
46
|
+
export default SpecMark;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx as _jsx } 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
|
+
* The mark of a catalogue entry: its icon, else its emoji, else nothing.
|
|
8
|
+
*
|
|
9
|
+
* An MCP server, a skill, a tool and a frontend tool set each name an icon by
|
|
10
|
+
* its package (`@datalayer/icons-react:odoo`). Each package is imported when
|
|
11
|
+
* the first icon from it is drawn, once, and never before: the Datalayer icons
|
|
12
|
+
* are five hundred components, and a chat that names three of them should not
|
|
13
|
+
* have to carry the rest up front.
|
|
14
|
+
*
|
|
15
|
+
* @module chat/marks/SpecMark
|
|
16
|
+
*/
|
|
17
|
+
import { useEffect, useState } from 'react';
|
|
18
|
+
import { parseIconRef } from './iconRef';
|
|
19
|
+
const LOADERS = {
|
|
20
|
+
'@datalayer/icons-react': () => import('@datalayer/icons-react'),
|
|
21
|
+
'@primer/octicons-react': () => import('@primer/octicons-react'),
|
|
22
|
+
};
|
|
23
|
+
const loaded = new Map();
|
|
24
|
+
const loading = new Map();
|
|
25
|
+
/** Import a package of icons, once. */
|
|
26
|
+
export function loadIconPackage(pkg) {
|
|
27
|
+
const pending = loading.get(pkg) ??
|
|
28
|
+
LOADERS[pkg]().then(module => {
|
|
29
|
+
loaded.set(pkg, module);
|
|
30
|
+
return module;
|
|
31
|
+
});
|
|
32
|
+
loading.set(pkg, pending);
|
|
33
|
+
return pending;
|
|
34
|
+
}
|
|
35
|
+
/** The icon a reference names, from its loaded package; refused when it has none. */
|
|
36
|
+
export function iconOf(ref, module) {
|
|
37
|
+
const icon = module[ref.exportName];
|
|
38
|
+
if (!icon) {
|
|
39
|
+
throw new Error(`${ref.pkg} has no icon ${ref.name} (${ref.exportName}): name one it has`);
|
|
40
|
+
}
|
|
41
|
+
return icon;
|
|
42
|
+
}
|
|
43
|
+
/** An icon reference resolved: the component, `undefined` while its package loads. */
|
|
44
|
+
export function useMarkIcon(icon) {
|
|
45
|
+
const ref = icon ? parseIconRef(icon) : undefined;
|
|
46
|
+
const pkg = ref?.pkg;
|
|
47
|
+
const ready = pkg ? loaded.get(pkg) : undefined;
|
|
48
|
+
const [, setLoaded] = useState(0);
|
|
49
|
+
const [failure, setFailure] = useState(null);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!pkg || ready) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
let live = true;
|
|
55
|
+
loadIconPackage(pkg).then(() => live && setLoaded(n => n + 1), error => live && setFailure(error));
|
|
56
|
+
return () => {
|
|
57
|
+
live = false;
|
|
58
|
+
};
|
|
59
|
+
}, [pkg, ready]);
|
|
60
|
+
if (failure) {
|
|
61
|
+
throw failure;
|
|
62
|
+
}
|
|
63
|
+
return ref && ready ? iconOf(ref, ready) : undefined;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The icon, else the emoji, else nothing.
|
|
67
|
+
*
|
|
68
|
+
* While the icon's package loads, a blank of the icon's size holds its place,
|
|
69
|
+
* so the line it sits in does not move when it arrives.
|
|
70
|
+
*/
|
|
71
|
+
export function SpecMark({ icon, emoji, size = 16 }) {
|
|
72
|
+
const Icon = useMarkIcon(icon);
|
|
73
|
+
if (icon) {
|
|
74
|
+
return Icon ? (_jsx("span", { "data-mark": icon, style: { display: 'inline-flex', flexShrink: 0, lineHeight: 0 }, children: _jsx(Icon, { size: size, "aria-hidden": "true" }) })) : (_jsx("span", { "data-mark": icon, "aria-hidden": "true", style: { display: 'inline-block', width: size, height: size } }));
|
|
75
|
+
}
|
|
76
|
+
if (emoji) {
|
|
77
|
+
return (_jsx("span", { "data-mark": "emoji", "aria-hidden": "true", style: {
|
|
78
|
+
display: 'inline-flex',
|
|
79
|
+
flexShrink: 0,
|
|
80
|
+
fontSize: Math.round(size * 0.9),
|
|
81
|
+
lineHeight: 1,
|
|
82
|
+
}, children: emoji }));
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
/** Whether an entry has a mark to draw. */
|
|
87
|
+
export function hasMark(marks) {
|
|
88
|
+
return Boolean(marks?.icon || marks?.emoji);
|
|
89
|
+
}
|
|
90
|
+
export default SpecMark;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An icon reference, as the agentspecs catalogues write it.
|
|
3
|
+
*
|
|
4
|
+
* `<package>:<name>`: the package the icon is in and the icon's own name, in
|
|
5
|
+
* kebab case, as that package names it — `@datalayer/icons-react:odoo` (the
|
|
6
|
+
* Datalayer icons' `odoo.svg`, exported as `OdooIcon`),
|
|
7
|
+
* `@primer/octicons-react:mark-github` (`MarkGithubIcon`). The rule is
|
|
8
|
+
* `agentspecs.marks`; this is the same rule, read on the page.
|
|
9
|
+
*
|
|
10
|
+
* @module chat/marks/iconRef
|
|
11
|
+
*/
|
|
12
|
+
/** The packages an icon may come from. */
|
|
13
|
+
export declare const ICON_PACKAGES: readonly ["@datalayer/icons-react", "@primer/octicons-react"];
|
|
14
|
+
export type IconPackage = (typeof ICON_PACKAGES)[number];
|
|
15
|
+
export interface IconRef {
|
|
16
|
+
/** The package the icon is in. */
|
|
17
|
+
pkg: IconPackage;
|
|
18
|
+
/** The icon's name, in kebab case: `odoo`, `mark-github`. */
|
|
19
|
+
name: string;
|
|
20
|
+
/** What the package exports it as: `OdooIcon`, `MarkGithubIcon`. */
|
|
21
|
+
exportName: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The component an icon's name is exported as, by the rule both packages
|
|
25
|
+
* follow: each word capitalised, joined, and `Icon` after it.
|
|
26
|
+
*/
|
|
27
|
+
export declare function exportNameOf(name: string): string;
|
|
28
|
+
/** Read an icon reference; anything else is refused. */
|
|
29
|
+
export declare function parseIconRef(ref: string): IconRef;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* An icon reference, as the agentspecs catalogues write it.
|
|
7
|
+
*
|
|
8
|
+
* `<package>:<name>`: the package the icon is in and the icon's own name, in
|
|
9
|
+
* kebab case, as that package names it — `@datalayer/icons-react:odoo` (the
|
|
10
|
+
* Datalayer icons' `odoo.svg`, exported as `OdooIcon`),
|
|
11
|
+
* `@primer/octicons-react:mark-github` (`MarkGithubIcon`). The rule is
|
|
12
|
+
* `agentspecs.marks`; this is the same rule, read on the page.
|
|
13
|
+
*
|
|
14
|
+
* @module chat/marks/iconRef
|
|
15
|
+
*/
|
|
16
|
+
/** The packages an icon may come from. */
|
|
17
|
+
export const ICON_PACKAGES = [
|
|
18
|
+
'@datalayer/icons-react',
|
|
19
|
+
'@primer/octicons-react',
|
|
20
|
+
];
|
|
21
|
+
const ICON_REF = /^(@datalayer\/icons-react|@primer\/octicons-react):([a-z0-9]+(?:-[a-z0-9]+)*)$/;
|
|
22
|
+
/**
|
|
23
|
+
* The component an icon's name is exported as, by the rule both packages
|
|
24
|
+
* follow: each word capitalised, joined, and `Icon` after it.
|
|
25
|
+
*/
|
|
26
|
+
export function exportNameOf(name) {
|
|
27
|
+
return (name
|
|
28
|
+
.split('-')
|
|
29
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
30
|
+
.join('') + 'Icon');
|
|
31
|
+
}
|
|
32
|
+
/** Read an icon reference; anything else is refused. */
|
|
33
|
+
export function parseIconRef(ref) {
|
|
34
|
+
const match = ICON_REF.exec(ref);
|
|
35
|
+
if (!match) {
|
|
36
|
+
throw new Error(`The icon ${JSON.stringify(ref)} is not <package>:<name>, the package one of ${ICON_PACKAGES.join(', ')} and the name in kebab case`);
|
|
37
|
+
}
|
|
38
|
+
const [, pkg, name] = match;
|
|
39
|
+
return { pkg: pkg, name, exportName: exportNameOf(name) };
|
|
40
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The marks of the catalogues: icons by package, emoji, and whose a tool call is.
|
|
3
|
+
*
|
|
4
|
+
* @module chat/marks
|
|
5
|
+
*/
|
|
6
|
+
export { ICON_PACKAGES, exportNameOf, parseIconRef, type IconPackage, type IconRef, } from './iconRef';
|
|
7
|
+
export { SpecMark, hasMark, iconOf, loadIconPackage, useMarkIcon, type MarkIcon, type SpecMarkProps, } from './SpecMark';
|
|
8
|
+
export { SKILL_TOOLS, marksOfFrontendTool, marksOfMcpServer, marksOfRuntimeTool, marksOfSkill, marksOfToolCall, skillIdOfCall, type MarkedMcpServer, type Marks, type ToolKind, type ToolMarks, } from './toolMarks';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The marks of the catalogues: icons by package, emoji, and whose a tool call is.
|
|
7
|
+
*
|
|
8
|
+
* @module chat/marks
|
|
9
|
+
*/
|
|
10
|
+
export { ICON_PACKAGES, exportNameOf, parseIconRef, } from './iconRef';
|
|
11
|
+
export { SpecMark, hasMark, iconOf, loadIconPackage, useMarkIcon, } from './SpecMark';
|
|
12
|
+
export { SKILL_TOOLS, marksOfFrontendTool, marksOfMcpServer, marksOfRuntimeTool, marksOfSkill, marksOfToolCall, skillIdOfCall, } from './toolMarks';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** The two marks of a catalogue entry. */
|
|
2
|
+
export interface Marks {
|
|
3
|
+
/** `<package>:<name>` */
|
|
4
|
+
icon?: string;
|
|
5
|
+
emoji?: string;
|
|
6
|
+
}
|
|
7
|
+
/** What a tool call is, by who it belongs to. */
|
|
8
|
+
export type ToolKind = 'mcp' | 'skill' | 'frontend' | 'runtime';
|
|
9
|
+
export interface ToolMarks extends Marks {
|
|
10
|
+
kind: ToolKind;
|
|
11
|
+
/** The catalogue entry (or the MCP server) it belongs to. */
|
|
12
|
+
ownerId: string;
|
|
13
|
+
}
|
|
14
|
+
/** An MCP server as the chat knows it: what it serves, and its marks if it says them. */
|
|
15
|
+
export interface MarkedMcpServer {
|
|
16
|
+
id: string;
|
|
17
|
+
tools: {
|
|
18
|
+
name: string;
|
|
19
|
+
}[];
|
|
20
|
+
icon?: string;
|
|
21
|
+
emoji?: string;
|
|
22
|
+
}
|
|
23
|
+
/** The tools the skills area runs a skill through. */
|
|
24
|
+
export declare const SKILL_TOOLS: Set<string>;
|
|
25
|
+
/** The skill a skill tool call names, without its version. */
|
|
26
|
+
export declare function skillIdOfCall(toolName: string, args: Record<string, unknown> | undefined): string | null;
|
|
27
|
+
/** An MCP server's marks: its own when it says them, the catalogue's by its id otherwise. */
|
|
28
|
+
export declare function marksOfMcpServer(server: {
|
|
29
|
+
id: string;
|
|
30
|
+
icon?: string;
|
|
31
|
+
emoji?: string;
|
|
32
|
+
}): Marks;
|
|
33
|
+
/** A skill's marks, from the catalogue, by its id with or without a version. */
|
|
34
|
+
export declare function marksOfSkill(skillId: string): Marks;
|
|
35
|
+
/** A frontend tool's marks, by its name. */
|
|
36
|
+
export declare function marksOfFrontendTool(toolName: string): Marks;
|
|
37
|
+
/** A runtime tool's marks, by the name it is registered under. */
|
|
38
|
+
export declare function marksOfRuntimeTool(toolName: string): Marks;
|
|
39
|
+
/**
|
|
40
|
+
* Who a call belongs to, and its marks; `null` when nobody the catalogue
|
|
41
|
+
* marks claims it.
|
|
42
|
+
*/
|
|
43
|
+
export declare function marksOfToolCall(toolName: string, args: Record<string, unknown> | undefined, mcpServers?: readonly MarkedMcpServer[]): ToolMarks | null;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Whose mark a tool call carries.
|
|
7
|
+
*
|
|
8
|
+
* A call names a tool, and the tool belongs to something the catalogue marks:
|
|
9
|
+
* an MCP server (by the tools the server says it serves), a skill (the skill
|
|
10
|
+
* tools carry the skill's name in their arguments), a frontend tool set (by
|
|
11
|
+
* the names in its `toolset`) or a runtime tool (by its `runtime.method`, the
|
|
12
|
+
* name the runtime registers it under). A tool none of them claims has no mark.
|
|
13
|
+
*
|
|
14
|
+
* @module chat/marks/toolMarks
|
|
15
|
+
*/
|
|
16
|
+
import { MCP_SERVER_LIBRARY } from '../../specs/mcpServers';
|
|
17
|
+
import { SKILLS_CATALOG } from '../../specs/skills';
|
|
18
|
+
import { FRONTEND_TOOL_CATALOG } from '../../specs/frontendTools';
|
|
19
|
+
import { TOOL_CATALOG } from '../../specs/tools';
|
|
20
|
+
/** The tools the skills area runs a skill through. */
|
|
21
|
+
export const SKILL_TOOLS = new Set([
|
|
22
|
+
'run_skill_script',
|
|
23
|
+
'load_skill',
|
|
24
|
+
'read_skill_resource',
|
|
25
|
+
]);
|
|
26
|
+
function marksOf(entry) {
|
|
27
|
+
return { icon: entry?.icon, emoji: entry?.emoji };
|
|
28
|
+
}
|
|
29
|
+
/** The skill a skill tool call names, without its version. */
|
|
30
|
+
export function skillIdOfCall(toolName, args) {
|
|
31
|
+
if (!SKILL_TOOLS.has(toolName)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const raw = args?.skill_name ?? args?.skill ?? args?.name;
|
|
35
|
+
if (typeof raw !== 'string' || raw.length === 0) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return raw.split(':', 1)[0] || raw;
|
|
39
|
+
}
|
|
40
|
+
let frontendIndex = null;
|
|
41
|
+
let runtimeIndex = null;
|
|
42
|
+
/** Each frontend tool's name, to the first set of the catalogue that has it. */
|
|
43
|
+
function frontendTools() {
|
|
44
|
+
if (!frontendIndex) {
|
|
45
|
+
frontendIndex = new Map();
|
|
46
|
+
for (const spec of Object.values(FRONTEND_TOOL_CATALOG)) {
|
|
47
|
+
for (const name of spec.toolset ?? []) {
|
|
48
|
+
if (!frontendIndex.has(name)) {
|
|
49
|
+
frontendIndex.set(name, { id: spec.id, ...marksOf(spec) });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return frontendIndex;
|
|
55
|
+
}
|
|
56
|
+
/** Each runtime tool's registered name (`runtime.method`), to its spec. */
|
|
57
|
+
function runtimeTools() {
|
|
58
|
+
if (!runtimeIndex) {
|
|
59
|
+
runtimeIndex = new Map();
|
|
60
|
+
for (const spec of Object.values(TOOL_CATALOG)) {
|
|
61
|
+
const method = spec.runtime?.method;
|
|
62
|
+
if (method && !runtimeIndex.has(method)) {
|
|
63
|
+
runtimeIndex.set(method, { id: spec.id, ...marksOf(spec) });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return runtimeIndex;
|
|
68
|
+
}
|
|
69
|
+
/** An MCP server's marks: its own when it says them, the catalogue's by its id otherwise. */
|
|
70
|
+
export function marksOfMcpServer(server) {
|
|
71
|
+
if (server.icon || server.emoji) {
|
|
72
|
+
return marksOf(server);
|
|
73
|
+
}
|
|
74
|
+
return marksOf(MCP_SERVER_LIBRARY[server.id]);
|
|
75
|
+
}
|
|
76
|
+
/** A skill's marks, from the catalogue, by its id with or without a version. */
|
|
77
|
+
export function marksOfSkill(skillId) {
|
|
78
|
+
return marksOf(SKILLS_CATALOG[skillId.split(':', 1)[0] || skillId]);
|
|
79
|
+
}
|
|
80
|
+
/** A frontend tool's marks, by its name. */
|
|
81
|
+
export function marksOfFrontendTool(toolName) {
|
|
82
|
+
return marksOf(frontendTools().get(toolName));
|
|
83
|
+
}
|
|
84
|
+
/** A runtime tool's marks, by the name it is registered under. */
|
|
85
|
+
export function marksOfRuntimeTool(toolName) {
|
|
86
|
+
return marksOf(runtimeTools().get(toolName));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Who a call belongs to, and its marks; `null` when nobody the catalogue
|
|
90
|
+
* marks claims it.
|
|
91
|
+
*/
|
|
92
|
+
export function marksOfToolCall(toolName, args, mcpServers = []) {
|
|
93
|
+
const skillId = skillIdOfCall(toolName, args);
|
|
94
|
+
if (skillId) {
|
|
95
|
+
return { kind: 'skill', ownerId: skillId, ...marksOfSkill(skillId) };
|
|
96
|
+
}
|
|
97
|
+
const server = mcpServers.find(candidate => candidate.tools.some(tool => tool.name === toolName));
|
|
98
|
+
if (server) {
|
|
99
|
+
return { kind: 'mcp', ownerId: server.id, ...marksOfMcpServer(server) };
|
|
100
|
+
}
|
|
101
|
+
const frontend = frontendTools().get(toolName);
|
|
102
|
+
if (frontend) {
|
|
103
|
+
return { kind: 'frontend', ownerId: frontend.id, ...marksOf(frontend) };
|
|
104
|
+
}
|
|
105
|
+
const runtime = runtimeTools().get(toolName);
|
|
106
|
+
if (runtime) {
|
|
107
|
+
return { kind: 'runtime', ownerId: runtime.id, ...marksOf(runtime) };
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @module chat/messages/MessageList
|
|
6
6
|
*/
|
|
7
7
|
import { type ReactNode, type RefObject } from 'react';
|
|
8
|
+
import { type MarkedMcpServer } from '../marks';
|
|
8
9
|
import type { DisplayItem, AvatarConfig, RenderToolResult } from '../../types/chat';
|
|
9
10
|
import type { ChatMessage } from '../../types/messages';
|
|
10
11
|
import type { ContextSnapshotData } from '../../types/context';
|
|
@@ -66,6 +67,12 @@ export interface ChatMessageListProps {
|
|
|
66
67
|
* `note`, which has no footer of its own.
|
|
67
68
|
*/
|
|
68
69
|
renderMessageFooter?: (message: ChatMessage) => ReactNode;
|
|
70
|
+
/**
|
|
71
|
+
* The MCP servers the agent has, with the tools each serves: a tool call is
|
|
72
|
+
* drawn with the marks of the server it belongs to. Skills, frontend tools
|
|
73
|
+
* and runtime tools are found in the catalogues.
|
|
74
|
+
*/
|
|
75
|
+
mcpServers?: readonly MarkedMcpServer[];
|
|
69
76
|
}
|
|
70
77
|
type SubagentAvatarConfig = Required<Pick<AvatarConfig, 'userAvatar' | 'assistantAvatar' | 'showAvatars' | 'avatarSize' | 'userAvatarBg' | 'assistantAvatarBg'>>;
|
|
71
78
|
export interface SubagentChatPanelProps {
|
|
@@ -85,5 +92,5 @@ export interface SubagentChatPanelProps {
|
|
|
85
92
|
* approval decisions are sent over the shared monitoring socket.
|
|
86
93
|
*/
|
|
87
94
|
export declare function SubagentChatPanel({ toolCallId, subagentName: subagentNameHint, height, avatarConfig, }: SubagentChatPanelProps): React.ReactElement | null;
|
|
88
|
-
export declare function ChatMessageList({ displayItems, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, renderToolResult, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters, agentUsage, onRemoveItems, renderMessageFooter, }: ChatMessageListProps): import("react").JSX.Element;
|
|
95
|
+
export declare function ChatMessageList({ displayItems, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, renderToolResult, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters, agentUsage, onRemoveItems, renderMessageFooter, mcpServers, }: ChatMessageListProps): import("react").JSX.Element;
|
|
89
96
|
export {};
|
|
@@ -16,6 +16,7 @@ import { Box } from '@datalayer/primer-addons';
|
|
|
16
16
|
import { Streamdown } from 'streamdown';
|
|
17
17
|
import { streamdownMarkdownStyles, streamdownCodeBlockStyles, } from '../styles/streamdownStyles';
|
|
18
18
|
import { ToolCallDisplay } from '../tools/ToolCallDisplay';
|
|
19
|
+
import { marksOfToolCall, skillIdOfCall } from '../marks';
|
|
19
20
|
import { TurnFooter } from './TurnFooter';
|
|
20
21
|
import { normalizeAssistantMarkdown } from './assistantMarkdown';
|
|
21
22
|
import { parseFormSubmission } from './formSubmission';
|
|
@@ -275,7 +276,7 @@ export function SubagentChatPanel({ toolCallId, subagentName: subagentNameHint,
|
|
|
275
276
|
// DefaultToolCallRenderer — reads approvals from the Zustand store,
|
|
276
277
|
// sends decisions over the shared WebSocket (no REST polling).
|
|
277
278
|
// ---------------------------------------------------------------------------
|
|
278
|
-
function DefaultToolCallRenderer({ item, onRespond, }) {
|
|
279
|
+
function DefaultToolCallRenderer({ item, onRespond, mcpServers, }) {
|
|
279
280
|
const resultObject = item.result && typeof item.result === 'object'
|
|
280
281
|
? item.result
|
|
281
282
|
: undefined;
|
|
@@ -300,21 +301,13 @@ function DefaultToolCallRenderer({ item, onRespond, }) {
|
|
|
300
301
|
// Capture the skill id carried in the tool-call arguments so we can match
|
|
301
302
|
// the inline approval button against that synthetic approval entry.
|
|
302
303
|
const skillApprovalKey = useMemo(() => {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
'read_skill_resource',
|
|
307
|
-
]);
|
|
308
|
-
if (!SKILL_TOOLS.has(item.toolName))
|
|
309
|
-
return null;
|
|
310
|
-
const a = (item.args ?? {});
|
|
311
|
-
const raw = a.skill_name ?? a.skill ?? a.name;
|
|
312
|
-
if (typeof raw !== 'string' || raw.length === 0)
|
|
313
|
-
return null;
|
|
314
|
-
// Strip optional ``<name>:<version>`` suffix to the base skill id.
|
|
315
|
-
const base = raw.split(':', 1)[0] || raw;
|
|
316
|
-
return `skill:${base}`.toLowerCase();
|
|
304
|
+
// The base skill id, without an optional ``<name>:<version>`` suffix.
|
|
305
|
+
const skillId = skillIdOfCall(item.toolName, item.args);
|
|
306
|
+
return skillId ? `skill:${skillId}`.toLowerCase() : null;
|
|
317
307
|
}, [item.toolName, item.args]);
|
|
308
|
+
// Whose tool it is — its MCP server, skill, frontend tool set or runtime
|
|
309
|
+
// tool — for the mark drawn before its name.
|
|
310
|
+
const marks = useMemo(() => marksOfToolCall(item.toolName, item.args, mcpServers), [item.toolName, item.args, mcpServers]);
|
|
318
311
|
// Read pending approvals from the Zustand store (fed by the agent-runtime WS).
|
|
319
312
|
const approvals = useAgentRuntimeStore(s => s.approvals);
|
|
320
313
|
// Prefer an exact match by approval id (when present in SSE tool result).
|
|
@@ -399,12 +392,12 @@ function DefaultToolCallRenderer({ item, onRespond, }) {
|
|
|
399
392
|
? () => makeDecision('approve')
|
|
400
393
|
: undefined, onDeny: effectiveApprovalId && !decision && !externalDecision
|
|
401
394
|
? () => makeDecision('reject')
|
|
402
|
-
: undefined, approvalLoading: false }), isSubagentDelegation && (_jsx(SubagentChatPanel, { toolCallId: item.toolCallId, subagentName: delegatedSubagentName }))] }));
|
|
395
|
+
: undefined, approvalLoading: false, marks: marks }), isSubagentDelegation && (_jsx(SubagentChatPanel, { toolCallId: item.toolCallId, subagentName: delegatedSubagentName }))] }));
|
|
403
396
|
}
|
|
404
397
|
// ---------------------------------------------------------------------------
|
|
405
398
|
// Component
|
|
406
399
|
// ---------------------------------------------------------------------------
|
|
407
|
-
export function ChatMessageList({ displayItems, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, renderToolResult, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters = false, agentUsage, onRemoveItems, renderMessageFooter, }) {
|
|
400
|
+
export function ChatMessageList({ displayItems, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, renderToolResult, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters = false, agentUsage, onRemoveItems, renderMessageFooter, mcpServers, }) {
|
|
408
401
|
if (displayItems.length === 0) {
|
|
409
402
|
return _jsx(_Fragment, { children: emptyContent });
|
|
410
403
|
}
|
|
@@ -506,7 +499,7 @@ export function ChatMessageList({ displayItems, isLoading, isStreaming, showLoad
|
|
|
506
499
|
not care about hid those tools entirely. Offering the default as a
|
|
507
500
|
node lets it compose instead of choosing.
|
|
508
501
|
*/
|
|
509
|
-
const defaultUI = (_jsx(DefaultToolCallRenderer, { item: item, approvalConfig: approvalConfig, onRespond: createRespondCallback(item.toolCallId) }));
|
|
502
|
+
const defaultUI = (_jsx(DefaultToolCallRenderer, { item: item, approvalConfig: approvalConfig, onRespond: createRespondCallback(item.toolCallId), mcpServers: mcpServers }));
|
|
510
503
|
const toolUI = renderToolResult
|
|
511
504
|
? renderToolResult({
|
|
512
505
|
toolCallId: item.toolCallId,
|
|
@@ -13,6 +13,7 @@ import React from 'react';
|
|
|
13
13
|
import { Text, Button } from '@primer/react';
|
|
14
14
|
import { Box } from '@datalayer/primer-addons';
|
|
15
15
|
import { ChevronDownIcon, AlertIcon } from '@primer/octicons-react';
|
|
16
|
+
import { SpecMark, hasMark, marksOfToolCall } from '../marks';
|
|
16
17
|
/**
|
|
17
18
|
* Check if output contains execution result with errors
|
|
18
19
|
*/
|
|
@@ -89,6 +90,10 @@ export function ToolPart({ part }) {
|
|
|
89
90
|
const executionResult = extractExecutionResult(part.output);
|
|
90
91
|
const statusInfo = getStatusInfo(part.state, executionResult);
|
|
91
92
|
const toolName = part.type.split('-').slice(1).join('-') || part.type;
|
|
93
|
+
// Whose tool it is, from the catalogues: its icon, else its emoji, else nothing.
|
|
94
|
+
const marks = marksOfToolCall(toolName, part.input && typeof part.input === 'object'
|
|
95
|
+
? part.input
|
|
96
|
+
: undefined);
|
|
92
97
|
const effectiveExitOutput = executionResult?.output ??
|
|
93
98
|
(typeof part.output === 'string'
|
|
94
99
|
? part.output
|
|
@@ -119,7 +124,7 @@ export function ToolPart({ part }) {
|
|
|
119
124
|
'&:hover': {
|
|
120
125
|
backgroundColor: 'neutral.muted',
|
|
121
126
|
},
|
|
122
|
-
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(
|
|
127
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [hasMark(marks) && (_jsx(Box, { sx: { display: 'flex', color: 'fg.muted' }, children: _jsx(SpecMark, { icon: marks?.icon, emoji: marks?.emoji, size: 14 }) })), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: toolName }), _jsxs(Box, { sx: {
|
|
123
128
|
display: 'inline-flex',
|
|
124
129
|
alignItems: 'center',
|
|
125
130
|
gap: 1,
|
|
@@ -195,7 +195,10 @@ export interface InputPromptProps {
|
|
|
195
195
|
decisions?: Decisions;
|
|
196
196
|
selectedModel?: string;
|
|
197
197
|
onModelSelect?: (modelId: string) => void;
|
|
198
|
+
/** The runtime's tools. */
|
|
198
199
|
availableTools?: BuiltinTool[];
|
|
200
|
+
/** The frontend tools: the ones this page runs for the agent. */
|
|
201
|
+
availableFrontendTools?: BuiltinTool[];
|
|
199
202
|
/** MCP servers to render (already filtered by selection) */
|
|
200
203
|
mcpServers?: MCPServerConfig[];
|
|
201
204
|
enabledMcpTools?: Map<string, Set<string>>;
|
|
@@ -220,4 +223,4 @@ export interface InputPromptProps {
|
|
|
220
223
|
/** Pre-fetched MCP status from WebSocket — bypasses REST polling */
|
|
221
224
|
mcpStatusData?: McpToolsetsStatusResponse | null;
|
|
222
225
|
}
|
|
223
|
-
export declare function InputPrompt({ input, setInput, isLoading, kernelStatus, connectionConfirmed, placeholder, typingSuggestions, autoFocus, focusTrigger, padding, placement, floatingAnchor, draggable, container, footerExtras, leadingPanel, trailingPanel, onSend, onStop, disableInputPrompt, promptVariant, mentionableAgents, promptHistory, headerContent, showAgentsMenu, showInlineAgentsMenu, agents, selectedAgentId, onSelectAgent, showTokenUsage, showContextRing, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, showSuggestionsMenu, suggestions, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, configLoading, models, decisions, selectedModel, onModelSelect, availableTools, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, approvedMcpTools, onToggleMcpToolApproval, skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, apiBase, authToken, mcpStatusData, }: InputPromptProps): import("react").JSX.Element;
|
|
226
|
+
export declare function InputPrompt({ input, setInput, isLoading, kernelStatus, connectionConfirmed, placeholder, typingSuggestions, autoFocus, focusTrigger, padding, placement, floatingAnchor, draggable, container, footerExtras, leadingPanel, trailingPanel, onSend, onStop, disableInputPrompt, promptVariant, mentionableAgents, promptHistory, headerContent, showAgentsMenu, showInlineAgentsMenu, agents, selectedAgentId, onSelectAgent, showTokenUsage, showContextRing, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, showSuggestionsMenu, suggestions, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, configLoading, models, decisions, selectedModel, onModelSelect, availableTools, availableFrontendTools, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, approvedMcpTools, onToggleMcpToolApproval, skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, apiBase, authToken, mcpStatusData, }: InputPromptProps): import("react").JSX.Element;
|
|
@@ -29,7 +29,7 @@ const EMPTY_ID_SET = new Set();
|
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
// Component
|
|
31
31
|
// ---------------------------------------------------------------------------
|
|
32
|
-
export function InputPrompt({ input, setInput, isLoading, kernelStatus, connectionConfirmed, placeholder, typingSuggestions, autoFocus, focusTrigger, padding, placement, floatingAnchor = 'bottom', draggable = false, container, footerExtras, leadingPanel, trailingPanel, onSend, onStop, disableInputPrompt = false, promptVariant, mentionableAgents, promptHistory, headerContent, showAgentsMenu = false, showInlineAgentsMenu = true, agents = [], selectedAgentId, onSelectAgent, showTokenUsage, showContextRing = false, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, showSuggestionsMenu = true, suggestions, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, configLoading = false, models = [], decisions, selectedModel = '', onModelSelect = () => { }, availableTools = [], mcpServers = [], enabledMcpTools = EMPTY_TOOL_MAP, enabledMcpToolCount = 0, onToggleMcpTool = () => { }, onToggleAllMcpServerTools = () => { }, approvedMcpTools = EMPTY_TOOL_MAP, onToggleMcpToolApproval = () => { }, skills = [], skillsLoading = false, enabledSkills = EMPTY_ID_SET, onToggleSkill = () => { }, onToggleAllSkills = () => { }, approvedSkills = EMPTY_ID_SET, onToggleSkillApproval = () => { }, apiBase, authToken, mcpStatusData, }) {
|
|
32
|
+
export function InputPrompt({ input, setInput, isLoading, kernelStatus, connectionConfirmed, placeholder, typingSuggestions, autoFocus, focusTrigger, padding, placement, floatingAnchor = 'bottom', draggable = false, container, footerExtras, leadingPanel, trailingPanel, onSend, onStop, disableInputPrompt = false, promptVariant, mentionableAgents, promptHistory, headerContent, showAgentsMenu = false, showInlineAgentsMenu = true, agents = [], selectedAgentId, onSelectAgent, showTokenUsage, showContextRing = false, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, showSuggestionsMenu = true, suggestions, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, configLoading = false, models = [], decisions, selectedModel = '', onModelSelect = () => { }, availableTools = [], availableFrontendTools = [], mcpServers = [], enabledMcpTools = EMPTY_TOOL_MAP, enabledMcpToolCount = 0, onToggleMcpTool = () => { }, onToggleAllMcpServerTools = () => { }, approvedMcpTools = EMPTY_TOOL_MAP, onToggleMcpToolApproval = () => { }, skills = [], skillsLoading = false, enabledSkills = EMPTY_ID_SET, onToggleSkill = () => { }, onToggleAllSkills = () => { }, approvedSkills = EMPTY_ID_SET, onToggleSkillApproval = () => { }, apiBase, authToken, mcpStatusData, }) {
|
|
33
33
|
const isKernelBusy = kernelStatus === 'busy';
|
|
34
34
|
/*
|
|
35
35
|
* Each menu is offered when it is switched on *and* has something behind it.
|
|
@@ -143,7 +143,7 @@ export function InputPrompt({ input, setInput, isLoading, kernelStatus, connecti
|
|
|
143
143
|
minHeight: 36,
|
|
144
144
|
bordered: true,
|
|
145
145
|
subtle: true,
|
|
146
|
-
content: (_jsx(_Fragment, { children: anyOffered || extrasOffered || suggestionsOffered ? (_jsxs(_Fragment, { children: [agentsOffered && (_jsx(AgentsMenu, { agents: agents, selectedAgentId: selectedAgentId, onSelectAgent: onSelectAgent })), toolsOffered && (_jsx(ToolsMenu, { codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, mcpServers: mcpServers, enabledMcpTools: enabledMcpTools, enabledMcpToolCount: enabledMcpToolCount, onToggleMcpTool: onToggleMcpTool, onToggleAllMcpServerTools: onToggleAllMcpServerTools, approvedMcpTools: approvedMcpTools, onToggleMcpToolApproval: onToggleMcpToolApproval, availableTools: availableTools })), skillsOffered && (_jsx(SkillsMenu, { skills: skills, skillsLoading: skillsLoading, enabledSkills: enabledSkills, onToggleSkill: onToggleSkill, onToggleAllSkills: onToggleAllSkills, approvedSkills: approvedSkills, onToggleSkillApproval: onToggleSkillApproval })), modelsOffered && (_jsx(ModelSelector, { models: models, decisions: decisions, selectedModel: selectedModel, onModelSelect: onModelSelect, isA2AProtocol: isA2AProtocol })), suggestionsOffered && (_jsx(SuggestionsMenu, { suggestions: suggestionItems, onPick: text => {
|
|
146
|
+
content: (_jsx(_Fragment, { children: anyOffered || extrasOffered || suggestionsOffered ? (_jsxs(_Fragment, { children: [agentsOffered && (_jsx(AgentsMenu, { agents: agents, selectedAgentId: selectedAgentId, onSelectAgent: onSelectAgent })), toolsOffered && (_jsx(ToolsMenu, { codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, mcpServers: mcpServers, enabledMcpTools: enabledMcpTools, enabledMcpToolCount: enabledMcpToolCount, onToggleMcpTool: onToggleMcpTool, onToggleAllMcpServerTools: onToggleAllMcpServerTools, approvedMcpTools: approvedMcpTools, onToggleMcpToolApproval: onToggleMcpToolApproval, availableTools: availableTools, availableFrontendTools: availableFrontendTools })), skillsOffered && (_jsx(SkillsMenu, { skills: skills, skillsLoading: skillsLoading, enabledSkills: enabledSkills, onToggleSkill: onToggleSkill, onToggleAllSkills: onToggleAllSkills, approvedSkills: approvedSkills, onToggleSkillApproval: onToggleSkillApproval })), modelsOffered && (_jsx(ModelSelector, { models: models, decisions: decisions, selectedModel: selectedModel, onModelSelect: onModelSelect, isA2AProtocol: isA2AProtocol })), suggestionsOffered && (_jsx(SuggestionsMenu, { suggestions: suggestionItems, onPick: text => {
|
|
147
147
|
setInput(text);
|
|
148
148
|
setPickedFocus(n => n + 1);
|
|
149
149
|
} })), extrasOffered && (_jsx(Box, { sx: {
|
|
@@ -11,6 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
11
11
|
import { Text, Button, ActionMenu, ActionList, Tooltip, ToggleSwitch, } from '@primer/react';
|
|
12
12
|
import { Box } from '@datalayer/primer-addons';
|
|
13
13
|
import { BriefcaseIcon } from '@primer/octicons-react';
|
|
14
|
+
import { SpecMark, hasMark, marksOfSkill } from '../../marks';
|
|
14
15
|
export function SkillsMenu({ skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, }) {
|
|
15
16
|
// A count, not a word — see `ToolsMenu`. The names go in the tooltip, which
|
|
16
17
|
// is where a person asking "which skills?" is already looking.
|
|
@@ -40,7 +41,7 @@ export function SkillsMenu({ skills, skillsLoading, enabledSkills, onToggleSkill
|
|
|
40
41
|
'&:hover': {
|
|
41
42
|
backgroundColor: 'canvas.subtle',
|
|
42
43
|
},
|
|
43
|
-
}, children: [_jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [_jsx(Text, { id: `toggle-skill-${skill.id}`, sx: { fontWeight: 'semibold' }, children: skill.name }), skill.status && (_jsx(Text, { sx: {
|
|
44
|
+
}, children: [_jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [hasMark(marksOfSkill(skill.id)) && (_jsx(SpecMark, { ...marksOfSkill(skill.id), size: 16 })), _jsx(Text, { id: `toggle-skill-${skill.id}`, sx: { fontWeight: 'semibold' }, children: skill.name }), skill.status && (_jsx(Text, { sx: {
|
|
44
45
|
fontSize: '10px',
|
|
45
46
|
px: 1,
|
|
46
47
|
borderRadius: 2,
|