From d67aa2889b4db619b3e2bf1325fc598a9fafa9f0 Mon Sep 17 00:00:00 2001
From: Etienne <45695613+etiennejouan@users.noreply.github.com>
Date: Thu, 18 Jun 2026 15:00:21 +0200
Subject: [PATCH] feat(workflow): add update_agent tool and
responseFormat-aware AI Agent step schema (#21755)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
Brings the workflow AI/MCP tooling for **AI Agent steps** to parity with
the existing **CODE / logic-function** flow, and makes AI Agent output
references reliable in validation and the variable picker.
Just like a CODE step needs a logic function, an `AI_AGENT` step needs
an agent. The agent is already created as a side effect of step
creation; this PR adds the missing "configure it" tooling and fixes the
output schema so it reflects the agent's actual response format.
## Changes
### New `update_agent` MCP tool
- `update-agent.tool.ts`: lets the assistant configure the agent backing
an `AI_AGENT` step — `prompt` (system prompt), optional `modelId`,
optional `responseFormat` (text or structured json) — via
`AgentService.updateOneAgent`. Direct analog of
`update_logic_function_source`.
- Wired through: added `agentService` to `WorkflowToolDependencies`,
injected `AgentService` and registered the tool in
`workflow-tool.workspace-service.ts`, imported `AiAgentModule` in
`workflow-tools.module.ts`.
### Guided creation flow (mirrors CODE)
- `create-workflow-version-step.tool.ts`: `enrichResultWithNextStep` now
returns an `AI_AGENT` hint instructing the assistant to call
`update_agent` with the step's `settings.input.agentId` (and to set the
task prompt via `update_workflow_version_step` if needed).
- `create-complete-workflow.tool.ts`: rejects `AI_AGENT` steps (it
inserts steps directly and never runs the side effect that creates the
agent), with a description note pointing to
`create_workflow_version_step` + `update_agent`. Same treatment CODE
already gets.
### Correct output schema for AI Agent steps
- Backend `computeStepOutputSchema`
(`workflow-schema.workspace-service.ts`): the `AI_AGENT` case now
derives the output schema from the agent's `responseFormat` instead of a
hardcoded `{ response }`:
- text → `{ response: string }`
- json → one leaf per `responseFormat.schema.properties` field
This makes workflow validation resolve `{{stepId.fieldName}}` references
against the agent's real output (previously json agents validated wrong:
real fields rejected, `{{stepId.response}}` accepted but undefined at
runtime).
- Frontend `useStepsOutputSchema.ts`: when an `AI_AGENT` step has no
persisted `outputSchema`, fall back to generating it from the agent's
`responseFormat` (via `FindManyAgents` + the existing
`agentResponseSchemaToOutputSchema`) instead of the hardcoded `{
response }`. Keeps the variable picker correct for structured agents.
## Why output schema matters
Validation resolves every `{{stepId.path}}` against the referenced
step's `settings.outputSchema` (`validateWorkflowVariableReferences`).
The agent step's output schema is therefore the single source of truth
for "is the right output referenced." Because the runtime output depends
on `responseFormat` (text → `{ response }`, json → schema fields
directly), the schema must be derived from `responseFormat` to be
accurate.
## Not solved issue
We want each AI_AGENT step's settings.outputSchema to always match the
backing agent's responseFormat:
- text → { response }
- json → one field per responseFormat.schema.properties
That output schema is what everything downstream relies on: validation
(validateWorkflowVariableReferences resolves {{stepId.field}} against
it), the frontend variable picker (useStepsOutputSchema), and it's also
persisted inside workflowVersion.steps.
Agent should be unique source of truth but syncing agent -> step is not
possible
---
.../src/metadata/generated/schema.graphql | 240 +++---
.../src/metadata/generated/schema.ts | 134 ++--
.../src/metadata/generated/types.ts | 738 +++++++++---------
.../workflow-schema.workspace-service.ts | 70 +-
.../workflow-tool.workspace-service.ts | 6 +
.../tools/create-complete-workflow.tool.ts | 13 +
.../create-workflow-version-step.tool.ts | 6 +
.../workflow-tools/tools/update-agent.tool.ts | 104 +++
.../types/workflow-tool-dependencies.type.ts | 2 +
.../workflow-tools/workflow-tools.module.ts | 2 +
10 files changed, 751 insertions(+), 564 deletions(-)
create mode 100644 packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-agent.tool.ts
diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
index 1841eaceec..b47f88af34 100644
--- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
+++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
@@ -3046,14 +3046,8 @@ type Query {
getPageLayout(id: String!): PageLayout
getPageLayoutWidgets(pageLayoutTabId: String!): [PageLayoutWidget!]!
getPageLayoutWidget(id: String!): PageLayoutWidget!
- findOneLogicFunction(input: LogicFunctionIdInput!): LogicFunction!
- findManyLogicFunctions: [LogicFunction!]!
- getAvailablePackages(input: LogicFunctionIdInput!): JSON!
- getLogicFunctionSourceCode(input: LogicFunctionIdInput!): String
- commandMenuItems: [CommandMenuItem!]!
- commandMenuItem(id: UUID!): CommandMenuItem
- frontComponents: [FrontComponent!]!
- frontComponent(id: UUID!): FrontComponent
+ findManyAgents: [Agent!]!
+ findOneAgent(input: AgentIdInput!): Agent!
objectRecordCounts: [ObjectRecordCount!]!
object(
"""The id of the record to find."""
@@ -3077,8 +3071,14 @@ type Query {
"""Specify to filter the records returned."""
filter: IndexFilter! = {}
): IndexConnection!
- findManyAgents: [Agent!]!
- findOneAgent(input: AgentIdInput!): Agent!
+ findOneLogicFunction(input: LogicFunctionIdInput!): LogicFunction!
+ findManyLogicFunctions: [LogicFunction!]!
+ getAvailablePackages(input: LogicFunctionIdInput!): JSON!
+ getLogicFunctionSourceCode(input: LogicFunctionIdInput!): String
+ commandMenuItems: [CommandMenuItem!]!
+ commandMenuItem(id: UUID!): CommandMenuItem
+ frontComponents: [FrontComponent!]!
+ frontComponent(id: UUID!): FrontComponent
previewMessageCampaignAudience(input: PreviewMessageCampaignAudienceInput!): CampaignAudiencePreviewDTO!
unsubscribeTopics: [UnsubscribeTopic!]!
unsubscribePagePreviewUrl: String!
@@ -3154,16 +3154,16 @@ input GetApiKeyInput {
id: UUID!
}
-input LogicFunctionIdInput {
- """The id of the function."""
- id: ID!
-}
-
input AgentIdInput {
"""The id of the agent."""
id: UUID!
}
+input LogicFunctionIdInput {
+ """The id of the function."""
+ id: ID!
+}
+
input PreviewMessageCampaignAudienceInput {
listId: String!
unsubscribeTopicId: String
@@ -3314,6 +3314,14 @@ type Mutation {
createPageLayoutWidget(input: CreatePageLayoutWidgetInput!): PageLayoutWidget!
updatePageLayoutWidget(id: String!, input: UpdatePageLayoutWidgetInput!): PageLayoutWidget!
destroyPageLayoutWidget(id: String!): Boolean!
+ createOneAgent(input: CreateAgentInput!): Agent!
+ updateOneAgent(input: UpdateAgentInput!): Agent!
+ deleteOneAgent(input: AgentIdInput!): Agent!
+ createOneObject(input: CreateOneObjectInput!): Object!
+ deleteOneObject(input: DeleteOneObjectInput!): Object!
+ updateOneObject(input: UpdateOneObjectInput!): Object!
+ createOneIndex(input: CreateOneIndexInput!): Index!
+ deleteOneIndex(input: DeleteOneIndexInput!): Index!
deleteOneLogicFunction(input: LogicFunctionIdInput!): LogicFunction!
createOneLogicFunction(input: CreateLogicFunctionFromSourceInput!): LogicFunction!
executeOneLogicFunction(input: ExecuteOneLogicFunctionInput!): LogicFunctionExecutionResult!
@@ -3325,14 +3333,6 @@ type Mutation {
createFrontComponent(input: CreateFrontComponentInput!): FrontComponent!
updateFrontComponent(input: UpdateFrontComponentInput!): FrontComponent!
deleteFrontComponent(id: UUID!): FrontComponent!
- createOneObject(input: CreateOneObjectInput!): Object!
- deleteOneObject(input: DeleteOneObjectInput!): Object!
- updateOneObject(input: UpdateOneObjectInput!): Object!
- createOneIndex(input: CreateOneIndexInput!): Index!
- deleteOneIndex(input: DeleteOneIndexInput!): Index!
- createOneAgent(input: CreateAgentInput!): Agent!
- updateOneAgent(input: UpdateAgentInput!): Agent!
- deleteOneAgent(input: AgentIdInput!): Agent!
sendEmailViaEmailingDomain(input: SendEmailViaDomainInput!): SendEmailViaDomainOutput!
sendMessageCampaign(input: SendMessageCampaignInput!): SendMessageCampaignOutputDTO!
createUnsubscribeTopic(input: CreateUnsubscribeTopicInput!): UnsubscribeTopic!
@@ -3903,6 +3903,103 @@ input UpdatePageLayoutWidgetInput {
conditionalAvailabilityExpression: String
}
+input CreateAgentInput {
+ name: String
+ label: String!
+ icon: String
+ description: String
+ prompt: String!
+ modelId: String!
+ roleId: UUID
+ responseFormat: JSON
+ modelConfiguration: JSON
+ evaluationInputs: [String!]
+}
+
+input UpdateAgentInput {
+ id: UUID!
+ name: String
+ label: String
+ icon: String
+ description: String
+ prompt: String
+ modelId: String
+ roleId: UUID
+ responseFormat: JSON
+ modelConfiguration: JSON
+ evaluationInputs: [String!]
+}
+
+input CreateOneObjectInput {
+ """The object to create"""
+ object: CreateObjectInput!
+}
+
+input CreateObjectInput {
+ nameSingular: String!
+ namePlural: String!
+ labelSingular: String!
+ labelPlural: String!
+ description: String
+ icon: String
+ shortcut: String
+ color: String
+ skipNameField: Boolean
+ isRemote: Boolean
+ primaryKeyColumnType: String
+ primaryKeyFieldMetadataSettings: JSON
+ isLabelSyncedWithName: Boolean
+}
+
+input DeleteOneObjectInput {
+ """The id of the record to delete."""
+ id: UUID!
+}
+
+input UpdateOneObjectInput {
+ update: UpdateObjectPayload!
+
+ """The id of the object to update"""
+ id: UUID!
+}
+
+input UpdateObjectPayload {
+ labelSingular: String
+ labelPlural: String
+ nameSingular: String
+ namePlural: String
+ description: String
+ icon: String
+ shortcut: String
+ color: String
+ isActive: Boolean
+ labelIdentifierFieldMetadataId: UUID
+ imageIdentifierFieldMetadataId: UUID
+ isLabelSyncedWithName: Boolean
+ isSearchable: Boolean
+}
+
+input CreateOneIndexInput {
+ """The custom index to create"""
+ index: CreateIndexInput!
+}
+
+input CreateIndexInput {
+ objectMetadataId: UUID!
+ fields: [CreateIndexFieldInput!]!
+ indexType: IndexType! = BTREE
+}
+
+input CreateIndexFieldInput {
+ fieldMetadataId: UUID!
+ subFieldName: String
+}
+
+input DeleteOneIndexInput {
+ """The id of the custom index to delete."""
+ id: UUID!
+}
+
input CreateLogicFunctionFromSourceInput {
id: UUID
universalIdentifier: UUID
@@ -4001,103 +4098,6 @@ input UpdateFrontComponentInputUpdates {
description: String
}
-input CreateOneObjectInput {
- """The object to create"""
- object: CreateObjectInput!
-}
-
-input CreateObjectInput {
- nameSingular: String!
- namePlural: String!
- labelSingular: String!
- labelPlural: String!
- description: String
- icon: String
- shortcut: String
- color: String
- skipNameField: Boolean
- isRemote: Boolean
- primaryKeyColumnType: String
- primaryKeyFieldMetadataSettings: JSON
- isLabelSyncedWithName: Boolean
-}
-
-input DeleteOneObjectInput {
- """The id of the record to delete."""
- id: UUID!
-}
-
-input UpdateOneObjectInput {
- update: UpdateObjectPayload!
-
- """The id of the object to update"""
- id: UUID!
-}
-
-input UpdateObjectPayload {
- labelSingular: String
- labelPlural: String
- nameSingular: String
- namePlural: String
- description: String
- icon: String
- shortcut: String
- color: String
- isActive: Boolean
- labelIdentifierFieldMetadataId: UUID
- imageIdentifierFieldMetadataId: UUID
- isLabelSyncedWithName: Boolean
- isSearchable: Boolean
-}
-
-input CreateOneIndexInput {
- """The custom index to create"""
- index: CreateIndexInput!
-}
-
-input CreateIndexInput {
- objectMetadataId: UUID!
- fields: [CreateIndexFieldInput!]!
- indexType: IndexType! = BTREE
-}
-
-input CreateIndexFieldInput {
- fieldMetadataId: UUID!
- subFieldName: String
-}
-
-input DeleteOneIndexInput {
- """The id of the custom index to delete."""
- id: UUID!
-}
-
-input CreateAgentInput {
- name: String
- label: String!
- icon: String
- description: String
- prompt: String!
- modelId: String!
- roleId: UUID
- responseFormat: JSON
- modelConfiguration: JSON
- evaluationInputs: [String!]
-}
-
-input UpdateAgentInput {
- id: UUID!
- name: String
- label: String
- icon: String
- description: String
- prompt: String
- modelId: String
- roleId: UUID
- responseFormat: JSON
- modelConfiguration: JSON
- evaluationInputs: [String!]
-}
-
input SendEmailViaDomainInput {
emailingDomainId: String!
to: [String!]!
diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts
index ad3e5c2294..bd42fca367 100644
--- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts
+++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts
@@ -2673,6 +2673,13 @@ export interface Query {
getPageLayout?: PageLayout
getPageLayoutWidgets: PageLayoutWidget[]
getPageLayoutWidget: PageLayoutWidget
+ findManyAgents: Agent[]
+ findOneAgent: Agent
+ objectRecordCounts: ObjectRecordCount[]
+ object: Object
+ objects: ObjectConnection
+ index: Index
+ indexMetadatas: IndexConnection
findOneLogicFunction: LogicFunction
findManyLogicFunctions: LogicFunction[]
getAvailablePackages: Scalars['JSON']
@@ -2681,13 +2688,6 @@ export interface Query {
commandMenuItem?: CommandMenuItem
frontComponents: FrontComponent[]
frontComponent?: FrontComponent
- objectRecordCounts: ObjectRecordCount[]
- object: Object
- objects: ObjectConnection
- index: Index
- indexMetadatas: IndexConnection
- findManyAgents: Agent[]
- findOneAgent: Agent
previewMessageCampaignAudience: CampaignAudiencePreviewDTO
unsubscribeTopics: UnsubscribeTopic[]
unsubscribePagePreviewUrl: Scalars['String']
@@ -2835,6 +2835,14 @@ export interface Mutation {
createPageLayoutWidget: PageLayoutWidget
updatePageLayoutWidget: PageLayoutWidget
destroyPageLayoutWidget: Scalars['Boolean']
+ createOneAgent: Agent
+ updateOneAgent: Agent
+ deleteOneAgent: Agent
+ createOneObject: Object
+ deleteOneObject: Object
+ updateOneObject: Object
+ createOneIndex: Index
+ deleteOneIndex: Index
deleteOneLogicFunction: LogicFunction
createOneLogicFunction: LogicFunction
executeOneLogicFunction: LogicFunctionExecutionResult
@@ -2846,14 +2854,6 @@ export interface Mutation {
createFrontComponent: FrontComponent
updateFrontComponent: FrontComponent
deleteFrontComponent: FrontComponent
- createOneObject: Object
- deleteOneObject: Object
- updateOneObject: Object
- createOneIndex: Index
- deleteOneIndex: Index
- createOneAgent: Agent
- updateOneAgent: Agent
- deleteOneAgent: Agent
sendEmailViaEmailingDomain: SendEmailViaDomainOutput
sendMessageCampaign: SendMessageCampaignOutputDTO
createUnsubscribeTopic: UnsubscribeTopic
@@ -5808,14 +5808,8 @@ export interface QueryGenqlSelection{
getPageLayout?: (PageLayoutGenqlSelection & { __args: {id: Scalars['String']} })
getPageLayoutWidgets?: (PageLayoutWidgetGenqlSelection & { __args: {pageLayoutTabId: Scalars['String']} })
getPageLayoutWidget?: (PageLayoutWidgetGenqlSelection & { __args: {id: Scalars['String']} })
- findOneLogicFunction?: (LogicFunctionGenqlSelection & { __args: {input: LogicFunctionIdInput} })
- findManyLogicFunctions?: LogicFunctionGenqlSelection
- getAvailablePackages?: { __args: {input: LogicFunctionIdInput} }
- getLogicFunctionSourceCode?: { __args: {input: LogicFunctionIdInput} }
- commandMenuItems?: CommandMenuItemGenqlSelection
- commandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {id: Scalars['UUID']} })
- frontComponents?: FrontComponentGenqlSelection
- frontComponent?: (FrontComponentGenqlSelection & { __args: {id: Scalars['UUID']} })
+ findManyAgents?: AgentGenqlSelection
+ findOneAgent?: (AgentGenqlSelection & { __args: {input: AgentIdInput} })
objectRecordCounts?: ObjectRecordCountGenqlSelection
object?: (ObjectGenqlSelection & { __args: {
/** The id of the record to find. */
@@ -5833,8 +5827,14 @@ export interface QueryGenqlSelection{
paging: CursorPaging,
/** Specify to filter the records returned. */
filter: IndexFilter} })
- findManyAgents?: AgentGenqlSelection
- findOneAgent?: (AgentGenqlSelection & { __args: {input: AgentIdInput} })
+ findOneLogicFunction?: (LogicFunctionGenqlSelection & { __args: {input: LogicFunctionIdInput} })
+ findManyLogicFunctions?: LogicFunctionGenqlSelection
+ getAvailablePackages?: { __args: {input: LogicFunctionIdInput} }
+ getLogicFunctionSourceCode?: { __args: {input: LogicFunctionIdInput} }
+ commandMenuItems?: CommandMenuItemGenqlSelection
+ commandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {id: Scalars['UUID']} })
+ frontComponents?: FrontComponentGenqlSelection
+ frontComponent?: (FrontComponentGenqlSelection & { __args: {id: Scalars['UUID']} })
previewMessageCampaignAudience?: (CampaignAudiencePreviewDTOGenqlSelection & { __args: {input: PreviewMessageCampaignAudienceInput} })
unsubscribeTopics?: UnsubscribeTopicGenqlSelection
unsubscribePagePreviewUrl?: boolean | number
@@ -5907,14 +5907,14 @@ export interface QueryGenqlSelection{
export interface GetApiKeyInput {id: Scalars['UUID']}
-export interface LogicFunctionIdInput {
-/** The id of the function. */
-id: Scalars['ID']}
-
export interface AgentIdInput {
/** The id of the agent. */
id: Scalars['UUID']}
+export interface LogicFunctionIdInput {
+/** The id of the function. */
+id: Scalars['ID']}
+
export interface PreviewMessageCampaignAudienceInput {listId: Scalars['String'],unsubscribeTopicId?: (Scalars['String'] | null)}
export interface ListAppConnectionsInput {providerName?: (Scalars['String'] | null),userWorkspaceId?: (Scalars['String'] | null),visibility?: (Scalars['String'] | null)}
@@ -6013,6 +6013,14 @@ export interface MutationGenqlSelection{
createPageLayoutWidget?: (PageLayoutWidgetGenqlSelection & { __args: {input: CreatePageLayoutWidgetInput} })
updatePageLayoutWidget?: (PageLayoutWidgetGenqlSelection & { __args: {id: Scalars['String'], input: UpdatePageLayoutWidgetInput} })
destroyPageLayoutWidget?: { __args: {id: Scalars['String']} }
+ createOneAgent?: (AgentGenqlSelection & { __args: {input: CreateAgentInput} })
+ updateOneAgent?: (AgentGenqlSelection & { __args: {input: UpdateAgentInput} })
+ deleteOneAgent?: (AgentGenqlSelection & { __args: {input: AgentIdInput} })
+ createOneObject?: (ObjectGenqlSelection & { __args: {input: CreateOneObjectInput} })
+ deleteOneObject?: (ObjectGenqlSelection & { __args: {input: DeleteOneObjectInput} })
+ updateOneObject?: (ObjectGenqlSelection & { __args: {input: UpdateOneObjectInput} })
+ createOneIndex?: (IndexGenqlSelection & { __args: {input: CreateOneIndexInput} })
+ deleteOneIndex?: (IndexGenqlSelection & { __args: {input: DeleteOneIndexInput} })
deleteOneLogicFunction?: (LogicFunctionGenqlSelection & { __args: {input: LogicFunctionIdInput} })
createOneLogicFunction?: (LogicFunctionGenqlSelection & { __args: {input: CreateLogicFunctionFromSourceInput} })
executeOneLogicFunction?: (LogicFunctionExecutionResultGenqlSelection & { __args: {input: ExecuteOneLogicFunctionInput} })
@@ -6024,14 +6032,6 @@ export interface MutationGenqlSelection{
createFrontComponent?: (FrontComponentGenqlSelection & { __args: {input: CreateFrontComponentInput} })
updateFrontComponent?: (FrontComponentGenqlSelection & { __args: {input: UpdateFrontComponentInput} })
deleteFrontComponent?: (FrontComponentGenqlSelection & { __args: {id: Scalars['UUID']} })
- createOneObject?: (ObjectGenqlSelection & { __args: {input: CreateOneObjectInput} })
- deleteOneObject?: (ObjectGenqlSelection & { __args: {input: DeleteOneObjectInput} })
- updateOneObject?: (ObjectGenqlSelection & { __args: {input: UpdateOneObjectInput} })
- createOneIndex?: (IndexGenqlSelection & { __args: {input: CreateOneIndexInput} })
- deleteOneIndex?: (IndexGenqlSelection & { __args: {input: DeleteOneIndexInput} })
- createOneAgent?: (AgentGenqlSelection & { __args: {input: CreateAgentInput} })
- updateOneAgent?: (AgentGenqlSelection & { __args: {input: UpdateAgentInput} })
- deleteOneAgent?: (AgentGenqlSelection & { __args: {input: AgentIdInput} })
sendEmailViaEmailingDomain?: (SendEmailViaDomainOutputGenqlSelection & { __args: {input: SendEmailViaDomainInput} })
sendMessageCampaign?: (SendMessageCampaignOutputDTOGenqlSelection & { __args: {input: SendMessageCampaignInput} })
createUnsubscribeTopic?: (UnsubscribeTopicGenqlSelection & { __args: {input: CreateUnsubscribeTopicInput} })
@@ -6323,35 +6323,9 @@ export interface CreatePageLayoutWidgetInput {pageLayoutTabId: Scalars['UUID'],t
export interface UpdatePageLayoutWidgetInput {pageLayoutTabId?: (Scalars['UUID'] | null),title?: (Scalars['String'] | null),type?: (WidgetType | null),objectMetadataId?: (Scalars['UUID'] | null),gridPosition?: (GridPositionInput | null),position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null),conditionalDisplay?: (Scalars['JSON'] | null),conditionalAvailabilityExpression?: (Scalars['String'] | null)}
-export interface CreateLogicFunctionFromSourceInput {id?: (Scalars['UUID'] | null),universalIdentifier?: (Scalars['UUID'] | null),name: Scalars['String'],description?: (Scalars['String'] | null),timeoutSeconds?: (Scalars['Float'] | null),source?: (Scalars['JSON'] | null),cronTriggerSettings?: (Scalars['JSON'] | null),databaseEventTriggerSettings?: (Scalars['JSON'] | null),httpRouteTriggerSettings?: (Scalars['JSON'] | null),toolTriggerSettings?: (Scalars['JSON'] | null),workflowActionTriggerSettings?: (Scalars['JSON'] | null)}
+export interface CreateAgentInput {name?: (Scalars['String'] | null),label: Scalars['String'],icon?: (Scalars['String'] | null),description?: (Scalars['String'] | null),prompt: Scalars['String'],modelId: Scalars['String'],roleId?: (Scalars['UUID'] | null),responseFormat?: (Scalars['JSON'] | null),modelConfiguration?: (Scalars['JSON'] | null),evaluationInputs?: (Scalars['String'][] | null)}
-export interface ExecuteOneLogicFunctionInput {
-/** Id of the logic function to execute */
-id: Scalars['UUID'],
-/** Payload in JSON format */
-payload: Scalars['JSON']}
-
-export interface UpdateLogicFunctionFromSourceInput {
-/** Id of the logic function to update */
-id: Scalars['UUID'],
-/** The logic function updates */
-update: UpdateLogicFunctionFromSourceInputUpdates}
-
-export interface UpdateLogicFunctionFromSourceInputUpdates {name?: (Scalars['String'] | null),description?: (Scalars['String'] | null),timeoutSeconds?: (Scalars['Float'] | null),sourceHandlerCode?: (Scalars['String'] | null),handlerName?: (Scalars['String'] | null),sourceHandlerPath?: (Scalars['String'] | null),cronTriggerSettings?: (Scalars['JSON'] | null),databaseEventTriggerSettings?: (Scalars['JSON'] | null),httpRouteTriggerSettings?: (Scalars['JSON'] | null),toolTriggerSettings?: (Scalars['JSON'] | null),workflowActionTriggerSettings?: (Scalars['JSON'] | null)}
-
-export interface CreateCommandMenuItemInput {workflowVersionId?: (Scalars['UUID'] | null),frontComponentId?: (Scalars['UUID'] | null),engineComponentKey: EngineComponentKey,label: Scalars['String'],icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),hotKeys?: (Scalars['String'][] | null),conditionalAvailabilityExpression?: (Scalars['String'] | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null),payload?: (Scalars['JSON'] | null),pageLayoutId?: (Scalars['UUID'] | null)}
-
-export interface UpdateCommandMenuItemInput {id: Scalars['UUID'],label?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null),engineComponentKey?: (EngineComponentKey | null),hotKeys?: (Scalars['String'][] | null),pageLayoutId?: (Scalars['UUID'] | null)}
-
-export interface CreateFrontComponentInput {id?: (Scalars['UUID'] | null),name: Scalars['String'],description?: (Scalars['String'] | null),sourceComponentPath: Scalars['String'],builtComponentPath: Scalars['String'],componentName: Scalars['String'],builtComponentChecksum: Scalars['String']}
-
-export interface UpdateFrontComponentInput {
-/** The id of the front component to update */
-id: Scalars['UUID'],
-/** The front component fields to update */
-update: UpdateFrontComponentInputUpdates}
-
-export interface UpdateFrontComponentInputUpdates {name?: (Scalars['String'] | null),description?: (Scalars['String'] | null)}
+export interface UpdateAgentInput {id: Scalars['UUID'],name?: (Scalars['String'] | null),label?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),description?: (Scalars['String'] | null),prompt?: (Scalars['String'] | null),modelId?: (Scalars['String'] | null),roleId?: (Scalars['UUID'] | null),responseFormat?: (Scalars['JSON'] | null),modelConfiguration?: (Scalars['JSON'] | null),evaluationInputs?: (Scalars['String'][] | null)}
export interface CreateOneObjectInput {
/** The object to create */
@@ -6381,9 +6355,35 @@ export interface DeleteOneIndexInput {
/** The id of the custom index to delete. */
id: Scalars['UUID']}
-export interface CreateAgentInput {name?: (Scalars['String'] | null),label: Scalars['String'],icon?: (Scalars['String'] | null),description?: (Scalars['String'] | null),prompt: Scalars['String'],modelId: Scalars['String'],roleId?: (Scalars['UUID'] | null),responseFormat?: (Scalars['JSON'] | null),modelConfiguration?: (Scalars['JSON'] | null),evaluationInputs?: (Scalars['String'][] | null)}
+export interface CreateLogicFunctionFromSourceInput {id?: (Scalars['UUID'] | null),universalIdentifier?: (Scalars['UUID'] | null),name: Scalars['String'],description?: (Scalars['String'] | null),timeoutSeconds?: (Scalars['Float'] | null),source?: (Scalars['JSON'] | null),cronTriggerSettings?: (Scalars['JSON'] | null),databaseEventTriggerSettings?: (Scalars['JSON'] | null),httpRouteTriggerSettings?: (Scalars['JSON'] | null),toolTriggerSettings?: (Scalars['JSON'] | null),workflowActionTriggerSettings?: (Scalars['JSON'] | null)}
-export interface UpdateAgentInput {id: Scalars['UUID'],name?: (Scalars['String'] | null),label?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),description?: (Scalars['String'] | null),prompt?: (Scalars['String'] | null),modelId?: (Scalars['String'] | null),roleId?: (Scalars['UUID'] | null),responseFormat?: (Scalars['JSON'] | null),modelConfiguration?: (Scalars['JSON'] | null),evaluationInputs?: (Scalars['String'][] | null)}
+export interface ExecuteOneLogicFunctionInput {
+/** Id of the logic function to execute */
+id: Scalars['UUID'],
+/** Payload in JSON format */
+payload: Scalars['JSON']}
+
+export interface UpdateLogicFunctionFromSourceInput {
+/** Id of the logic function to update */
+id: Scalars['UUID'],
+/** The logic function updates */
+update: UpdateLogicFunctionFromSourceInputUpdates}
+
+export interface UpdateLogicFunctionFromSourceInputUpdates {name?: (Scalars['String'] | null),description?: (Scalars['String'] | null),timeoutSeconds?: (Scalars['Float'] | null),sourceHandlerCode?: (Scalars['String'] | null),handlerName?: (Scalars['String'] | null),sourceHandlerPath?: (Scalars['String'] | null),cronTriggerSettings?: (Scalars['JSON'] | null),databaseEventTriggerSettings?: (Scalars['JSON'] | null),httpRouteTriggerSettings?: (Scalars['JSON'] | null),toolTriggerSettings?: (Scalars['JSON'] | null),workflowActionTriggerSettings?: (Scalars['JSON'] | null)}
+
+export interface CreateCommandMenuItemInput {workflowVersionId?: (Scalars['UUID'] | null),frontComponentId?: (Scalars['UUID'] | null),engineComponentKey: EngineComponentKey,label: Scalars['String'],icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),hotKeys?: (Scalars['String'][] | null),conditionalAvailabilityExpression?: (Scalars['String'] | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null),payload?: (Scalars['JSON'] | null),pageLayoutId?: (Scalars['UUID'] | null)}
+
+export interface UpdateCommandMenuItemInput {id: Scalars['UUID'],label?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null),engineComponentKey?: (EngineComponentKey | null),hotKeys?: (Scalars['String'][] | null),pageLayoutId?: (Scalars['UUID'] | null)}
+
+export interface CreateFrontComponentInput {id?: (Scalars['UUID'] | null),name: Scalars['String'],description?: (Scalars['String'] | null),sourceComponentPath: Scalars['String'],builtComponentPath: Scalars['String'],componentName: Scalars['String'],builtComponentChecksum: Scalars['String']}
+
+export interface UpdateFrontComponentInput {
+/** The id of the front component to update */
+id: Scalars['UUID'],
+/** The front component fields to update */
+update: UpdateFrontComponentInputUpdates}
+
+export interface UpdateFrontComponentInputUpdates {name?: (Scalars['String'] | null),description?: (Scalars['String'] | null)}
export interface SendEmailViaDomainInput {emailingDomainId: Scalars['String'],to: Scalars['String'][],cc?: (Scalars['String'][] | null),bcc?: (Scalars['String'][] | null),subject: Scalars['String'],text: Scalars['String'],html?: (Scalars['String'] | null),from: Scalars['String'],replyTo?: (Scalars['String'][] | null)}
diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts
index b7ab046889..043f8d5c09 100644
--- a/packages/twenty-client-sdk/src/metadata/generated/types.ts
+++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts
@@ -6186,57 +6186,15 @@ export default {
]
}
],
- "findOneLogicFunction": [
- 41,
+ "findManyAgents": [
+ 25
+ ],
+ "findOneAgent": [
+ 25,
{
"input": [
338,
- "LogicFunctionIdInput!"
- ]
- }
- ],
- "findManyLogicFunctions": [
- 41
- ],
- "getAvailablePackages": [
- 15,
- {
- "input": [
- 338,
- "LogicFunctionIdInput!"
- ]
- }
- ],
- "getLogicFunctionSourceCode": [
- 1,
- {
- "input": [
- 338,
- "LogicFunctionIdInput!"
- ]
- }
- ],
- "commandMenuItems": [
- 35
- ],
- "commandMenuItem": [
- 35,
- {
- "id": [
- 3,
- "UUID!"
- ]
- }
- ],
- "frontComponents": [
- 34
- ],
- "frontComponent": [
- 34,
- {
- "id": [
- 3,
- "UUID!"
+ "AgentIdInput!"
]
}
],
@@ -6287,15 +6245,57 @@ export default {
]
}
],
- "findManyAgents": [
- 25
- ],
- "findOneAgent": [
- 25,
+ "findOneLogicFunction": [
+ 41,
{
"input": [
339,
- "AgentIdInput!"
+ "LogicFunctionIdInput!"
+ ]
+ }
+ ],
+ "findManyLogicFunctions": [
+ 41
+ ],
+ "getAvailablePackages": [
+ 15,
+ {
+ "input": [
+ 339,
+ "LogicFunctionIdInput!"
+ ]
+ }
+ ],
+ "getLogicFunctionSourceCode": [
+ 1,
+ {
+ "input": [
+ 339,
+ "LogicFunctionIdInput!"
+ ]
+ }
+ ],
+ "commandMenuItems": [
+ 35
+ ],
+ "commandMenuItem": [
+ 35,
+ {
+ "id": [
+ 3,
+ "UUID!"
+ ]
+ }
+ ],
+ "frontComponents": [
+ 34
+ ],
+ "frontComponent": [
+ 34,
+ {
+ "id": [
+ 3,
+ "UUID!"
]
}
],
@@ -6747,17 +6747,17 @@ export default {
1
]
},
- "LogicFunctionIdInput": {
+ "AgentIdInput": {
"id": [
- 218
+ 3
],
"__typename": [
1
]
},
- "AgentIdInput": {
+ "LogicFunctionIdInput": {
"id": [
- 3
+ 218
],
"__typename": [
1
@@ -7604,11 +7604,83 @@ export default {
]
}
],
+ "createOneAgent": [
+ 25,
+ {
+ "input": [
+ 406,
+ "CreateAgentInput!"
+ ]
+ }
+ ],
+ "updateOneAgent": [
+ 25,
+ {
+ "input": [
+ 407,
+ "UpdateAgentInput!"
+ ]
+ }
+ ],
+ "deleteOneAgent": [
+ 25,
+ {
+ "input": [
+ 338,
+ "AgentIdInput!"
+ ]
+ }
+ ],
+ "createOneObject": [
+ 56,
+ {
+ "input": [
+ 408,
+ "CreateOneObjectInput!"
+ ]
+ }
+ ],
+ "deleteOneObject": [
+ 56,
+ {
+ "input": [
+ 410,
+ "DeleteOneObjectInput!"
+ ]
+ }
+ ],
+ "updateOneObject": [
+ 56,
+ {
+ "input": [
+ 411,
+ "UpdateOneObjectInput!"
+ ]
+ }
+ ],
+ "createOneIndex": [
+ 47,
+ {
+ "input": [
+ 413,
+ "CreateOneIndexInput!"
+ ]
+ }
+ ],
+ "deleteOneIndex": [
+ 47,
+ {
+ "input": [
+ 416,
+ "DeleteOneIndexInput!"
+ ]
+ }
+ ],
"deleteOneLogicFunction": [
41,
{
"input": [
- 338,
+ 339,
"LogicFunctionIdInput!"
]
}
@@ -7617,7 +7689,7 @@ export default {
41,
{
"input": [
- 406,
+ 417,
"CreateLogicFunctionFromSourceInput!"
]
}
@@ -7626,7 +7698,7 @@ export default {
168,
{
"input": [
- 407,
+ 418,
"ExecuteOneLogicFunctionInput!"
]
}
@@ -7635,7 +7707,7 @@ export default {
6,
{
"input": [
- 408,
+ 419,
"UpdateLogicFunctionFromSourceInput!"
]
}
@@ -7644,7 +7716,7 @@ export default {
35,
{
"input": [
- 410,
+ 421,
"CreateCommandMenuItemInput!"
]
}
@@ -7653,7 +7725,7 @@ export default {
35,
{
"input": [
- 411,
+ 422,
"UpdateCommandMenuItemInput!"
]
}
@@ -7680,7 +7752,7 @@ export default {
34,
{
"input": [
- 412,
+ 423,
"CreateFrontComponentInput!"
]
}
@@ -7689,7 +7761,7 @@ export default {
34,
{
"input": [
- 413,
+ 424,
"UpdateFrontComponentInput!"
]
}
@@ -7703,78 +7775,6 @@ export default {
]
}
],
- "createOneObject": [
- 56,
- {
- "input": [
- 415,
- "CreateOneObjectInput!"
- ]
- }
- ],
- "deleteOneObject": [
- 56,
- {
- "input": [
- 417,
- "DeleteOneObjectInput!"
- ]
- }
- ],
- "updateOneObject": [
- 56,
- {
- "input": [
- 418,
- "UpdateOneObjectInput!"
- ]
- }
- ],
- "createOneIndex": [
- 47,
- {
- "input": [
- 420,
- "CreateOneIndexInput!"
- ]
- }
- ],
- "deleteOneIndex": [
- 47,
- {
- "input": [
- 423,
- "DeleteOneIndexInput!"
- ]
- }
- ],
- "createOneAgent": [
- 25,
- {
- "input": [
- 424,
- "CreateAgentInput!"
- ]
- }
- ],
- "updateOneAgent": [
- 25,
- {
- "input": [
- 425,
- "UpdateAgentInput!"
- ]
- }
- ],
- "deleteOneAgent": [
- 25,
- {
- "input": [
- 339,
- "AgentIdInput!"
- ]
- }
- ],
"sendEmailViaEmailingDomain": [
283,
{
@@ -10021,6 +10021,235 @@ export default {
1
]
},
+ "CreateAgentInput": {
+ "name": [
+ 1
+ ],
+ "label": [
+ 1
+ ],
+ "icon": [
+ 1
+ ],
+ "description": [
+ 1
+ ],
+ "prompt": [
+ 1
+ ],
+ "modelId": [
+ 1
+ ],
+ "roleId": [
+ 3
+ ],
+ "responseFormat": [
+ 15
+ ],
+ "modelConfiguration": [
+ 15
+ ],
+ "evaluationInputs": [
+ 1
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "UpdateAgentInput": {
+ "id": [
+ 3
+ ],
+ "name": [
+ 1
+ ],
+ "label": [
+ 1
+ ],
+ "icon": [
+ 1
+ ],
+ "description": [
+ 1
+ ],
+ "prompt": [
+ 1
+ ],
+ "modelId": [
+ 1
+ ],
+ "roleId": [
+ 3
+ ],
+ "responseFormat": [
+ 15
+ ],
+ "modelConfiguration": [
+ 15
+ ],
+ "evaluationInputs": [
+ 1
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "CreateOneObjectInput": {
+ "object": [
+ 409
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "CreateObjectInput": {
+ "nameSingular": [
+ 1
+ ],
+ "namePlural": [
+ 1
+ ],
+ "labelSingular": [
+ 1
+ ],
+ "labelPlural": [
+ 1
+ ],
+ "description": [
+ 1
+ ],
+ "icon": [
+ 1
+ ],
+ "shortcut": [
+ 1
+ ],
+ "color": [
+ 1
+ ],
+ "skipNameField": [
+ 6
+ ],
+ "isRemote": [
+ 6
+ ],
+ "primaryKeyColumnType": [
+ 1
+ ],
+ "primaryKeyFieldMetadataSettings": [
+ 15
+ ],
+ "isLabelSyncedWithName": [
+ 6
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "DeleteOneObjectInput": {
+ "id": [
+ 3
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "UpdateOneObjectInput": {
+ "update": [
+ 412
+ ],
+ "id": [
+ 3
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "UpdateObjectPayload": {
+ "labelSingular": [
+ 1
+ ],
+ "labelPlural": [
+ 1
+ ],
+ "nameSingular": [
+ 1
+ ],
+ "namePlural": [
+ 1
+ ],
+ "description": [
+ 1
+ ],
+ "icon": [
+ 1
+ ],
+ "shortcut": [
+ 1
+ ],
+ "color": [
+ 1
+ ],
+ "isActive": [
+ 6
+ ],
+ "labelIdentifierFieldMetadataId": [
+ 3
+ ],
+ "imageIdentifierFieldMetadataId": [
+ 3
+ ],
+ "isLabelSyncedWithName": [
+ 6
+ ],
+ "isSearchable": [
+ 6
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "CreateOneIndexInput": {
+ "index": [
+ 414
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "CreateIndexInput": {
+ "objectMetadataId": [
+ 3
+ ],
+ "fields": [
+ 415
+ ],
+ "indexType": [
+ 48
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "CreateIndexFieldInput": {
+ "fieldMetadataId": [
+ 3
+ ],
+ "subFieldName": [
+ 1
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "DeleteOneIndexInput": {
+ "id": [
+ 3
+ ],
+ "__typename": [
+ 1
+ ]
+ },
"CreateLogicFunctionFromSourceInput": {
"id": [
3
@@ -10075,7 +10304,7 @@ export default {
3
],
"update": [
- 409
+ 420
],
"__typename": [
1
@@ -10235,7 +10464,7 @@ export default {
3
],
"update": [
- 414
+ 425
],
"__typename": [
1
@@ -10252,235 +10481,6 @@ export default {
1
]
},
- "CreateOneObjectInput": {
- "object": [
- 416
- ],
- "__typename": [
- 1
- ]
- },
- "CreateObjectInput": {
- "nameSingular": [
- 1
- ],
- "namePlural": [
- 1
- ],
- "labelSingular": [
- 1
- ],
- "labelPlural": [
- 1
- ],
- "description": [
- 1
- ],
- "icon": [
- 1
- ],
- "shortcut": [
- 1
- ],
- "color": [
- 1
- ],
- "skipNameField": [
- 6
- ],
- "isRemote": [
- 6
- ],
- "primaryKeyColumnType": [
- 1
- ],
- "primaryKeyFieldMetadataSettings": [
- 15
- ],
- "isLabelSyncedWithName": [
- 6
- ],
- "__typename": [
- 1
- ]
- },
- "DeleteOneObjectInput": {
- "id": [
- 3
- ],
- "__typename": [
- 1
- ]
- },
- "UpdateOneObjectInput": {
- "update": [
- 419
- ],
- "id": [
- 3
- ],
- "__typename": [
- 1
- ]
- },
- "UpdateObjectPayload": {
- "labelSingular": [
- 1
- ],
- "labelPlural": [
- 1
- ],
- "nameSingular": [
- 1
- ],
- "namePlural": [
- 1
- ],
- "description": [
- 1
- ],
- "icon": [
- 1
- ],
- "shortcut": [
- 1
- ],
- "color": [
- 1
- ],
- "isActive": [
- 6
- ],
- "labelIdentifierFieldMetadataId": [
- 3
- ],
- "imageIdentifierFieldMetadataId": [
- 3
- ],
- "isLabelSyncedWithName": [
- 6
- ],
- "isSearchable": [
- 6
- ],
- "__typename": [
- 1
- ]
- },
- "CreateOneIndexInput": {
- "index": [
- 421
- ],
- "__typename": [
- 1
- ]
- },
- "CreateIndexInput": {
- "objectMetadataId": [
- 3
- ],
- "fields": [
- 422
- ],
- "indexType": [
- 48
- ],
- "__typename": [
- 1
- ]
- },
- "CreateIndexFieldInput": {
- "fieldMetadataId": [
- 3
- ],
- "subFieldName": [
- 1
- ],
- "__typename": [
- 1
- ]
- },
- "DeleteOneIndexInput": {
- "id": [
- 3
- ],
- "__typename": [
- 1
- ]
- },
- "CreateAgentInput": {
- "name": [
- 1
- ],
- "label": [
- 1
- ],
- "icon": [
- 1
- ],
- "description": [
- 1
- ],
- "prompt": [
- 1
- ],
- "modelId": [
- 1
- ],
- "roleId": [
- 3
- ],
- "responseFormat": [
- 15
- ],
- "modelConfiguration": [
- 15
- ],
- "evaluationInputs": [
- 1
- ],
- "__typename": [
- 1
- ]
- },
- "UpdateAgentInput": {
- "id": [
- 3
- ],
- "name": [
- 1
- ],
- "label": [
- 1
- ],
- "icon": [
- 1
- ],
- "description": [
- 1
- ],
- "prompt": [
- 1
- ],
- "modelId": [
- 1
- ],
- "roleId": [
- 3
- ],
- "responseFormat": [
- 15
- ],
- "modelConfiguration": [
- 15
- ],
- "evaluationInputs": [
- 1
- ],
- "__typename": [
- 1
- ]
- },
"SendEmailViaDomainInput": {
"emailingDomainId": [
1
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts
index 834ae46d18..e2174e58a6 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts
@@ -30,6 +30,7 @@ import { generateFakeValue } from 'src/engine/utils/generate-fake-value';
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
import { DEFAULT_ITERATOR_CURRENT_ITEM } from 'src/modules/workflow/workflow-builder/workflow-schema/constants/default-iterator-current-item.const';
import {
+ type BaseOutputSchema,
Leaf,
Node,
type OutputSchema,
@@ -134,14 +135,10 @@ export class WorkflowSchemaWorkspaceService {
};
}
case WorkflowActionType.AI_AGENT: {
- return {
- response: {
- label: 'Response',
- isLeaf: true,
- type: 'string',
- value: 'Response of the agent',
- },
- };
+ return this.computeAiAgentActionOutputSchema({
+ agentId: step.settings.input.agentId,
+ workspaceId,
+ });
}
case WorkflowTriggerType.WEBHOOK:
case WorkflowActionType.CODE:
@@ -379,6 +376,63 @@ export class WorkflowSchemaWorkspaceService {
return { success: { isLeaf: true, type: 'boolean', value: true } };
}
+ private async computeAiAgentActionOutputSchema({
+ agentId,
+ workspaceId,
+ }: {
+ agentId?: string;
+ workspaceId: string;
+ }): Promise {
+ const textResponseOutputSchema: OutputSchema = {
+ response: {
+ label: 'Response',
+ isLeaf: true,
+ type: 'string',
+ value: 'Response of the agent',
+ },
+ };
+
+ if (!isDefined(agentId)) {
+ return textResponseOutputSchema;
+ }
+
+ const { flatAgentMaps } =
+ await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
+ {
+ workspaceId,
+ flatMapsKeys: ['flatAgentMaps'],
+ },
+ );
+
+ const flatAgent = findFlatEntityByIdInFlatEntityMaps({
+ flatEntityId: agentId,
+ flatEntityMaps: flatAgentMaps,
+ });
+
+ const responseFormat = flatAgent?.responseFormat;
+
+ if (responseFormat?.type !== 'json') {
+ return textResponseOutputSchema;
+ }
+
+ return Object.entries(responseFormat.schema.properties || {}).reduce(
+ (outputSchema, [propertyName, property]) => {
+ outputSchema[propertyName] = {
+ isLeaf: true,
+ type: property.type,
+ label: propertyName,
+ ...(isDefined(property.description)
+ ? { description: property.description }
+ : {}),
+ value: generateFakeValue(property.type),
+ };
+
+ return outputSchema;
+ },
+ {} as BaseOutputSchema,
+ );
+ }
+
private async computeFormActionOutputSchema({
formFieldMetadataItems,
workspaceId,
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/services/workflow-tool.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/services/workflow-tool.workspace-service.ts
index 1a8c4c546f..9c7c22a258 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-tools/services/workflow-tool.workspace-service.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/services/workflow-tool.workspace-service.ts
@@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common';
import { type ToolSet } from 'ai';
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
+import { AgentService } from 'src/engine/metadata-modules/ai/ai-agent/agent.service';
import { LogicFunctionFromSourceService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source.service';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@@ -24,6 +25,7 @@ import { createDeleteWorkflowVersionEdgeTool } from 'src/modules/workflow/workfl
import { createDeleteWorkflowVersionStepTool } from 'src/modules/workflow/workflow-tools/tools/delete-workflow-version-step.tool';
import { createGetWorkflowCurrentVersionTool } from 'src/modules/workflow/workflow-tools/tools/get-workflow-current-version.tool';
import { createListLogicFunctionToolsTool } from 'src/modules/workflow/workflow-tools/tools/list-logic-function-tools.tool';
+import { createUpdateAgentTool } from 'src/modules/workflow/workflow-tools/tools/update-agent.tool';
import { createUpdateLogicFunctionSourceTool } from 'src/modules/workflow/workflow-tools/tools/update-logic-function-source.tool';
import { createUpdateWorkflowVersionPositionsTool } from 'src/modules/workflow/workflow-tools/tools/update-workflow-version-positions.tool';
import { createUpdateWorkflowVersionStepTool } from 'src/modules/workflow/workflow-tools/tools/update-workflow-version-step.tool';
@@ -48,6 +50,7 @@ export class WorkflowToolWorkspaceService {
recordPositionService: RecordPositionService,
logicFunctionFromSourceService: LogicFunctionFromSourceService,
flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
+ agentService: AgentService,
) {
this.deps = {
workflowVersionStepService,
@@ -61,6 +64,7 @@ export class WorkflowToolWorkspaceService {
recordPositionService,
logicFunctionFromSourceService,
flatEntityMapsCacheService,
+ agentService,
};
}
@@ -128,6 +132,7 @@ export class WorkflowToolWorkspaceService {
this.deps,
context,
);
+ const updateAgent = createUpdateAgentTool(this.deps, context);
const validateWorkflow = createValidateWorkflowTool(this.deps, context);
return {
@@ -146,6 +151,7 @@ export class WorkflowToolWorkspaceService {
[getWorkflowCurrentVersion.name]: getWorkflowCurrentVersion,
[updateLogicFunctionSource.name]: updateLogicFunctionSource,
[listLogicFunctionTools.name]: listLogicFunctionTools,
+ [updateAgent.name]: updateAgent,
[validateWorkflow.name]: validateWorkflow,
};
}
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts
index 0e7f6d6acf..7f650e9e77 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts
@@ -1,5 +1,6 @@
import {
workflowActionSchema,
+ WorkflowActionType,
workflowTriggerSchema,
} from 'twenty-shared/workflow';
import { v4 as uuidv4 } from 'uuid';
@@ -99,6 +100,7 @@ Common mistakes to avoid:
- Missing the "objectRecord" field in CREATE_RECORD actions
- Using "fieldsToUpdate" instead of "objectRecord" in CREATE_RECORD actions
- Including CODE steps in this tool — this tool does NOT create the underlying logic function needed by CODE steps. Instead, create the workflow without CODE steps first, then add CODE steps individually using create_workflow_version_step (which properly creates the logic function), then call update_logic_function_source to define the code.
+- Including AI_AGENT steps in this tool — this tool does NOT create the underlying agent needed by AI_AGENT steps. Instead, create the workflow without AI_AGENT steps first, then add AI_AGENT steps individually using create_workflow_version_step (which properly creates the agent), then call update_agent to configure the agent.
IMPORTANT: The tool schema provides comprehensive field descriptions, examples, and validation rules. Always refer to the schema for:
- Field requirements and data types
@@ -134,6 +136,17 @@ The response includes a compact validation summary. For the full validation repo
WorkflowVersionStepExceptionCode.INVALID_REQUEST,
);
}
+
+ const aiAgentSteps = parameters.steps.filter(
+ (step) => step.type === WorkflowActionType.AI_AGENT,
+ );
+
+ if (aiAgentSteps.length > 0) {
+ throw new WorkflowVersionStepException(
+ 'AI_AGENT steps cannot be created via create_complete_workflow because it does not create the underlying agent. Use create_workflow_version_step instead, then call update_agent to configure the agent.',
+ WorkflowVersionStepExceptionCode.INVALID_REQUEST,
+ );
+ }
const workflowId = await createWorkflow({
deps,
context,
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-workflow-version-step.tool.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-workflow-version-step.tool.ts
index eeb354dae3..169bfd1995 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-workflow-version-step.tool.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-workflow-version-step.tool.ts
@@ -86,6 +86,12 @@ const enrichResultWithNextStep = ({
nextStep:
'This CODE step was created with a default placeholder function. You MUST now call update_logic_function_source with the logicFunctionId from this step to define the actual code. IMPORTANT: Also provide outputSchema (an example return value, e.g. { datePlus7: "2026-06-16" }) so downstream steps can reference this step\'s output variables via {{stepId.fieldName}}.',
};
+ case WorkflowActionType.AI_AGENT:
+ return {
+ ...result,
+ nextStep:
+ 'This AI_AGENT step was created with a default placeholder agent. You MUST now call update_agent with the agentId from this step\'s settings.input.agentId to set the agent\'s system prompt (and optionally its model and responseFormat). Use responseFormat { type: "json", schema: { ... } } when downstream steps need to reference structured fields via {{stepId.fieldName}}, otherwise the output is referenced as {{stepId.response}}. If the step needs a task-specific prompt, also set it via update_workflow_version_step on settings.input.prompt.',
+ };
default:
return result;
}
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-agent.tool.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-agent.tool.ts
new file mode 100644
index 0000000000..179d3a30ae
--- /dev/null
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-agent.tool.ts
@@ -0,0 +1,104 @@
+import { isDefined } from 'twenty-shared/utils';
+import { z } from 'zod';
+
+import { type AgentResponseFormat } from 'src/engine/metadata-modules/ai/ai-agent/types/agent-response-format.type';
+import { type ModelId } from 'src/engine/metadata-modules/ai/ai-models/types/model-id.type';
+import {
+ type WorkflowToolContext,
+ type WorkflowToolDependencies,
+} from 'src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type';
+
+const agentResponseFormatSchema = z.union([
+ z.object({ type: z.literal('text') }),
+ z.object({
+ type: z.literal('json'),
+ schema: z.object({
+ type: z.literal('object'),
+ properties: z.record(
+ z.string(),
+ z.object({
+ type: z.enum(['string', 'number', 'boolean']),
+ description: z.string().optional(),
+ }),
+ ),
+ required: z.array(z.string()).optional(),
+ additionalProperties: z.literal(false).optional(),
+ }),
+ }),
+]);
+
+const updateAgentSchema = z.object({
+ agentId: z
+ .string()
+ .uuid()
+ .describe(
+ "The ID of the agent to update (from the AI_AGENT step's settings.input.agentId)",
+ ),
+ prompt: z
+ .string()
+ .optional()
+ .describe(
+ "The agent's system prompt describing its role, behavior and the task it must accomplish.",
+ ),
+ modelId: z
+ .string()
+ .optional()
+ .describe(
+ 'Optional model id to use for the agent. Leave empty to keep the auto-selected model.',
+ ),
+ responseFormat: agentResponseFormatSchema
+ .optional()
+ .describe(
+ 'Optional response format. Use { type: "text" } for free-form text output, or { type: "json", schema: { type: "object", properties: { fieldName: { type: "string" } } } } for structured output. Downstream steps can reference structured fields via {{stepId.fieldName}} (or {{stepId.response}} for text format).',
+ ),
+});
+
+export const createUpdateAgentTool = (
+ deps: Pick,
+ context: WorkflowToolContext,
+) => ({
+ name: 'update_agent' as const,
+ description: `Update the AI agent used by a workflow AI_AGENT step.
+
+Use this tool to configure the agent created when an AI_AGENT step is added: set its system prompt, the model it should use, and the format of its output.
+
+- prompt: the agent's system prompt (its role, behavior and task).
+- modelId: optional model id; omit to keep the auto-selected model.
+- responseFormat: { type: "text" } for free-form text (referenced as {{stepId.response}}), or { type: "json", schema: { ... } } for structured output whose fields can be referenced as {{stepId.fieldName}}.
+
+To find the agentId, look at the AI_AGENT step's settings.input.agentId field.`,
+ inputSchema: updateAgentSchema,
+ execute: async (parameters: {
+ agentId: string;
+ prompt?: string;
+ modelId?: string;
+ responseFormat?: AgentResponseFormat;
+ }) => {
+ try {
+ const { agentId, prompt, modelId, responseFormat } = parameters;
+ const { workspaceId } = context;
+
+ const updatedAgent = await deps.agentService.updateOneAgent({
+ input: {
+ id: agentId,
+ ...(isDefined(prompt) ? { prompt } : {}),
+ ...(isDefined(modelId) ? { modelId: modelId as ModelId } : {}),
+ ...(isDefined(responseFormat) ? { responseFormat } : {}),
+ },
+ workspaceId,
+ });
+
+ return {
+ success: true,
+ message: `Successfully updated agent ${agentId}`,
+ agentId: updatedAgent.id,
+ };
+ } catch (error) {
+ return {
+ success: false,
+ error: error.message,
+ message: `Failed to update agent: ${error.message}`,
+ };
+ }
+ },
+});
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type.ts
index ae71996652..67c75eeac7 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type.ts
@@ -1,4 +1,5 @@
import type { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
+import type { AgentService } from 'src/engine/metadata-modules/ai/ai-agent/agent.service';
import type { LogicFunctionFromSourceService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source.service';
import type { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import type { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
@@ -22,6 +23,7 @@ export type WorkflowToolDependencies = {
recordPositionService: RecordPositionService;
logicFunctionFromSourceService: LogicFunctionFromSourceService;
flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService;
+ agentService: AgentService;
};
export type WorkflowToolContext = {
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/workflow-tools.module.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/workflow-tools.module.ts
index bb926a6c01..95fcf83fef 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-tools/workflow-tools.module.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/workflow-tools.module.ts
@@ -2,6 +2,7 @@ import { Global, Module } from '@nestjs/common';
import { RecordPositionModule } from 'src/engine/core-modules/record-position/record-position.module';
import { WORKFLOW_TOOL_SERVICE_TOKEN } from 'src/engine/core-modules/tool-provider/constants/workflow-tool-service.token';
+import { AiAgentModule } from 'src/engine/metadata-modules/ai/ai-agent/ai-agent.module';
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
import { LogicFunctionModule } from 'src/engine/metadata-modules/logic-function/logic-function.module';
import { WorkflowSchemaModule } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module';
@@ -27,6 +28,7 @@ import { WorkflowToolWorkspaceService } from './services/workflow-tool.workspace
RecordPositionModule,
LogicFunctionModule,
WorkspaceManyOrAllFlatEntityMapsCacheModule,
+ AiAgentModule,
],
providers: [
WorkflowToolWorkspaceService,