diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
index d28d20aa6d..5bfc1853a5 100644
--- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
+++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql
@@ -2132,6 +2132,18 @@ type FieldConnection {
edges: [FieldEdge!]!
}
+type AppConnection {
+ id: ID!
+ providerName: String!
+ name: String!
+ handle: String!
+ visibility: String!
+ userWorkspaceId: String!
+ accessToken: String!
+ scopes: [String!]!
+ authFailedAt: String
+}
+
type ResendEmailVerificationToken {
success: Boolean!
}
@@ -2529,6 +2541,12 @@ type AgentMessagePart {
createdAt: DateTime!
}
+type RunAgentResult {
+ result: JSON
+ error: String
+ success: Boolean!
+}
+
type ChannelSyncSuccess {
success: Boolean!
}
@@ -3011,6 +3029,8 @@ type Query {
myMessageChannels(connectedAccountId: UUID): [MessageChannel!]!
myCalendarChannels(connectedAccountId: UUID): [CalendarChannel!]!
minimalMetadata: MinimalMetadata!
+ appConnections(filter: ListAppConnectionsInput): [AppConnection!]!
+ appConnection(id: ID!): AppConnection!
findWorkspaceAiStats: WorkspaceAiStats!
chatThreads: [AgentChatThread!]!
chatThread(id: UUID!): AgentChatThread!
@@ -3065,6 +3085,12 @@ input AgentIdInput {
id: UUID!
}
+input ListAppConnectionsInput {
+ providerName: String
+ userWorkspaceId: String
+ visibility: String
+}
+
input EventLogQueryInput {
table: EventLogTable!
filters: EventLogFiltersInput
@@ -3238,6 +3264,7 @@ type Mutation {
upsertRowLevelPermissionPredicates(input: UpsertRowLevelPermissionPredicatesInput!): UpsertRowLevelPermissionPredicatesResult!
assignRoleToAgent(agentId: UUID!, roleId: UUID!): Boolean!
removeRoleFromAgent(agentId: UUID!): Boolean!
+ runAgent(input: RunAgentInput!): RunAgentResult!
createWebhook(input: CreateWebhookInput!): Webhook!
updateWebhook(input: UpdateWebhookInput!): Webhook!
deleteWebhook(id: UUID!): Webhook!
@@ -4093,6 +4120,11 @@ input RowLevelPermissionPredicateGroupInput {
positionInRowLevelPermissionPredicateGroup: Float
}
+input RunAgentInput {
+ agentUniversalIdentifier: String!
+ prompt: String!
+}
+
input CreateWebhookInput {
id: UUID
targetUrl: String!
diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts
index eb1c33c4bc..bd7d9ebf4a 100644
--- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts
+++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts
@@ -1762,6 +1762,19 @@ export interface FieldConnection {
__typename: 'FieldConnection'
}
+export interface AppConnection {
+ id: Scalars['ID']
+ providerName: Scalars['String']
+ name: Scalars['String']
+ handle: Scalars['String']
+ visibility: Scalars['String']
+ userWorkspaceId: Scalars['String']
+ accessToken: Scalars['String']
+ scopes: Scalars['String'][]
+ authFailedAt?: Scalars['String']
+ __typename: 'AppConnection'
+}
+
export interface ResendEmailVerificationToken {
success: Scalars['Boolean']
__typename: 'ResendEmailVerificationToken'
@@ -2211,6 +2224,13 @@ export interface AgentMessagePart {
__typename: 'AgentMessagePart'
}
+export interface RunAgentResult {
+ result?: Scalars['JSON']
+ error?: Scalars['String']
+ success: Scalars['Boolean']
+ __typename: 'RunAgentResult'
+}
+
export interface ChannelSyncSuccess {
success: Scalars['Boolean']
__typename: 'ChannelSyncSuccess'
@@ -2607,6 +2627,8 @@ export interface Query {
myMessageChannels: MessageChannel[]
myCalendarChannels: CalendarChannel[]
minimalMetadata: MinimalMetadata
+ appConnections: AppConnection[]
+ appConnection: AppConnection
findWorkspaceAiStats: WorkspaceAiStats
chatThreads: AgentChatThread[]
chatThread: AgentChatThread
@@ -2767,6 +2789,7 @@ export interface Mutation {
upsertRowLevelPermissionPredicates: UpsertRowLevelPermissionPredicatesResult
assignRoleToAgent: Scalars['Boolean']
removeRoleFromAgent: Scalars['Boolean']
+ runAgent: RunAgentResult
createWebhook: Webhook
updateWebhook: Webhook
deleteWebhook: Webhook
@@ -4722,6 +4745,20 @@ export interface FieldConnectionGenqlSelection{
__scalar?: boolean | number
}
+export interface AppConnectionGenqlSelection{
+ id?: boolean | number
+ providerName?: boolean | number
+ name?: boolean | number
+ handle?: boolean | number
+ visibility?: boolean | number
+ userWorkspaceId?: boolean | number
+ accessToken?: boolean | number
+ scopes?: boolean | number
+ authFailedAt?: boolean | number
+ __typename?: boolean | number
+ __scalar?: boolean | number
+}
+
export interface ResendEmailVerificationTokenGenqlSelection{
success?: boolean | number
__typename?: boolean | number
@@ -5226,6 +5263,14 @@ export interface AgentMessagePartGenqlSelection{
__scalar?: boolean | number
}
+export interface RunAgentResultGenqlSelection{
+ result?: boolean | number
+ error?: boolean | number
+ success?: boolean | number
+ __typename?: boolean | number
+ __scalar?: boolean | number
+}
+
export interface ChannelSyncSuccessGenqlSelection{
success?: boolean | number
__typename?: boolean | number
@@ -5646,6 +5691,8 @@ export interface QueryGenqlSelection{
myMessageChannels?: (MessageChannelGenqlSelection & { __args?: {connectedAccountId?: (Scalars['UUID'] | null)} })
myCalendarChannels?: (CalendarChannelGenqlSelection & { __args?: {connectedAccountId?: (Scalars['UUID'] | null)} })
minimalMetadata?: MinimalMetadataGenqlSelection
+ appConnections?: (AppConnectionGenqlSelection & { __args?: {filter?: (ListAppConnectionsInput | null)} })
+ appConnection?: (AppConnectionGenqlSelection & { __args: {id: Scalars['ID']} })
findWorkspaceAiStats?: WorkspaceAiStatsGenqlSelection
chatThreads?: AgentChatThreadGenqlSelection
chatThread?: (AgentChatThreadGenqlSelection & { __args: {id: Scalars['UUID']} })
@@ -5698,6 +5745,8 @@ export interface AgentIdInput {
/** The id of the agent. */
id: Scalars['UUID']}
+export interface ListAppConnectionsInput {providerName?: (Scalars['String'] | null),userWorkspaceId?: (Scalars['String'] | null),visibility?: (Scalars['String'] | null)}
+
export interface EventLogQueryInput {table: EventLogTable,filters?: (EventLogFiltersInput | null),first?: (Scalars['Int'] | null),after?: (Scalars['String'] | null)}
export interface EventLogFiltersInput {eventType?: (Scalars['String'] | null),userWorkspaceId?: (Scalars['String'] | null),dateRange?: (EventLogDateRangeInput | null),recordId?: (Scalars['String'] | null),objectMetadataId?: (Scalars['String'] | null)}
@@ -5827,6 +5876,7 @@ export interface MutationGenqlSelection{
upsertRowLevelPermissionPredicates?: (UpsertRowLevelPermissionPredicatesResultGenqlSelection & { __args: {input: UpsertRowLevelPermissionPredicatesInput} })
assignRoleToAgent?: { __args: {agentId: Scalars['UUID'], roleId: Scalars['UUID']} }
removeRoleFromAgent?: { __args: {agentId: Scalars['UUID']} }
+ runAgent?: (RunAgentResultGenqlSelection & { __args: {input: RunAgentInput} })
createWebhook?: (WebhookGenqlSelection & { __args: {input: CreateWebhookInput} })
updateWebhook?: (WebhookGenqlSelection & { __args: {input: UpdateWebhookInput} })
deleteWebhook?: (WebhookGenqlSelection & { __args: {id: Scalars['UUID']} })
@@ -6184,6 +6234,8 @@ export interface RowLevelPermissionPredicateInput {id?: (Scalars['UUID'] | null)
export interface RowLevelPermissionPredicateGroupInput {id?: (Scalars['UUID'] | null),objectMetadataId: Scalars['UUID'],parentRowLevelPermissionPredicateGroupId?: (Scalars['UUID'] | null),logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator,positionInRowLevelPermissionPredicateGroup?: (Scalars['Float'] | null)}
+export interface RunAgentInput {agentUniversalIdentifier: Scalars['String'],prompt: Scalars['String']}
+
export interface CreateWebhookInput {id?: (Scalars['UUID'] | null),targetUrl: Scalars['String'],operations: Scalars['String'][],description?: (Scalars['String'] | null),secret?: (Scalars['String'] | null)}
export interface UpdateWebhookInput {
@@ -7535,6 +7587,14 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
+ const AppConnection_possibleTypes: string[] = ['AppConnection']
+ export const isAppConnection = (obj?: { __typename?: any } | null): obj is AppConnection => {
+ if (!obj?.__typename) throw new Error('__typename is missing in "isAppConnection"')
+ return AppConnection_possibleTypes.includes(obj.__typename)
+ }
+
+
+
const ResendEmailVerificationToken_possibleTypes: string[] = ['ResendEmailVerificationToken']
export const isResendEmailVerificationToken = (obj?: { __typename?: any } | null): obj is ResendEmailVerificationToken => {
if (!obj?.__typename) throw new Error('__typename is missing in "isResendEmailVerificationToken"')
@@ -7991,6 +8051,14 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
+ const RunAgentResult_possibleTypes: string[] = ['RunAgentResult']
+ export const isRunAgentResult = (obj?: { __typename?: any } | null): obj is RunAgentResult => {
+ if (!obj?.__typename) throw new Error('__typename is missing in "isRunAgentResult"')
+ return RunAgentResult_possibleTypes.includes(obj.__typename)
+ }
+
+
+
const ChannelSyncSuccess_possibleTypes: string[] = ['ChannelSyncSuccess']
export const isChannelSyncSuccess = (obj?: { __typename?: any } | null): obj is ChannelSyncSuccess => {
if (!obj?.__typename) throw new Error('__typename is missing in "isChannelSyncSuccess"')
diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts
index c676ed29bf..1f21251391 100644
--- a/packages/twenty-client-sdk/src/metadata/generated/types.ts
+++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts
@@ -60,27 +60,27 @@ export default {
190,
193,
206,
- 231,
- 294,
- 304,
- 305,
+ 221,
+ 233,
306,
307,
+ 308,
309,
- 310,
311,
312,
313,
314,
315,
- 318,
+ 316,
+ 317,
320,
- 329,
- 336,
- 343,
- 379,
- 461,
- 473
+ 322,
+ 332,
+ 339,
+ 346,
+ 382,
+ 465,
+ 477
],
"types": {
"BillingProductDTO": {
@@ -1895,7 +1895,7 @@ export default {
151
],
"billingEntitlements": [
- 230
+ 232
],
"hasValidEnterpriseKey": [
6
@@ -1996,7 +1996,7 @@ export default {
20
],
"deletedWorkspaceMembers": [
- 229
+ 231
],
"hasPassword": [
6
@@ -2008,7 +2008,7 @@ export default {
17
],
"availableWorkspaces": [
- 228
+ 230
],
"__typename": [
1
@@ -4106,6 +4106,39 @@ export default {
1
]
},
+ "AppConnection": {
+ "id": [
+ 221
+ ],
+ "providerName": [
+ 1
+ ],
+ "name": [
+ 1
+ ],
+ "handle": [
+ 1
+ ],
+ "visibility": [
+ 1
+ ],
+ "userWorkspaceId": [
+ 1
+ ],
+ "accessToken": [
+ 1
+ ],
+ "scopes": [
+ 1
+ ],
+ "authFailedAt": [
+ 1
+ ],
+ "__typename": [
+ 1
+ ]
+ },
+ "ID": {},
"ResendEmailVerificationToken": {
"success": [
6
@@ -4170,7 +4203,7 @@ export default {
180
],
"workspace": [
- 223
+ 225
],
"__typename": [
1
@@ -4239,7 +4272,7 @@ export default {
1
],
"sso": [
- 226
+ 228
],
"__typename": [
1
@@ -4247,10 +4280,10 @@ export default {
},
"AvailableWorkspaces": {
"availableWorkspacesForSignIn": [
- 227
+ 229
],
"availableWorkspacesForSignUp": [
- 227
+ 229
],
"__typename": [
1
@@ -4278,7 +4311,7 @@ export default {
},
"BillingEntitlement": {
"key": [
- 231
+ 233
],
"value": [
6
@@ -4316,7 +4349,7 @@ export default {
1
],
"records": [
- 232
+ 234
],
"__typename": [
1
@@ -4386,10 +4419,10 @@ export default {
},
"AvailableWorkspacesAndAccessTokens": {
"tokens": [
- 240
+ 242
],
"availableWorkspaces": [
- 228
+ 230
],
"__typename": [
1
@@ -4441,7 +4474,7 @@ export default {
32
],
"workspace": [
- 245
+ 247
],
"__typename": [
1
@@ -4490,7 +4523,7 @@ export default {
},
"AuthTokens": {
"tokens": [
- 240
+ 242
],
"__typename": [
1
@@ -4531,7 +4564,7 @@ export default {
32
],
"workspace": [
- 245
+ 247
],
"__typename": [
1
@@ -4553,7 +4586,7 @@ export default {
1
],
"dailyUsage": [
- 256
+ 258
],
"__typename": [
1
@@ -4570,7 +4603,7 @@ export default {
199
],
"timeSeries": [
- 256
+ 258
],
"periodStart": [
4
@@ -4579,7 +4612,7 @@ export default {
4
],
"userDailyUsage": [
- 257
+ 259
],
"__typename": [
1
@@ -4744,7 +4777,7 @@ export default {
1
],
"location": [
- 266
+ 268
],
"__typename": [
1
@@ -4769,13 +4802,13 @@ export default {
},
"PublicImapSmtpCaldavConnectionParameters": {
"IMAP": [
- 268
+ 270
],
"SMTP": [
- 268
+ 270
],
"CALDAV": [
- 268
+ 270
],
"__typename": [
1
@@ -4828,7 +4861,7 @@ export default {
4
],
"connectionParameters": [
- 269
+ 271
],
"__typename": [
1
@@ -4853,13 +4886,13 @@ export default {
},
"ImapSmtpCaldavPublicConnectionParameters": {
"IMAP": [
- 271
+ 273
],
"SMTP": [
- 271
+ 273
],
"CALDAV": [
- 271
+ 273
],
"__typename": [
1
@@ -4879,7 +4912,7 @@ export default {
3
],
"connectionParameters": [
- 272
+ 274
],
"__typename": [
1
@@ -5037,6 +5070,20 @@ export default {
1
]
},
+ "RunAgentResult": {
+ "result": [
+ 15
+ ],
+ "error": [
+ 1
+ ],
+ "success": [
+ 6
+ ],
+ "__typename": [
+ 1
+ ]
+ },
"ChannelSyncSuccess": {
"success": [
6
@@ -5067,7 +5114,7 @@ export default {
1
],
"series": [
- 279
+ 282
],
"xAxisLabel": [
1
@@ -5116,7 +5163,7 @@ export default {
1
],
"data": [
- 281
+ 284
],
"__typename": [
1
@@ -5124,7 +5171,7 @@ export default {
},
"LineChartData": {
"series": [
- 282
+ 285
],
"xAxisLabel": [
1
@@ -5161,7 +5208,7 @@ export default {
},
"PieChartData": {
"data": [
- 284
+ 287
],
"showLegend": [
6
@@ -5255,13 +5302,13 @@ export default {
},
"EventLogQueryResult": {
"records": [
- 288
+ 291
],
"totalCount": [
21
],
"pageInfo": [
- 289
+ 292
],
"__typename": [
1
@@ -5325,7 +5372,7 @@ export default {
1
],
"parts": [
- 277
+ 279
],
"processedAt": [
4
@@ -5339,7 +5386,7 @@ export default {
},
"AgentChatThread": {
"id": [
- 294
+ 221
],
"title": [
1
@@ -5378,7 +5425,6 @@ export default {
1
]
},
- "ID": {},
"AiSystemPromptSection": {
"title": [
1
@@ -5395,7 +5441,7 @@ export default {
},
"AiSystemPromptPreview": {
"sections": [
- 295
+ 297
],
"estimatedTokenCount": [
21
@@ -5471,10 +5517,10 @@ export default {
3
],
"evaluations": [
- 300
+ 302
],
"messages": [
- 292
+ 295
],
"createdAt": [
4
@@ -5505,19 +5551,19 @@ export default {
1
],
"syncStatus": [
- 304
+ 306
],
"syncStage": [
- 305
+ 307
],
"visibility": [
- 306
+ 308
],
"isContactAutoCreationEnabled": [
6
],
"contactAutoCreationPolicy": [
- 307
+ 309
],
"isSyncEnabled": [
6
@@ -5553,22 +5599,22 @@ export default {
3
],
"visibility": [
- 309
+ 311
],
"handle": [
1
],
"type": [
- 310
+ 312
],
"isContactAutoCreationEnabled": [
6
],
"contactAutoCreationPolicy": [
- 311
+ 313
],
"messageFolderImportPolicy": [
- 312
+ 314
],
"excludeNonProfessionalEmails": [
6
@@ -5577,7 +5623,7 @@ export default {
6
],
"pendingGroupEmailsAction": [
- 313
+ 315
],
"isSyncEnabled": [
6
@@ -5586,10 +5632,10 @@ export default {
4
],
"syncStatus": [
- 314
+ 316
],
"syncStage": [
- 315
+ 317
],
"syncStageStartedAt": [
4
@@ -5610,7 +5656,7 @@ export default {
4
],
"connectedAccount": [
- 270
+ 272
],
"__typename": [
1
@@ -5625,7 +5671,7 @@ export default {
"MessageChannelSyncStage": {},
"CreateEmailGroupChannelOutput": {
"messageChannel": [
- 308
+ 310
],
"forwardingAddress": [
1
@@ -5654,7 +5700,7 @@ export default {
1
],
"pendingSyncAction": [
- 318
+ 320
],
"messageChannelId": [
3
@@ -5672,7 +5718,7 @@ export default {
"MessageFolderPendingSyncAction": {},
"CollectionHash": {
"collectionName": [
- 320
+ 322
],
"hash": [
1
@@ -5739,13 +5785,13 @@ export default {
},
"MinimalMetadata": {
"objectMetadataItems": [
- 321
+ 323
],
"views": [
- 322
+ 324
],
"collectionHashes": [
- 319
+ 321
],
"__typename": [
1
@@ -5898,7 +5944,7 @@ export default {
2,
{
"input": [
- 325,
+ 327,
"GetApiKeyInput!"
]
}
@@ -5995,7 +6041,7 @@ export default {
41,
{
"input": [
- 326,
+ 328,
"LogicFunctionIdInput!"
]
}
@@ -6007,7 +6053,7 @@ export default {
15,
{
"input": [
- 326,
+ 328,
"LogicFunctionIdInput!"
]
}
@@ -6016,7 +6062,7 @@ export default {
1,
{
"input": [
- 326,
+ 328,
"LogicFunctionIdInput!"
]
}
@@ -6099,19 +6145,19 @@ export default {
25,
{
"input": [
- 327,
+ 329,
"AgentIdInput!"
]
}
],
"myConnectedAccounts": [
- 270
+ 272
],
"getRoles": [
29
],
"getToolIndex": [
- 276
+ 278
],
"getToolInputSchema": [
15,
@@ -6123,10 +6169,10 @@ export default {
}
],
"webhooks": [
- 275
+ 277
],
"webhook": [
- 275,
+ 277,
{
"id": [
3,
@@ -6174,7 +6220,7 @@ export default {
}
],
"myMessageFolders": [
- 317,
+ 319,
{
"messageChannelId": [
3
@@ -6182,7 +6228,7 @@ export default {
}
],
"myMessageChannels": [
- 308,
+ 310,
{
"connectedAccountId": [
3
@@ -6190,7 +6236,7 @@ export default {
}
],
"myCalendarChannels": [
- 303,
+ 305,
{
"connectedAccountId": [
3
@@ -6198,16 +6244,33 @@ export default {
}
],
"minimalMetadata": [
- 323
+ 325
+ ],
+ "appConnections": [
+ 220,
+ {
+ "filter": [
+ 330
+ ]
+ }
+ ],
+ "appConnection": [
+ 220,
+ {
+ "id": [
+ 221,
+ "ID!"
+ ]
+ }
],
"findWorkspaceAiStats": [
- 302
+ 304
],
"chatThreads": [
- 293
+ 296
],
"chatThread": [
- 293,
+ 296,
{
"id": [
3,
@@ -6216,7 +6279,7 @@ export default {
}
],
"chatMessages": [
- 292,
+ 295,
{
"threadId": [
3,
@@ -6225,7 +6288,7 @@ export default {
}
],
"chatStreamCatchupChunks": [
- 297,
+ 299,
{
"threadId": [
3,
@@ -6234,13 +6297,13 @@ export default {
}
],
"getAiSystemPromptPreview": [
- 296
+ 298
],
"skills": [
- 291
+ 294
],
"skill": [
- 291,
+ 294,
{
"id": [
3,
@@ -6249,7 +6312,7 @@ export default {
}
],
"agentTurns": [
- 301,
+ 303,
{
"agentId": [
3,
@@ -6258,7 +6321,7 @@ export default {
}
],
"checkUserExists": [
- 253,
+ 255,
{
"email": [
1,
@@ -6270,7 +6333,7 @@ export default {
}
],
"checkWorkspaceInviteHashIsValid": [
- 254,
+ 256,
{
"inviteHash": [
1,
@@ -6288,7 +6351,7 @@ export default {
}
],
"validatePasswordResetToken": [
- 248,
+ 250,
{
"passwordResetToken": [
1,
@@ -6391,46 +6454,46 @@ export default {
}
],
"getSSOIdentityProviders": [
- 224
+ 226
],
"eventLogs": [
- 290,
+ 293,
{
"input": [
- 328,
+ 331,
"EventLogQueryInput!"
]
}
],
"pieChartData": [
- 285,
+ 288,
{
"input": [
- 332,
+ 335,
"PieChartDataInput!"
]
}
],
"lineChartData": [
- 283,
+ 286,
{
"input": [
- 333,
+ 336,
"LineChartDataInput!"
]
}
],
"barChartData": [
- 280,
+ 283,
{
"input": [
- 334,
+ 337,
"BarChartDataInput!"
]
}
],
"getConnectedImapSmtpCaldavAccount": [
- 273,
+ 275,
{
"id": [
3,
@@ -6439,7 +6502,7 @@ export default {
}
],
"getAutoCompleteAddress": [
- 265,
+ 267,
{
"address": [
1,
@@ -6458,7 +6521,7 @@ export default {
}
],
"getAddressDetails": [
- 267,
+ 269,
{
"placeId": [
1,
@@ -6471,21 +6534,21 @@ export default {
}
],
"getUsageAnalytics": [
- 258,
+ 260,
{
"input": [
- 335
+ 338
]
}
],
"findManyPublicDomains": [
- 264
+ 266
],
"findManyMarketplaceApps": [
- 262
+ 264
],
"findMarketplaceAppDetail": [
- 263,
+ 265,
{
"universalIdentifier": [
1,
@@ -6507,7 +6570,7 @@ export default {
},
"LogicFunctionIdInput": {
"id": [
- 294
+ 221
],
"__typename": [
1
@@ -6521,12 +6584,26 @@ export default {
1
]
},
+ "ListAppConnectionsInput": {
+ "providerName": [
+ 1
+ ],
+ "userWorkspaceId": [
+ 1
+ ],
+ "visibility": [
+ 1
+ ],
+ "__typename": [
+ 1
+ ]
+ },
"EventLogQueryInput": {
"table": [
- 329
+ 332
],
"filters": [
- 330
+ 333
],
"first": [
21
@@ -6547,7 +6624,7 @@ export default {
1
],
"dateRange": [
- 331
+ 334
],
"recordId": [
1
@@ -6614,7 +6691,7 @@ export default {
1
],
"operationTypes": [
- 336
+ 339
],
"__typename": [
1
@@ -6626,7 +6703,7 @@ export default {
6,
{
"input": [
- 338,
+ 341,
"AddQuerySubscriptionInput!"
]
}
@@ -6635,7 +6712,7 @@ export default {
6,
{
"input": [
- 339,
+ 342,
"RemoveQueryFromEventStreamInput!"
]
}
@@ -6644,7 +6721,7 @@ export default {
162,
{
"inputs": [
- 340,
+ 343,
"[CreateNavigationMenuItemInput!]!"
]
}
@@ -6653,7 +6730,7 @@ export default {
162,
{
"input": [
- 340,
+ 343,
"CreateNavigationMenuItemInput!"
]
}
@@ -6662,7 +6739,7 @@ export default {
162,
{
"inputs": [
- 341,
+ 344,
"[UpdateOneNavigationMenuItemInput!]!"
]
}
@@ -6671,7 +6748,7 @@ export default {
162,
{
"input": [
- 341,
+ 344,
"UpdateOneNavigationMenuItemInput!"
]
}
@@ -6698,7 +6775,7 @@ export default {
136,
{
"file": [
- 343,
+ 346,
"Upload!"
]
}
@@ -6719,7 +6796,7 @@ export default {
136,
{
"file": [
- 343,
+ 346,
"Upload!"
]
}
@@ -6728,7 +6805,7 @@ export default {
136,
{
"file": [
- 343,
+ 346,
"Upload!"
]
}
@@ -6737,7 +6814,7 @@ export default {
136,
{
"file": [
- 343,
+ 346,
"Upload!"
]
}
@@ -6746,7 +6823,7 @@ export default {
136,
{
"file": [
- 343,
+ 346,
"Upload!"
]
}
@@ -6755,7 +6832,7 @@ export default {
136,
{
"file": [
- 343,
+ 346,
"Upload!"
],
"fieldMetadataId": [
@@ -6768,7 +6845,7 @@ export default {
136,
{
"file": [
- 343,
+ 346,
"Upload!"
],
"fieldMetadataUniversalIdentifier": [
@@ -6781,7 +6858,7 @@ export default {
62,
{
"input": [
- 344,
+ 347,
"CreateViewFilterGroupInput!"
]
}
@@ -6794,7 +6871,7 @@ export default {
"String!"
],
"input": [
- 345,
+ 348,
"UpdateViewFilterGroupInput!"
]
}
@@ -6821,7 +6898,7 @@ export default {
64,
{
"input": [
- 346,
+ 349,
"CreateViewFilterInput!"
]
}
@@ -6830,7 +6907,7 @@ export default {
64,
{
"input": [
- 347,
+ 350,
"UpdateViewFilterInput!"
]
}
@@ -6839,7 +6916,7 @@ export default {
64,
{
"input": [
- 349,
+ 352,
"DeleteViewFilterInput!"
]
}
@@ -6848,7 +6925,7 @@ export default {
64,
{
"input": [
- 350,
+ 353,
"DestroyViewFilterInput!"
]
}
@@ -6857,7 +6934,7 @@ export default {
70,
{
"input": [
- 351,
+ 354,
"CreateViewInput!"
]
}
@@ -6870,7 +6947,7 @@ export default {
"String!"
],
"input": [
- 352,
+ 355,
"UpdateViewInput!"
]
}
@@ -6897,7 +6974,7 @@ export default {
70,
{
"input": [
- 353,
+ 356,
"UpsertViewWidgetInput!"
]
}
@@ -6906,7 +6983,7 @@ export default {
67,
{
"input": [
- 358,
+ 361,
"CreateViewSortInput!"
]
}
@@ -6915,7 +6992,7 @@ export default {
67,
{
"input": [
- 359,
+ 362,
"UpdateViewSortInput!"
]
}
@@ -6924,7 +7001,7 @@ export default {
6,
{
"input": [
- 361,
+ 364,
"DeleteViewSortInput!"
]
}
@@ -6933,7 +7010,7 @@ export default {
6,
{
"input": [
- 362,
+ 365,
"DestroyViewSortInput!"
]
}
@@ -6942,7 +7019,7 @@ export default {
60,
{
"input": [
- 363,
+ 366,
"UpdateViewFieldInput!"
]
}
@@ -6951,7 +7028,7 @@ export default {
60,
{
"input": [
- 365,
+ 368,
"CreateViewFieldInput!"
]
}
@@ -6960,7 +7037,7 @@ export default {
60,
{
"inputs": [
- 365,
+ 368,
"[CreateViewFieldInput!]!"
]
}
@@ -6969,7 +7046,7 @@ export default {
60,
{
"input": [
- 366,
+ 369,
"DeleteViewFieldInput!"
]
}
@@ -6978,7 +7055,7 @@ export default {
60,
{
"input": [
- 367,
+ 370,
"DestroyViewFieldInput!"
]
}
@@ -6987,7 +7064,7 @@ export default {
69,
{
"input": [
- 368,
+ 371,
"UpdateViewFieldGroupInput!"
]
}
@@ -6996,7 +7073,7 @@ export default {
69,
{
"input": [
- 370,
+ 373,
"CreateViewFieldGroupInput!"
]
}
@@ -7005,7 +7082,7 @@ export default {
69,
{
"inputs": [
- 370,
+ 373,
"[CreateViewFieldGroupInput!]!"
]
}
@@ -7014,7 +7091,7 @@ export default {
69,
{
"input": [
- 371,
+ 374,
"DeleteViewFieldGroupInput!"
]
}
@@ -7023,7 +7100,7 @@ export default {
69,
{
"input": [
- 372,
+ 375,
"DestroyViewFieldGroupInput!"
]
}
@@ -7032,7 +7109,7 @@ export default {
70,
{
"input": [
- 373,
+ 376,
"UpsertFieldsWidgetInput!"
]
}
@@ -7041,7 +7118,7 @@ export default {
2,
{
"input": [
- 376,
+ 379,
"CreateApiKeyInput!"
]
}
@@ -7050,7 +7127,7 @@ export default {
2,
{
"input": [
- 377,
+ 380,
"UpdateApiKeyInput!"
]
}
@@ -7059,7 +7136,7 @@ export default {
2,
{
"input": [
- 378,
+ 381,
"RevokeApiKeyInput!"
]
}
@@ -7101,7 +7178,7 @@ export default {
129,
{
"type": [
- 379,
+ 382,
"AnalyticsType!"
],
"name": [
@@ -7202,7 +7279,7 @@ export default {
135,
{
"input": [
- 380,
+ 383,
"CreateApprovedAccessDomainInput!"
]
}
@@ -7211,7 +7288,7 @@ export default {
6,
{
"input": [
- 381,
+ 384,
"DeleteApprovedAccessDomainInput!"
]
}
@@ -7220,7 +7297,7 @@ export default {
135,
{
"input": [
- 382,
+ 385,
"ValidateApprovedAccessDomainInput!"
]
}
@@ -7229,7 +7306,7 @@ export default {
122,
{
"input": [
- 383,
+ 386,
"CreatePageLayoutTabInput!"
]
}
@@ -7242,7 +7319,7 @@ export default {
"String!"
],
"input": [
- 384,
+ 387,
"UpdatePageLayoutTabInput!"
]
}
@@ -7260,7 +7337,7 @@ export default {
123,
{
"input": [
- 385,
+ 388,
"CreatePageLayoutInput!"
]
}
@@ -7273,7 +7350,7 @@ export default {
"String!"
],
"input": [
- 386,
+ 389,
"UpdatePageLayoutInput!"
]
}
@@ -7295,7 +7372,7 @@ export default {
"String!"
],
"input": [
- 387,
+ 390,
"UpdatePageLayoutWithTabsInput!"
]
}
@@ -7362,7 +7439,7 @@ export default {
134,
{
"input": [
- 391,
+ 394,
"SendEmailViaDomainInput!"
]
}
@@ -7388,7 +7465,7 @@ export default {
85,
{
"input": [
- 392,
+ 395,
"CreatePageLayoutWidgetInput!"
]
}
@@ -7401,7 +7478,7 @@ export default {
"String!"
],
"input": [
- 393,
+ 396,
"UpdatePageLayoutWidgetInput!"
]
}
@@ -7419,7 +7496,7 @@ export default {
41,
{
"input": [
- 326,
+ 328,
"LogicFunctionIdInput!"
]
}
@@ -7428,7 +7505,7 @@ export default {
41,
{
"input": [
- 394,
+ 397,
"CreateLogicFunctionFromSourceInput!"
]
}
@@ -7437,7 +7514,7 @@ export default {
171,
{
"input": [
- 395,
+ 398,
"ExecuteOneLogicFunctionInput!"
]
}
@@ -7446,7 +7523,7 @@ export default {
6,
{
"input": [
- 396,
+ 399,
"UpdateLogicFunctionFromSourceInput!"
]
}
@@ -7455,7 +7532,7 @@ export default {
35,
{
"input": [
- 398,
+ 401,
"CreateCommandMenuItemInput!"
]
}
@@ -7464,7 +7541,7 @@ export default {
35,
{
"input": [
- 399,
+ 402,
"UpdateCommandMenuItemInput!"
]
}
@@ -7482,7 +7559,7 @@ export default {
34,
{
"input": [
- 400,
+ 403,
"CreateFrontComponentInput!"
]
}
@@ -7491,7 +7568,7 @@ export default {
34,
{
"input": [
- 401,
+ 404,
"UpdateFrontComponentInput!"
]
}
@@ -7509,7 +7586,7 @@ export default {
56,
{
"input": [
- 403,
+ 406,
"CreateOneObjectInput!"
]
}
@@ -7518,7 +7595,7 @@ export default {
56,
{
"input": [
- 405,
+ 408,
"DeleteOneObjectInput!"
]
}
@@ -7527,7 +7604,7 @@ export default {
56,
{
"input": [
- 406,
+ 409,
"UpdateOneObjectInput!"
]
}
@@ -7536,7 +7613,7 @@ export default {
47,
{
"input": [
- 408,
+ 411,
"CreateOneIndexInput!"
]
}
@@ -7545,7 +7622,7 @@ export default {
47,
{
"input": [
- 411,
+ 414,
"DeleteOneIndexInput!"
]
}
@@ -7554,7 +7631,7 @@ export default {
25,
{
"input": [
- 412,
+ 415,
"CreateAgentInput!"
]
}
@@ -7563,7 +7640,7 @@ export default {
25,
{
"input": [
- 413,
+ 416,
"UpdateAgentInput!"
]
}
@@ -7572,13 +7649,13 @@ export default {
25,
{
"input": [
- 327,
+ 329,
"AgentIdInput!"
]
}
],
"deleteConnectedAccount": [
- 270,
+ 272,
{
"id": [
3,
@@ -7603,7 +7680,7 @@ export default {
29,
{
"createRoleInput": [
- 414,
+ 417,
"CreateRoleInput!"
]
}
@@ -7612,7 +7689,7 @@ export default {
29,
{
"updateRoleInput": [
- 415,
+ 418,
"UpdateRoleInput!"
]
}
@@ -7630,7 +7707,7 @@ export default {
16,
{
"upsertObjectPermissionsInput": [
- 417,
+ 420,
"UpsertObjectPermissionsInput!"
]
}
@@ -7639,7 +7716,7 @@ export default {
27,
{
"upsertPermissionFlagsInput": [
- 419,
+ 422,
"UpsertPermissionFlagsInput!"
]
}
@@ -7648,16 +7725,16 @@ export default {
26,
{
"upsertFieldPermissionsInput": [
- 420,
+ 423,
"UpsertFieldPermissionsInput!"
]
}
],
"upsertRowLevelPermissionPredicates": [
- 234,
+ 236,
{
"input": [
- 422,
+ 425,
"UpsertRowLevelPermissionPredicatesInput!"
]
}
@@ -7684,26 +7761,35 @@ export default {
]
}
],
- "createWebhook": [
- 275,
+ "runAgent": [
+ 280,
{
"input": [
- 425,
+ 428,
+ "RunAgentInput!"
+ ]
+ }
+ ],
+ "createWebhook": [
+ 277,
+ {
+ "input": [
+ 429,
"CreateWebhookInput!"
]
}
],
"updateWebhook": [
- 275,
+ 277,
{
"input": [
- 426,
+ 430,
"UpdateWebhookInput!"
]
}
],
"deleteWebhook": [
- 275,
+ 277,
{
"id": [
3,
@@ -7715,7 +7801,7 @@ export default {
44,
{
"input": [
- 428,
+ 432,
"CreateOneFieldMetadataInput!"
]
}
@@ -7724,7 +7810,7 @@ export default {
44,
{
"input": [
- 430,
+ 434,
"UpdateOneFieldMetadataInput!"
]
}
@@ -7733,7 +7819,7 @@ export default {
44,
{
"input": [
- 432,
+ 436,
"DeleteOneFieldInput!"
]
}
@@ -7742,7 +7828,7 @@ export default {
66,
{
"input": [
- 433,
+ 437,
"CreateViewGroupInput!"
]
}
@@ -7751,7 +7837,7 @@ export default {
66,
{
"inputs": [
- 433,
+ 437,
"[CreateViewGroupInput!]!"
]
}
@@ -7760,7 +7846,7 @@ export default {
66,
{
"input": [
- 434,
+ 438,
"UpdateViewGroupInput!"
]
}
@@ -7769,7 +7855,7 @@ export default {
66,
{
"inputs": [
- 434,
+ 438,
"[UpdateViewGroupInput!]!"
]
}
@@ -7778,7 +7864,7 @@ export default {
66,
{
"input": [
- 436,
+ 440,
"DeleteViewGroupInput!"
]
}
@@ -7787,49 +7873,49 @@ export default {
66,
{
"input": [
- 437,
+ 441,
"DestroyViewGroupInput!"
]
}
],
"updateMessageFolder": [
- 317,
+ 319,
{
"input": [
- 438,
+ 442,
"UpdateMessageFolderInput!"
]
}
],
"updateMessageFolders": [
- 317,
+ 319,
{
"input": [
- 440,
+ 444,
"UpdateMessageFoldersInput!"
]
}
],
"updateMessageChannel": [
- 308,
+ 310,
{
"input": [
- 441,
+ 445,
"UpdateMessageChannelInput!"
]
}
],
"createEmailGroupChannel": [
- 316,
+ 318,
{
"input": [
- 443,
+ 447,
"CreateEmailGroupChannelInput!"
]
}
],
"deleteEmailGroupChannel": [
- 308,
+ 310,
{
"id": [
3,
@@ -7838,19 +7924,19 @@ export default {
}
],
"updateCalendarChannel": [
- 303,
+ 305,
{
"input": [
- 444,
+ 448,
"UpdateCalendarChannelInput!"
]
}
],
"createChatThread": [
- 293
+ 296
],
"sendChatMessage": [
- 298,
+ 300,
{
"threadId": [
3,
@@ -7871,7 +7957,7 @@ export default {
1
],
"fileAttachments": [
- 446,
+ 450,
"[FileAttachmentInput!]"
]
}
@@ -7886,7 +7972,7 @@ export default {
}
],
"renameChatThread": [
- 293,
+ 296,
{
"id": [
3,
@@ -7899,7 +7985,7 @@ export default {
}
],
"archiveChatThread": [
- 293,
+ 296,
{
"id": [
3,
@@ -7908,7 +7994,7 @@ export default {
}
],
"unarchiveChatThread": [
- 293,
+ 296,
{
"id": [
3,
@@ -7935,25 +8021,25 @@ export default {
}
],
"createSkill": [
- 291,
+ 294,
{
"input": [
- 447,
+ 451,
"CreateSkillInput!"
]
}
],
"updateSkill": [
- 291,
+ 294,
{
"input": [
- 448,
+ 452,
"UpdateSkillInput!"
]
}
],
"deleteSkill": [
- 291,
+ 294,
{
"id": [
3,
@@ -7962,7 +8048,7 @@ export default {
}
],
"activateSkill": [
- 291,
+ 294,
{
"id": [
3,
@@ -7971,7 +8057,7 @@ export default {
}
],
"deactivateSkill": [
- 291,
+ 294,
{
"id": [
3,
@@ -7980,7 +8066,7 @@ export default {
}
],
"evaluateAgentTurn": [
- 300,
+ 302,
{
"turnId": [
3,
@@ -7989,7 +8075,7 @@ export default {
}
],
"runEvaluationInput": [
- 301,
+ 303,
{
"agentId": [
3,
@@ -8002,16 +8088,16 @@ export default {
}
],
"getAuthorizationUrlForSSO": [
- 243,
+ 245,
{
"input": [
- 449,
+ 453,
"GetAuthorizationUrlForSSOInput!"
]
}
],
"getLoginTokenFromCredentials": [
- 252,
+ 254,
{
"email": [
1,
@@ -8037,7 +8123,7 @@ export default {
}
],
"signIn": [
- 241,
+ 243,
{
"email": [
1,
@@ -8059,7 +8145,7 @@ export default {
}
],
"verifyEmailAndGetLoginToken": [
- 249,
+ 251,
{
"emailVerificationToken": [
1,
@@ -8079,7 +8165,7 @@ export default {
}
],
"verifyEmailAndGetWorkspaceAgnosticToken": [
- 241,
+ 243,
{
"emailVerificationToken": [
1,
@@ -8095,7 +8181,7 @@ export default {
}
],
"getAuthTokensFromOTP": [
- 251,
+ 253,
{
"otp": [
1,
@@ -8115,7 +8201,7 @@ export default {
}
],
"signUp": [
- 241,
+ 243,
{
"email": [
1,
@@ -8137,7 +8223,7 @@ export default {
}
],
"signUpInWorkspace": [
- 246,
+ 248,
{
"email": [
1,
@@ -8168,13 +8254,13 @@ export default {
}
],
"signUpInNewWorkspace": [
- 246
+ 248
],
"generateTransientToken": [
- 247
+ 249
],
"getAuthTokensFromLoginToken": [
- 251,
+ 253,
{
"loginToken": [
1,
@@ -8187,7 +8273,7 @@ export default {
}
],
"authorizeApp": [
- 239,
+ 241,
{
"clientId": [
1,
@@ -8209,7 +8295,7 @@ export default {
}
],
"renewToken": [
- 251,
+ 253,
{
"appToken": [
1,
@@ -8218,7 +8304,7 @@ export default {
}
],
"generateApiKeyToken": [
- 250,
+ 252,
{
"apiKeyId": [
3,
@@ -8234,7 +8320,7 @@ export default {
32
],
"emailPasswordResetLink": [
- 242,
+ 244,
{
"email": [
1,
@@ -8246,7 +8332,7 @@ export default {
}
],
"updatePasswordViaResetToken": [
- 244,
+ 246,
{
"passwordResetToken": [
1,
@@ -8262,7 +8348,7 @@ export default {
202,
{
"input": [
- 450,
+ 454,
"CreateApplicationRegistrationInput!"
]
}
@@ -8271,7 +8357,7 @@ export default {
7,
{
"input": [
- 451,
+ 455,
"UpdateApplicationRegistrationInput!"
]
}
@@ -8298,7 +8384,7 @@ export default {
5,
{
"input": [
- 453,
+ 457,
"CreateApplicationRegistrationVariableInput!"
]
}
@@ -8307,7 +8393,7 @@ export default {
5,
{
"input": [
- 454,
+ 458,
"UpdateApplicationRegistrationVariableInput!"
]
}
@@ -8325,7 +8411,7 @@ export default {
7,
{
"file": [
- 343,
+ 346,
"Upload!"
],
"universalIdentifier": [
@@ -8347,7 +8433,7 @@ export default {
}
],
"initiateOTPProvisioning": [
- 237,
+ 239,
{
"loginToken": [
1,
@@ -8360,10 +8446,10 @@ export default {
}
],
"initiateOTPProvisioningForAuthenticatedUser": [
- 237
+ 239
],
"deleteTwoFactorAuthenticationMethod": [
- 236,
+ 238,
{
"twoFactorAuthenticationMethodId": [
3,
@@ -8372,7 +8458,7 @@ export default {
}
],
"verifyTwoFactorAuthenticationMethodForAuthenticatedUser": [
- 238,
+ 240,
{
"otp": [
1,
@@ -8396,7 +8482,7 @@ export default {
6,
{
"input": [
- 456,
+ 460,
"UpdateWorkspaceMemberSettingsInput!"
]
}
@@ -8414,7 +8500,7 @@ export default {
}
],
"resendEmailVerificationToken": [
- 220,
+ 222,
{
"email": [
1,
@@ -8430,7 +8516,7 @@ export default {
76,
{
"data": [
- 457,
+ 461,
"ActivateWorkspaceInput!"
]
}
@@ -8439,7 +8525,7 @@ export default {
76,
{
"data": [
- 458,
+ 462,
"UpdateWorkspaceInput!"
]
}
@@ -8448,13 +8534,13 @@ export default {
76
],
"checkCustomDomainValidRecords": [
- 233
+ 235
],
"runWorkspaceMigration": [
6,
{
"workspaceMigration": [
- 459,
+ 463,
"WorkspaceMigrationInput!"
]
}
@@ -8469,43 +8555,43 @@ export default {
}
],
"createOIDCIdentityProvider": [
- 225,
+ 227,
{
"input": [
- 462,
+ 466,
"SetupOIDCSsoInput!"
]
}
],
"createSAMLIdentityProvider": [
- 225,
+ 227,
{
"input": [
- 463,
+ 467,
"SetupSAMLSsoInput!"
]
}
],
"deleteSSOIdentityProvider": [
- 221,
+ 223,
{
"input": [
- 464,
+ 468,
"DeleteSsoInput!"
]
}
],
"editSSOIdentityProvider": [
- 222,
+ 224,
{
"input": [
- 465,
+ 469,
"EditSsoInput!"
]
}
],
"duplicateDashboard": [
- 286,
+ 289,
{
"id": [
3,
@@ -8514,7 +8600,7 @@ export default {
}
],
"impersonate": [
- 255,
+ 257,
{
"userId": [
3,
@@ -8527,16 +8613,16 @@ export default {
}
],
"sendEmail": [
- 287,
+ 290,
{
"input": [
- 466,
+ 470,
"SendEmailInput!"
]
}
],
"startChannelSync": [
- 278,
+ 281,
{
"connectedAccountId": [
3,
@@ -8545,14 +8631,14 @@ export default {
}
],
"saveImapSmtpCaldavAccount": [
- 274,
+ 276,
{
"handle": [
1,
"String!"
],
"connectionParameters": [
- 468,
+ 472,
"EmailAccountConnectionParameters!"
],
"id": [
@@ -8564,13 +8650,13 @@ export default {
173,
{
"input": [
- 470,
+ 474,
"UpdateLabPublicFeatureFlagInput!"
]
}
],
"createPublicDomain": [
- 264,
+ 266,
{
"domain": [
1,
@@ -8582,7 +8668,7 @@ export default {
}
],
"updatePublicDomain": [
- 264,
+ 266,
{
"domain": [
1,
@@ -8603,7 +8689,7 @@ export default {
}
],
"checkPublicDomainValidRecords": [
- 233,
+ 235,
{
"domain": [
1,
@@ -8615,7 +8701,7 @@ export default {
78,
{
"input": [
- 471,
+ 475,
"CreateOneAppTokenInput!"
]
}
@@ -8648,7 +8734,7 @@ export default {
6
],
"createDevelopmentApplication": [
- 259,
+ 261,
{
"universalIdentifier": [
1,
@@ -8670,7 +8756,7 @@ export default {
}
],
"syncApplication": [
- 260,
+ 262,
{
"manifest": [
15,
@@ -8679,10 +8765,10 @@ export default {
}
],
"uploadApplicationFile": [
- 261,
+ 263,
{
"file": [
- 343,
+ 346,
"Upload!"
],
"applicationUniversalIdentifier": [
@@ -8690,7 +8776,7 @@ export default {
"String!"
],
"fileFolder": [
- 473,
+ 477,
"FileFolder!"
],
"filePath": [
@@ -8799,7 +8885,7 @@ export default {
3
],
"update": [
- 342
+ 345
],
"__typename": [
1
@@ -8909,7 +8995,7 @@ export default {
3
],
"update": [
- 348
+ 351
],
"__typename": [
1
@@ -9068,16 +9154,16 @@ export default {
3
],
"viewFields": [
- 354
+ 357
],
"viewFilters": [
- 355
+ 358
],
"viewFilterGroups": [
- 356
+ 359
],
"viewSorts": [
- 357
+ 360
],
"__typename": [
1
@@ -9188,7 +9274,7 @@ export default {
3
],
"update": [
- 360
+ 363
],
"__typename": [
1
@@ -9226,7 +9312,7 @@ export default {
3
],
"update": [
- 364
+ 367
],
"__typename": [
1
@@ -9302,7 +9388,7 @@ export default {
3
],
"update": [
- 369
+ 372
],
"__typename": [
1
@@ -9366,10 +9452,10 @@ export default {
3
],
"groups": [
- 374
+ 377
],
"fields": [
- 375
+ 378
],
"__typename": [
1
@@ -9389,7 +9475,7 @@ export default {
6
],
"fields": [
- 375
+ 378
],
"__typename": [
1
@@ -9558,7 +9644,7 @@ export default {
3
],
"tabs": [
- 388
+ 391
],
"__typename": [
1
@@ -9581,7 +9667,7 @@ export default {
89
],
"widgets": [
- 389
+ 392
],
"__typename": [
1
@@ -9604,7 +9690,7 @@ export default {
3
],
"gridPosition": [
- 390
+ 393
],
"position": [
15
@@ -9685,7 +9771,7 @@ export default {
3
],
"gridPosition": [
- 390
+ 393
],
"position": [
15
@@ -9711,7 +9797,7 @@ export default {
3
],
"gridPosition": [
- 390
+ 393
],
"position": [
15
@@ -9783,7 +9869,7 @@ export default {
3
],
"update": [
- 397
+ 400
],
"__typename": [
1
@@ -9943,7 +10029,7 @@ export default {
3
],
"update": [
- 402
+ 405
],
"__typename": [
1
@@ -9962,7 +10048,7 @@ export default {
},
"CreateOneObjectInput": {
"object": [
- 404
+ 407
],
"__typename": [
1
@@ -10022,7 +10108,7 @@ export default {
},
"UpdateOneObjectInput": {
"update": [
- 407
+ 410
],
"id": [
3
@@ -10077,7 +10163,7 @@ export default {
},
"CreateOneIndexInput": {
"index": [
- 409
+ 412
],
"__typename": [
1
@@ -10088,7 +10174,7 @@ export default {
3
],
"fields": [
- 410
+ 413
],
"indexType": [
48
@@ -10235,7 +10321,7 @@ export default {
},
"UpdateRoleInput": {
"update": [
- 416
+ 419
],
"id": [
3
@@ -10290,7 +10376,7 @@ export default {
3
],
"objectPermissions": [
- 418
+ 421
],
"__typename": [
1
@@ -10332,7 +10418,7 @@ export default {
3
],
"fieldPermissions": [
- 421
+ 424
],
"__typename": [
1
@@ -10363,10 +10449,10 @@ export default {
3
],
"predicates": [
- 423
+ 426
],
"predicateGroups": [
- 424
+ 427
],
"__typename": [
1
@@ -10424,6 +10510,17 @@ export default {
1
]
},
+ "RunAgentInput": {
+ "agentUniversalIdentifier": [
+ 1
+ ],
+ "prompt": [
+ 1
+ ],
+ "__typename": [
+ 1
+ ]
+ },
"CreateWebhookInput": {
"id": [
3
@@ -10449,7 +10546,7 @@ export default {
3
],
"update": [
- 427
+ 431
],
"__typename": [
1
@@ -10474,7 +10571,7 @@ export default {
},
"CreateOneFieldMetadataInput": {
"field": [
- 429
+ 433
],
"__typename": [
1
@@ -10547,7 +10644,7 @@ export default {
3
],
"update": [
- 431
+ 435
],
"__typename": [
1
@@ -10639,7 +10736,7 @@ export default {
3
],
"update": [
- 435
+ 439
],
"__typename": [
1
@@ -10683,7 +10780,7 @@ export default {
3
],
"update": [
- 439
+ 443
],
"__typename": [
1
@@ -10702,7 +10799,7 @@ export default {
3
],
"update": [
- 439
+ 443
],
"__typename": [
1
@@ -10713,7 +10810,7 @@ export default {
3
],
"update": [
- 442
+ 446
],
"__typename": [
1
@@ -10721,16 +10818,16 @@ export default {
},
"UpdateMessageChannelInputUpdates": {
"visibility": [
- 309
+ 311
],
"isContactAutoCreationEnabled": [
6
],
"contactAutoCreationPolicy": [
- 311
+ 313
],
"messageFolderImportPolicy": [
- 312
+ 314
],
"isSyncEnabled": [
6
@@ -10758,7 +10855,7 @@ export default {
3
],
"update": [
- 445
+ 449
],
"__typename": [
1
@@ -10766,13 +10863,13 @@ export default {
},
"UpdateCalendarChannelInputUpdates": {
"visibility": [
- 306
+ 308
],
"isContactAutoCreationEnabled": [
6
],
"contactAutoCreationPolicy": [
- 307
+ 309
],
"isSyncEnabled": [
6
@@ -10874,7 +10971,7 @@ export default {
1
],
"update": [
- 452
+ 456
],
"__typename": [
1
@@ -10922,7 +11019,7 @@ export default {
1
],
"update": [
- 455
+ 459
],
"__typename": [
1
@@ -11040,7 +11137,7 @@ export default {
},
"WorkspaceMigrationInput": {
"actions": [
- 460
+ 464
],
"__typename": [
1
@@ -11048,10 +11145,10 @@ export default {
},
"WorkspaceMigrationDeleteActionInput": {
"type": [
- 461
+ 465
],
"metadataName": [
- 320
+ 322
],
"universalIdentifier": [
1
@@ -11143,7 +11240,7 @@ export default {
1
],
"files": [
- 467
+ 471
],
"__typename": [
1
@@ -11162,13 +11259,13 @@ export default {
},
"EmailAccountConnectionParameters": {
"IMAP": [
- 469
+ 473
],
"SMTP": [
- 469
+ 473
],
"CALDAV": [
- 469
+ 473
],
"__typename": [
1
@@ -11207,7 +11304,7 @@ export default {
},
"CreateOneAppTokenInput": {
"appToken": [
- 472
+ 476
],
"__typename": [
1
@@ -11233,16 +11330,16 @@ export default {
}
],
"logicFunctionLogs": [
- 235,
+ 237,
{
"input": [
- 475,
+ 479,
"LogicFunctionLogsInput!"
]
}
],
"onAgentChatEvent": [
- 299,
+ 301,
{
"threadId": [
3,
diff --git a/packages/twenty-docs/developers/extend/apps/config/roles.mdx b/packages/twenty-docs/developers/extend/apps/config/roles.mdx
index 811589ef64..a780600a7e 100644
--- a/packages/twenty-docs/developers/extend/apps/config/roles.mdx
+++ b/packages/twenty-docs/developers/extend/apps/config/roles.mdx
@@ -9,8 +9,8 @@ A **role** is a permission set: which objects an app can read or write, which fi
```ts src/roles/restricted-company-role.ts
import {
defineRole,
- PermissionFlag,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
+ SystemPermissionFlag,
} from 'twenty-sdk/define';
export default defineRole({
@@ -46,7 +46,7 @@ export default defineRole({
canUpdateFieldValue: false,
},
],
- permissionFlags: [PermissionFlag.APPLICATIONS],
+ permissionFlagUniversalIdentifiers: [SystemPermissionFlag.APPLICATIONS],
});
```
@@ -55,7 +55,7 @@ export default defineRole({
When you scaffold a new app, the CLI creates a default role file declared with `defineApplicationRole()`:
```ts src/roles/default-role.ts
-import { defineApplicationRole, PermissionFlag } from 'twenty-sdk/define';
+import { defineApplicationRole } from 'twenty-sdk/define';
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
'b648f87b-1d26-4961-b974-0908fd991061';
@@ -74,7 +74,7 @@ export default defineApplicationRole({
canBeAssignedToApiKeys: false,
objectPermissions: [],
fieldPermissions: [],
- permissionFlags: [],
+ permissionFlagUniversalIdentifiers: [],
});
```
@@ -90,5 +90,5 @@ Notes:
- Start from the scaffolded role, then progressively restrict it — the default grants broad read access, which is rarely what you want in production.
- Replace `objectPermissions` and `fieldPermissions` with the exact objects and fields your functions actually need.
-- `permissionFlags` control access to platform-level capabilities. Keep them minimal.
+- `permissionFlagUniversalIdentifiers` control access to platform-level capabilities. Keep them minimal.
- See a working example: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
diff --git a/packages/twenty-docs/developers/extend/apps/logic/skills-and-agents.mdx b/packages/twenty-docs/developers/extend/apps/logic/skills-and-agents.mdx
index cd0a969b77..b6e7a0b781 100644
--- a/packages/twenty-docs/developers/extend/apps/logic/skills-and-agents.mdx
+++ b/packages/twenty-docs/developers/extend/apps/logic/skills-and-agents.mdx
@@ -64,6 +64,86 @@ Key points:
- `description` (optional) provides context about what the agent does.
- `icon` (optional) sets the icon displayed in the UI.
- `modelId` (optional) overrides the default AI model used by the agent.
+- `responseFormat` (optional) controls the shape of the agent's output. Defaults to `{ type: 'text' }` for free-form text. Use `{ type: 'json', schema }` to force structured JSON output.
+
+By default an agent returns free-form text. To get structured output, set `responseFormat` to `{ type: 'json' }` and provide a `schema`:
+
+```ts src/agents/structured-agent.ts
+import { defineAgent } from 'twenty-sdk/define';
+
+export default defineAgent({
+ universalIdentifier: 'c4d5e6f7-a8b9-0123-cdef-456789012345',
+ name: 'lead-scorer',
+ label: 'Lead Scorer',
+ prompt: 'Score the lead and explain your reasoning.',
+ responseFormat: {
+ type: 'json',
+ schema: {
+ type: 'object',
+ properties: {
+ score: { type: 'number', description: 'Lead score from 0 to 100' },
+ summary: { type: 'string', description: 'Short reasoning for the score' },
+ },
+ required: ['score', 'summary'],
+ additionalProperties: false,
+ },
+ },
+});
+```
+
+Schema notes:
+- The schema is a flat object: each property's `type` must be a primitive (`string`, `number`, or `boolean`). Nested objects and arrays are not supported.
+- `description` (optional) on each property guides the model on what to put there.
+- `required` (optional) lists the properties the model must always return.
+- `additionalProperties: false` (optional) forbids any property not declared in `properties`.
+
+
+
+
+`runAgent()` lets a logic function run one of your app's agents (with its
+skills and tools). Identify the agent by the `universalIdentifier` you passed
+to `defineAgent()`:
+
+```ts src/logic-functions/run-enricher.ts
+import { runAgent } from 'twenty-sdk/logic-function';
+
+const { result, error, success } = await runAgent({
+ agentUniversalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123',
+ prompt: 'Enrich House Ad : fill empty fields from its listing URL.',
+});
+```
+
+Key points:
+- The agent runs **synchronously** and can read/update records itself via its
+ own tools — `runAgent()` resolves once the run completes.
+- An app can only run its own agents.
+- The app's [default role](/developers/extend/apps/config/roles) must grant the
+ `AI` permission flag — add `SystemPermissionFlag.AI` to its
+ `permissionFlagUniversalIdentifiers` (or set `canAccessAllTools: true`).
+ Without it, `runAgent()` fails with a permission error.
+- Set a generous `timeoutSeconds` on the logic function — agent runs can take
+ several seconds.
+- `success` is `true` and `result` is non-null when the run completes; on
+ failure `success` is `false`, `result` is `null`, and `error` holds the
+ reason (for example, when the workspace ran out of AI credits mid-run).
+
+```ts src/roles/default-role.ts
+import { defineApplicationRole, SystemPermissionFlag } from 'twenty-sdk/define';
+
+export default defineApplicationRole({
+ universalIdentifier: 'b648f87b-1d26-4961-b974-0908fd991061',
+ label: 'Default function role',
+ // runAgent() requires the AI permission flag on the app's default role.
+ permissionFlagUniversalIdentifiers: [SystemPermissionFlag.AI],
+});
+```
+
+
+ **Avoid loops:** if you call `runAgent()` from a `*.updated` database-event
+ trigger and the agent updates the same record, scope the trigger with
+ `updatedFields` to a field the agent never writes (e.g. the source URL), or
+ guard on whether any target field is still empty before calling `runAgent()`.
+
diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts
index f01bc182b2..6f8c442351 100644
--- a/packages/twenty-front/src/generated-metadata/graphql.ts
+++ b/packages/twenty-front/src/generated-metadata/graphql.ts
@@ -256,6 +256,19 @@ export type ApiKeyToken = {
token: Scalars['String'];
};
+export type AppConnection = {
+ __typename?: 'AppConnection';
+ accessToken: Scalars['String'];
+ authFailedAt?: Maybe;
+ handle: Scalars['String'];
+ id: Scalars['ID'];
+ name: Scalars['String'];
+ providerName: Scalars['String'];
+ scopes: Array;
+ userWorkspaceId: Scalars['String'];
+ visibility: Scalars['String'];
+};
+
export type AppToken = {
__typename?: 'AppToken';
createdAt: Scalars['DateTime'];
@@ -2116,6 +2129,12 @@ export type LineChartSeries = {
label: Scalars['String'];
};
+export type ListAppConnectionsInput = {
+ providerName?: InputMaybe;
+ userWorkspaceId?: InputMaybe;
+ visibility?: InputMaybe;
+};
+
export type Location = {
__typename?: 'Location';
lat?: Maybe;
@@ -2495,6 +2514,7 @@ export type Mutation = {
resetPageLayoutWidgetToDefault: PageLayoutWidget;
revokeApiKey?: Maybe;
rotateApplicationRegistrationClientSecret: RotateClientSecret;
+ runAgent: RunAgentResult;
runEvaluationInput: AgentTurn;
runWorkspaceMigration: Scalars['Boolean'];
saveImapSmtpCaldavAccount: ImapSmtpCaldavConnectionSuccess;
@@ -3194,6 +3214,11 @@ export type MutationRotateApplicationRegistrationClientSecretArgs = {
};
+export type MutationRunAgentArgs = {
+ input: RunAgentInput;
+};
+
+
export type MutationRunEvaluationInputArgs = {
agentId: Scalars['UUID'];
input: Scalars['String'];
@@ -4114,6 +4139,8 @@ export type Query = {
agentTurns: Array;
apiKey?: Maybe;
apiKeys: Array;
+ appConnection: AppConnection;
+ appConnections: Array;
applicationConnectionProviders: Array;
applicationRegistrationTarballUrl?: Maybe;
barChartData: BarChartData;
@@ -4223,6 +4250,16 @@ export type QueryApiKeyArgs = {
};
+export type QueryAppConnectionArgs = {
+ id: Scalars['ID'];
+};
+
+
+export type QueryAppConnectionsArgs = {
+ filter?: InputMaybe;
+};
+
+
export type QueryApplicationConnectionProvidersArgs = {
applicationId: Scalars['UUID'];
};
@@ -4735,6 +4772,18 @@ export enum RowLevelPermissionPredicateOperand {
VECTOR_SEARCH = 'VECTOR_SEARCH'
}
+export type RunAgentInput = {
+ agentUniversalIdentifier: Scalars['String'];
+ prompt: Scalars['String'];
+};
+
+export type RunAgentResult = {
+ __typename?: 'RunAgentResult';
+ error?: Maybe;
+ result?: Maybe;
+ success: Scalars['Boolean'];
+};
+
export type SsoConnection = {
__typename?: 'SSOConnection';
id: Scalars['UUID'];
diff --git a/packages/twenty-front/src/modules/marketplace/utils/buildPermissionSummaryFromRoleManifest.ts b/packages/twenty-front/src/modules/marketplace/utils/buildPermissionSummaryFromRoleManifest.ts
index 7046dd9fdb..2a37018452 100644
--- a/packages/twenty-front/src/modules/marketplace/utils/buildPermissionSummaryFromRoleManifest.ts
+++ b/packages/twenty-front/src/modules/marketplace/utils/buildPermissionSummaryFromRoleManifest.ts
@@ -9,6 +9,7 @@ import {
IconKey,
IconSettings,
IconSettingsAutomation,
+ IconSparkles,
IconTool,
IconUsers,
} from 'twenty-ui/display';
@@ -114,6 +115,10 @@ export const buildPermissionSummaryFromRoleManifest = (
label: 'Manage API keys and webhooks',
Icon: IconCode,
},
+ [SystemPermissionFlag.AI]: {
+ label: 'Run AI agents',
+ Icon: IconSparkles,
+ },
};
for (const flag of otherFlags) {
diff --git a/packages/twenty-sdk/src/cli/utilities/entity/entity-agent-template.ts b/packages/twenty-sdk/src/cli/utilities/entity/entity-agent-template.ts
index 5197ba8a3e..af6c2c827b 100644
--- a/packages/twenty-sdk/src/cli/utilities/entity/entity-agent-template.ts
+++ b/packages/twenty-sdk/src/cli/utilities/entity/entity-agent-template.ts
@@ -21,6 +21,7 @@ export default defineAgent({
label: '${name}',
description: 'Add a description for your agent',
prompt: 'Add the agent system prompt here',
+ responseFormat: {type: 'text'},
});
`;
};
diff --git a/packages/twenty-sdk/src/sdk/define/agents/define-agent.ts b/packages/twenty-sdk/src/sdk/define/agents/define-agent.ts
index 006988a209..c72f988702 100644
--- a/packages/twenty-sdk/src/sdk/define/agents/define-agent.ts
+++ b/packages/twenty-sdk/src/sdk/define/agents/define-agent.ts
@@ -4,6 +4,7 @@ import { type AgentManifest } from 'twenty-shared/application';
export const defineAgent: DefineEntity = (config) => {
const errors: string[] = [];
+ const warnings: string[] = [];
if (!config.universalIdentifier) {
errors.push('Agent must have a universalIdentifier');
@@ -21,5 +22,11 @@ export const defineAgent: DefineEntity = (config) => {
errors.push('Agent must have a prompt');
}
- return createValidationResult({ config, errors });
+ if (!config.responseFormat) {
+ warnings.push(
+ `Agent '${config.name}' has no responseFormat, it will default to { type: 'text' }. Set it explicitly to control the agent's output shape.`,
+ );
+ }
+
+ return createValidationResult({ config, errors, warnings });
};
diff --git a/packages/twenty-sdk/src/sdk/logic-function/agents/__tests__/run-agent.spec.ts b/packages/twenty-sdk/src/sdk/logic-function/agents/__tests__/run-agent.spec.ts
new file mode 100644
index 0000000000..edaaf800a3
--- /dev/null
+++ b/packages/twenty-sdk/src/sdk/logic-function/agents/__tests__/run-agent.spec.ts
@@ -0,0 +1,103 @@
+import {
+ afterEach,
+ beforeEach,
+ describe,
+ expect,
+ it,
+ vi,
+ type MockInstance,
+} from 'vitest';
+
+import { runAgent } from '@/sdk/logic-function/agents/run-agent';
+
+describe('runAgent', () => {
+ let fetchSpy: MockInstance;
+
+ beforeEach(() => {
+ process.env.TWENTY_API_URL = 'https://api.test';
+ process.env.TWENTY_APP_ACCESS_TOKEN = 'app-token';
+ fetchSpy = vi.spyOn(globalThis, 'fetch');
+ });
+
+ afterEach(() => {
+ delete process.env.TWENTY_API_URL;
+ delete process.env.TWENTY_APP_ACCESS_TOKEN;
+ fetchSpy.mockRestore();
+ });
+
+ it('POSTs the runAgent mutation to /metadata and returns the result', async () => {
+ const payload = {
+ result: { response: 'done' },
+ error: null,
+ success: true,
+ };
+
+ fetchSpy.mockResolvedValue(
+ new Response(JSON.stringify({ data: { runAgent: payload } }), {
+ status: 200,
+ }),
+ );
+
+ const result = await runAgent({
+ agentUniversalIdentifier: 'agent-uid',
+ prompt: 'Enrich record 123',
+ });
+
+ expect(result).toEqual(payload);
+
+ const [url, requestInit] = fetchSpy.mock.calls[0];
+
+ expect(url).toBe('https://api.test/metadata');
+ expect(requestInit).toEqual(
+ expect.objectContaining({
+ method: 'POST',
+ headers: expect.objectContaining({
+ Authorization: 'Bearer app-token',
+ }),
+ }),
+ );
+
+ const sentBody = JSON.parse(requestInit?.body as string);
+
+ expect(sentBody.query).toContain('runAgent(input: $input)');
+ expect(sentBody.variables).toEqual({
+ input: {
+ agentUniversalIdentifier: 'agent-uid',
+ prompt: 'Enrich record 123',
+ },
+ });
+ });
+
+ it('surfaces GraphQL errors as a regular Error', async () => {
+ fetchSpy.mockResolvedValue(
+ new Response(
+ JSON.stringify({ errors: [{ message: 'Agent not found' }] }),
+ { status: 200 },
+ ),
+ );
+
+ await expect(
+ runAgent({ agentUniversalIdentifier: 'a', prompt: 'p' }),
+ ).rejects.toThrow(/Agent not found/);
+ });
+
+ it('surfaces non-2xx HTTP responses as a regular Error', async () => {
+ fetchSpy.mockResolvedValue(
+ new Response('boom', { status: 500, statusText: 'Server Error' }),
+ );
+
+ await expect(
+ runAgent({ agentUniversalIdentifier: 'a', prompt: 'p' }),
+ ).rejects.toThrow(/HTTP 500/);
+ });
+
+ it('throws when the runtime env vars are missing', async () => {
+ delete process.env.TWENTY_API_URL;
+
+ await expect(
+ runAgent({ agentUniversalIdentifier: 'a', prompt: 'p' }),
+ ).rejects.toThrow(/requires the app runtime env vars/);
+
+ expect(fetchSpy).not.toHaveBeenCalled();
+ });
+});
diff --git a/packages/twenty-sdk/src/sdk/logic-function/agents/run-agent.ts b/packages/twenty-sdk/src/sdk/logic-function/agents/run-agent.ts
new file mode 100644
index 0000000000..84308ef7bf
--- /dev/null
+++ b/packages/twenty-sdk/src/sdk/logic-function/agents/run-agent.ts
@@ -0,0 +1,31 @@
+import {
+ type RunAgentInput,
+ type RunAgentResult,
+} from 'twenty-shared/application';
+
+import { postGraphqlRequest } from '@/sdk/logic-function/utils/post-graphql-request.util';
+
+const RUN_AGENT_MUTATION = `
+ mutation RunAgent($input: RunAgentInput!) {
+ runAgent(input: $input) {
+ result
+ error
+ success
+ }
+ }
+`;
+
+export const runAgent = async (
+ input: RunAgentInput,
+): Promise => {
+ const { runAgent: result } = await postGraphqlRequest<
+ { input: RunAgentInput },
+ { runAgent: RunAgentResult }
+ >({
+ query: RUN_AGENT_MUTATION,
+ variables: { input },
+ caller: 'runAgent',
+ });
+
+ return result;
+};
diff --git a/packages/twenty-sdk/src/sdk/logic-function/connections/__tests__/get-connection.spec.ts b/packages/twenty-sdk/src/sdk/logic-function/connections/__tests__/get-connection.spec.ts
index 7174f2a80b..99440f9365 100644
--- a/packages/twenty-sdk/src/sdk/logic-function/connections/__tests__/get-connection.spec.ts
+++ b/packages/twenty-sdk/src/sdk/logic-function/connections/__tests__/get-connection.spec.ts
@@ -42,37 +42,48 @@ describe('getConnection', () => {
fetchSpy.mockRestore();
});
- it('POSTs the id to /apps/connections/get and returns the response', async () => {
+ it('queries appConnection by id over GraphQL and returns the response', async () => {
const connection = buildConnection({ id: 'persisted' });
fetchSpy.mockResolvedValue(
- new Response(JSON.stringify(connection), { status: 200 }),
+ new Response(JSON.stringify({ data: { appConnection: connection } }), {
+ status: 200,
+ }),
);
const result = await getConnection('persisted');
expect(result).toEqual(connection);
- expect(fetchSpy).toHaveBeenCalledWith(
- 'https://api.test/apps/connections/get',
+
+ const [url, requestInit] = fetchSpy.mock.calls[0];
+
+ expect(url).toBe('https://api.test/metadata');
+ expect(requestInit).toEqual(
expect.objectContaining({
method: 'POST',
- body: JSON.stringify({ id: 'persisted' }),
headers: expect.objectContaining({
Authorization: 'Bearer app-token',
}),
}),
);
+
+ const sentBody = JSON.parse(requestInit?.body as string);
+
+ expect(sentBody.query).toContain('appConnection(id: $id)');
+ expect(sentBody.variables).toEqual({ id: 'persisted' });
});
it('throws AppConnectionAuthFailedError when the connection needs reconnect', async () => {
fetchSpy.mockResolvedValue(
new Response(
- JSON.stringify(
- buildConnection({
- id: 'broken',
- authFailedAt: '2024-01-02T00:00:00.000Z',
- }),
- ),
+ JSON.stringify({
+ data: {
+ appConnection: buildConnection({
+ id: 'broken',
+ authFailedAt: '2024-01-02T00:00:00.000Z',
+ }),
+ },
+ }),
{ status: 200 },
),
);
diff --git a/packages/twenty-sdk/src/sdk/logic-function/connections/find-connection-for-request.ts b/packages/twenty-sdk/src/sdk/logic-function/connections/find-connection-for-request.ts
index fc1c755c7a..a4bfed4246 100644
--- a/packages/twenty-sdk/src/sdk/logic-function/connections/find-connection-for-request.ts
+++ b/packages/twenty-sdk/src/sdk/logic-function/connections/find-connection-for-request.ts
@@ -1,20 +1,9 @@
import { type AppConnection } from '@/sdk/logic-function/connections/types/app-connection.type';
-// Resolution rule for an HTTP-route handler that wants "the request user's
-// connection, or fall back to a workspace-shared one." Pure function — no
-// network. Pass it the result of `listConnections` and the trigger event.
-//
-// The plan-of-record for picking a connection is documented in the v3 plan
-// notes; this utility encodes the most common case so handlers don't have
-// to repeat the same `find(...) ?? find(...)` chain.
-//
-// Returns `null` when no candidate exists — caller decides whether that's
-// a 4xx for the end user or a hard error.
export const findConnectionForRequest = (
connections: AppConnection[],
event: { userWorkspaceId: string | null },
): AppConnection | null => {
- // 1. Personal credential of the request user (highest specificity).
if (event.userWorkspaceId !== null) {
const personal = connections.find(
(connection) =>
@@ -27,7 +16,6 @@ export const findConnectionForRequest = (
}
}
- // 2. Any workspace-shared credential (team-managed service account).
const workspaceShared = connections.find(
(connection) => connection.visibility === 'workspace',
);
diff --git a/packages/twenty-sdk/src/sdk/logic-function/connections/get-connection.ts b/packages/twenty-sdk/src/sdk/logic-function/connections/get-connection.ts
index 010bce0027..d74d2acaee 100644
--- a/packages/twenty-sdk/src/sdk/logic-function/connections/get-connection.ts
+++ b/packages/twenty-sdk/src/sdk/logic-function/connections/get-connection.ts
@@ -1,25 +1,36 @@
import { AppConnectionAuthFailedError } from '@/sdk/logic-function/connections/errors/app-connection-auth-failed.error';
import { type AppConnection } from '@/sdk/logic-function/connections/types/app-connection.type';
-import { postConnectionsEndpoint } from '@/sdk/logic-function/connections/utils/post-connections-endpoint.util';
+import { postGraphqlRequest } from '@/sdk/logic-function/utils/post-graphql-request.util';
+
+const GET_APP_CONNECTION_QUERY = `
+ query GetAppConnection($id: ID!) {
+ appConnection(id: $id) {
+ id
+ providerName
+ name
+ handle
+ visibility
+ userWorkspaceId
+ accessToken
+ scopes
+ authFailedAt
+ }
+ }
+`;
-// Look up a single connection by id. The id is stable across reconnects
-// (the row keeps its id when the user clicks "Reconnect"), so apps can
-// safely persist it in their own data and call this helper on each
-// invocation to retrieve a fresh access token.
-//
-// Throws `AppConnectionAuthFailedError` if the credential is in a
-// permanent-failure state (the user must reconnect from the app's
-// settings tab). Throws a regular `Error` for any other failure
-// (network, not-found, transient refresh failure).
export const getConnection = async (id: string): Promise => {
- const connection = await postConnectionsEndpoint<
+ const { appConnection } = await postGraphqlRequest<
{ id: string },
- AppConnection
- >('get', { id });
+ { appConnection: AppConnection }
+ >({
+ query: GET_APP_CONNECTION_QUERY,
+ variables: { id },
+ caller: 'getConnection',
+ });
- if (connection.authFailedAt !== null) {
- throw new AppConnectionAuthFailedError(connection.id);
+ if (appConnection.authFailedAt !== null) {
+ throw new AppConnectionAuthFailedError(appConnection.id);
}
- return connection;
+ return appConnection;
};
diff --git a/packages/twenty-sdk/src/sdk/logic-function/connections/list-connections.ts b/packages/twenty-sdk/src/sdk/logic-function/connections/list-connections.ts
index 1ae16be23f..49315bf3df 100644
--- a/packages/twenty-sdk/src/sdk/logic-function/connections/list-connections.ts
+++ b/packages/twenty-sdk/src/sdk/logic-function/connections/list-connections.ts
@@ -1,26 +1,39 @@
import { type AppConnection } from '@/sdk/logic-function/connections/types/app-connection.type';
-import { postConnectionsEndpoint } from '@/sdk/logic-function/connections/utils/post-connections-endpoint.util';
+import { postGraphqlRequest } from '@/sdk/logic-function/utils/post-graphql-request.util';
+
+const LIST_APP_CONNECTIONS_QUERY = `
+ query ListAppConnections($filter: ListAppConnectionsInput) {
+ appConnections(filter: $filter) {
+ id
+ providerName
+ name
+ handle
+ visibility
+ userWorkspaceId
+ accessToken
+ scopes
+ authFailedAt
+ }
+ }
+`;
export type ListConnectionsFilter = {
- // Provider name as declared on `defineConnectionProvider({ name })`.
providerName?: string;
- // Restrict to credentials owned by a specific user. Useful in cron
- // triggers when picking a service-account user via app config.
userWorkspaceId?: string;
- // Restrict by row visibility — 'user' (private) or 'workspace' (shared).
visibility?: 'user' | 'workspace';
};
-// Returns every connection owned by the running app, optionally filtered.
-// The server refreshes each access token on read, so the returned values
-// are usable immediately. When the running execution carries a user
-// context (HTTP-route trigger with `isAuthRequired`, tool calls, etc.),
-// `scope: 'user'` connections belonging to other users are filtered out
-// server-side. Cron and database-event triggers see all connections.
export const listConnections = async (
filter: ListConnectionsFilter = {},
-): Promise =>
- postConnectionsEndpoint(
- 'list',
- filter,
- );
+): Promise => {
+ const { appConnections } = await postGraphqlRequest<
+ { filter: ListConnectionsFilter },
+ { appConnections: AppConnection[] }
+ >({
+ query: LIST_APP_CONNECTIONS_QUERY,
+ variables: { filter },
+ caller: 'listConnections',
+ });
+
+ return appConnections;
+};
diff --git a/packages/twenty-sdk/src/sdk/logic-function/connections/utils/post-connections-endpoint.util.ts b/packages/twenty-sdk/src/sdk/logic-function/connections/utils/post-connections-endpoint.util.ts
deleted file mode 100644
index 845d9ba012..0000000000
--- a/packages/twenty-sdk/src/sdk/logic-function/connections/utils/post-connections-endpoint.util.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import {
- DEFAULT_API_URL_NAME,
- DEFAULT_APP_ACCESS_TOKEN_NAME,
-} from 'twenty-shared/application';
-
-// Shared transport for `/apps/connections/*` endpoints. Centralises the
-// env-var check, auth header, and HTTP error translation so each helper
-// stays focused on its own input/output shape.
-export const postConnectionsEndpoint = async (
- path: 'list' | 'get',
- body: TBody,
-): Promise => {
- const apiUrl = process.env[DEFAULT_API_URL_NAME];
- const accessToken = process.env[DEFAULT_APP_ACCESS_TOKEN_NAME];
-
- if (!apiUrl || !accessToken) {
- throw new Error(
- `${path === 'list' ? 'listConnections' : 'getConnection'}() requires the app runtime env vars ` +
- `${DEFAULT_API_URL_NAME} and ${DEFAULT_APP_ACCESS_TOKEN_NAME}.`,
- );
- }
-
- const response = await fetch(`${apiUrl}/apps/connections/${path}`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${accessToken}`,
- },
- body: JSON.stringify(body),
- });
-
- if (!response.ok) {
- throw new Error(
- `${path === 'list' ? 'listConnections' : 'getConnection'}() failed: ` +
- `HTTP ${response.status} ${response.statusText}`,
- );
- }
-
- return (await response.json()) as TResponse;
-};
diff --git a/packages/twenty-sdk/src/sdk/logic-function/index.ts b/packages/twenty-sdk/src/sdk/logic-function/index.ts
index f72aa89fee..b29b72288f 100644
--- a/packages/twenty-sdk/src/sdk/logic-function/index.ts
+++ b/packages/twenty-sdk/src/sdk/logic-function/index.ts
@@ -44,5 +44,9 @@ export type { ListConnectionsFilter } from '@/sdk/logic-function/connections/lis
export { findConnectionForRequest } from '@/sdk/logic-function/connections/find-connection-for-request';
export { AppConnectionAuthFailedError } from '@/sdk/logic-function/connections/errors/app-connection-auth-failed.error';
export type { AppConnection } from '@/sdk/logic-function/connections/types/app-connection.type';
+
+export { runAgent } from '@/sdk/logic-function/agents/run-agent';
+export type { RunAgentInput, RunAgentResult } from 'twenty-shared/application';
+
export { Response } from '@/sdk/logic-function/response';
export type { ResponseInit } from '@/sdk/logic-function/response';
diff --git a/packages/twenty-sdk/src/sdk/logic-function/utils/post-graphql-request.util.ts b/packages/twenty-sdk/src/sdk/logic-function/utils/post-graphql-request.util.ts
new file mode 100644
index 0000000000..34a2072286
--- /dev/null
+++ b/packages/twenty-sdk/src/sdk/logic-function/utils/post-graphql-request.util.ts
@@ -0,0 +1,56 @@
+import {
+ DEFAULT_API_URL_NAME,
+ DEFAULT_APP_ACCESS_TOKEN_NAME,
+} from 'twenty-shared/application';
+
+export const postGraphqlRequest = async ({
+ query,
+ variables,
+ caller,
+}: {
+ query: string;
+ variables: TVariables;
+ caller: string;
+}): Promise => {
+ const apiUrl = process.env[DEFAULT_API_URL_NAME];
+ const accessToken = process.env[DEFAULT_APP_ACCESS_TOKEN_NAME];
+
+ if (!apiUrl || !accessToken) {
+ throw new Error(
+ `${caller}() requires the app runtime env vars ` +
+ `${DEFAULT_API_URL_NAME} and ${DEFAULT_APP_ACCESS_TOKEN_NAME}.`,
+ );
+ }
+
+ const response = await fetch(`${apiUrl}/metadata`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ },
+ body: JSON.stringify({ query, variables }),
+ });
+
+ if (!response.ok) {
+ throw new Error(
+ `${caller}() failed: HTTP ${response.status} ${response.statusText}`,
+ );
+ }
+
+ const body = (await response.json()) as {
+ data?: TData;
+ errors?: { message: string }[];
+ };
+
+ if (body.errors && body.errors.length > 0) {
+ throw new Error(
+ `${caller}() failed: ${body.errors.map((error) => error.message).join(', ')}`,
+ );
+ }
+
+ if (!body.data) {
+ throw new Error(`${caller}() failed: response contained no data.`);
+ }
+
+ return body.data;
+};
diff --git a/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.controller.ts b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.controller.ts
index d49100f761..29eb1892f5 100644
--- a/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.controller.ts
+++ b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.controller.ts
@@ -22,9 +22,10 @@ import { JwtAuthGuard } from 'src/engine/guards/jwt-auth.guard';
import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
-// On-demand connection lookup for app logic functions. Authenticated via the
-// application access token (already injected into the function runtime as
-// TWENTY_APP_ACCESS_TOKEN). Apps can only list their own connections.
+/** @deprecated Superseded by the `appConnections` / `appConnection` GraphQL
+ * queries on the metadata schema (ApplicationConnectionsResolver). The SDK
+ * helpers (`listConnections`, `getConnection`) now call GraphQL. Kept for
+ * backward compatibility with already-deployed app runtimes. */
@Controller('apps/connections')
@UseGuards(JwtAuthGuard, WorkspaceAuthGuard, NoPermissionGuard)
@UsePipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }))
diff --git a/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.module.ts b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.module.ts
index 52fb9e6f48..075472748e 100644
--- a/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.module.ts
+++ b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.module.ts
@@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { ConnectionProviderEntity } from 'src/engine/core-modules/application/connection-provider/connection-provider.entity';
import { ApplicationConnectionsController } from 'src/engine/core-modules/application/connection-provider/connections/application-connections.controller';
+import { ApplicationConnectionsResolver } from 'src/engine/core-modules/application/connection-provider/connections/application-connections.resolver';
import { ApplicationConnectionsListService } from 'src/engine/core-modules/application/connection-provider/connections/services/application-connections-list.service';
import { TokenModule } from 'src/engine/core-modules/auth/token/token.module';
import { ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity';
@@ -25,7 +26,10 @@ import { RefreshTokensManagerModule } from 'src/modules/connected-account/refres
RefreshTokensManagerModule,
ConnectedAccountTokenEncryptionModule,
],
- providers: [ApplicationConnectionsListService],
+ providers: [
+ ApplicationConnectionsListService,
+ ApplicationConnectionsResolver,
+ ],
controllers: [ApplicationConnectionsController],
exports: [ApplicationConnectionsListService],
})
diff --git a/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.resolver.ts b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.resolver.ts
new file mode 100644
index 0000000000..2ffbdaee10
--- /dev/null
+++ b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/application-connections.resolver.ts
@@ -0,0 +1,54 @@
+import { UseGuards } from '@nestjs/common';
+import { Args, ID, Query } from '@nestjs/graphql';
+
+import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator';
+import { AppConnectionObjectDto } from 'src/engine/core-modules/application/connection-provider/connections/dtos/app-connection.object';
+import { ListAppConnectionsInput } from 'src/engine/core-modules/application/connection-provider/connections/dtos/list-app-connections.input';
+import { ApplicationConnectionsListService } from 'src/engine/core-modules/application/connection-provider/connections/services/application-connections-list.service';
+import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
+import { type FlatWorkspace } from 'src/engine/core-modules/workspace/types/flat-workspace.type';
+import { AuthApplication } from 'src/engine/decorators/auth/auth-application.decorator';
+import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator';
+import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
+import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
+import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
+
+@UseGuards(WorkspaceAuthGuard, NoPermissionGuard)
+@MetadataResolver()
+export class ApplicationConnectionsResolver {
+ constructor(
+ private readonly listService: ApplicationConnectionsListService,
+ ) {}
+
+ @Query(() => [AppConnectionObjectDto])
+ async appConnections(
+ @AuthApplication() application: FlatApplication,
+ @AuthWorkspace() workspace: FlatWorkspace,
+ @AuthUserWorkspaceId({ allowUndefined: true })
+ userWorkspaceId: string | undefined,
+ @Args('filter', { nullable: true }) filter?: ListAppConnectionsInput,
+ ): Promise {
+ return this.listService.list({
+ applicationId: application.id,
+ workspaceId: workspace.id,
+ requestUserWorkspaceId: userWorkspaceId ?? null,
+ filter: filter ?? {},
+ });
+ }
+
+ @Query(() => AppConnectionObjectDto)
+ async appConnection(
+ @AuthApplication() application: FlatApplication,
+ @AuthWorkspace() workspace: FlatWorkspace,
+ @AuthUserWorkspaceId({ allowUndefined: true })
+ userWorkspaceId: string | undefined,
+ @Args('id', { type: () => ID }) id: string,
+ ): Promise {
+ return this.listService.getOne({
+ applicationId: application.id,
+ workspaceId: workspace.id,
+ requestUserWorkspaceId: userWorkspaceId ?? null,
+ id,
+ });
+ }
+}
diff --git a/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/dtos/app-connection.object.ts b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/dtos/app-connection.object.ts
new file mode 100644
index 0000000000..cbab44db00
--- /dev/null
+++ b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/dtos/app-connection.object.ts
@@ -0,0 +1,33 @@
+import { Field, ID, ObjectType } from '@nestjs/graphql';
+
+import { type AppConnection } from 'twenty-shared/application';
+
+@ObjectType('AppConnection')
+export class AppConnectionObjectDto implements AppConnection {
+ @Field(() => ID)
+ id: string;
+
+ @Field()
+ providerName: string;
+
+ @Field()
+ name: string;
+
+ @Field()
+ handle: string;
+
+ @Field(() => String)
+ visibility: 'user' | 'workspace';
+
+ @Field()
+ userWorkspaceId: string;
+
+ @Field()
+ accessToken: string;
+
+ @Field(() => [String])
+ scopes: string[];
+
+ @Field(() => String, { nullable: true })
+ authFailedAt: string | null;
+}
diff --git a/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/dtos/list-app-connections.input.ts b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/dtos/list-app-connections.input.ts
new file mode 100644
index 0000000000..10dfd2c945
--- /dev/null
+++ b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connections/dtos/list-app-connections.input.ts
@@ -0,0 +1,21 @@
+import { Field, InputType } from '@nestjs/graphql';
+
+import { IsIn, IsOptional, IsString, IsUUID } from 'class-validator';
+
+@InputType('ListAppConnectionsInput')
+export class ListAppConnectionsInput {
+ @IsString()
+ @IsOptional()
+ @Field({ nullable: true })
+ providerName?: string;
+
+ @IsUUID()
+ @IsOptional()
+ @Field({ nullable: true })
+ userWorkspaceId?: string;
+
+ @IsIn(['user', 'workspace'])
+ @IsOptional()
+ @Field(() => String, { nullable: true })
+ visibility?: 'user' | 'workspace';
+}
diff --git a/packages/twenty-server/src/engine/core-modules/application/utils/from-agent-manifest-to-universal-flat-agent.util.ts b/packages/twenty-server/src/engine/core-modules/application/utils/from-agent-manifest-to-universal-flat-agent.util.ts
index 1ca6978486..6f76df35e1 100644
--- a/packages/twenty-server/src/engine/core-modules/application/utils/from-agent-manifest-to-universal-flat-agent.util.ts
+++ b/packages/twenty-server/src/engine/core-modules/application/utils/from-agent-manifest-to-universal-flat-agent.util.ts
@@ -22,7 +22,7 @@ export const fromAgentManifestToUniversalFlatAgent = ({
description: agentManifest.description ?? null,
prompt: agentManifest.prompt,
modelId: (agentManifest.modelId as ModelId) ?? AUTO_SELECT_SMART_MODEL_ID,
- responseFormat: { type: 'text' },
+ responseFormat: agentManifest.responseFormat ?? { type: 'text' },
modelConfiguration: null,
evaluationInputs: [],
isCustom: false,
diff --git a/packages/twenty-server/src/engine/decorators/auth/auth-application.decorator.ts b/packages/twenty-server/src/engine/decorators/auth/auth-application.decorator.ts
new file mode 100644
index 0000000000..63020fe466
--- /dev/null
+++ b/packages/twenty-server/src/engine/decorators/auth/auth-application.decorator.ts
@@ -0,0 +1,25 @@
+import {
+ type ExecutionContext,
+ ForbiddenException,
+ createParamDecorator,
+} from '@nestjs/common';
+
+import { getRequest } from 'src/utils/extract-request';
+
+interface DecoratorOptions {
+ allowUndefined?: boolean;
+}
+
+export const AuthApplication = createParamDecorator(
+ (options: DecoratorOptions | undefined, ctx: ExecutionContext) => {
+ const request = getRequest(ctx);
+
+ if (!options?.allowUndefined && !request.application) {
+ throw new ForbiddenException(
+ 'This endpoint requires an APPLICATION_ACCESS token.',
+ );
+ }
+
+ return request.application;
+ },
+);
diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/ai-agent-execution.module.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/ai-agent-execution.module.ts
index e5d576d38a..1b9579b22d 100644
--- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/ai-agent-execution.module.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/ai-agent-execution.module.ts
@@ -1,6 +1,7 @@
import { forwardRef, Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
+import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
import { WorkspaceDomainsModule } from 'src/engine/core-modules/domain/workspace-domains/workspace-domains.module';
import { FileUrlModule } from 'src/engine/core-modules/file/file-url/file-url.module';
@@ -21,14 +22,17 @@ import { AgentMessagePartEntity } from './entities/agent-message-part.entity';
import { AgentMessageEntity } from './entities/agent-message.entity';
import { AgentTurnEntity } from './entities/agent-turn.entity';
import { AgentMessagePartResolver } from './resolvers/agent-message-part.resolver';
+import { AgentRunResolver } from './resolvers/agent-run.resolver';
import { AgentActorContextService } from './services/agent-actor-context.service';
import { AgentAsyncExecutorService } from './services/agent-async-executor.service';
+import { AgentRunService } from './services/agent-run.service';
@Module({
imports: [
AiBillingModule,
AiModelsModule,
AiAgentModule,
+ ApplicationModule,
BillingModule,
FileUrlModule,
WorkspaceDomainsModule,
@@ -50,7 +54,10 @@ import { AgentAsyncExecutorService } from './services/agent-async-executor.servi
AgentAsyncExecutorService,
AgentActorContextService,
AgentMessagePartResolver,
+ AgentRunResolver,
+ AgentRunService,
provideWorkspaceScopedRepository(RoleTargetEntity),
+ provideWorkspaceScopedRepository(AgentEntity),
],
exports: [
AgentAsyncExecutorService,
diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/dtos/run-agent-result.dto.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/dtos/run-agent-result.dto.ts
new file mode 100644
index 0000000000..3f673c43b2
--- /dev/null
+++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/dtos/run-agent-result.dto.ts
@@ -0,0 +1,16 @@
+import { Field, ObjectType } from '@nestjs/graphql';
+
+import GraphQLJSON from 'graphql-type-json';
+import { type RunAgentResult } from 'twenty-shared/application';
+
+@ObjectType('RunAgentResult')
+export class RunAgentResultDTO implements RunAgentResult {
+ @Field(() => GraphQLJSON, { nullable: true })
+ result: object | null;
+
+ @Field(() => String, { nullable: true })
+ error: string | null;
+
+ @Field()
+ success: boolean;
+}
diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/dtos/run-agent.input.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/dtos/run-agent.input.ts
new file mode 100644
index 0000000000..c021020a18
--- /dev/null
+++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/dtos/run-agent.input.ts
@@ -0,0 +1,17 @@
+import { Field, InputType } from '@nestjs/graphql';
+
+import { IsNotEmpty, IsString } from 'class-validator';
+import { type RunAgentInput } from 'twenty-shared/application';
+
+@InputType('RunAgentInput')
+export class RunAgentInputDTO implements RunAgentInput {
+ @IsString()
+ @IsNotEmpty()
+ @Field()
+ agentUniversalIdentifier: string;
+
+ @IsString()
+ @IsNotEmpty()
+ @Field()
+ prompt: string;
+}
diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/resolvers/agent-run.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/resolvers/agent-run.resolver.ts
new file mode 100644
index 0000000000..245b8aaedb
--- /dev/null
+++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/resolvers/agent-run.resolver.ts
@@ -0,0 +1,34 @@
+import { UseGuards } from '@nestjs/common';
+import { Args, Mutation } from '@nestjs/graphql';
+
+import { PermissionFlagType } from 'twenty-shared/constants';
+
+import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator';
+import { type FlatWorkspace } from 'src/engine/core-modules/workspace/types/flat-workspace.type';
+import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator';
+import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
+import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
+import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
+import { RunAgentInputDTO } from 'src/engine/metadata-modules/ai/ai-agent-execution/dtos/run-agent.input';
+import { RunAgentResultDTO } from 'src/engine/metadata-modules/ai/ai-agent-execution/dtos/run-agent-result.dto';
+import { AgentRunService } from 'src/engine/metadata-modules/ai/ai-agent-execution/services/agent-run.service';
+
+@UseGuards(WorkspaceAuthGuard, SettingsPermissionGuard(PermissionFlagType.AI))
+@MetadataResolver()
+export class AgentRunResolver {
+ constructor(private readonly agentRunService: AgentRunService) {}
+
+ @Mutation(() => RunAgentResultDTO)
+ async runAgent(
+ @Args('input') input: RunAgentInputDTO,
+ @AuthWorkspace() workspace: FlatWorkspace,
+ @AuthUserWorkspaceId({ allowUndefined: true })
+ userWorkspaceId: string | undefined,
+ ): Promise {
+ return this.agentRunService.run({
+ workspace,
+ requestUserWorkspaceId: userWorkspaceId ?? null,
+ input,
+ });
+ }
+}
diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/__tests__/agent-run.service.spec.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/__tests__/agent-run.service.spec.ts
new file mode 100644
index 0000000000..cfc2fe0bb5
--- /dev/null
+++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/__tests__/agent-run.service.spec.ts
@@ -0,0 +1,145 @@
+import { Test, type TestingModule } from '@nestjs/testing';
+
+import { type FlatWorkspace } from 'src/engine/core-modules/workspace/types/flat-workspace.type';
+import { ApplicationService } from 'src/engine/core-modules/application/application.service';
+import { AgentAsyncExecutorService } from 'src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service';
+import { AgentRunService } from 'src/engine/metadata-modules/ai/ai-agent-execution/services/agent-run.service';
+import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
+import { getWorkspaceScopedRepositoryToken } from 'src/engine/twenty-orm/workspace-scoped-repository/get-workspace-scoped-repository-token.util';
+
+describe('AgentRunService', () => {
+ let service: AgentRunService;
+ let agentRepository: { findOne: jest.Mock };
+ let applicationService: { findById: jest.Mock };
+ let agentAsyncExecutorService: { executeAgent: jest.Mock };
+
+ const workspace = { id: 'workspace-1' } as FlatWorkspace;
+
+ const agent = { id: 'agent-1', applicationId: 'app-1' } as AgentEntity;
+
+ const input = {
+ agentUniversalIdentifier: 'agent-uid',
+ prompt: 'Enrich record 123',
+ };
+
+ beforeEach(async () => {
+ agentRepository = { findOne: jest.fn().mockResolvedValue(agent) };
+ applicationService = {
+ findById: jest.fn().mockResolvedValue({ id: 'app-1' }),
+ };
+ agentAsyncExecutorService = {
+ executeAgent: jest.fn().mockResolvedValue({
+ result: { response: 'done' },
+ hasNoMoreAvailableCredits: false,
+ }),
+ };
+
+ const module: TestingModule = await Test.createTestingModule({
+ providers: [
+ AgentRunService,
+ {
+ provide: AgentAsyncExecutorService,
+ useValue: agentAsyncExecutorService,
+ },
+ {
+ provide: ApplicationService,
+ useValue: applicationService,
+ },
+ {
+ provide: getWorkspaceScopedRepositoryToken(AgentEntity),
+ useValue: agentRepository,
+ },
+ ],
+ }).compile();
+
+ service = module.get(AgentRunService);
+ });
+
+ it('runs the agent found by its universal identifier and returns a success result', async () => {
+ const result = await service.run({
+ workspace,
+ requestUserWorkspaceId: 'user-workspace-1',
+ input,
+ });
+
+ expect(agentRepository.findOne).toHaveBeenCalledWith(workspace.id, {
+ where: {
+ universalIdentifier: input.agentUniversalIdentifier,
+ },
+ });
+ expect(applicationService.findById).toHaveBeenCalledWith(
+ agent.applicationId,
+ );
+ expect(result).toEqual({
+ result: { response: 'done' },
+ error: null,
+ success: true,
+ });
+ });
+
+ it('builds the application auth context from the agent application', async () => {
+ await service.run({
+ workspace,
+ requestUserWorkspaceId: 'user-workspace-1',
+ input,
+ });
+
+ expect(agentAsyncExecutorService.executeAgent).toHaveBeenCalledWith(
+ expect.objectContaining({
+ authContext: {
+ type: 'application',
+ workspace,
+ application: { id: 'app-1' },
+ },
+ }),
+ );
+ });
+
+ it('returns an error result when the workspace ran out of credits', async () => {
+ agentAsyncExecutorService.executeAgent.mockResolvedValue({
+ result: { response: 'partial' },
+ hasNoMoreAvailableCredits: true,
+ });
+
+ const result = await service.run({
+ workspace,
+ requestUserWorkspaceId: 'user-workspace-1',
+ input,
+ });
+
+ expect(result).toEqual({
+ result: null,
+ error: 'AI agent stopped: no more available credits.',
+ success: false,
+ });
+ });
+
+ it('throws when no agent matches the identifier', async () => {
+ agentRepository.findOne.mockResolvedValue(null);
+
+ await expect(
+ service.run({
+ workspace,
+ requestUserWorkspaceId: null,
+ input,
+ }),
+ ).rejects.toThrow(/not found/);
+
+ expect(applicationService.findById).not.toHaveBeenCalled();
+ expect(agentAsyncExecutorService.executeAgent).not.toHaveBeenCalled();
+ });
+
+ it("throws when the agent's application cannot be found", async () => {
+ applicationService.findById.mockResolvedValue(null);
+
+ await expect(
+ service.run({
+ workspace,
+ requestUserWorkspaceId: null,
+ input,
+ }),
+ ).rejects.toThrow(/not found/);
+
+ expect(agentAsyncExecutorService.executeAgent).not.toHaveBeenCalled();
+ });
+});
diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-run.service.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-run.service.ts
new file mode 100644
index 0000000000..88943427f9
--- /dev/null
+++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-run.service.ts
@@ -0,0 +1,83 @@
+import { Injectable, NotFoundException } from '@nestjs/common';
+
+import {
+ type RunAgentInput,
+ type RunAgentResult,
+} from 'twenty-shared/application';
+
+import { ApplicationService } from 'src/engine/core-modules/application/application.service';
+import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type';
+import { UsageOperationType } from 'src/engine/core-modules/usage/enums/usage-operation-type.enum';
+import { type FlatWorkspace } from 'src/engine/core-modules/workspace/types/flat-workspace.type';
+import { AgentAsyncExecutorService } from 'src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service';
+import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
+import { InjectWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/inject-workspace-scoped-repository.decorator';
+import { WorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/workspace-scoped-repository';
+
+@Injectable()
+export class AgentRunService {
+ constructor(
+ private readonly agentAsyncExecutorService: AgentAsyncExecutorService,
+ private readonly applicationService: ApplicationService,
+ @InjectWorkspaceScopedRepository(AgentEntity)
+ private readonly agentRepository: WorkspaceScopedRepository,
+ ) {}
+
+ async run({
+ workspace,
+ requestUserWorkspaceId,
+ input,
+ }: {
+ workspace: FlatWorkspace;
+ requestUserWorkspaceId: string | null;
+ input: RunAgentInput;
+ }): Promise {
+ const agent = await this.agentRepository.findOne(workspace.id, {
+ where: {
+ universalIdentifier: input.agentUniversalIdentifier,
+ },
+ });
+
+ if (!agent) {
+ throw new NotFoundException(
+ `Agent ${input.agentUniversalIdentifier} not found`,
+ );
+ }
+
+ const application = await this.applicationService.findById(
+ agent.applicationId,
+ );
+
+ if (!application) {
+ throw new NotFoundException(
+ `Application ${agent.applicationId} not found for agent ${input.agentUniversalIdentifier}`,
+ );
+ }
+
+ const authContext: WorkspaceAuthContext = {
+ type: 'application',
+ workspace,
+ application,
+ };
+
+ const { result, hasNoMoreAvailableCredits } =
+ await this.agentAsyncExecutorService.executeAgent({
+ agent,
+ userPrompt: input.prompt,
+ authContext,
+ workspaceId: workspace.id,
+ userWorkspaceId: requestUserWorkspaceId,
+ operationType: UsageOperationType.AI_WORKFLOW_TOKEN,
+ });
+
+ if (hasNoMoreAvailableCredits) {
+ return {
+ result: null,
+ error: 'AI agent stopped: no more available credits.',
+ success: false,
+ };
+ }
+
+ return { result, error: null, success: true };
+ }
+}
diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/types/agent-response-format.type.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/types/agent-response-format.type.ts
index d0c2a4eb64..0bf547e724 100644
--- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/types/agent-response-format.type.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/types/agent-response-format.type.ts
@@ -1,12 +1,6 @@
-import { type AgentResponseSchema } from 'twenty-shared/ai';
-
-export type AgentResponseFormatType = AgentResponseFormat['type'];
-
-export type AgentTextResponseFormat = { type: 'text' };
-export type AgentJsonResponseFormat = {
- type: 'json';
- schema: AgentResponseSchema;
-};
-export type AgentResponseFormat =
- | AgentTextResponseFormat
- | AgentJsonResponseFormat;
+export type {
+ AgentResponseFormat,
+ AgentResponseFormatType,
+ AgentTextResponseFormat,
+ AgentJsonResponseFormat,
+} from 'twenty-shared/ai';
diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-engine.module.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-engine.module.ts
index 83762b3793..c2ec2f13c0 100644
--- a/packages/twenty-server/src/engine/metadata-modules/metadata-engine.module.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/metadata-engine.module.ts
@@ -6,6 +6,7 @@ import { AiAgentModule } from 'src/engine/metadata-modules/ai/ai-agent/ai-agent.
import { AiChatModule } from 'src/engine/metadata-modules/ai/ai-chat/ai-chat.module';
import { AiGenerateTextModule } from 'src/engine/metadata-modules/ai/ai-generate-text/ai-generate-text.module';
import { AiWorkspaceStatsModule } from 'src/engine/metadata-modules/ai/ai-workspace-stats/ai-workspace-stats.module';
+import { ApplicationConnectionsModule } from 'src/engine/core-modules/application/connection-provider/connections/application-connections.module';
import { CalendarChannelMetadataModule } from 'src/engine/metadata-modules/calendar-channel/calendar-channel-metadata.module';
import { ConnectedAccountMetadataModule } from 'src/engine/metadata-modules/connected-account/connected-account-metadata.module';
import { CommandMenuItemModule } from 'src/engine/metadata-modules/command-menu-item/command-menu-item.module';
@@ -45,6 +46,7 @@ import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/work
AiChatModule,
AiGenerateTextModule,
AiWorkspaceStatsModule,
+ ApplicationConnectionsModule,
MinimalMetadataModule,
ViewModule,
WorkspaceMetadataVersionModule,
diff --git a/packages/twenty-shared/src/ai/index.ts b/packages/twenty-shared/src/ai/index.ts
index 005c37fee4..8a7b6ea4d0 100644
--- a/packages/twenty-shared/src/ai/index.ts
+++ b/packages/twenty-shared/src/ai/index.ts
@@ -15,6 +15,12 @@ export { DATA_RESIDENCY_KEYS } from './constants/data-residency.const';
export type { NativeAiSdkProviderId } from './constants/native-ai-sdk-provider-ids.const';
export { NATIVE_AI_SDK_PROVIDER_IDS } from './constants/native-ai-sdk-provider-ids.const';
export { ToolCategory } from './constants/tool-category.const';
+export type {
+ AgentResponseFormatType,
+ AgentTextResponseFormat,
+ AgentJsonResponseFormat,
+ AgentResponseFormat,
+} from './types/agent-response-format.type';
export type {
AgentResponseFieldType,
AgentResponseSchema,
diff --git a/packages/twenty-shared/src/ai/types/agent-response-format.type.ts b/packages/twenty-shared/src/ai/types/agent-response-format.type.ts
new file mode 100644
index 0000000000..54ba9a303b
--- /dev/null
+++ b/packages/twenty-shared/src/ai/types/agent-response-format.type.ts
@@ -0,0 +1,12 @@
+import { type AgentResponseSchema } from '@/ai/types/agent-response-schema.type';
+
+export type AgentResponseFormatType = AgentResponseFormat['type'];
+
+export type AgentTextResponseFormat = { type: 'text' };
+export type AgentJsonResponseFormat = {
+ type: 'json';
+ schema: AgentResponseSchema;
+};
+export type AgentResponseFormat =
+ | AgentTextResponseFormat
+ | AgentJsonResponseFormat;
diff --git a/packages/twenty-shared/src/application/agentManifestType.ts b/packages/twenty-shared/src/application/agentManifestType.ts
index 4084226bee..9d3cef9591 100644
--- a/packages/twenty-shared/src/application/agentManifestType.ts
+++ b/packages/twenty-shared/src/application/agentManifestType.ts
@@ -1,3 +1,4 @@
+import { type AgentResponseFormat } from '@/ai/types/agent-response-format.type';
import { type SyncableEntityOptions } from '@/application/syncableEntityOptionsType';
export type AgentManifest = SyncableEntityOptions & {
@@ -7,4 +8,5 @@ export type AgentManifest = SyncableEntityOptions & {
description?: string;
prompt: string;
modelId?: string;
+ responseFormat?: AgentResponseFormat;
};
diff --git a/packages/twenty-shared/src/application/index.ts b/packages/twenty-shared/src/application/index.ts
index 47a7081f96..4feb6130e2 100644
--- a/packages/twenty-shared/src/application/index.ts
+++ b/packages/twenty-shared/src/application/index.ts
@@ -66,6 +66,7 @@ export type {
FieldPermissionManifest,
RoleManifest,
} from './roleManifestType';
+export type { RunAgentInput, RunAgentResult } from './runAgentType';
export type { ServerVariables } from './server-variables.type';
export type { SkillManifest } from './skillManifestType';
export type { StoredOAuthConnectionProviderConfig } from './storedOAuthConnectionProviderConfigType';
diff --git a/packages/twenty-shared/src/application/runAgentType.ts b/packages/twenty-shared/src/application/runAgentType.ts
new file mode 100644
index 0000000000..e5f1992cd6
--- /dev/null
+++ b/packages/twenty-shared/src/application/runAgentType.ts
@@ -0,0 +1,10 @@
+export type RunAgentInput = {
+ agentUniversalIdentifier: string;
+ prompt: string;
+};
+
+export type RunAgentResult = {
+ result: object | null;
+ error: string | null;
+ success: boolean;
+};