feat: implement skills system for AI agents (#16865)
## Summary This PR introduces a Skills system for AI agents, inspired by the [Agent Skills specification](https://agentskills.io/specification). ## Changes ### Backend - **SkillEntity**: New database entity with migration for storing skills - **V2 Sync Mechanism**: Implemented FlatSkill, builders, validators, and action handlers following the v2 flat entity pattern - **Standard Skills**: Pre-defined skills (workflow-building, data-manipulation, dashboard-building, metadata-building, research, code-interpreter, xlsx, pdf, docx, pptx) - **GraphQL API**: CRUD operations for skills with proper guards and permissions - **Workspace Cache**: Integrated skills into the workspace cache system ### Frontend - **Skills Table**: Searchable table in AI settings showing all skills - **Skill Form**: Create/edit page with Label (primary), Description, and Content (markdown editor) - **API Name**: Following existing patterns, name is derived from label with advanced settings toggle for custom API names - **Standard vs Custom**: Standard skills are read-only, custom skills can be edited/deleted ## Key Design Decisions - Skills are stored in the database (Salesforce-like approach) rather than files - Name is derived from Label by default (isLabelSyncedWithName pattern) - Skills reference functions/files via @ mentions in markdown content rather than explicit relations - Standard skills are synced from code, custom skills are created via UI ## Screenshots Skills table and form UI follow existing settings patterns. ## Testing - [x] Lint passes - [x] Typecheck passes - [ ] CI tests
This commit is contained in:
+14
-3
@@ -1,5 +1,8 @@
|
||||
import { AdvancedTextEditor } from '@/advanced-text-editor/components/AdvancedTextEditor';
|
||||
import { useAdvancedTextEditor } from '@/advanced-text-editor/hooks/useAdvancedTextEditor';
|
||||
import {
|
||||
type AdvancedTextEditorContentType,
|
||||
useAdvancedTextEditor,
|
||||
} from '@/advanced-text-editor/hooks/useAdvancedTextEditor';
|
||||
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
|
||||
import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent';
|
||||
import { InputErrorHelper } from '@/ui/input/components/InputErrorHelper';
|
||||
@@ -86,6 +89,7 @@ type FormAdvancedTextFieldInputProps = {
|
||||
fullScreenBreadcrumbs?: BreadcrumbProps['links'];
|
||||
minHeight: number;
|
||||
maxWidth: number;
|
||||
contentType?: AdvancedTextEditorContentType;
|
||||
};
|
||||
|
||||
export const FormAdvancedTextFieldInput = ({
|
||||
@@ -103,6 +107,7 @@ export const FormAdvancedTextFieldInput = ({
|
||||
fullScreenBreadcrumbs,
|
||||
minHeight,
|
||||
maxWidth,
|
||||
contentType = 'json',
|
||||
}: FormAdvancedTextFieldInputProps) => {
|
||||
const instanceId = useId();
|
||||
const isMobile = useIsMobile();
|
||||
@@ -119,9 +124,15 @@ export const FormAdvancedTextFieldInput = ({
|
||||
placeholder: placeholder,
|
||||
readonly,
|
||||
defaultValue,
|
||||
contentType,
|
||||
onUpdate: (editor) => {
|
||||
const jsonContent = editor.getJSON();
|
||||
onChange(JSON.stringify(jsonContent));
|
||||
if (contentType === 'markdown') {
|
||||
// For markdown mode, output the HTML which preserves formatting
|
||||
onChange(editor.getHTML());
|
||||
} else {
|
||||
const jsonContent = editor.getJSON();
|
||||
onChange(JSON.stringify(jsonContent));
|
||||
}
|
||||
},
|
||||
onFocus: () => {
|
||||
pushFocusItemToFocusStack({
|
||||
|
||||
Reference in New Issue
Block a user