Agent skill dev (#6668)

* chore: Rename service & container names for consistency in Docker configs (#6710)

* chore: Rename container names for consistency in Docker configs

* chore: Rename service names for consistency in Docker configs

chore: Update OpenSandbox versions and image repositories (#6709)

* chore: Update OpenSandbox versions and image repositories

* yml version

* images

* init yml

* port

---------

Co-authored-by: archer <545436317@qq.com>

refactor(chat): optimize sandbox status logic and decouple UI/Status hooks (#6713)

* refactor(chat): optimize sandbox status logic and decouple UI/Status hooks

* fix: useRef, rename onClose to afterClose

Update .env.template (#6720)

aiproxy默认的请求地址改成http协议

feat: comprehensive agent skill management and sandbox infrastructure optimization

- Skill System: Implemented a full skill management module including CRUD operations, folder organization, AI-driven skill generation, and versioning (switch/update).
- Sandbox Infrastructure: Introduced 'volume-manager' for PVC and Docker volume lifecycle management, replacing the MinIO sync-agent for better data persistence.
- Workflow Integration: Enhanced the Agent node to support skill selection and configuration, including new UI components and data normalization.
- Permission Management: Added granular permission controls for skills, supporting collaborators, owner transfers, and permission inheritance.
- UI/UX: Added a dedicated Skill dashboard, sandbox debug interface (terminal, logs, and iframe proxy), and comprehensive i18n support.
- Maintenance: Migrated Docker services to named volumes, optimized sandbox instance limits, and improved error handling for sandbox providers.

Co-authored-by: chanzhi82020 <chenzhi@sangfor.com.cn>
Co-authored-by: lavine77
Signed-off-by: Jon <ljp@sangfor.com.cn>

feat: hide skill

prettier

* perf: hide skill code

* fix: ts

* lock

* perf: tool code

* fix: ts

* lock

* fix: test

* fix: openapi

* lock

* fix: test

* null model

---------

Co-authored-by: archer <545436317@qq.com>
This commit is contained in:
Jon
2026-04-07 22:52:03 +08:00
committed by GitHub
parent 5c709afef0
commit 57a505f837
271 changed files with 24108 additions and 454 deletions
@@ -95,6 +95,7 @@ export const iconPaths = {
'common/selectLight': () => import('./icons/common/selectLight.svg'),
'common/setting': () => import('./icons/common/setting.svg'),
'common/settingLight': () => import('./icons/common/settingLight.svg'),
'common/skill': () => import('./icons/common/skill.svg'),
'common/solidChevronDown': () => import('./icons/common/solidChevronDown.svg'),
'common/solidChevronUp': () => import('./icons/common/solidChevronUp.svg'),
'common/templateMarket': () => import('./icons/common/templateMarket.svg'),
@@ -103,6 +104,8 @@ export const iconPaths = {
'common/toolkit': () => import('./icons/common/toolkit.svg'),
'common/trash': () => import('./icons/common/trash.svg'),
'common/uploadFileFill': () => import('./icons/common/uploadFileFill.svg'),
'common/upperRight': () => import('./icons/common/upperRight.svg'),
'common/user': () => import('./icons/common/user.svg'),
'common/userInfo': () => import('./icons/common/userInfo.svg'),
'common/variable': () => import('./icons/common/variable.svg'),
'common/viewLight': () => import('./icons/common/viewLight.svg'),
@@ -236,6 +239,9 @@ export const iconPaths = {
'core/modules/basicNode': () => import('./icons/core/modules/basicNode.svg'),
'core/modules/fitView': () => import('./icons/core/modules/fitView.svg'),
'core/modules/variable': () => import('./icons/core/modules/variable.svg'),
'core/modules/welcomeText': () => import('./icons/core/modules/welcomeText.svg'),
'core/skill/default': () => import('./icons/core/skill/default.svg'),
'core/skill/help': () => import('./icons/core/skill/help.svg'),
'core/workflow/closeEdge': () => import('./icons/core/workflow/closeEdge.svg'),
'core/workflow/debug': () => import('./icons/core/workflow/debug.svg'),
'core/workflow/debugBlue': () => import('./icons/core/workflow/debugBlue.svg'),
@@ -282,6 +288,11 @@ export const iconPaths = {
'core/workflow/runSkip': () => import('./icons/core/workflow/runSkip.svg'),
'core/workflow/runSuccess': () => import('./icons/core/workflow/runSuccess.svg'),
'core/workflow/running': () => import('./icons/core/workflow/running.svg'),
'core/workflow/template/BI': () => import('./icons/core/workflow/template/BI.svg'),
'core/workflow/template/FileRead': () => import('./icons/core/workflow/template/FileRead.svg'),
'core/workflow/template/agent': () => import('./icons/core/workflow/template/agent.svg'),
'core/workflow/template/agentLinear': () =>
import('./icons/core/workflow/template/agentLinear.tsx'),
'core/workflow/template/aiChat': () => import('./icons/core/workflow/template/aiChat.svg'),
'core/workflow/template/aiChatLinear': () =>
import('./icons/core/workflow/template/aiChatLinear.tsx'),
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.9 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" viewBox="0 0 24 24"><defs><radialGradient cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" id="master_svg0_1780_06419" gradientTransform="translate(0 -1.8666579723358154) rotate(56.4373879668184) scale(16.64101115449701 16.64101115449701)"><stop offset="32.69553184509277%" stop-color="#FFFFFF" stop-opacity="0.20000000298023224"/><stop offset="100%" stop-color="#FFFFFF" stop-opacity="0"/></radialGradient><clipPath id="master_svg1_1780_14500"><rect x="0" y="0" width="24" height="24" rx="6"/></clipPath></defs><g clip-path="url(#master_svg1_1780_14500)"><rect x="0" y="0" width="24" height="24" rx="6" fill="#14A846" fill-opacity="1"/><rect x="0" y="0" width="24" height="24" rx="6" fill="url(#master_svg0_1780_06419)" fill-opacity="1"/><g><path d="M17.99735626068115,5.78602L17.158356260681153,4L16.319356260681154,5.78602L14.533356260681153,6.625L16.319356260681154,7.463979999999999L17.158356260681153,9.25L17.99735626068115,7.463979999999999L19.783356260681153,6.625L17.99735626068115,5.78602ZM12.899866260681152,10.23951C12.929606260681151,10.30282,12.980536260681152,10.35375,13.043846260681152,10.38349L15.519356260681151,11.54637L15.639256260681153,11.60266L16.705356260681153,12.10347C16.810556260681153,12.15291,16.877756260681153,12.25872,16.877756260681153,12.375C16.877756260681153,12.49128,16.810556260681153,12.59709,16.705356260681153,12.64653L15.639256260681153,13.14734L15.519356260681151,13.20363L13.043846260681152,14.3665C12.980536260681152,14.3963,12.929606260681151,14.4472,12.899866260681152,14.5105L11.736986260681153,16.986L11.680696260681152,17.1059L11.179896260681152,18.172C11.130446260681152,18.2772,11.024636260681152,18.3444,10.908356260681153,18.3444C10.792076260681153,18.3444,10.686266260681151,18.2772,10.636826260681152,18.172L10.136016260681153,17.1059L10.079726260681152,16.986L8.916846260681153,14.5105C8.887106260681152,14.4472,8.836176260681153,14.3963,8.772866260681152,14.3665L6.297336260681153,13.20363L6.177496260681153,13.14734L5.111394260681152,12.64653C5.0061456606811525,12.59709,4.938947478931152,12.49128,4.938947478931152,12.375C4.938947527431153,12.25872,5.0061456606811525,12.15291,5.111394260681152,12.10347L6.177496260681153,11.60266L6.297336260681153,11.54637L8.772866260681152,10.38349C8.836176260681153,10.35375,8.887106260681152,10.30282,8.916846260681153,10.23951L10.079726260681152,7.76398L10.136016260681153,7.64414L10.636826260681152,6.57804C10.686266260681151,6.47279,10.792076260681153,6.40559,10.908356260681153,6.40559C11.024636260681152,6.40559,11.130446260681152,6.47279,11.179896260681152,6.57804L11.680696260681152,7.64414L11.736986260681153,7.76398L12.899866260681152,10.23951ZM12.406086260681153,11.74116L13.755406260681152,12.375L12.406086260681153,13.00885Q11.818306260681151,13.28495,11.542196260681152,13.87274L10.908356260681153,15.2221L10.274516260681153,13.87273Q9.998406260681152,13.28495,9.410626260681152,13.00884L8.061306260681153,12.375L9.410626260681152,11.74116Q9.998406260681152,11.46505,10.274516260681153,10.877279999999999L10.908356260681153,9.52795L11.542206260681152,10.87727Q11.818306260681151,11.46505,12.406086260681153,11.74116Z" fill-rule="evenodd" fill="#FFFFFF" fill-opacity="1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -0,0 +1,39 @@
import React, { useId } from 'react';
type AgentLinearProps = React.SVGProps<SVGSVGElement>;
const AgentLinear: React.FC<AgentLinearProps> = (props) => {
const gradientId = useId();
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" {...props}>
<path
d="M36 11.57L34.32 8L32.64 11.57L29.07 13.25L32.64 14.93L34.32 18.5L36 14.93L39.57 13.25L36 11.57Z"
stroke={`url(#${gradientId})`}
strokeWidth="2"
strokeLinejoin="round"
/>
<path
d="M25.8 20.48C25.86 20.61 25.96 20.71 26.09 20.77L31.04 23.09L33.41 24.21C33.62 24.31 33.76 24.52 33.76 24.75C33.76 24.98 33.62 25.19 33.41 25.29L31.04 26.41L26.09 28.73C25.96 28.79 25.86 28.89 25.8 29.02L23.47 33.97L21.18 36.69C21.07 36.9 20.86 37.01 20.65 37.01C20.44 37.01 20.23 36.9 20.12 36.69L17.83 33.97L15.5 29.02C15.44 28.89 15.34 28.79 15.21 28.73L10.26 26.41L7.89 25.29C7.68 25.19 7.54 24.98 7.54 24.75C7.54 24.52 7.68 24.31 7.89 24.21L10.26 23.09L15.21 20.77C15.34 20.71 15.44 20.61 15.5 20.48L17.83 15.53L20.12 12.81C20.23 12.6 20.44 12.49 20.65 12.49C20.86 12.49 21.07 12.6 21.18 12.81L23.47 15.53L25.8 20.48Z"
stroke={`url(#${gradientId})`}
strokeWidth="2"
strokeLinejoin="round"
/>
<defs>
<linearGradient
id={gradientId}
x1="22"
y1="8"
x2="22"
y2="40"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#4ADE80" />
<stop offset="1" stopColor="#14A846" />
</linearGradient>
</defs>
</svg>
);
};
export default AgentLinear;
+68 -50
View File
@@ -16,6 +16,7 @@ import MyDivider from '../MyDivider';
import type { IconNameType } from '../Icon/type';
import { useSystem } from '../../../hooks/useSystem';
import Avatar from '../Avatar';
import MyTooltip from '../MyTooltip';
export type MenuItemType = 'primary' | 'danger' | 'gray' | 'grayBg';
export type MenuSizeType = 'sm' | 'md' | 'xs' | 'mini';
@@ -30,6 +31,8 @@ export type MenuItemData = {
description?: string;
onClick?: () => any;
menuItemStyles?: MenuItemProps;
disabled?: boolean;
disabledTip?: string;
}>;
};
export type Props = {
@@ -286,59 +289,74 @@ const MyMenu = ({
<Box key={i}>
{item.label && <Box fontSize={'sm'}>{item.label}</Box>}
{i !== 0 && <MyDivider h={'1.5px'} {...sizeMapStyle[size].dividerStyle} />}
{item.children.map((child, index) => (
<MenuItem
key={index}
borderRadius={'sm'}
onClick={(e) => {
e.stopPropagation();
if (child.onClick) {
setIsOpen(false);
child.onClick();
}
}}
alignItems={'center'}
fontSize={'sm'}
color={child.isActive ? 'primary.700' : 'myGray.600'}
whiteSpace={'pre-wrap'}
{...typeMapStyle[child.type || 'primary'].styles}
{...sizeMapStyle[size].menuItemStyle}
{...child.menuItemStyles}
>
{!!child.icon && (
<Avatar
src={child.icon as any}
mr={2}
{...sizeMapStyle[size].iconStyle}
color={
child.isActive
? 'inherit'
: typeMapStyle[child.type || 'primary'].iconColor
{item.children.map((child, index) => {
const menuItem = (
<MenuItem
key={index}
borderRadius={'sm'}
isDisabled={child.disabled}
onClick={(e) => {
e.stopPropagation();
if (child.disabled) {
return;
}
sx={{
'[role="menuitem"]:hover &': {
color: 'inherit'
if (child.onClick) {
setIsOpen(false);
child.onClick();
}
}}
alignItems={'center'}
fontSize={'sm'}
color={child.isActive ? 'primary.700' : 'myGray.600'}
whiteSpace={'pre-wrap'}
{...typeMapStyle[child.type || 'primary'].styles}
{...sizeMapStyle[size].menuItemStyle}
{...child.menuItemStyles}
>
{!!child.icon && (
<Avatar
src={child.icon as any}
mr={2}
{...sizeMapStyle[size].iconStyle}
color={
child.isActive
? 'inherit'
: typeMapStyle[child.type || 'primary'].iconColor
}
}}
/>
)}
<Box w={'100%'}>
<Box
w={'100%'}
color={child.description ? 'myGray.900' : 'inherit'}
pr={child.icon ? 4 : 0}
{...sizeMapStyle[size].labelStyle}
>
{child.label}
</Box>
{child.description && (
<Box color={'myGray.500'} fontSize={'mini'} w={'100%'}>
{child.description}
</Box>
sx={{
'[role="menuitem"]:hover &': {
color: 'inherit'
}
}}
/>
)}
</Box>
</MenuItem>
))}
<Box w={'100%'}>
<Box
w={'100%'}
color={child.description ? 'myGray.900' : 'inherit'}
pr={child.icon ? 4 : 0}
{...sizeMapStyle[size].labelStyle}
>
{child.label}
</Box>
{child.description && !child.disabled && (
<Box color={'myGray.500'} fontSize={'mini'} w={'100%'}>
{child.description}
</Box>
)}
</Box>
</MenuItem>
);
if (child.disabled && child.disabledTip) {
return (
<MyTooltip shouldWrapChildren={false} key={index} label={child.disabledTip}>
{menuItem}
</MyTooltip>
);
}
return menuItem;
})}
</Box>
);
})}
@@ -0,0 +1,7 @@
import { type Monaco } from '@monaco-editor/react';
type CompletionItemProvider = Parameters<Monaco['languages']['registerCompletionItemProvider']>[1];
type ProvideCompletionItemsFn = CompletionItemProvider['provideCompletionItems'];
export type CompletionModel = Parameters<ProvideCompletionItemsFn>[0];
export type CompletionPosition = Parameters<ProvideCompletionItemsFn>[1];
@@ -1,5 +1,6 @@
import { type Monaco } from '@monaco-editor/react';
import { useCallback } from 'react';
import { type CompletionModel, type CompletionPosition } from './type';
/**
* Sandbox runtime type declarations.
@@ -103,7 +104,7 @@ const useJSCompletion = () => {
monaco.languages.registerCompletionItemProvider('javascript', {
triggerCharacters: ['.'],
provideCompletionItems: (model, position) => {
provideCompletionItems: (model: CompletionModel, position: CompletionPosition) => {
const wordInfo = model.getWordUntilPosition(position);
const currentWordPrefix = wordInfo.word;
@@ -1,5 +1,6 @@
import { type Monaco } from '@monaco-editor/react';
import { useCallback } from 'react';
import { type CompletionModel, type CompletionPosition } from './type';
let monacoInstance: Monaco | null = null;
@@ -10,7 +11,7 @@ const usePythonCompletion = () => {
monaco.languages.registerCompletionItemProvider('python', {
triggerCharacters: ['_'],
provideCompletionItems: (model, position) => {
provideCompletionItems: (model: CompletionModel, position: CompletionPosition) => {
const wordInfo = model.getWordUntilPosition(position);
const currentWordPrefix = wordInfo.word;
const lineContent = model.getLineContent(position.lineNumber);
@@ -1,5 +1,6 @@
import { type Monaco } from '@monaco-editor/react';
import { useCallback } from 'react';
import { type CompletionModel, type CompletionPosition } from './type';
let monacoInstance: Monaco | null = null;
@@ -10,16 +11,8 @@ const useSystemHelperCompletion = () => {
const buildSuggestions = (
monaco: Monaco,
model: Parameters<
Parameters<
typeof monaco.languages.registerCompletionItemProvider
>[1]['provideCompletionItems']
>[0],
position: Parameters<
Parameters<
typeof monaco.languages.registerCompletionItemProvider
>[1]['provideCompletionItems']
>[1],
model: CompletionModel,
position: CompletionPosition,
memberSnippet: string
) => {
const lineContent = model.getLineContent(position.lineNumber);
@@ -76,7 +69,7 @@ const useSystemHelperCompletion = () => {
for (const lang of ['javascript', 'typescript'] as const) {
monaco.languages.registerCompletionItemProvider(lang, {
triggerCharacters: ['.'],
provideCompletionItems: (model, position) =>
provideCompletionItems: (model: CompletionModel, position: CompletionPosition) =>
buildSuggestions(monaco, model, position, jsSnippet)
});
}
@@ -85,7 +78,7 @@ const useSystemHelperCompletion = () => {
const pySnippet = 'httpRequest(${1:url}, method="${2:GET}", headers={}, timeout=${3:60})';
monaco.languages.registerCompletionItemProvider('python', {
triggerCharacters: ['.'],
provideCompletionItems: (model, position) =>
provideCompletionItems: (model: CompletionModel, position: CompletionPosition) =>
buildSuggestions(monaco, model, position, pySnippet)
});
}, []);
@@ -98,6 +98,16 @@ const NodeInputSelect = ({
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.custom].icon,
title: t('common:core.workflow.inputType.Manual input')
},
{
type: FlowNodeInputTypeEnum.selectSkill,
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.selectSkill].icon,
title: t('common:core.workflow.inputType.Manual select')
},
{
type: FlowNodeInputTypeEnum.selectTool,
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.selectTool].icon,
title: t('common:core.workflow.inputType.Manual select')
},
{
type: FlowNodeInputTypeEnum.fileSelect,
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.fileSelect].icon,