From 21ff42074daa446aaf0df38189ad496addbe4cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Fri, 2 Jan 2026 15:22:01 +0100 Subject: [PATCH] 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 --- .github/workflows/ci-server.yaml | 4 +- packages/twenty-front/package.json | 32 +- .../src/generated-metadata/graphql.ts | 368 ++++++ .../twenty-front/src/generated/graphql.ts | 16 + .../hooks/useAdvancedTextEditor.ts | 24 +- .../ai/graphql/fragments/skillFragment.ts | 16 + .../ai/graphql/mutations/activateSkill.ts | 12 + .../ai/graphql/mutations/createSkill.ts | 12 + .../ai/graphql/mutations/deactivateSkill.ts | 12 + .../ai/graphql/mutations/deleteSkill.ts | 12 + .../ai/graphql/mutations/updateSkill.ts | 12 + .../ai/graphql/queries/findManySkills.ts | 12 + .../ai/graphql/queries/findOneSkill.ts | 12 + .../modules/app/components/SettingsRoutes.tsx | 14 + .../hooks/useMetadataErrorHandler.ts | 1 + .../components/FormAdvancedTextFieldInput.tsx | 17 +- .../src/pages/settings/ai/SettingsAI.tsx | 72 +- .../pages/settings/ai/SettingsSkillForm.tsx | 600 ++++++++++ .../settings/ai/components/SettingsAIMCP.tsx | 18 +- .../SettingsSkillInactiveMenuDropDown.tsx | 69 ++ .../ai/components/SettingsSkillTableRow.tsx | 62 + .../ai/components/SettingsSkillsTable.tsx | 227 ++++ .../constants/SettingsSkillTableMetadata.ts | 26 + .../common/1767003000000-add-skill-entity.ts | 43 + .../skills/skill-definition.type.ts | 6 - .../core-modules/skills/skills.module.ts | 9 - .../core-modules/skills/skills.service.ts | 66 -- .../skills/skills/code-interpreter.skill.ts | 105 -- .../skills/skills/dashboard-building.skill.ts | 78 -- .../skills/skills/data-manipulation.skill.ts | 44 - .../core-modules/skills/skills/docx.skill.ts | 152 --- .../skills/skills/metadata-building.skill.ts | 64 - .../core-modules/skills/skills/pdf.skill.ts | 131 --- .../core-modules/skills/skills/pptx.skill.ts | 170 --- .../skills/skills/research.skill.ts | 34 - .../skills/skills/workflow-building.skill.ts | 62 - .../core-modules/skills/skills/xlsx.skill.ts | 131 --- .../tool-provider/tools/load-skill.tool.ts | 6 +- .../constants/dollar-to-credit-multiplier.ts | 4 +- .../convert-cents-to-billing-credits.util.ts | 4 +- .../ai/ai-chat/ai-chat.module.ts | 4 +- .../constants/chat-system-prompts.const.ts | 6 + .../services/chat-execution.service.ts | 23 +- .../constants/dollar-to-credit-multiplier.ts | 4 +- ...rties-to-compare-and-stringify.constant.ts | 5 + ...metadata-many-to-one-relations.constant.ts | 1 + ...-metadata-relations-properties.constant.ts | 4 + ...quired-metadata-for-validation.constant.ts | 1 + .../all-flat-entity-types-by-metadata-name.ts | 16 + ...flat-skill-editable-properties.constant.ts | 10 + .../flat-skill/flat-skill.module.ts | 16 + .../workspace-flat-skill-map-cache.service.ts | 44 + .../flat-skill/types/flat-skill-maps.type.ts | 4 + .../flat-skill/types/flat-skill.type.ts | 4 + ...kill-input-to-flat-skill-to-create.util.ts | 45 + ...skill-input-to-flat-skill-or-throw.util.ts | 38 + .../from-flat-skill-to-skill-dto.util.ts | 18 + ...t-to-flat-skill-to-update-or-throw.util.ts | 50 + ...ansform-skill-entity-to-flat-skill.util.ts | 23 + .../metadata-engine.module.ts | 3 + .../skill/dtos/create-skill.input.ts | 31 + .../metadata-modules/skill/dtos/skill.dto.ts | 65 + .../skill/dtos/update-skill.input.ts | 49 + .../skill/entities/skill.entity.ts | 54 + ...skill-graphql-api-exception.interceptor.ts | 20 + .../metadata-modules/skill/skill.exception.ts | 40 + .../metadata-modules/skill/skill.module.ts | 29 + .../metadata-modules/skill/skill.resolver.ts | 81 ++ .../metadata-modules/skill/skill.service.ts | 381 ++++++ ...kill-graphql-api-exception-handler.util.ts | 32 + .../types/workspace-cache-key.type.ts | 1 + .../constants/standard-skill.constant.ts | 37 + ...nty-standard-all-metadata-name.constant.ts | 1 + .../types/all-standard-skill-name.type.ts | 3 + ...-standard-flat-skill-metadata-maps.util.ts | 25 + ...reate-standard-flat-skill-metadata.util.ts | 1044 +++++++++++++++++ ...reate-standard-skill-flat-metadata.util.ts | 46 + ...plication-all-flat-entity-maps.constant.ts | 10 + ...metadata-validation-response-error.util.ts | 1 + ...ce-migration-build-orchestrator.service.ts | 39 + ...orkspace-migration-v2-skill-action.type.ts | 18 + ...ration-v2-skill-actions-builder.service.ts | 96 ++ .../services/flat-skill-validator.service.ts | 217 ++++ .../validate-skill-name-uniqueness.util.ts | 25 + ...validate-skill-required-properties.util.ts | 51 + ...ace-migration-builder-validators.module.ts | 3 + .../workspace-migration-builder-v2.module.ts | 3 + .../create-skill-action-handler.service.ts | 59 + .../delete-skill-action-handler.service.ts | 56 + .../update-skill-action-handler.service.ts | 71 ++ ...migration-runner-action-handlers.module.ts | 7 + ...ny-view-fields-v2.integration-spec.ts.snap | 2 + ...ate-view-field-v2.integration-spec.ts.snap | 6 + ...ect-on-view-field.integration-spec.ts.snap | 10 + ...ng-agent-creation.integration-spec.ts.snap | 14 + ...ng-agent-deletion.integration-spec.ts.snap | 2 + ...ling-agent-update.integration-spec.ts.snap | 18 + ...ne-field-metadata.integration-spec.ts.snap | 2 + ...ne-field-metadata.integration-spec.ts.snap | 6 + ...ne-field-metadata.integration-spec.ts.snap | 4 + ...-metadata-enum-v2.integration-spec.ts.snap | 140 +++ ...field-metadata-v2.integration-spec.ts.snap | 136 +++ ...morph-relation-v2.integration-spec.ts.snap | 4 + ...morph-relation-v2.integration-spec.ts.snap | 34 + ...relation-creation.integration-spec.ts.snap | 64 + ...a-relation-update.integration-spec.ts.snap | 2 + ...ue-field-metadata.integration-spec.ts.snap | 4 + ...bject-metadata-v2.integration-spec.ts.snap | 34 + ...e-object-metadata.integration-spec.ts.snap | 8 + ...yout-tab-creation.integration-spec.ts.snap | 2 + ...t-widget-creation.integration-spec.ts.snap | 2 + ...ing-role-creation.integration-spec.ts.snap | 10 + ...iling-role-update.integration-spec.ts.snap | 14 + ...ng-skill-creation.integration-spec.ts.snap | 268 +++++ ...ng-skill-deletion.integration-spec.ts.snap | 25 + ...ling-skill-update.integration-spec.ts.snap | 173 +++ ...failing-skill-creation.integration-spec.ts | 128 ++ ...failing-skill-deletion.integration-spec.ts | 69 ++ .../failing-skill-update.integration-spec.ts | 164 +++ ...ssful-skill-activation.integration-spec.ts | 113 ++ ...cessful-skill-creation.integration-spec.ts | 147 +++ ...cessful-skill-deletion.integration-spec.ts | 44 + ...uccessful-skill-update.integration-spec.ts | 131 +++ .../activate-skill-query-factory.util.ts | 36 + .../suites/skill/utils/activate-skill.util.ts | 43 + .../utils/create-skill-query-factory.util.ts | 36 + .../suites/skill/utils/create-skill.util.ts | 43 + .../deactivate-skill-query-factory.util.ts | 36 + .../skill/utils/deactivate-skill.util.ts | 43 + .../utils/delete-skill-query-factory.util.ts | 28 + .../suites/skill/utils/delete-skill.util.ts | 43 + .../utils/find-skill-query-factory.util.ts | 36 + .../suites/skill/utils/find-skill.util.ts | 43 + .../utils/find-skills-query-factory.util.ts | 27 + .../suites/skill/utils/find-skills.util.ts | 39 + .../utils/update-skill-query-factory.util.ts | 36 + .../suites/skill/utils/update-skill.util.ts | 43 + ...rcular-dependency.integration-spec.ts.snap | 4 + ...er-group-creation.integration-spec.ts.snap | 6 + ...rcular-dependency.integration-spec.ts.snap | 4 + ...lter-group-update.integration-spec.ts.snap | 2 + ...w-filter-creation.integration-spec.ts.snap | 6 + .../view-group.integration-spec.ts.snap | 2 + .../metadata/all-metadata-name.constant.ts | 1 + .../twenty-shared/src/types/SettingsPath.ts | 2 + yarn.lock | 234 ++-- 146 files changed, 6964 insertions(+), 1282 deletions(-) create mode 100644 packages/twenty-front/src/modules/ai/graphql/fragments/skillFragment.ts create mode 100644 packages/twenty-front/src/modules/ai/graphql/mutations/activateSkill.ts create mode 100644 packages/twenty-front/src/modules/ai/graphql/mutations/createSkill.ts create mode 100644 packages/twenty-front/src/modules/ai/graphql/mutations/deactivateSkill.ts create mode 100644 packages/twenty-front/src/modules/ai/graphql/mutations/deleteSkill.ts create mode 100644 packages/twenty-front/src/modules/ai/graphql/mutations/updateSkill.ts create mode 100644 packages/twenty-front/src/modules/ai/graphql/queries/findManySkills.ts create mode 100644 packages/twenty-front/src/modules/ai/graphql/queries/findOneSkill.ts create mode 100644 packages/twenty-front/src/pages/settings/ai/SettingsSkillForm.tsx create mode 100644 packages/twenty-front/src/pages/settings/ai/components/SettingsSkillInactiveMenuDropDown.tsx create mode 100644 packages/twenty-front/src/pages/settings/ai/components/SettingsSkillTableRow.tsx create mode 100644 packages/twenty-front/src/pages/settings/ai/components/SettingsSkillsTable.tsx create mode 100644 packages/twenty-front/src/pages/settings/ai/constants/SettingsSkillTableMetadata.ts create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/common/1767003000000-add-skill-entity.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skill-definition.type.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills.module.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills.service.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills/code-interpreter.skill.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills/dashboard-building.skill.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills/data-manipulation.skill.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills/docx.skill.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills/metadata-building.skill.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills/pdf.skill.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills/pptx.skill.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills/research.skill.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills/workflow-building.skill.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/skills/skills/xlsx.skill.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-skill/constants/flat-skill-editable-properties.constant.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-skill/flat-skill.module.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-skill/services/workspace-flat-skill-map-cache.service.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-skill/types/flat-skill-maps.type.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-skill/types/flat-skill.type.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-skill/utils/from-create-skill-input-to-flat-skill-to-create.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-skill/utils/from-delete-skill-input-to-flat-skill-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-skill/utils/from-flat-skill-to-skill-dto.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-skill/utils/from-update-skill-input-to-flat-skill-to-update-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-skill/utils/transform-skill-entity-to-flat-skill.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/skill/dtos/create-skill.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/skill/dtos/skill.dto.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/skill/dtos/update-skill.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/skill/entities/skill.entity.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/skill/interceptors/skill-graphql-api-exception.interceptor.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/skill/skill.exception.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/skill/skill.module.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/skill/skill.resolver.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/skill/skill.service.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/skill/utils/skill-graphql-api-exception-handler.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-skill.constant.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/types/all-standard-skill-name.type.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/skill-metadata/build-standard-flat-skill-metadata-maps.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/skill-metadata/create-standard-flat-skill-metadata.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/skill-metadata/create-standard-skill-flat-metadata.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/skill/types/workspace-migration-v2-skill-action.type.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/skill/workspace-migration-v2-skill-actions-builder.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-skill-validator.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/utils/validate-skill-name-uniqueness.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/utils/validate-skill-required-properties.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/skill/services/create-skill-action-handler.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/skill/services/delete-skill-action-handler.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/skill/services/update-skill-action-handler.service.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/__snapshots__/failing-skill-creation.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/__snapshots__/failing-skill-deletion.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/__snapshots__/failing-skill-update.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/failing-skill-creation.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/failing-skill-deletion.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/failing-skill-update.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/successful-skill-activation.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/successful-skill-creation.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/successful-skill-deletion.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/successful-skill-update.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/activate-skill-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/activate-skill.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/create-skill-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/create-skill.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/deactivate-skill-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/deactivate-skill.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/delete-skill-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/delete-skill.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/find-skill-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/find-skill.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/find-skills-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/find-skills.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/update-skill-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/skill/utils/update-skill.util.ts diff --git a/.github/workflows/ci-server.yaml b/.github/workflows/ci-server.yaml index a0d940de605..6126b14ebed 100644 --- a/.github/workflows/ci-server.yaml +++ b/.github/workflows/ci-server.yaml @@ -171,7 +171,7 @@ jobs: strategy: fail-fast: false matrix: - shard: [1, 2, 3, 4, 5, 6] + shard: [1, 2, 3, 4, 5, 6, 7, 8] services: postgres: image: twentycrm/twenty-postgres-spilo @@ -209,7 +209,7 @@ jobs: ANALYTICS_ENABLED: true CLICKHOUSE_URL: "http://default:clickhousePassword@localhost:8123/twenty" CLICKHOUSE_PASSWORD: clickhousePassword - SHARD_COUNTER: 6 + SHARD_COUNTER: 8 steps: - name: Fetch custom Github Actions and base branch history uses: actions/checkout@v4 diff --git a/packages/twenty-front/package.json b/packages/twenty-front/package.json index 5f275c9aa9c..ebbdf49a417 100644 --- a/packages/twenty-front/package.json +++ b/packages/twenty-front/package.json @@ -56,21 +56,22 @@ "@react-pdf/renderer": "^4.1.6", "@scalar/api-reference-react": "^0.4.36", "@sentry/react": "^10.27.0", - "@tiptap/core": "^3.4.2", - "@tiptap/extension-bold": "^3.4.2", - "@tiptap/extension-document": "^3.4.2", - "@tiptap/extension-hard-break": "^3.4.2", - "@tiptap/extension-heading": "^3.4.2", - "@tiptap/extension-image": "^3.4.4", - "@tiptap/extension-italic": "^3.4.2", - "@tiptap/extension-link": "^3.4.2", + "@tiptap/core": "3.4.2", + "@tiptap/extension-bold": "3.4.2", + "@tiptap/extension-document": "3.4.2", + "@tiptap/extension-hard-break": "3.4.2", + "@tiptap/extension-heading": "3.4.2", + "@tiptap/extension-image": "3.4.4", + "@tiptap/extension-italic": "3.4.2", + "@tiptap/extension-link": "3.4.2", "@tiptap/extension-list": "3.4.2", - "@tiptap/extension-paragraph": "^3.4.2", - "@tiptap/extension-strike": "^3.4.2", - "@tiptap/extension-text": "^3.4.2", - "@tiptap/extension-underline": "^3.4.2", - "@tiptap/extensions": "^3.4.2", - "@tiptap/react": "^3.4.2", + "@tiptap/extension-paragraph": "3.4.2", + "@tiptap/extension-strike": "3.4.2", + "@tiptap/extension-text": "3.4.2", + "@tiptap/extension-underline": "3.4.2", + "@tiptap/extensions": "3.4.2", + "@tiptap/react": "3.4.2", + "@types/marked": "^6.0.0", "@xyflow/react": "^12.4.2", "ai": "5.0.52", "apollo-link-rest": "^0.9.0", @@ -90,6 +91,7 @@ "jwt-decode": "^4.0.0", "linkify-react": "^4.1.3", "linkifyjs": "^4.1.3", + "marked": "^17.0.1", "qs": "^6.11.2", "react-data-grid": "7.0.0-beta.13", "react-datepicker": "^6.7.1", @@ -120,7 +122,7 @@ "@lingui/swc-plugin": "^5.6.0", "@lingui/vite-plugin": "^5.1.2", "@playwright/test": "^1.56.1", - "@tiptap/suggestion": "^3.4.2", + "@tiptap/suggestion": "3.4.2", "@types/apollo-upload-client": "^17.0.2", "@types/file-saver": "^2.0.7", "@types/js-cookie": "^3.0.3", diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 0f081a0a511..ab4e353af8f 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -972,6 +972,14 @@ export type CreateServerlessFunctionInput = { timeoutSeconds?: InputMaybe; }; +export type CreateSkillInput = { + content: Scalars['String']; + description?: InputMaybe; + icon?: InputMaybe; + label: Scalars['String']; + name: Scalars['String']; +}; + export type CreateViewFieldInput = { aggregateOperation?: InputMaybe; fieldMetadataId: Scalars['UUID']; @@ -1807,6 +1815,7 @@ export enum ModelProvider { export type Mutation = { __typename?: 'Mutation'; + activateSkill: Skill; activateWorkflowVersion: Scalars['Boolean']; activateWorkspace: Workspace; assignRoleToAgent: Scalars['Boolean']; @@ -1854,9 +1863,11 @@ export type Mutation = { createRowLevelPermissionPredicate: RowLevelPermissionPredicate; createRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; createSAMLIdentityProvider: SetupSsoOutput; + createSkill: Skill; createWebhook: Webhook; createWorkflowVersionEdge: WorkflowVersionStepChanges; createWorkflowVersionStep: WorkflowVersionStepChanges; + deactivateSkill: Skill; deactivateWorkflowVersion: Scalars['Boolean']; deleteApprovedAccessDomain: Scalars['Boolean']; deleteCoreView: Scalars['Boolean']; @@ -1886,6 +1897,7 @@ export type Mutation = { deleteRowLevelPermissionPredicate: RowLevelPermissionPredicate; deleteRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; deleteSSOIdentityProvider: DeleteSsoOutput; + deleteSkill: Skill; deleteTwoFactorAuthenticationMethod: DeleteTwoFactorAuthenticationMethodOutput; deleteUser: User; deleteUserFromWorkspace: UserWorkspace; @@ -1981,6 +1993,7 @@ export type Mutation = { updatePasswordViaResetToken: InvalidatePasswordOutput; updateRowLevelPermissionPredicate: RowLevelPermissionPredicate; updateRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; + updateSkill: Skill; updateUserEmail: Scalars['Boolean']; updateWebhook?: Maybe; updateWorkflowRunStep: WorkflowAction; @@ -2005,6 +2018,11 @@ export type Mutation = { }; +export type MutationActivateSkillArgs = { + id: Scalars['UUID']; +}; + + export type MutationActivateWorkflowVersionArgs = { workflowVersionId: Scalars['UUID']; }; @@ -2228,6 +2246,11 @@ export type MutationCreateSamlIdentityProviderArgs = { }; +export type MutationCreateSkillArgs = { + input: CreateSkillInput; +}; + + export type MutationCreateWebhookArgs = { input: CreateWebhookInput; }; @@ -2243,6 +2266,11 @@ export type MutationCreateWorkflowVersionStepArgs = { }; +export type MutationDeactivateSkillArgs = { + id: Scalars['UUID']; +}; + + export type MutationDeactivateWorkflowVersionArgs = { workflowVersionId: Scalars['UUID']; }; @@ -2384,6 +2412,11 @@ export type MutationDeleteSsoIdentityProviderArgs = { }; +export type MutationDeleteSkillArgs = { + id: Scalars['UUID']; +}; + + export type MutationDeleteTwoFactorAuthenticationMethodArgs = { twoFactorAuthenticationMethodId: Scalars['UUID']; }; @@ -2854,6 +2887,11 @@ export type MutationUpdateRowLevelPermissionPredicateGroupArgs = { }; +export type MutationUpdateSkillArgs = { + input: UpdateSkillInput; +}; + + export type MutationUpdateUserEmailArgs = { newEmail: Scalars['String']; verifyEmailRedirectPath?: InputMaybe; @@ -3390,6 +3428,8 @@ export type Query = { object: Object; objects: ObjectConnection; search: SearchResultConnection; + skill?: Maybe; + skills: Array; validatePasswordResetToken: ValidatePasswordResetTokenOutput; versionInfo: VersionInfo; webhook?: Maybe; @@ -3732,6 +3772,11 @@ export type QuerySearchArgs = { }; +export type QuerySkillArgs = { + id: Scalars['UUID']; +}; + + export type QueryValidatePasswordResetTokenArgs = { passwordResetToken: Scalars['String']; }; @@ -4159,6 +4204,22 @@ export type SignedFile = { token: Scalars['String']; }; +export type Skill = { + __typename?: 'Skill'; + applicationId?: Maybe; + content: Scalars['String']; + createdAt: Scalars['DateTime']; + description?: Maybe; + icon?: Maybe; + id: Scalars['UUID']; + isActive: Scalars['Boolean']; + isCustom: Scalars['Boolean']; + label: Scalars['String']; + name: Scalars['String']; + standardId?: Maybe; + updatedAt: Scalars['DateTime']; +}; + export type StandaloneRichTextConfiguration = { __typename?: 'StandaloneRichTextConfiguration'; body: RichTextV2Body; @@ -4603,6 +4664,16 @@ export type UpdateServerlessFunctionInputUpdates = { timeoutSeconds?: InputMaybe; }; +export type UpdateSkillInput = { + content?: InputMaybe; + description?: InputMaybe; + icon?: InputMaybe; + id: Scalars['UUID']; + isActive?: InputMaybe; + label?: InputMaybe; + name?: InputMaybe; +}; + export type UpdateViewFieldInput = { /** The id of the view field to update */ id: Scalars['UUID']; @@ -5220,6 +5291,15 @@ export type WorkspaceUrlsAndId = { export type AgentFieldsFragment = { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array, applicationId?: string | null, createdAt: string, updatedAt: string }; +export type SkillFieldsFragment = { __typename?: 'Skill', id: string, name: string, label: string, description?: string | null, icon?: string | null, content: string, isCustom: boolean, isActive: boolean, createdAt: string, updatedAt: string }; + +export type ActivateSkillMutationVariables = Exact<{ + id: Scalars['UUID']; +}>; + + +export type ActivateSkillMutation = { __typename?: 'Mutation', activateSkill: { __typename?: 'Skill', id: string, name: string, label: string, description?: string | null, icon?: string | null, content: string, isCustom: boolean, isActive: boolean, createdAt: string, updatedAt: string } }; + export type AssignRoleToAgentMutationVariables = Exact<{ agentId: Scalars['UUID']; roleId: Scalars['UUID']; @@ -5240,6 +5320,20 @@ export type CreateOneAgentMutationVariables = Exact<{ export type CreateOneAgentMutation = { __typename?: 'Mutation', createOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array, applicationId?: string | null, createdAt: string, updatedAt: string } }; +export type CreateSkillMutationVariables = Exact<{ + input: CreateSkillInput; +}>; + + +export type CreateSkillMutation = { __typename?: 'Mutation', createSkill: { __typename?: 'Skill', id: string, name: string, label: string, description?: string | null, icon?: string | null, content: string, isCustom: boolean, isActive: boolean, createdAt: string, updatedAt: string } }; + +export type DeactivateSkillMutationVariables = Exact<{ + id: Scalars['UUID']; +}>; + + +export type DeactivateSkillMutation = { __typename?: 'Mutation', deactivateSkill: { __typename?: 'Skill', id: string, name: string, label: string, description?: string | null, icon?: string | null, content: string, isCustom: boolean, isActive: boolean, createdAt: string, updatedAt: string } }; + export type DeleteOneAgentMutationVariables = Exact<{ input: AgentIdInput; }>; @@ -5247,6 +5341,13 @@ export type DeleteOneAgentMutationVariables = Exact<{ export type DeleteOneAgentMutation = { __typename?: 'Mutation', deleteOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array, applicationId?: string | null, createdAt: string, updatedAt: string } }; +export type DeleteSkillMutationVariables = Exact<{ + id: Scalars['UUID']; +}>; + + +export type DeleteSkillMutation = { __typename?: 'Mutation', deleteSkill: { __typename?: 'Skill', id: string, name: string, label: string, description?: string | null, icon?: string | null, content: string, isCustom: boolean, isActive: boolean, createdAt: string, updatedAt: string } }; + export type EvaluateAgentTurnMutationVariables = Exact<{ turnId: Scalars['UUID']; }>; @@ -5276,11 +5377,23 @@ export type UpdateOneAgentMutationVariables = Exact<{ export type UpdateOneAgentMutation = { __typename?: 'Mutation', updateOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array, applicationId?: string | null, createdAt: string, updatedAt: string } }; +export type UpdateSkillMutationVariables = Exact<{ + input: UpdateSkillInput; +}>; + + +export type UpdateSkillMutation = { __typename?: 'Mutation', updateSkill: { __typename?: 'Skill', id: string, name: string, label: string, description?: string | null, icon?: string | null, content: string, isCustom: boolean, isActive: boolean, createdAt: string, updatedAt: string } }; + export type FindManyAgentsQueryVariables = Exact<{ [key: string]: never; }>; export type FindManyAgentsQuery = { __typename?: 'Query', findManyAgents: Array<{ __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array, applicationId?: string | null, createdAt: string, updatedAt: string }> }; +export type FindManySkillsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type FindManySkillsQuery = { __typename?: 'Query', skills: Array<{ __typename?: 'Skill', id: string, name: string, label: string, description?: string | null, icon?: string | null, content: string, isCustom: boolean, isActive: boolean, createdAt: string, updatedAt: string }> }; + export type FindOneAgentQueryVariables = Exact<{ id: Scalars['UUID']; }>; @@ -5288,6 +5401,13 @@ export type FindOneAgentQueryVariables = Exact<{ export type FindOneAgentQuery = { __typename?: 'Query', findOneAgent: { __typename?: 'Agent', id: string, name: string, label: string, description?: string | null, icon?: string | null, prompt: string, modelId: string, responseFormat?: any | null, roleId?: string | null, isCustom: boolean, modelConfiguration?: any | null, evaluationInputs: Array, applicationId?: string | null, createdAt: string, updatedAt: string } }; +export type FindOneSkillQueryVariables = Exact<{ + id: Scalars['UUID']; +}>; + + +export type FindOneSkillQuery = { __typename?: 'Query', skill?: { __typename?: 'Skill', id: string, name: string, label: string, description?: string | null, icon?: string | null, content: string, isCustom: boolean, isActive: boolean, createdAt: string, updatedAt: string } | null }; + export type GetAgentTurnsQueryVariables = Exact<{ agentId: Scalars['UUID']; }>; @@ -6758,6 +6878,20 @@ export type GetWorkspaceFromInviteHashQueryVariables = Exact<{ export type GetWorkspaceFromInviteHashQuery = { __typename?: 'Query', findWorkspaceFromInviteHash: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, allowImpersonation: boolean } }; +export const SkillFieldsFragmentDoc = gql` + fragment SkillFields on Skill { + id + name + label + description + icon + content + isCustom + isActive + createdAt + updatedAt +} + `; export const AgentFieldsFragmentDoc = gql` fragment AgentFields on Agent { id @@ -7417,6 +7551,39 @@ export const WorkflowDiffFragmentFragmentDoc = gql` stepsDiff } `; +export const ActivateSkillDocument = gql` + mutation ActivateSkill($id: UUID!) { + activateSkill(id: $id) { + ...SkillFields + } +} + ${SkillFieldsFragmentDoc}`; +export type ActivateSkillMutationFn = Apollo.MutationFunction; + +/** + * __useActivateSkillMutation__ + * + * To run a mutation, you first call `useActivateSkillMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useActivateSkillMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [activateSkillMutation, { data, loading, error }] = useActivateSkillMutation({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useActivateSkillMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(ActivateSkillDocument, options); + } +export type ActivateSkillMutationHookResult = ReturnType; +export type ActivateSkillMutationResult = Apollo.MutationResult; +export type ActivateSkillMutationOptions = Apollo.BaseMutationOptions; export const AssignRoleToAgentDocument = gql` mutation AssignRoleToAgent($agentId: UUID!, $roleId: UUID!) { assignRoleToAgent(agentId: $agentId, roleId: $roleId) @@ -7517,6 +7684,72 @@ export function useCreateOneAgentMutation(baseOptions?: Apollo.MutationHookOptio export type CreateOneAgentMutationHookResult = ReturnType; export type CreateOneAgentMutationResult = Apollo.MutationResult; export type CreateOneAgentMutationOptions = Apollo.BaseMutationOptions; +export const CreateSkillDocument = gql` + mutation CreateSkill($input: CreateSkillInput!) { + createSkill(input: $input) { + ...SkillFields + } +} + ${SkillFieldsFragmentDoc}`; +export type CreateSkillMutationFn = Apollo.MutationFunction; + +/** + * __useCreateSkillMutation__ + * + * To run a mutation, you first call `useCreateSkillMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useCreateSkillMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [createSkillMutation, { data, loading, error }] = useCreateSkillMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useCreateSkillMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(CreateSkillDocument, options); + } +export type CreateSkillMutationHookResult = ReturnType; +export type CreateSkillMutationResult = Apollo.MutationResult; +export type CreateSkillMutationOptions = Apollo.BaseMutationOptions; +export const DeactivateSkillDocument = gql` + mutation DeactivateSkill($id: UUID!) { + deactivateSkill(id: $id) { + ...SkillFields + } +} + ${SkillFieldsFragmentDoc}`; +export type DeactivateSkillMutationFn = Apollo.MutationFunction; + +/** + * __useDeactivateSkillMutation__ + * + * To run a mutation, you first call `useDeactivateSkillMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useDeactivateSkillMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [deactivateSkillMutation, { data, loading, error }] = useDeactivateSkillMutation({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useDeactivateSkillMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(DeactivateSkillDocument, options); + } +export type DeactivateSkillMutationHookResult = ReturnType; +export type DeactivateSkillMutationResult = Apollo.MutationResult; +export type DeactivateSkillMutationOptions = Apollo.BaseMutationOptions; export const DeleteOneAgentDocument = gql` mutation DeleteOneAgent($input: AgentIdInput!) { deleteOneAgent(input: $input) { @@ -7550,6 +7783,39 @@ export function useDeleteOneAgentMutation(baseOptions?: Apollo.MutationHookOptio export type DeleteOneAgentMutationHookResult = ReturnType; export type DeleteOneAgentMutationResult = Apollo.MutationResult; export type DeleteOneAgentMutationOptions = Apollo.BaseMutationOptions; +export const DeleteSkillDocument = gql` + mutation DeleteSkill($id: UUID!) { + deleteSkill(id: $id) { + ...SkillFields + } +} + ${SkillFieldsFragmentDoc}`; +export type DeleteSkillMutationFn = Apollo.MutationFunction; + +/** + * __useDeleteSkillMutation__ + * + * To run a mutation, you first call `useDeleteSkillMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useDeleteSkillMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [deleteSkillMutation, { data, loading, error }] = useDeleteSkillMutation({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useDeleteSkillMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(DeleteSkillDocument, options); + } +export type DeleteSkillMutationHookResult = ReturnType; +export type DeleteSkillMutationResult = Apollo.MutationResult; +export type DeleteSkillMutationOptions = Apollo.BaseMutationOptions; export const EvaluateAgentTurnDocument = gql` mutation EvaluateAgentTurn($turnId: UUID!) { evaluateAgentTurn(turnId: $turnId) { @@ -7694,6 +7960,39 @@ export function useUpdateOneAgentMutation(baseOptions?: Apollo.MutationHookOptio export type UpdateOneAgentMutationHookResult = ReturnType; export type UpdateOneAgentMutationResult = Apollo.MutationResult; export type UpdateOneAgentMutationOptions = Apollo.BaseMutationOptions; +export const UpdateSkillDocument = gql` + mutation UpdateSkill($input: UpdateSkillInput!) { + updateSkill(input: $input) { + ...SkillFields + } +} + ${SkillFieldsFragmentDoc}`; +export type UpdateSkillMutationFn = Apollo.MutationFunction; + +/** + * __useUpdateSkillMutation__ + * + * To run a mutation, you first call `useUpdateSkillMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateSkillMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateSkillMutation, { data, loading, error }] = useUpdateSkillMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useUpdateSkillMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(UpdateSkillDocument, options); + } +export type UpdateSkillMutationHookResult = ReturnType; +export type UpdateSkillMutationResult = Apollo.MutationResult; +export type UpdateSkillMutationOptions = Apollo.BaseMutationOptions; export const FindManyAgentsDocument = gql` query FindManyAgents { findManyAgents { @@ -7728,6 +8027,40 @@ export function useFindManyAgentsLazyQuery(baseOptions?: Apollo.LazyQueryHookOpt export type FindManyAgentsQueryHookResult = ReturnType; export type FindManyAgentsLazyQueryHookResult = ReturnType; export type FindManyAgentsQueryResult = Apollo.QueryResult; +export const FindManySkillsDocument = gql` + query FindManySkills { + skills { + ...SkillFields + } +} + ${SkillFieldsFragmentDoc}`; + +/** + * __useFindManySkillsQuery__ + * + * To run a query within a React component, call `useFindManySkillsQuery` and pass it any options that fit your needs. + * When your component renders, `useFindManySkillsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useFindManySkillsQuery({ + * variables: { + * }, + * }); + */ +export function useFindManySkillsQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(FindManySkillsDocument, options); + } +export function useFindManySkillsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(FindManySkillsDocument, options); + } +export type FindManySkillsQueryHookResult = ReturnType; +export type FindManySkillsLazyQueryHookResult = ReturnType; +export type FindManySkillsQueryResult = Apollo.QueryResult; export const FindOneAgentDocument = gql` query FindOneAgent($id: UUID!) { findOneAgent(input: {id: $id}) { @@ -7763,6 +8096,41 @@ export function useFindOneAgentLazyQuery(baseOptions?: Apollo.LazyQueryHookOptio export type FindOneAgentQueryHookResult = ReturnType; export type FindOneAgentLazyQueryHookResult = ReturnType; export type FindOneAgentQueryResult = Apollo.QueryResult; +export const FindOneSkillDocument = gql` + query FindOneSkill($id: UUID!) { + skill(id: $id) { + ...SkillFields + } +} + ${SkillFieldsFragmentDoc}`; + +/** + * __useFindOneSkillQuery__ + * + * To run a query within a React component, call `useFindOneSkillQuery` and pass it any options that fit your needs. + * When your component renders, `useFindOneSkillQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useFindOneSkillQuery({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useFindOneSkillQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(FindOneSkillDocument, options); + } +export function useFindOneSkillLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(FindOneSkillDocument, options); + } +export type FindOneSkillQueryHookResult = ReturnType; +export type FindOneSkillLazyQueryHookResult = ReturnType; +export type FindOneSkillQueryResult = Apollo.QueryResult; export const GetAgentTurnsDocument = gql` query GetAgentTurns($agentId: UUID!) { agentTurns(agentId: $agentId) { diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 25295430535..a7005a36c2e 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -3996,6 +3996,22 @@ export type SignedFile = { token: Scalars['String']; }; +export type Skill = { + __typename?: 'Skill'; + applicationId?: Maybe; + content: Scalars['String']; + createdAt: Scalars['DateTime']; + description?: Maybe; + icon?: Maybe; + id: Scalars['UUID']; + isActive: Scalars['Boolean']; + isCustom: Scalars['Boolean']; + label: Scalars['String']; + name: Scalars['String']; + standardId?: Maybe; + updatedAt: Scalars['DateTime']; +}; + export type StandaloneRichTextConfiguration = { __typename?: 'StandaloneRichTextConfiguration'; body: RichTextV2Body; diff --git a/packages/twenty-front/src/modules/advanced-text-editor/hooks/useAdvancedTextEditor.ts b/packages/twenty-front/src/modules/advanced-text-editor/hooks/useAdvancedTextEditor.ts index f4593038ad7..53f26b57835 100644 --- a/packages/twenty-front/src/modules/advanced-text-editor/hooks/useAdvancedTextEditor.ts +++ b/packages/twenty-front/src/modules/advanced-text-editor/hooks/useAdvancedTextEditor.ts @@ -17,9 +17,12 @@ import { Text } from '@tiptap/extension-text'; import { Underline } from '@tiptap/extension-underline'; import { Dropcursor, Placeholder, UndoRedo } from '@tiptap/extensions'; import { type Editor, useEditor } from '@tiptap/react'; +import { marked } from 'marked'; import { type DependencyList, useMemo } from 'react'; import { isDefined } from 'twenty-shared/utils'; +export type AdvancedTextEditorContentType = 'json' | 'markdown'; + type UseAdvancedTextEditorProps = { placeholder: string | undefined; readonly: boolean | undefined; @@ -30,6 +33,7 @@ type UseAdvancedTextEditorProps = { onImageUpload?: (file: File) => Promise; onImageUploadError?: (error: Error, file: File) => void; enableSlashCommand?: boolean; + contentType?: AdvancedTextEditorContentType; }; export const useAdvancedTextEditor = ( @@ -43,9 +47,12 @@ export const useAdvancedTextEditor = ( onImageUpload, onImageUploadError, enableSlashCommand, + contentType = 'json', }: UseAdvancedTextEditorProps, dependencies?: DependencyList, ) => { + const isMarkdownMode = contentType === 'markdown'; + const extensions = useMemo( () => [ Document, @@ -87,12 +94,23 @@ export const useAdvancedTextEditor = ( ], ); + const getEditorContent = () => { + if (!isDefined(defaultValue)) { + return undefined; + } + + if (isMarkdownMode) { + // Convert markdown to HTML, then TipTap will parse the HTML + return marked.parse(defaultValue, { async: false }) as string; + } + + return getInitialAdvancedTextEditorContent(defaultValue); + }; + const editor = useEditor( { extensions, - content: isDefined(defaultValue) - ? getInitialAdvancedTextEditorContent(defaultValue) - : undefined, + content: getEditorContent(), editable: !readonly, onUpdate: ({ editor }) => { onUpdate(editor); diff --git a/packages/twenty-front/src/modules/ai/graphql/fragments/skillFragment.ts b/packages/twenty-front/src/modules/ai/graphql/fragments/skillFragment.ts new file mode 100644 index 00000000000..32976c77fc3 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/fragments/skillFragment.ts @@ -0,0 +1,16 @@ +import { gql } from '@apollo/client'; + +export const SKILL_FRAGMENT = gql` + fragment SkillFields on Skill { + id + name + label + description + icon + content + isCustom + isActive + createdAt + updatedAt + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/mutations/activateSkill.ts b/packages/twenty-front/src/modules/ai/graphql/mutations/activateSkill.ts new file mode 100644 index 00000000000..29877dc27cc --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/mutations/activateSkill.ts @@ -0,0 +1,12 @@ +import { gql } from '@apollo/client'; + +import { SKILL_FRAGMENT } from '@/ai/graphql/fragments/skillFragment'; + +export const ACTIVATE_SKILL = gql` + ${SKILL_FRAGMENT} + mutation ActivateSkill($id: UUID!) { + activateSkill(id: $id) { + ...SkillFields + } + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/mutations/createSkill.ts b/packages/twenty-front/src/modules/ai/graphql/mutations/createSkill.ts new file mode 100644 index 00000000000..90f70dfa707 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/mutations/createSkill.ts @@ -0,0 +1,12 @@ +import { gql } from '@apollo/client'; + +import { SKILL_FRAGMENT } from '@/ai/graphql/fragments/skillFragment'; + +export const CREATE_SKILL = gql` + ${SKILL_FRAGMENT} + mutation CreateSkill($input: CreateSkillInput!) { + createSkill(input: $input) { + ...SkillFields + } + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/mutations/deactivateSkill.ts b/packages/twenty-front/src/modules/ai/graphql/mutations/deactivateSkill.ts new file mode 100644 index 00000000000..f5611caa7c1 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/mutations/deactivateSkill.ts @@ -0,0 +1,12 @@ +import { gql } from '@apollo/client'; + +import { SKILL_FRAGMENT } from '@/ai/graphql/fragments/skillFragment'; + +export const DEACTIVATE_SKILL = gql` + ${SKILL_FRAGMENT} + mutation DeactivateSkill($id: UUID!) { + deactivateSkill(id: $id) { + ...SkillFields + } + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/mutations/deleteSkill.ts b/packages/twenty-front/src/modules/ai/graphql/mutations/deleteSkill.ts new file mode 100644 index 00000000000..6936a72dd99 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/mutations/deleteSkill.ts @@ -0,0 +1,12 @@ +import { gql } from '@apollo/client'; + +import { SKILL_FRAGMENT } from '@/ai/graphql/fragments/skillFragment'; + +export const DELETE_SKILL = gql` + ${SKILL_FRAGMENT} + mutation DeleteSkill($id: UUID!) { + deleteSkill(id: $id) { + ...SkillFields + } + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/mutations/updateSkill.ts b/packages/twenty-front/src/modules/ai/graphql/mutations/updateSkill.ts new file mode 100644 index 00000000000..580c88d7b16 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/mutations/updateSkill.ts @@ -0,0 +1,12 @@ +import { gql } from '@apollo/client'; + +import { SKILL_FRAGMENT } from '@/ai/graphql/fragments/skillFragment'; + +export const UPDATE_SKILL = gql` + ${SKILL_FRAGMENT} + mutation UpdateSkill($input: UpdateSkillInput!) { + updateSkill(input: $input) { + ...SkillFields + } + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/queries/findManySkills.ts b/packages/twenty-front/src/modules/ai/graphql/queries/findManySkills.ts new file mode 100644 index 00000000000..9ae2855de72 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/queries/findManySkills.ts @@ -0,0 +1,12 @@ +import { gql } from '@apollo/client'; + +import { SKILL_FRAGMENT } from '@/ai/graphql/fragments/skillFragment'; + +export const FIND_MANY_SKILLS = gql` + ${SKILL_FRAGMENT} + query FindManySkills { + skills { + ...SkillFields + } + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/queries/findOneSkill.ts b/packages/twenty-front/src/modules/ai/graphql/queries/findOneSkill.ts new file mode 100644 index 00000000000..5e52085013f --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/queries/findOneSkill.ts @@ -0,0 +1,12 @@ +import { gql } from '@apollo/client'; + +import { SKILL_FRAGMENT } from '@/ai/graphql/fragments/skillFragment'; + +export const FIND_ONE_SKILL = gql` + ${SKILL_FRAGMENT} + query FindOneSkill($id: UUID!) { + skill(id: $id) { + ...SkillFields + } + } +`; diff --git a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx index 28f2557a70d..cadf50811d4 100644 --- a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx +++ b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx @@ -171,6 +171,12 @@ const SettingsAgentTurnDetail = lazy(() => })), ); +const SettingsSkillForm = lazy(() => + import('~/pages/settings/ai/SettingsSkillForm').then((module) => ({ + default: module.SettingsSkillForm, + })), +); + const SettingsWorkspaceMembers = lazy(() => import('~/pages/settings/members/SettingsWorkspaceMembers').then( (module) => ({ @@ -440,6 +446,14 @@ export const SettingsRoutes = ({ isAdminPageEnabled }: SettingsRoutesProps) => ( path={SettingsPath.AIAgentTurnDetail} element={} /> + } + /> + } + /> } /> } /> { role: t`role`, roleTarget: t`role target`, agent: t`agent`, + skill: t`skill`, pageLayout: t`page layout`, pageLayoutTab: t`page layout tab`, pageLayoutWidget: t`page layout widget`, diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput.tsx index 7722e3ba0b5..cdf40c240ec 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput.tsx @@ -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({ diff --git a/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx b/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx index 41ff414c37e..35a15133a47 100644 --- a/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx +++ b/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx @@ -1,82 +1,17 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { TabList } from '@/ui/layout/tab-list/components/TabList'; -import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { SettingsPath } from 'twenty-shared/types'; import { getSettingsPath } from 'twenty-shared/utils'; -import { - IconPlus, - IconRobot, - IconServer, - IconSettings, -} from 'twenty-ui/display'; -import { Button } from 'twenty-ui/input'; -import { UndecoratedLink } from 'twenty-ui/navigation'; import { t } from '@lingui/core/macro'; -import { SettingsAIAgentsTable } from './components/SettingsAIAgentsTable'; import { SettingsAIMCP } from './components/SettingsAIMCP'; import { SettingsAIRouterSettings } from './components/SettingsAIRouterSettings'; - -const SETTINGS_AI_TABS_ID = 'settings-ai-tabs-id'; - -const SETTINGS_AI_TABS = { - AGENTS: 'agents', - SETTINGS: 'settings', - MCP: 'mcp', -}; +import { SettingsSkillsTable } from './components/SettingsSkillsTable'; export const SettingsAI = () => { - const activeTabId = useRecoilComponentValue( - activeTabIdComponentState, - SETTINGS_AI_TABS_ID, - ); - - const tabs = [ - { - id: SETTINGS_AI_TABS.AGENTS, - title: t`Agents`, - Icon: IconRobot, - }, - { - id: SETTINGS_AI_TABS.MCP, - title: t`MCP`, - Icon: IconServer, - }, - { - id: SETTINGS_AI_TABS.SETTINGS, - title: t`Settings`, - Icon: IconSettings, - }, - ]; - - const renderActiveTabContent = () => { - switch (activeTabId) { - case SETTINGS_AI_TABS.AGENTS: - return ; - case SETTINGS_AI_TABS.SETTINGS: - return ; - case SETTINGS_AI_TABS.MCP: - return ; - } - }; - return ( -