From 563e27831bfd90af9c1ded2c0a945c588a5ad46c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Sat, 4 Apr 2026 14:05:42 +0200 Subject: [PATCH] Clean up tool output architecture: remove wrappers, enforce ToolOutput everywhere (#19321) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - **Remove `ExecuteToolResult` wrapper** — `execute_tool` is now a transparent dispatcher that returns the raw `ToolOutput` from underlying tools. No more `{ toolName, result }` envelope. - **Type the entire execution chain as `Promise`** — from `ToolExecutorService.dispatch()` through `resolveAndExecute()` to `execute_tool.execute()`. Zero `Promise` remaining in the tool layer. - **Use `Extract`** for dispatch methods, enabling exhaustive switch checking and removing `as never` casts. - **Relax `ToolOutput.result` to accept `null`** — removes `?? undefined` hacks at the boundary with logic function results. - **Enforce 1-export-per-file** across tool type/interface files (split `tool-descriptor.type.ts`, `tool-provider.interface.ts`, `tool.type.ts`, `tool-output.type.ts`, `tool-executor.service.ts`). - **Simplify error handling** — `wrapWithErrorHandler` and all meta-errors (tool not found, tool excluded) now return consistent `ToolOutput` shape with `error` as a plain string. - **Frontend reads output directly** — removed `unwrapToolOutput` utility; `ToolStepRenderer` and `ThinkingStepsDisplay` extract `message`/`error` from the raw output with simple type guards. - **Add permission error detection** for email tools via `isInsufficientPermissionsError`, guiding the AI model to suggest account reconnection instead of hallucinating about visibility settings. ## Test plan - [ ] AI chat tool calls return visible output (not "null") in the UI - [ ] Tool errors display correctly in the JSON tree - [ ] Email draft/send tools return actionable permission errors - [ ] Code interpreter output renders correctly - [ ] Thinking steps display tool outputs properly Made with [Cursor](https://cursor.com) --------- Co-authored-by: Claude Opus 4.6 --- .../ai/components/ThinkingStepsDisplay.tsx | 17 +++--- .../ai/components/ToolStepRenderer.tsx | 58 ++++++++----------- .../ai/schemas/toolOutputMessageSchema.ts | 3 - .../ai/schemas/toolOutputResultSchema.ts | 3 - .../code-execution-stream-emitter.type.ts | 3 + .../generate-descriptor-options.type.ts | 3 + .../native-tool-provider.interface.ts | 12 ++++ .../static-tool-handler.interface.ts | 7 +++ .../interfaces/tool-provider-context.type.ts | 18 ++++++ .../interfaces/tool-provider.interface.ts | 52 ++--------------- .../interfaces/tool-retrieval-options.type.ts | 7 +++ .../providers/action-tool.provider.ts | 20 +++---- .../providers/dashboard-tool.provider.ts | 14 ++--- .../providers/database-tool.provider.ts | 14 ++--- .../providers/logic-function-tool.provider.ts | 14 ++--- .../providers/metadata-tool.provider.ts | 14 ++--- .../providers/native-model-tool.provider.ts | 6 +- .../providers/view-field-tool.provider.ts | 14 ++--- .../providers/view-tool.provider.ts | 14 ++--- .../providers/workflow-tool.provider.ts | 14 ++--- .../services/tool-executor.service.ts | 51 ++++++---------- .../services/tool-registry.service.ts | 58 ++++++------------- .../tool-provider/tools/execute-tool.tool.ts | 24 ++------ .../tools/get-tool-catalog.tool.ts | 2 +- .../core-modules/tool-provider/tools/index.ts | 1 - .../types/category-tool-generator.type.ts | 7 +++ .../types/database-crud-operation.type.ts | 8 +++ .../tool-provider/types/tool-context.type.ts | 2 +- .../types/tool-descriptor.type.ts | 32 +--------- .../types/tool-execution-ref.type.ts | 10 ++++ .../types/tool-index-entry.type.ts | 13 +++++ .../tool-provider/utils/tool-error.util.ts | 50 ++-------------- .../utils/tool-set-to-descriptors.util.ts | 6 +- .../code-interpreter-tool.ts | 6 +- .../tool/tools/email-tool/draft-email-tool.ts | 17 ++++-- .../email-tool/email-composer.service.ts | 2 +- .../tool/tools/email-tool/send-email-tool.ts | 17 ++++-- .../is-insufficient-permissions-error.util.ts | 23 ++++++++ .../tool/tools/http-tool/http-tool.ts | 6 +- .../tools/navigate-tool/navigate-app-tool.ts | 6 +- .../search-help-center-tool.ts | 6 +- .../tool/types/record-reference.type.ts | 5 ++ .../tool/types/tool-execution-context.type.ts | 8 +++ .../tool/types/tool-output.type.ts | 7 +-- .../core-modules/tool/types/tool.type.ts | 10 +--- .../services/agent-async-executor.service.ts | 2 +- .../services/chat-execution.service.ts | 2 +- .../services/system-prompt-builder.service.ts | 2 +- 48 files changed, 297 insertions(+), 393 deletions(-) delete mode 100644 packages/twenty-front/src/modules/ai/schemas/toolOutputMessageSchema.ts delete mode 100644 packages/twenty-front/src/modules/ai/schemas/toolOutputResultSchema.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/code-execution-stream-emitter.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/native-tool-provider.interface.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/static-tool-handler.interface.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-retrieval-options.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool-provider/types/category-tool-generator.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool-provider/types/database-crud-operation.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-execution-ref.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-index-entry.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/utils/is-insufficient-permissions-error.util.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool/types/record-reference.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/tool/types/tool-execution-context.type.ts diff --git a/packages/twenty-front/src/modules/ai/components/ThinkingStepsDisplay.tsx b/packages/twenty-front/src/modules/ai/components/ThinkingStepsDisplay.tsx index d407e41b59..ad9ae1b4e9 100644 --- a/packages/twenty-front/src/modules/ai/components/ThinkingStepsDisplay.tsx +++ b/packages/twenty-front/src/modules/ai/components/ThinkingStepsDisplay.tsx @@ -15,7 +15,6 @@ import { AnimatedExpandableContainer } from 'twenty-ui/layout'; import { themeCssVariables } from 'twenty-ui/theme-constants'; import { type JsonValue } from 'type-fest'; -import { ToolOutputResultSchema } from '@/ai/schemas/toolOutputResultSchema'; import { getToolIcon } from '@/ai/utils/getToolIcon'; import { getToolDisplayMessage, @@ -275,15 +274,13 @@ const ThinkingToolStepRow = ({ const hasError = isDefined(part.errorText); const isExpandable = isDefined(part.output) || hasError; - const outputResult = ToolOutputResultSchema.safeParse(part.output); - const unwrappedOutput = - rawToolName === 'execute_tool' && outputResult.success - ? outputResult.data.result - : part.output; - const unwrappedResult = ToolOutputResultSchema.safeParse(unwrappedOutput); - const toolOutput = unwrappedResult.success - ? unwrappedResult.data.result - : unwrappedOutput; + const outputObj = + typeof part.output === 'object' && part.output !== null + ? (part.output as Record) + : null; + const toolError = + typeof outputObj?.error === 'string' ? outputObj.error : null; + const toolOutput = toolError ? { error: toolError } : outputObj; const toolTabListComponentInstanceId = `ai-thinking-tool-tabs-${part.toolCallId ?? rawToolName}-${rowIndex}`; const activeTabId = useAtomComponentStateValue( activeTabIdComponentState, diff --git a/packages/twenty-front/src/modules/ai/components/ToolStepRenderer.tsx b/packages/twenty-front/src/modules/ai/components/ToolStepRenderer.tsx index de024008ba..01ea41444b 100644 --- a/packages/twenty-front/src/modules/ai/components/ToolStepRenderer.tsx +++ b/packages/twenty-front/src/modules/ai/components/ToolStepRenderer.tsx @@ -8,8 +8,6 @@ import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; import { CodeExecutionDisplay } from '@/ai/components/CodeExecutionDisplay'; import { ShimmeringText } from '@/ai/components/ShimmeringText'; -import { ToolOutputMessageSchema } from '@/ai/schemas/toolOutputMessageSchema'; -import { ToolOutputResultSchema } from '@/ai/schemas/toolOutputResultSchema'; import { getToolDisplayMessage, resolveToolInput, @@ -152,37 +150,38 @@ export const ToolStepRenderer = ({ const isExpandable = isDefined(output) || hasError; const ToolIcon = getToolIcon(toolName); - const outputResult = ToolOutputResultSchema.safeParse(output); - const unwrappedOutput = - rawToolName === 'execute_tool' && outputResult.success - ? outputResult.data.result - : output; + const outputObj = + typeof output === 'object' && output !== null + ? (output as Record) + : null; + const toolMessage = + typeof outputObj?.message === 'string' ? outputObj.message : null; + const toolError = + typeof outputObj?.error === 'string' ? outputObj.error : null; if (toolName === 'code_interpreter') { const codeInput = toolInput as { code?: string } | undefined; - const codeOutput = unwrappedOutput as { - result?: { - stdout?: string; - stderr?: string; - exitCode?: number; - files?: Array<{ - fileId: string; - filename: string; - url: string; - mimeType?: string; - }>; - }; + const codeOutput = outputObj as { + stdout?: string; + stderr?: string; + exitCode?: number; + files?: Array<{ + fileId: string; + filename: string; + url: string; + mimeType?: string; + }>; } | null; - const isRunning = !unwrappedOutput && !hasError && isStreaming; + const isRunning = !outputObj && !hasError && isStreaming; return ( ); @@ -216,22 +215,15 @@ export const ToolStepRenderer = ({ ); } - const unwrappedResult = ToolOutputResultSchema.safeParse(unwrappedOutput); - const unwrappedMessage = ToolOutputMessageSchema.safeParse(unwrappedOutput); - const displayMessage = hasError ? t`Tool execution failed` : rawToolName === 'learn_tools' || rawToolName === 'execute_tool' || rawToolName === 'load_skills' ? getToolDisplayMessage(input, rawToolName, true) - : unwrappedMessage.success - ? unwrappedMessage.data.message - : getToolDisplayMessage(input, rawToolName, true); + : (toolMessage ?? getToolDisplayMessage(input, rawToolName, true)); - const result = unwrappedResult.success - ? unwrappedResult.data.result - : unwrappedOutput; + const result = toolError ? { error: toolError } : outputObj; return ( diff --git a/packages/twenty-front/src/modules/ai/schemas/toolOutputMessageSchema.ts b/packages/twenty-front/src/modules/ai/schemas/toolOutputMessageSchema.ts deleted file mode 100644 index ceb2505a9d..0000000000 --- a/packages/twenty-front/src/modules/ai/schemas/toolOutputMessageSchema.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { z } from 'zod'; - -export const ToolOutputMessageSchema = z.object({ message: z.string() }); diff --git a/packages/twenty-front/src/modules/ai/schemas/toolOutputResultSchema.ts b/packages/twenty-front/src/modules/ai/schemas/toolOutputResultSchema.ts deleted file mode 100644 index 41881e8703..0000000000 --- a/packages/twenty-front/src/modules/ai/schemas/toolOutputResultSchema.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { z } from 'zod'; - -export const ToolOutputResultSchema = z.object({ result: z.unknown() }); diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/code-execution-stream-emitter.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/code-execution-stream-emitter.type.ts new file mode 100644 index 0000000000..c4560b9090 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/code-execution-stream-emitter.type.ts @@ -0,0 +1,3 @@ +import { type CodeExecutionData } from 'twenty-shared/ai'; + +export type CodeExecutionStreamEmitter = (data: CodeExecutionData) => void; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type.ts new file mode 100644 index 0000000000..5ba326b9d4 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type.ts @@ -0,0 +1,3 @@ +export type GenerateDescriptorOptions = { + includeSchemas?: boolean; +}; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/native-tool-provider.interface.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/native-tool-provider.interface.ts new file mode 100644 index 0000000000..e9058ec005 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/native-tool-provider.interface.ts @@ -0,0 +1,12 @@ +import { type ToolSet } from 'ai'; +import { type ToolCategory } from 'twenty-shared/ai'; + +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; + +export interface NativeToolProvider { + readonly category: ToolCategory; + + isAvailable(context: ToolProviderContext): Promise; + + generateTools(context: ToolProviderContext): Promise; +} diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/static-tool-handler.interface.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/static-tool-handler.interface.ts new file mode 100644 index 0000000000..da897e0fb4 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/static-tool-handler.interface.ts @@ -0,0 +1,7 @@ +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; +import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type'; +import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; + +export interface StaticToolHandler { + execute(args: ToolInput, context: ToolProviderContext): Promise; +} diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type.ts new file mode 100644 index 0000000000..2d3bbcd9ac --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type.ts @@ -0,0 +1,18 @@ +import { type ActorMetadata } from 'twenty-shared/types'; + +import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type'; +import { type CodeExecutionStreamEmitter } from 'src/engine/core-modules/tool-provider/interfaces/code-execution-stream-emitter.type'; +import { type FlatAgentWithRoleId } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type'; +import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config'; + +export type ToolProviderContext = { + workspaceId: string; + roleId: string; + rolePermissionConfig: RolePermissionConfig; + authContext?: WorkspaceAuthContext; + actorContext?: ActorMetadata; + userId?: string; + userWorkspaceId?: string; + agent?: FlatAgentWithRoleId | null; + onCodeExecutionUpdate?: CodeExecutionStreamEmitter; +}; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-provider.interface.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-provider.interface.ts index a10bd14077..1deacb3524 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-provider.interface.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-provider.interface.ts @@ -1,41 +1,9 @@ -import { type ToolSet } from 'ai'; -import { type CodeExecutionData, type ToolCategory } from 'twenty-shared/ai'; -import { type ActorMetadata } from 'twenty-shared/types'; +import { type ToolCategory } from 'twenty-shared/ai'; -import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; -import { type FlatAgentWithRoleId } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type'; -import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config'; - -export type CodeExecutionStreamEmitter = (data: CodeExecutionData) => void; - -// Unified context for tool generation - used by all consumers -export type ToolProviderContext = { - workspaceId: string; - roleId: string; - rolePermissionConfig: RolePermissionConfig; - // Optional fields for different use cases - authContext?: WorkspaceAuthContext; - actorContext?: ActorMetadata; - userId?: string; - userWorkspaceId?: string; - agent?: FlatAgentWithRoleId | null; - onCodeExecutionUpdate?: CodeExecutionStreamEmitter; -}; - -// Options for tool retrieval -export type ToolRetrievalOptions = { - categories?: ToolCategory[]; - excludeTools?: string[]; - wrapWithErrorContext?: boolean; -}; - -export type GenerateDescriptorOptions = { - includeSchemas?: boolean; // defaults to true for backward compat -}; +import { type GenerateDescriptorOptions } from 'src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; export interface ToolProvider { readonly category: ToolCategory; @@ -47,13 +15,3 @@ export interface ToolProvider { options?: GenerateDescriptorOptions, ): Promise<(ToolIndexEntry | ToolDescriptor)[]>; } - -// NativeModelToolProvider is special: SDK-native tools are opaque and not -// serializable. It keeps the old generateTools() contract. -export interface NativeToolProvider { - readonly category: ToolCategory; - - isAvailable(context: ToolProviderContext): Promise; - - generateTools(context: ToolProviderContext): Promise; -} diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-retrieval-options.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-retrieval-options.type.ts new file mode 100644 index 0000000000..ef8b3d071f --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/interfaces/tool-retrieval-options.type.ts @@ -0,0 +1,7 @@ +import { type ToolCategory } from 'twenty-shared/ai'; + +export type ToolRetrievalOptions = { + categories?: ToolCategory[]; + excludeTools?: string[]; + wrapWithErrorContext?: boolean; +}; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/action-tool.provider.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/action-tool.provider.ts index 5b545f2856..18edbf2cf0 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/action-tool.provider.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/action-tool.provider.ts @@ -3,21 +3,15 @@ import { Injectable } from '@nestjs/common'; import { PermissionFlagType } from 'twenty-shared/constants'; import { z } from 'zod'; -import { - type GenerateDescriptorOptions, - type ToolProvider, - type ToolProviderContext, -} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type GenerateDescriptorOptions } from 'src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type'; +import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { ToolCategory } from 'twenty-shared/ai'; -import { - type StaticToolHandler, - ToolExecutorService, -} from 'src/engine/core-modules/tool-provider/services/tool-executor.service'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type StaticToolHandler } from 'src/engine/core-modules/tool-provider/interfaces/static-tool-handler.interface'; +import { ToolExecutorService } from 'src/engine/core-modules/tool-provider/services/tool-executor.service'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; import { CodeInterpreterService } from 'src/engine/core-modules/code-interpreter/code-interpreter.service'; import { CodeInterpreterTool } from 'src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool'; import { DraftEmailTool } from 'src/engine/core-modules/tool/tools/email-tool/draft-email-tool'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/dashboard-tool.provider.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/dashboard-tool.provider.ts index 63f262e0d4..d322c341d6 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/dashboard-tool.provider.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/dashboard-tool.provider.ts @@ -2,20 +2,16 @@ import { Inject, Injectable, OnModuleInit, Optional } from '@nestjs/common'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { - type GenerateDescriptorOptions, - type ToolProvider, - type ToolProviderContext, -} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type GenerateDescriptorOptions } from 'src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type'; +import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { DASHBOARD_TOOL_SERVICE_TOKEN } from 'src/engine/core-modules/tool-provider/constants/dashboard-tool-service.token'; import { ToolCategory } from 'twenty-shared/ai'; import { CoreObjectNameSingular } from 'twenty-shared/types'; import { ToolExecutorService } from 'src/engine/core-modules/tool-provider/services/tool-executor.service'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; import { toolSetToDescriptors } from 'src/engine/core-modules/tool-provider/utils/tool-set-to-descriptors.util'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/database-tool.provider.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/database-tool.provider.ts index 01be47004e..7df0c60c86 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/database-tool.provider.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/database-tool.provider.ts @@ -7,11 +7,9 @@ import { import { camelToSnakeCase, isDefined } from 'twenty-shared/utils'; import { z } from 'zod'; -import { - type GenerateDescriptorOptions, - type ToolProvider, - type ToolProviderContext, -} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type GenerateDescriptorOptions } from 'src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type'; +import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-flat-fields-for-flat-object-metadata.util'; import { generateCreateManyRecordInputSchema } from 'src/engine/core-modules/record-crud/utils/generate-create-many-record-input-schema.util'; @@ -22,10 +20,8 @@ import { DeleteToolInputSchema } from 'src/engine/core-modules/record-crud/zod-s import { FindOneToolInputSchema } from 'src/engine/core-modules/record-crud/zod-schemas/find-one-tool.zod-schema'; import { generateFindToolInputSchema } from 'src/engine/core-modules/record-crud/zod-schemas/find-tool.zod-schema'; import { ToolCategory } from 'twenty-shared/ai'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; import { isFavoriteRelatedObject } from 'src/engine/metadata-modules/ai/ai-agent/utils/is-favorite-related-object.util'; import { isWorkflowRelatedObject } from 'src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-related-object.util'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/logic-function-tool.provider.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/logic-function-tool.provider.ts index 9159dfca44..2281a92f63 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/logic-function-tool.provider.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/logic-function-tool.provider.ts @@ -3,17 +3,13 @@ import { Injectable } from '@nestjs/common'; import { isDefined } from 'twenty-shared/utils'; import { DEFAULT_TOOL_INPUT_SCHEMA } from 'twenty-shared/logic-function'; -import { - type GenerateDescriptorOptions, - type ToolProvider, - type ToolProviderContext, -} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type GenerateDescriptorOptions } from 'src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type'; +import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { ToolCategory } from 'twenty-shared/ai'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/metadata-tool.provider.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/metadata-tool.provider.ts index ad72919207..890ba84f4d 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/metadata-tool.provider.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/metadata-tool.provider.ts @@ -2,18 +2,14 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { - type GenerateDescriptorOptions, - type ToolProvider, - type ToolProviderContext, -} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type GenerateDescriptorOptions } from 'src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type'; +import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { ToolCategory } from 'twenty-shared/ai'; import { ToolExecutorService } from 'src/engine/core-modules/tool-provider/services/tool-executor.service'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; import { toolSetToDescriptors } from 'src/engine/core-modules/tool-provider/utils/tool-set-to-descriptors.util'; import { FieldMetadataToolsFactory } from 'src/engine/metadata-modules/field-metadata/tools/field-metadata-tools.factory'; import { ObjectMetadataToolsFactory } from 'src/engine/metadata-modules/object-metadata/tools/object-metadata-tools.factory'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/native-model-tool.provider.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/native-model-tool.provider.ts index 09277b6997..582466efd1 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/native-model-tool.provider.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/native-model-tool.provider.ts @@ -3,10 +3,8 @@ import { Injectable } from '@nestjs/common'; import { type ToolSet } from 'ai'; import { isDefined } from 'twenty-shared/utils'; -import { - type NativeToolProvider, - type ToolProviderContext, -} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type NativeToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/native-tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { ToolCategory } from 'twenty-shared/ai'; import { AgentModelConfigService } from 'src/engine/metadata-modules/ai/ai-models/services/agent-model-config.service'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/view-field-tool.provider.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/view-field-tool.provider.ts index 965c7e7602..31cc564eb4 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/view-field-tool.provider.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/view-field-tool.provider.ts @@ -2,18 +2,14 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { - type GenerateDescriptorOptions, - type ToolProvider, - type ToolProviderContext, -} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type GenerateDescriptorOptions } from 'src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type'; +import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { ToolCategory } from 'twenty-shared/ai'; import { ToolExecutorService } from 'src/engine/core-modules/tool-provider/services/tool-executor.service'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; import { toolSetToDescriptors } from 'src/engine/core-modules/tool-provider/utils/tool-set-to-descriptors.util'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; import { ViewFieldToolsFactory } from 'src/engine/metadata-modules/view-field/tools/view-field-tools.factory'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/view-tool.provider.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/view-tool.provider.ts index b68019459a..bfac01c718 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/view-tool.provider.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/view-tool.provider.ts @@ -2,18 +2,14 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { - type GenerateDescriptorOptions, - type ToolProvider, - type ToolProviderContext, -} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type GenerateDescriptorOptions } from 'src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type'; +import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { ToolCategory } from 'twenty-shared/ai'; import { ToolExecutorService } from 'src/engine/core-modules/tool-provider/services/tool-executor.service'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; import { toolSetToDescriptors } from 'src/engine/core-modules/tool-provider/utils/tool-set-to-descriptors.util'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; import { ViewFilterToolsFactory } from 'src/engine/metadata-modules/view-filter/tools/view-filter-tools.factory'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/workflow-tool.provider.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/workflow-tool.provider.ts index be1afa1f7f..a9fd793c5d 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/providers/workflow-tool.provider.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/providers/workflow-tool.provider.ts @@ -2,20 +2,16 @@ import { Inject, Injectable, OnModuleInit, Optional } from '@nestjs/common'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { - type GenerateDescriptorOptions, - type ToolProvider, - type ToolProviderContext, -} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type GenerateDescriptorOptions } from 'src/engine/core-modules/tool-provider/interfaces/generate-descriptor-options.type'; +import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { WORKFLOW_TOOL_SERVICE_TOKEN } from 'src/engine/core-modules/tool-provider/constants/workflow-tool-service.token'; import { ToolCategory } from 'twenty-shared/ai'; import { CoreObjectNameSingular } from 'twenty-shared/types'; import { ToolExecutorService } from 'src/engine/core-modules/tool-provider/services/tool-executor.service'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; import { toolSetToDescriptors } from 'src/engine/core-modules/tool-provider/utils/tool-set-to-descriptors.util'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-executor.service.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-executor.service.ts index 97e3a7d3c9..61160cf8b0 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-executor.service.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-executor.service.ts @@ -1,13 +1,12 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { type ToolSet } from 'ai'; import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; import { type FlatWorkspace } from 'src/engine/core-modules/workspace/types/flat-workspace.type'; import { fromUserEntityToFlat } from 'src/engine/core-modules/user/utils/from-user-entity-to-flat.util'; -import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { AuthException, @@ -20,29 +19,20 @@ import { CreateManyRecordsService } from 'src/engine/core-modules/record-crud/se import { CreateRecordService } from 'src/engine/core-modules/record-crud/services/create-record.service'; import { DeleteRecordService } from 'src/engine/core-modules/record-crud/services/delete-record.service'; import { FindRecordsService } from 'src/engine/core-modules/record-crud/services/find-records.service'; +import { type FindRecordsParams } from 'src/engine/core-modules/record-crud/types/find-records-params.type'; import { UpdateManyRecordsService } from 'src/engine/core-modules/record-crud/services/update-many-records.service'; import { UpdateRecordService } from 'src/engine/core-modules/record-crud/services/update-record.service'; import { type ToolCategory } from 'twenty-shared/ai'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; -import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type'; +import { type StaticToolHandler } from 'src/engine/core-modules/tool-provider/interfaces/static-tool-handler.interface'; +import { type CategoryToolGenerator } from 'src/engine/core-modules/tool-provider/types/category-tool-generator.type'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolExecutionRef } from 'src/engine/core-modules/tool-provider/types/tool-execution-ref.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; +import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; import { stripLoadingMessage } from 'src/engine/core-modules/tool/utils/wrap-tool-for-execution.util'; import { UserEntity } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; -// Handler for individually registered static tools (e.g., action tools) -export interface StaticToolHandler { - execute(args: ToolInput, context: ToolProviderContext): Promise; -} - -// Generator that produces a ToolSet on demand for a category (workflow, view, etc.) -// Used as a fallback when no per-tool handler is registered. -export type CategoryToolGenerator = ( - context: ToolProviderContext, -) => Promise; - @Injectable() export class ToolExecutorService { private readonly logger = new Logger(ToolExecutorService.name); @@ -84,7 +74,7 @@ export class ToolExecutorService { descriptor: ToolIndexEntry | ToolDescriptor, args: Record, context: ToolProviderContext, - ): Promise { + ): Promise { const cleanArgs = stripLoadingMessage(args); switch (descriptor.executionRef.kind) { @@ -106,10 +96,10 @@ export class ToolExecutorService { } private async dispatchDatabaseCrud( - ref: { objectNameSingular: string; operation: string }, + ref: Extract, args: Record, context: ToolProviderContext, - ): Promise { + ): Promise { const authContext = context.authContext ?? (await this.buildAuthContext(context)); @@ -120,7 +110,7 @@ export class ToolExecutorService { return this.findRecordsService.execute({ objectName: ref.objectNameSingular, filter, - orderBy: orderBy as never, + orderBy: orderBy as FindRecordsParams['orderBy'], limit: limit as number | undefined, offset: offset as number | undefined, authContext, @@ -191,9 +181,6 @@ export class ToolExecutorService { rolePermissionConfig: context.rolePermissionConfig, soft: true, }); - - default: - throw new Error(`Unknown database_crud operation: ${ref.operation}`); } } @@ -201,7 +188,7 @@ export class ToolExecutorService { descriptor: ToolIndexEntry | ToolDescriptor, args: Record, context: ToolProviderContext, - ): Promise { + ): Promise { if (descriptor.executionRef.kind !== 'static') { throw new Error('Expected static executionRef'); } @@ -231,19 +218,17 @@ export class ToolExecutorService { ); } - // The tool's execute expects (args, ToolExecutionOptions). Pass args with - // a dummy loadingMessage since the tool's internal strip is harmless. return tool.execute( { loadingMessage: '', ...args }, { toolCallId: '', messages: [] }, - ); + ) as Promise; } private async dispatchLogicFunction( - ref: { logicFunctionId: string }, + ref: Extract, args: Record, context: ToolProviderContext, - ): Promise { + ): Promise { const result = await this.logicFunctionExecutorService.execute({ logicFunctionId: ref.logicFunctionId, workspaceId: context.workspaceId, @@ -253,13 +238,15 @@ export class ToolExecutorService { if (result.error) { return { success: false, + message: 'Logic function execution failed', error: result.error.errorMessage, }; } return { success: true, - result: result.data, + message: 'Logic function executed successfully', + result: result.data ?? undefined, }; } diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-registry.service.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-registry.service.ts index 0011b366f2..fae767a5f4 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-registry.service.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/services/tool-registry.service.ts @@ -2,34 +2,24 @@ import { Inject, Injectable, Logger } from '@nestjs/common'; import { type ToolExecutionOptions, type ToolSet, jsonSchema } from 'ai'; -import { - type NativeToolProvider, - type ToolProvider, - type ToolProviderContext, - type ToolRetrievalOptions, -} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type NativeToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/native-tool-provider.interface'; +import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; +import { type ToolRetrievalOptions } from 'src/engine/core-modules/tool-provider/interfaces/tool-retrieval-options.type'; import { TOOL_PROVIDERS } from 'src/engine/core-modules/tool-provider/constants/tool-providers.token'; import { ToolCategory } from 'twenty-shared/ai'; -import { compactToolOutput } from 'src/engine/core-modules/tool-provider/output-serialization/compact-tool-output.util'; import { NativeModelToolProvider } from 'src/engine/core-modules/tool-provider/providers/native-model-tool.provider'; import { ToolExecutorService } from 'src/engine/core-modules/tool-provider/services/tool-executor.service'; -import { type ExecuteToolResult } from 'src/engine/core-modules/tool-provider/tools/execute-tool.tool'; import { type LearnToolsAspect } from 'src/engine/core-modules/tool-provider/tools/learn-tools.tool'; import { type ToolContext } from 'src/engine/core-modules/tool-provider/types/tool-context.type'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; -import { - generateErrorSuggestion, - wrapWithErrorHandler, -} from 'src/engine/core-modules/tool-provider/utils/tool-error.util'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; +import { wrapWithErrorHandler } from 'src/engine/core-modules/tool-provider/utils/tool-error.util'; +import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; import { wrapJsonSchemaForExecution } from 'src/engine/core-modules/tool/utils/wrap-tool-for-execution.util'; import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config'; -export { type ToolContext } from 'src/engine/core-modules/tool-provider/types/tool-context.type'; - @Injectable() export class ToolRegistryService { private readonly logger = new Logger(ToolRegistryService.name); @@ -125,7 +115,7 @@ export class ToolRegistryService { const executeFn = async ( args: Record, - ): Promise => + ): Promise => this.toolExecutorService.dispatch(descriptor, args, context); toolSet[descriptor.name] = { @@ -220,7 +210,7 @@ export class ToolRegistryService { args: Record, context: ToolContext, _options: ToolExecutionOptions, - ): Promise { + ): Promise { try { const fullContext = this.buildContextFromToolContext(context); @@ -229,25 +219,13 @@ export class ToolRegistryService { if (!entry) { return { - toolName, - error: { - message: `Tool "${toolName}" not found. Check the tool catalog for correct names.`, - suggestion: - 'Use learn_tools to discover available tools and their correct names.', - }, + success: false, + message: `Tool "${toolName}" not found`, + error: `Tool "${toolName}" not found. Use learn_tools to discover available tools.`, }; } - const result = await this.toolExecutorService.dispatch( - entry, - args, - fullContext, - ); - - return { - toolName, - result: compactToolOutput(result), - }; + return await this.toolExecutorService.dispatch(entry, args, fullContext); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -255,11 +233,9 @@ export class ToolRegistryService { this.logger.error(`Error executing tool "${toolName}": ${errorMessage}`); return { - toolName, - error: { - message: errorMessage, - suggestion: generateErrorSuggestion(toolName, errorMessage), - }, + success: false, + message: `Failed to execute ${toolName}`, + error: errorMessage, }; } } diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/execute-tool.tool.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/execute-tool.tool.ts index bb8a2954d0..552af38ae5 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/execute-tool.tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/execute-tool.tool.ts @@ -4,6 +4,7 @@ import { z } from 'zod'; import { type ToolRegistryService } from 'src/engine/core-modules/tool-provider/services/tool-registry.service'; import { type ToolContext } from 'src/engine/core-modules/tool-provider/types/tool-context.type'; +import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; export const EXECUTE_TOOL_TOOL_NAME = 'execute_tool'; @@ -40,15 +41,6 @@ export const executeToolInputSchema = jsonSchema( }, ); -export type ExecuteToolResult = { - toolName: string; - result?: unknown; - error?: { - message: string; - suggestion: string; - }; -}; - export const createExecuteToolTool = ( toolRegistry: ToolRegistryService, context: ToolContext, @@ -61,25 +53,21 @@ export const createExecuteToolTool = ( execute: async ( parameters: ExecuteToolInput, options: ToolExecutionOptions, - ): Promise => { + ): Promise => { const { toolName, arguments: args } = parameters; if (excludeTools?.has(toolName)) { return { - toolName, - error: { - message: `Tool "${toolName}" is not available in this context.`, - suggestion: 'Use get_tool_catalog to see which tools are available.', - }, + success: false, + message: `Tool "${toolName}" is not available`, + error: `Tool "${toolName}" is not available in this context. Use get_tool_catalog to see which tools are available.`, }; } const directTool = directTools?.[toolName]; if (directTool?.execute) { - const result = await directTool.execute(args, options); - - return { toolName, result }; + return directTool.execute(args, options) as Promise; } return toolRegistry.resolveAndExecute(toolName, args, context, options); diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/get-tool-catalog.tool.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/get-tool-catalog.tool.ts index 3ca996e06d..6b13af2ef8 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/get-tool-catalog.tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/get-tool-catalog.tool.ts @@ -2,7 +2,7 @@ import { z } from 'zod'; import { ToolCategory } from 'twenty-shared/ai'; import { type ToolRegistryService } from 'src/engine/core-modules/tool-provider/services/tool-registry.service'; -import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; export const GET_TOOL_CATALOG_TOOL_NAME = 'get_tool_catalog'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/index.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/index.ts index ea59445794..5f19c145ce 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/index.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/index.ts @@ -12,7 +12,6 @@ export { createExecuteToolTool, executeToolInputSchema, type ExecuteToolInput, - type ExecuteToolResult, } from './execute-tool.tool'; export { diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/types/category-tool-generator.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/types/category-tool-generator.type.ts new file mode 100644 index 0000000000..0988d7c1b8 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/types/category-tool-generator.type.ts @@ -0,0 +1,7 @@ +import { type ToolSet } from 'ai'; + +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; + +export type CategoryToolGenerator = ( + context: ToolProviderContext, +) => Promise; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/types/database-crud-operation.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/types/database-crud-operation.type.ts new file mode 100644 index 0000000000..a87e58e31d --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/types/database-crud-operation.type.ts @@ -0,0 +1,8 @@ +export type DatabaseCrudOperation = + | 'find' + | 'find_one' + | 'create' + | 'create_many' + | 'update' + | 'update_many' + | 'delete'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-context.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-context.type.ts index 0d8ae1ef38..ab915b6e25 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-context.type.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-context.type.ts @@ -1,6 +1,6 @@ import { type ActorMetadata } from 'twenty-shared/types'; -import { type CodeExecutionStreamEmitter } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type CodeExecutionStreamEmitter } from 'src/engine/core-modules/tool-provider/interfaces/code-execution-stream-emitter.type'; import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-descriptor.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-descriptor.type.ts index beb6771358..bbc48f526e 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-descriptor.type.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-descriptor.type.ts @@ -1,35 +1,5 @@ -import { type ToolCategory } from 'twenty-shared/ai'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; -export type DatabaseCrudOperation = - | 'find' - | 'find_one' - | 'create' - | 'create_many' - | 'update' - | 'update_many' - | 'delete'; - -export type ToolExecutionRef = - | { - kind: 'database_crud'; - objectNameSingular: string; - operation: DatabaseCrudOperation; - } - | { kind: 'static'; toolId: string } - | { kind: 'logic_function'; logicFunctionId: string }; - -// Lightweight entry for catalog/index (no schema) -export type ToolIndexEntry = { - name: string; - description: string; - category: ToolCategory; - executionRef: ToolExecutionRef; - objectName?: string; - operation?: string; - icon?: string; -}; - -// Full descriptor with schema (on-demand) export type ToolDescriptor = ToolIndexEntry & { inputSchema: object; }; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-execution-ref.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-execution-ref.type.ts new file mode 100644 index 0000000000..205e840062 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-execution-ref.type.ts @@ -0,0 +1,10 @@ +import { type DatabaseCrudOperation } from 'src/engine/core-modules/tool-provider/types/database-crud-operation.type'; + +export type ToolExecutionRef = + | { + kind: 'database_crud'; + objectNameSingular: string; + operation: DatabaseCrudOperation; + } + | { kind: 'static'; toolId: string } + | { kind: 'logic_function'; logicFunctionId: string }; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-index-entry.type.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-index-entry.type.ts new file mode 100644 index 0000000000..3706b01295 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/types/tool-index-entry.type.ts @@ -0,0 +1,13 @@ +import { type ToolCategory } from 'twenty-shared/ai'; + +import { type ToolExecutionRef } from 'src/engine/core-modules/tool-provider/types/tool-execution-ref.type'; + +export type ToolIndexEntry = { + name: string; + description: string; + category: ToolCategory; + executionRef: ToolExecutionRef; + objectName?: string; + operation?: string; + icon?: string; +}; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/utils/tool-error.util.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/utils/tool-error.util.ts index 8318716937..1d0a22c97e 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/utils/tool-error.util.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/utils/tool-error.util.ts @@ -1,46 +1,9 @@ -export const generateErrorSuggestion = ( - _toolName: string, - errorMessage: string, -): string => { - const lowerError = errorMessage.toLowerCase(); - - if ( - lowerError.includes('not found') || - lowerError.includes('does not exist') - ) { - return 'Verify the ID or name exists with a search query first'; - } - - if ( - lowerError.includes('permission') || - lowerError.includes('forbidden') || - lowerError.includes('unauthorized') - ) { - return 'This operation requires elevated permissions or a different role'; - } - - if (lowerError.includes('invalid') || lowerError.includes('validation')) { - return 'Check the tool schema for valid parameter formats and types'; - } - - if ( - lowerError.includes('duplicate') || - lowerError.includes('already exists') - ) { - return 'A record with this identifier already exists. Try updating instead of creating'; - } - - if (lowerError.includes('required') || lowerError.includes('missing')) { - return 'Required fields are missing. Check which fields are mandatory for this operation'; - } - - return 'Try adjusting the parameters or using a different approach'; -}; +import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; export const wrapWithErrorHandler = ( toolName: string, - executeFn: (args: Record) => Promise, -): ((args: Record) => Promise) => { + executeFn: (args: Record) => Promise, +): ((args: Record) => Promise) => { return async (args: Record) => { try { return await executeFn(args); @@ -50,11 +13,8 @@ export const wrapWithErrorHandler = ( return { success: false, - error: { - message: errorMessage, - tool: toolName, - suggestion: generateErrorSuggestion(toolName, errorMessage), - }, + message: `Failed to execute ${toolName}`, + error: errorMessage, }; } }; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/utils/tool-set-to-descriptors.util.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/utils/tool-set-to-descriptors.util.ts index b10a751da9..63eb3eaa14 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/utils/tool-set-to-descriptors.util.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/utils/tool-set-to-descriptors.util.ts @@ -2,10 +2,8 @@ import { type ToolSet } from 'ai'; import { z } from 'zod'; import { type ToolCategory } from 'twenty-shared/ai'; -import { - type ToolDescriptor, - type ToolIndexEntry, -} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolDescriptor } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; export type ToolSetToDescriptorsOptions = { includeSchemas?: boolean; diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool.ts index f9851d10a7..cf23a9b32c 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/code-interpreter-tool/code-interpreter-tool.ts @@ -34,10 +34,8 @@ import { } from 'src/engine/core-modules/tool/tools/code-interpreter-tool/types/code-interpreter-input.type'; import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type'; import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; -import { - type Tool, - type ToolExecutionContext, -} from 'src/engine/core-modules/tool/types/tool.type'; +import { type ToolExecutionContext } from 'src/engine/core-modules/tool/types/tool-execution-context.type'; +import { type Tool } from 'src/engine/core-modules/tool/types/tool.type'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { AuthProviderEnum } from 'src/engine/core-modules/workspace/types/workspace.type'; diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/draft-email-tool.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/draft-email-tool.ts index 2a42339495..037334d87a 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/draft-email-tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/draft-email-tool.ts @@ -3,13 +3,12 @@ import { Injectable, Logger } from '@nestjs/common'; import { EmailComposerService } from 'src/engine/core-modules/tool/tools/email-tool/email-composer.service'; import { EmailToolInputZodSchema } from 'src/engine/core-modules/tool/tools/email-tool/email-tool.schema'; import { EmailToolException } from 'src/engine/core-modules/tool/tools/email-tool/exceptions/email-tool.exception'; +import { isInsufficientPermissionsError } from 'src/engine/core-modules/tool/tools/email-tool/utils/is-insufficient-permissions-error.util'; import { type ComposedEmail } from 'src/engine/core-modules/tool/tools/email-tool/types/composed-email.type'; import { type EmailToolInput } from 'src/engine/core-modules/tool/tools/email-tool/types/email-tool-input.type'; import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; -import { - type Tool, - type ToolExecutionContext, -} from 'src/engine/core-modules/tool/types/tool.type'; +import { type ToolExecutionContext } from 'src/engine/core-modules/tool/types/tool-execution-context.type'; +import { type Tool } from 'src/engine/core-modules/tool/types/tool.type'; import { MessagingMessageOutboundService } from 'src/modules/messaging/message-outbound-manager/services/messaging-message-outbound.service'; @Injectable() @@ -70,6 +69,16 @@ export class DraftEmailTool implements Tool { this.logger.error(`Failed to create draft: ${error}`); + if (isInsufficientPermissionsError(error)) { + return { + success: false, + message: 'Failed to create draft due to insufficient permissions', + error: + 'The connected email account does not have permission to create drafts. ' + + 'The user should disconnect and reconnect their account in Settings > Accounts to grant the required permissions.', + }; + } + return { success: false, message: 'Failed to create draft', diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/email-composer.service.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/email-composer.service.ts index ee8e926333..da56980849 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/email-composer.service.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/email-composer.service.ts @@ -19,7 +19,7 @@ import { import { EmailComposerResult } from 'src/engine/core-modules/tool/tools/email-tool/types/email-composer-result.type'; import { EmailToolInput } from 'src/engine/core-modules/tool/tools/email-tool/types/email-tool-input.type'; import { parseCommaSeparatedEmails } from 'src/engine/core-modules/tool/tools/email-tool/utils/parse-comma-separated-emails.util'; -import { ToolExecutionContext } from 'src/engine/core-modules/tool/types/tool.type'; +import { type ToolExecutionContext } from 'src/engine/core-modules/tool/types/tool-execution-context.type'; import { ConnectedAccountDataAccessService } from 'src/engine/metadata-modules/connected-account/data-access/services/connected-account-data-access.service'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/send-email-tool.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/send-email-tool.ts index c5e0b37d27..c54ca6311b 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/send-email-tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/send-email-tool.ts @@ -3,13 +3,12 @@ import { Injectable, Logger } from '@nestjs/common'; import { EmailComposerService } from 'src/engine/core-modules/tool/tools/email-tool/email-composer.service'; import { EmailToolInputZodSchema } from 'src/engine/core-modules/tool/tools/email-tool/email-tool.schema'; import { EmailToolException } from 'src/engine/core-modules/tool/tools/email-tool/exceptions/email-tool.exception'; +import { isInsufficientPermissionsError } from 'src/engine/core-modules/tool/tools/email-tool/utils/is-insufficient-permissions-error.util'; import { type ComposedEmail } from 'src/engine/core-modules/tool/tools/email-tool/types/composed-email.type'; import { type EmailToolInput } from 'src/engine/core-modules/tool/tools/email-tool/types/email-tool-input.type'; import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; -import { - type Tool, - type ToolExecutionContext, -} from 'src/engine/core-modules/tool/types/tool.type'; +import { type ToolExecutionContext } from 'src/engine/core-modules/tool/types/tool-execution-context.type'; +import { type Tool } from 'src/engine/core-modules/tool/types/tool.type'; import { MessagingMessageOutboundService } from 'src/modules/messaging/message-outbound-manager/services/messaging-message-outbound.service'; @Injectable() @@ -70,6 +69,16 @@ export class SendEmailTool implements Tool { this.logger.error(`Failed to send email: ${error}`); + if (isInsufficientPermissionsError(error)) { + return { + success: false, + message: 'Failed to send email due to insufficient permissions', + error: + 'The connected email account does not have permission to send emails. ' + + 'The user should disconnect and reconnect their account in Settings > Accounts to grant the required permissions.', + }; + } + return { success: false, message: 'Failed to send email', diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/utils/is-insufficient-permissions-error.util.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/utils/is-insufficient-permissions-error.util.ts new file mode 100644 index 0000000000..201ff0fdea --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/email-tool/utils/is-insufficient-permissions-error.util.ts @@ -0,0 +1,23 @@ +// Detects permission/scope errors from email provider APIs (Google, Microsoft). +// These typically occur when the OAuth consent didn't include the required +// scope for the operation (e.g., gmail.compose for drafts). +export const isInsufficientPermissionsError = (error: unknown): boolean => { + if (!(error instanceof Error)) { + return false; + } + + const message = error.message.toLowerCase(); + + if ( + message.includes('insufficient permission') || + message.includes('insufficient authentication scopes') || + message.includes('access denied') || + message.includes('forbidden') + ) { + return true; + } + + const response = (error as { response?: { status?: number } }).response; + + return response?.status === 401 || response?.status === 403; +}; diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/http-tool/http-tool.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/http-tool/http-tool.ts index bba9e8940f..c3f2db5b81 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/http-tool/http-tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/http-tool/http-tool.ts @@ -9,10 +9,8 @@ import { HttpRequestInputZodSchema } from 'src/engine/core-modules/tool/tools/ht import { type HttpRequestInput } from 'src/engine/core-modules/tool/tools/http-tool/types/http-request-input.type'; import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type'; import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; -import { - type Tool, - type ToolExecutionContext, -} from 'src/engine/core-modules/tool/types/tool.type'; +import { type ToolExecutionContext } from 'src/engine/core-modules/tool/types/tool-execution-context.type'; +import { type Tool } from 'src/engine/core-modules/tool/types/tool.type'; @Injectable() export class HttpTool implements Tool { diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/navigate-tool/navigate-app-tool.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/navigate-tool/navigate-app-tool.ts index 31088d48d8..c55e44c269 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/navigate-tool/navigate-app-tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/navigate-tool/navigate-app-tool.ts @@ -12,10 +12,8 @@ import { } from 'src/engine/core-modules/tool/tools/navigate-tool/navigate-app-tool.schema'; import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type'; import { ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; -import { - type Tool, - type ToolExecutionContext, -} from 'src/engine/core-modules/tool/types/tool.type'; +import { type ToolExecutionContext } from 'src/engine/core-modules/tool/types/tool-execution-context.type'; +import { type Tool } from 'src/engine/core-modules/tool/types/tool.type'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool.ts index 32a5cf20af..79434e48b0 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool.ts @@ -6,10 +6,8 @@ import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-cli import { SearchHelpCenterInputZodSchema } from 'src/engine/core-modules/tool/tools/search-help-center-tool/search-help-center-tool.schema'; import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type'; import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; -import { - type Tool, - type ToolExecutionContext, -} from 'src/engine/core-modules/tool/types/tool.type'; +import { type ToolExecutionContext } from 'src/engine/core-modules/tool/types/tool-execution-context.type'; +import { type Tool } from 'src/engine/core-modules/tool/types/tool.type'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; @Injectable() diff --git a/packages/twenty-server/src/engine/core-modules/tool/types/record-reference.type.ts b/packages/twenty-server/src/engine/core-modules/tool/types/record-reference.type.ts new file mode 100644 index 0000000000..f713aea1a7 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool/types/record-reference.type.ts @@ -0,0 +1,5 @@ +export type RecordReference = { + objectNameSingular: string; + recordId: string; + displayName: string; +}; diff --git a/packages/twenty-server/src/engine/core-modules/tool/types/tool-execution-context.type.ts b/packages/twenty-server/src/engine/core-modules/tool/types/tool-execution-context.type.ts new file mode 100644 index 0000000000..e36ddd2dd0 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool/types/tool-execution-context.type.ts @@ -0,0 +1,8 @@ +import { type CodeExecutionStreamEmitter } from 'src/engine/core-modules/tool-provider/interfaces/code-execution-stream-emitter.type'; + +export type ToolExecutionContext = { + workspaceId: string; + userId?: string; + userWorkspaceId?: string; + onCodeExecutionUpdate?: CodeExecutionStreamEmitter; +}; diff --git a/packages/twenty-server/src/engine/core-modules/tool/types/tool-output.type.ts b/packages/twenty-server/src/engine/core-modules/tool/types/tool-output.type.ts index ab62d07452..801884350c 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/types/tool-output.type.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/types/tool-output.type.ts @@ -1,8 +1,4 @@ -export type RecordReference = { - objectNameSingular: string; - recordId: string; - displayName: string; -}; +import { type RecordReference } from 'src/engine/core-modules/tool/types/record-reference.type'; export type ToolOutput = { success: boolean; @@ -12,6 +8,5 @@ export type ToolOutput = { status?: number; statusText?: string; headers?: Record; - // Record references for linking to created/found records recordReferences?: RecordReference[]; }; diff --git a/packages/twenty-server/src/engine/core-modules/tool/types/tool.type.ts b/packages/twenty-server/src/engine/core-modules/tool/types/tool.type.ts index 88b83b558a..5cfb49ab0d 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/types/tool.type.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/types/tool.type.ts @@ -1,18 +1,10 @@ import { type FlexibleSchema } from '@ai-sdk/provider-utils'; import { type PermissionFlagType } from 'twenty-shared/constants'; -import { type CodeExecutionStreamEmitter } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; - +import { type ToolExecutionContext } from 'src/engine/core-modules/tool/types/tool-execution-context.type'; import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type'; import { type ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type'; -export type ToolExecutionContext = { - workspaceId: string; - userId?: string; - userWorkspaceId?: string; - onCodeExecutionUpdate?: CodeExecutionStreamEmitter; -}; - export type Tool = { description: string; inputSchema: FlexibleSchema; diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service.ts index 1ca818f0b3..5433ed8508 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service.ts @@ -12,7 +12,7 @@ import { type ActorMetadata } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { type Repository } from 'typeorm'; -import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type'; import { isUserAuthContext } from 'src/engine/core-modules/auth/guards/is-user-auth-context.guard'; import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type'; diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/chat-execution.service.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/chat-execution.service.ts index 7f81f3314d..36e48fb531 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/chat-execution.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/chat-execution.service.ts @@ -17,7 +17,7 @@ import { getAppPath, isDefined } from 'twenty-shared/utils'; import { UsageOperationType } from 'src/engine/core-modules/usage/enums/usage-operation-type.enum'; -import { type CodeExecutionStreamEmitter } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface'; +import { type CodeExecutionStreamEmitter } from 'src/engine/core-modules/tool-provider/interfaces/code-execution-stream-emitter.type'; import { CodeInterpreterService } from 'src/engine/core-modules/code-interpreter/code-interpreter.service'; import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service'; diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/system-prompt-builder.service.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/system-prompt-builder.service.ts index 060b17b636..9a8d251580 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/system-prompt-builder.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/system-prompt-builder.service.ts @@ -10,7 +10,7 @@ import { LEARN_TOOLS_TOOL_NAME, LOAD_SKILL_TOOL_NAME, } from 'src/engine/core-modules/tool-provider/tools'; -import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type'; +import { type ToolIndexEntry } from 'src/engine/core-modules/tool-provider/types/tool-index-entry.type'; import { AgentActorContextService, type UserContext,