diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index a0ddb720e1..59b1f54452 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -2609,19 +2609,6 @@ type Skill { updatedAt: DateTime! } -type AgentChatThread { - id: UUID! - title: String - totalInputTokens: Int! - totalOutputTokens: Int! - contextWindowTokens: Int - conversationSize: Int! - totalInputCredits: Float! - totalOutputCredits: Float! - createdAt: DateTime! - updatedAt: DateTime! -} - type AgentMessage { id: UUID! threadId: UUID! @@ -2634,6 +2621,21 @@ type AgentMessage { createdAt: DateTime! } +type AgentChatThread { + id: ID! + title: String + totalInputTokens: Int! + totalOutputTokens: Int! + contextWindowTokens: Int + conversationSize: Int! + totalInputCredits: Float! + totalOutputCredits: Float! + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime + lastMessageAt: DateTime +} + type AiSystemPromptSection { title: String! content: String! @@ -2661,22 +2663,6 @@ type AgentChatEvent { event: JSON! } -type AgentChatThreadEdge { - """The node containing the AgentChatThread""" - node: AgentChatThread! - - """Cursor for this node.""" - cursor: ConnectionCursor! -} - -type AgentChatThreadConnection { - """Paging information""" - pageInfo: PageInfo! - - """Array of edges.""" - edges: [AgentChatThreadEdge!]! -} - type AgentTurnEvaluation { id: UUID! turnId: UUID! @@ -2993,22 +2979,13 @@ type Query { webhooks: [Webhook!]! webhook(id: UUID!): Webhook minimalMetadata: MinimalMetadata! + chatThreads: [AgentChatThread!]! chatThread(id: UUID!): AgentChatThread! chatMessages(threadId: UUID!): [AgentMessage!]! chatStreamCatchupChunks(threadId: UUID!): ChatStreamCatchupChunks! getAiSystemPromptPreview: AiSystemPromptPreview! skills: [Skill!]! skill(id: UUID!): Skill - chatThreads( - """Limit or page results.""" - paging: CursorPaging! = {first: 10} - - """Specify to filter the records returned.""" - filter: AgentChatThreadFilter! = {} - - """Specify to sort results.""" - sorting: [AgentChatThreadSort!]! = [{field: updatedAt, direction: DESC}] - ): AgentChatThreadConnection! agentTurns(agentId: UUID!): [AgentTurn!]! checkUserExists(email: String!, captchaToken: String): CheckUserExist! checkWorkspaceInviteHashIsValid(inviteHash: String!): WorkspaceInviteHashValid! @@ -3057,56 +3034,6 @@ input AgentIdInput { id: UUID! } -input AgentChatThreadFilter { - and: [AgentChatThreadFilter!] - or: [AgentChatThreadFilter!] - id: UUIDFilterComparison - updatedAt: DateFieldComparison -} - -input DateFieldComparison { - is: Boolean - isNot: Boolean - eq: DateTime - neq: DateTime - gt: DateTime - gte: DateTime - lt: DateTime - lte: DateTime - in: [DateTime!] - notIn: [DateTime!] - between: DateFieldComparisonBetween - notBetween: DateFieldComparisonBetween -} - -input DateFieldComparisonBetween { - lower: DateTime! - upper: DateTime! -} - -input AgentChatThreadSort { - field: AgentChatThreadSortFields! - direction: SortDirection! - nulls: SortNulls -} - -enum AgentChatThreadSortFields { - id - updatedAt -} - -"""Sort Directions""" -enum SortDirection { - ASC - DESC -} - -"""Sort Nulls Options""" -enum SortNulls { - NULLS_FIRST - NULLS_LAST -} - input EventLogQueryInput { table: EventLogTable! filters: EventLogFiltersInput @@ -3292,6 +3219,10 @@ type Mutation { createChatThread: AgentChatThread! sendChatMessage(threadId: UUID!, text: String!, messageId: UUID!, browsingContext: JSON, modelId: String, fileIds: [UUID!]): SendChatMessageResult! stopAgentChatStream(threadId: UUID!): Boolean! + renameChatThread(id: UUID!, title: String!): AgentChatThread! + archiveChatThread(id: UUID!): AgentChatThread! + unarchiveChatThread(id: UUID!): AgentChatThread! + deleteChatThread(id: UUID!): Boolean! deleteQueuedChatMessage(messageId: UUID!): Boolean! createSkill(input: CreateSkillInput!): Skill! updateSkill(input: UpdateSkillInput!): Skill! diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index 996cc41d89..358f109d0d 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -2304,20 +2304,6 @@ export interface Skill { __typename: 'Skill' } -export interface AgentChatThread { - id: Scalars['UUID'] - title?: Scalars['String'] - totalInputTokens: Scalars['Int'] - totalOutputTokens: Scalars['Int'] - contextWindowTokens?: Scalars['Int'] - conversationSize: Scalars['Int'] - totalInputCredits: Scalars['Float'] - totalOutputCredits: Scalars['Float'] - createdAt: Scalars['DateTime'] - updatedAt: Scalars['DateTime'] - __typename: 'AgentChatThread' -} - export interface AgentMessage { id: Scalars['UUID'] threadId: Scalars['UUID'] @@ -2331,6 +2317,22 @@ export interface AgentMessage { __typename: 'AgentMessage' } +export interface AgentChatThread { + id: Scalars['ID'] + title?: Scalars['String'] + totalInputTokens: Scalars['Int'] + totalOutputTokens: Scalars['Int'] + contextWindowTokens?: Scalars['Int'] + conversationSize: Scalars['Int'] + totalInputCredits: Scalars['Float'] + totalOutputCredits: Scalars['Float'] + createdAt: Scalars['DateTime'] + updatedAt: Scalars['DateTime'] + deletedAt?: Scalars['DateTime'] + lastMessageAt?: Scalars['DateTime'] + __typename: 'AgentChatThread' +} + export interface AiSystemPromptSection { title: Scalars['String'] content: Scalars['String'] @@ -2363,22 +2365,6 @@ export interface AgentChatEvent { __typename: 'AgentChatEvent' } -export interface AgentChatThreadEdge { - /** The node containing the AgentChatThread */ - node: AgentChatThread - /** Cursor for this node. */ - cursor: Scalars['ConnectionCursor'] - __typename: 'AgentChatThreadEdge' -} - -export interface AgentChatThreadConnection { - /** Paging information */ - pageInfo: PageInfo - /** Array of edges. */ - edges: AgentChatThreadEdge[] - __typename: 'AgentChatThreadConnection' -} - export interface AgentTurnEvaluation { id: Scalars['UUID'] turnId: Scalars['UUID'] @@ -2591,13 +2577,13 @@ export interface Query { webhooks: Webhook[] webhook?: Webhook minimalMetadata: MinimalMetadata + chatThreads: AgentChatThread[] chatThread: AgentChatThread chatMessages: AgentMessage[] chatStreamCatchupChunks: ChatStreamCatchupChunks getAiSystemPromptPreview: AiSystemPromptPreview skills: Skill[] skill?: Skill - chatThreads: AgentChatThreadConnection agentTurns: AgentTurn[] checkUserExists: CheckUserExist checkWorkspaceInviteHashIsValid: WorkspaceInviteHashValid @@ -2633,16 +2619,6 @@ export interface Query { __typename: 'Query' } -export type AgentChatThreadSortFields = 'id' | 'updatedAt' - - -/** Sort Directions */ -export type SortDirection = 'ASC' | 'DESC' - - -/** Sort Nulls Options */ -export type SortNulls = 'NULLS_FIRST' | 'NULLS_LAST' - export type EventLogTable = 'WORKSPACE_EVENT' | 'PAGEVIEW' | 'OBJECT_EVENT' | 'USAGE_EVENT' | 'APPLICATION_LOG' export type UsageOperationType = 'AI_CHAT_TOKEN' | 'AI_WORKFLOW_TOKEN' | 'WORKFLOW_EXECUTION' | 'CODE_EXECUTION' | 'WEB_SEARCH' @@ -2774,6 +2750,10 @@ export interface Mutation { createChatThread: AgentChatThread sendChatMessage: SendChatMessageResult stopAgentChatStream: Scalars['Boolean'] + renameChatThread: AgentChatThread + archiveChatThread: AgentChatThread + unarchiveChatThread: AgentChatThread + deleteChatThread: Scalars['Boolean'] deleteQueuedChatMessage: Scalars['Boolean'] createSkill: Skill updateSkill: Skill @@ -5319,6 +5299,20 @@ export interface SkillGenqlSelection{ __scalar?: boolean | number } +export interface AgentMessageGenqlSelection{ + id?: boolean | number + threadId?: boolean | number + turnId?: boolean | number + agentId?: boolean | number + role?: boolean | number + status?: boolean | number + parts?: AgentMessagePartGenqlSelection + processedAt?: boolean | number + createdAt?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + export interface AgentChatThreadGenqlSelection{ id?: boolean | number title?: boolean | number @@ -5330,20 +5324,8 @@ export interface AgentChatThreadGenqlSelection{ totalOutputCredits?: boolean | number createdAt?: boolean | number updatedAt?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface AgentMessageGenqlSelection{ - id?: boolean | number - threadId?: boolean | number - turnId?: boolean | number - agentId?: boolean | number - role?: boolean | number - status?: boolean | number - parts?: AgentMessagePartGenqlSelection - processedAt?: boolean | number - createdAt?: boolean | number + deletedAt?: boolean | number + lastMessageAt?: boolean | number __typename?: boolean | number __scalar?: boolean | number } @@ -5385,24 +5367,6 @@ export interface AgentChatEventGenqlSelection{ __scalar?: boolean | number } -export interface AgentChatThreadEdgeGenqlSelection{ - /** The node containing the AgentChatThread */ - node?: AgentChatThreadGenqlSelection - /** Cursor for this node. */ - cursor?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface AgentChatThreadConnectionGenqlSelection{ - /** Paging information */ - pageInfo?: PageInfoGenqlSelection - /** Array of edges. */ - edges?: AgentChatThreadEdgeGenqlSelection - __typename?: boolean | number - __scalar?: boolean | number -} - export interface AgentTurnEvaluationGenqlSelection{ id?: boolean | number turnId?: boolean | number @@ -5617,19 +5581,13 @@ export interface QueryGenqlSelection{ webhooks?: WebhookGenqlSelection webhook?: (WebhookGenqlSelection & { __args: {id: Scalars['UUID']} }) minimalMetadata?: MinimalMetadataGenqlSelection + chatThreads?: AgentChatThreadGenqlSelection chatThread?: (AgentChatThreadGenqlSelection & { __args: {id: Scalars['UUID']} }) chatMessages?: (AgentMessageGenqlSelection & { __args: {threadId: Scalars['UUID']} }) chatStreamCatchupChunks?: (ChatStreamCatchupChunksGenqlSelection & { __args: {threadId: Scalars['UUID']} }) getAiSystemPromptPreview?: AiSystemPromptPreviewGenqlSelection skills?: SkillGenqlSelection skill?: (SkillGenqlSelection & { __args: {id: Scalars['UUID']} }) - chatThreads?: (AgentChatThreadConnectionGenqlSelection & { __args: { - /** Limit or page results. */ - paging: CursorPaging, - /** Specify to filter the records returned. */ - filter: AgentChatThreadFilter, - /** Specify to sort results. */ - sorting: AgentChatThreadSort[]} }) agentTurns?: (AgentTurnGenqlSelection & { __args: {agentId: Scalars['UUID']} }) checkUserExists?: (CheckUserExistGenqlSelection & { __args: {email: Scalars['String'], captchaToken?: (Scalars['String'] | null)} }) checkWorkspaceInviteHashIsValid?: (WorkspaceInviteHashValidGenqlSelection & { __args: {inviteHash: Scalars['String']} }) @@ -5676,14 +5634,6 @@ export interface AgentIdInput { /** The id of the agent. */ id: Scalars['UUID']} -export interface AgentChatThreadFilter {and?: (AgentChatThreadFilter[] | null),or?: (AgentChatThreadFilter[] | null),id?: (UUIDFilterComparison | null),updatedAt?: (DateFieldComparison | null)} - -export interface DateFieldComparison {is?: (Scalars['Boolean'] | null),isNot?: (Scalars['Boolean'] | null),eq?: (Scalars['DateTime'] | null),neq?: (Scalars['DateTime'] | null),gt?: (Scalars['DateTime'] | null),gte?: (Scalars['DateTime'] | null),lt?: (Scalars['DateTime'] | null),lte?: (Scalars['DateTime'] | null),in?: (Scalars['DateTime'][] | null),notIn?: (Scalars['DateTime'][] | null),between?: (DateFieldComparisonBetween | null),notBetween?: (DateFieldComparisonBetween | null)} - -export interface DateFieldComparisonBetween {lower: Scalars['DateTime'],upper: Scalars['DateTime']} - -export interface AgentChatThreadSort {field: AgentChatThreadSortFields,direction: SortDirection,nulls?: (SortNulls | 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)} @@ -5825,6 +5775,10 @@ export interface MutationGenqlSelection{ createChatThread?: AgentChatThreadGenqlSelection sendChatMessage?: (SendChatMessageResultGenqlSelection & { __args: {threadId: Scalars['UUID'], text: Scalars['String'], messageId: Scalars['UUID'], browsingContext?: (Scalars['JSON'] | null), modelId?: (Scalars['String'] | null), fileIds?: (Scalars['UUID'][] | null)} }) stopAgentChatStream?: { __args: {threadId: Scalars['UUID']} } + renameChatThread?: (AgentChatThreadGenqlSelection & { __args: {id: Scalars['UUID'], title: Scalars['String']} }) + archiveChatThread?: (AgentChatThreadGenqlSelection & { __args: {id: Scalars['UUID']} }) + unarchiveChatThread?: (AgentChatThreadGenqlSelection & { __args: {id: Scalars['UUID']} }) + deleteChatThread?: { __args: {id: Scalars['UUID']} } deleteQueuedChatMessage?: { __args: {messageId: Scalars['UUID']} } createSkill?: (SkillGenqlSelection & { __args: {input: CreateSkillInput} }) updateSkill?: (SkillGenqlSelection & { __args: {input: UpdateSkillInput} }) @@ -8045,14 +7999,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null - const AgentChatThread_possibleTypes: string[] = ['AgentChatThread'] - export const isAgentChatThread = (obj?: { __typename?: any } | null): obj is AgentChatThread => { - if (!obj?.__typename) throw new Error('__typename is missing in "isAgentChatThread"') - return AgentChatThread_possibleTypes.includes(obj.__typename) - } - - - const AgentMessage_possibleTypes: string[] = ['AgentMessage'] export const isAgentMessage = (obj?: { __typename?: any } | null): obj is AgentMessage => { if (!obj?.__typename) throw new Error('__typename is missing in "isAgentMessage"') @@ -8061,6 +8007,14 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null + const AgentChatThread_possibleTypes: string[] = ['AgentChatThread'] + export const isAgentChatThread = (obj?: { __typename?: any } | null): obj is AgentChatThread => { + if (!obj?.__typename) throw new Error('__typename is missing in "isAgentChatThread"') + return AgentChatThread_possibleTypes.includes(obj.__typename) + } + + + const AiSystemPromptSection_possibleTypes: string[] = ['AiSystemPromptSection'] export const isAiSystemPromptSection = (obj?: { __typename?: any } | null): obj is AiSystemPromptSection => { if (!obj?.__typename) throw new Error('__typename is missing in "isAiSystemPromptSection"') @@ -8101,22 +8055,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null - const AgentChatThreadEdge_possibleTypes: string[] = ['AgentChatThreadEdge'] - export const isAgentChatThreadEdge = (obj?: { __typename?: any } | null): obj is AgentChatThreadEdge => { - if (!obj?.__typename) throw new Error('__typename is missing in "isAgentChatThreadEdge"') - return AgentChatThreadEdge_possibleTypes.includes(obj.__typename) - } - - - - const AgentChatThreadConnection_possibleTypes: string[] = ['AgentChatThreadConnection'] - export const isAgentChatThreadConnection = (obj?: { __typename?: any } | null): obj is AgentChatThreadConnection => { - if (!obj?.__typename) throw new Error('__typename is missing in "isAgentChatThreadConnection"') - return AgentChatThreadConnection_possibleTypes.includes(obj.__typename) - } - - - const AgentTurnEvaluation_possibleTypes: string[] = ['AgentTurnEvaluation'] export const isAgentTurnEvaluation = (obj?: { __typename?: any } | null): obj is AgentTurnEvaluation => { if (!obj?.__typename) throw new Error('__typename is missing in "isAgentTurnEvaluation"') @@ -8848,21 +8786,6 @@ export const enumAllMetadataName = { webhook: 'webhook' as const } -export const enumAgentChatThreadSortFields = { - id: 'id' as const, - updatedAt: 'updatedAt' as const -} - -export const enumSortDirection = { - ASC: 'ASC' as const, - DESC: 'DESC' as const -} - -export const enumSortNulls = { - NULLS_FIRST: 'NULLS_FIRST' as const, - NULLS_LAST: 'NULLS_LAST' as const -} - export const enumEventLogTable = { WORKSPACE_EVENT: 'WORKSPACE_EVENT' as const, PAGEVIEW: 'PAGEVIEW' as const, diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index c888bc731b..2019bbd814 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -60,29 +60,26 @@ export default { 254, 264, 265, + 291, + 300, 301, 302, 303, - 304, + 305, 306, 307, 308, 309, 310, 311, - 312, - 314, - 316, - 324, - 330, - 331, + 313, + 315, + 325, 332, - 334, - 341, - 348, - 384, - 459, - 471 + 339, + 375, + 450, + 462 ], "types": { "BillingProductDTO": { @@ -5242,41 +5239,6 @@ export default { 1 ] }, - "AgentChatThread": { - "id": [ - 3 - ], - "title": [ - 1 - ], - "totalInputTokens": [ - 21 - ], - "totalOutputTokens": [ - 21 - ], - "contextWindowTokens": [ - 21 - ], - "conversationSize": [ - 21 - ], - "totalInputCredits": [ - 11 - ], - "totalOutputCredits": [ - 11 - ], - "createdAt": [ - 4 - ], - "updatedAt": [ - 4 - ], - "__typename": [ - 1 - ] - }, "AgentMessage": { "id": [ 3 @@ -5309,6 +5271,48 @@ export default { 1 ] }, + "AgentChatThread": { + "id": [ + 291 + ], + "title": [ + 1 + ], + "totalInputTokens": [ + 21 + ], + "totalOutputTokens": [ + 21 + ], + "contextWindowTokens": [ + 21 + ], + "conversationSize": [ + 21 + ], + "totalInputCredits": [ + 11 + ], + "totalOutputCredits": [ + 11 + ], + "createdAt": [ + 4 + ], + "updatedAt": [ + 4 + ], + "deletedAt": [ + 4 + ], + "lastMessageAt": [ + 4 + ], + "__typename": [ + 1 + ] + }, + "ID": {}, "AiSystemPromptSection": { "title": [ 1 @@ -5325,7 +5329,7 @@ export default { }, "AiSystemPromptPreview": { "sections": [ - 291 + 292 ], "estimatedTokenCount": [ 21 @@ -5370,28 +5374,6 @@ export default { 1 ] }, - "AgentChatThreadEdge": { - "node": [ - 289 - ], - "cursor": [ - 43 - ], - "__typename": [ - 1 - ] - }, - "AgentChatThreadConnection": { - "pageInfo": [ - 204 - ], - "edges": [ - 296 - ], - "__typename": [ - 1 - ] - }, "AgentTurnEvaluation": { "id": [ 3 @@ -5423,10 +5405,10 @@ export default { 3 ], "evaluations": [ - 298 + 297 ], "messages": [ - 290 + 289 ], "createdAt": [ 4 @@ -5443,19 +5425,19 @@ export default { 1 ], "syncStatus": [ - 301 + 300 ], "syncStage": [ - 302 + 301 ], "visibility": [ - 303 + 302 ], "isContactAutoCreationEnabled": [ 6 ], "contactAutoCreationPolicy": [ - 304 + 303 ], "isSyncEnabled": [ 6 @@ -5491,22 +5473,22 @@ export default { 3 ], "visibility": [ - 306 + 305 ], "handle": [ 1 ], "type": [ - 307 + 306 ], "isContactAutoCreationEnabled": [ 6 ], "contactAutoCreationPolicy": [ - 308 + 307 ], "messageFolderImportPolicy": [ - 309 + 308 ], "excludeNonProfessionalEmails": [ 6 @@ -5515,7 +5497,7 @@ export default { 6 ], "pendingGroupEmailsAction": [ - 310 + 309 ], "isSyncEnabled": [ 6 @@ -5524,10 +5506,10 @@ export default { 4 ], "syncStatus": [ - 311 + 310 ], "syncStage": [ - 312 + 311 ], "syncStageStartedAt": [ 4 @@ -5578,7 +5560,7 @@ export default { 1 ], "pendingSyncAction": [ - 314 + 313 ], "messageChannelId": [ 3 @@ -5596,7 +5578,7 @@ export default { "MessageFolderPendingSyncAction": {}, "CollectionHash": { "collectionName": [ - 316 + 315 ], "hash": [ 1 @@ -5663,13 +5645,13 @@ export default { }, "MinimalMetadata": { "objectMetadataItems": [ - 317 + 316 ], "views": [ - 318 + 317 ], "collectionHashes": [ - 315 + 314 ], "__typename": [ 1 @@ -5835,7 +5817,7 @@ export default { 2, { "input": [ - 322, + 321, "GetApiKeyInput!" ] } @@ -5939,7 +5921,7 @@ export default { 35, { "input": [ - 323, + 322, "LogicFunctionIdInput!" ] } @@ -5951,7 +5933,7 @@ export default { 15, { "input": [ - 323, + 322, "LogicFunctionIdInput!" ] } @@ -5960,7 +5942,7 @@ export default { 1, { "input": [ - 323, + 322, "LogicFunctionIdInput!" ] } @@ -6043,7 +6025,7 @@ export default { 25, { "input": [ - 325, + 323, "AgentIdInput!" ] } @@ -6103,7 +6085,7 @@ export default { } ], "myMessageFolders": [ - 313, + 312, { "messageChannelId": [ 3 @@ -6111,7 +6093,7 @@ export default { } ], "myMessageChannels": [ - 305, + 304, { "connectedAccountId": [ 3 @@ -6134,7 +6116,7 @@ export default { 280 ], "myCalendarChannels": [ - 300, + 299, { "connectedAccountId": [ 3 @@ -6142,10 +6124,10 @@ export default { } ], "webhooks": [ - 320 + 319 ], "webhook": [ - 320, + 319, { "id": [ 3, @@ -6154,10 +6136,13 @@ export default { } ], "minimalMetadata": [ - 319 + 318 + ], + "chatThreads": [ + 290 ], "chatThread": [ - 289, + 290, { "id": [ 3, @@ -6166,7 +6151,7 @@ export default { } ], "chatMessages": [ - 290, + 289, { "threadId": [ 3, @@ -6175,7 +6160,7 @@ export default { } ], "chatStreamCatchupChunks": [ - 293, + 294, { "threadId": [ 3, @@ -6184,7 +6169,7 @@ export default { } ], "getAiSystemPromptPreview": [ - 292 + 293 ], "skills": [ 288 @@ -6198,25 +6183,8 @@ export default { ] } ], - "chatThreads": [ - 297, - { - "paging": [ - 42, - "CursorPaging!" - ], - "filter": [ - 326, - "AgentChatThreadFilter!" - ], - "sorting": [ - 329, - "[AgentChatThreadSort!]!" - ] - } - ], "agentTurns": [ - 299, + 298, { "agentId": [ 3, @@ -6364,7 +6332,7 @@ export default { 287, { "input": [ - 333, + 324, "EventLogQueryInput!" ] } @@ -6373,7 +6341,7 @@ export default { 278, { "input": [ - 337, + 328, "PieChartDataInput!" ] } @@ -6382,7 +6350,7 @@ export default { 276, { "input": [ - 338, + 329, "LineChartDataInput!" ] } @@ -6391,7 +6359,7 @@ export default { 273, { "input": [ - 339, + 330, "BarChartDataInput!" ] } @@ -6441,7 +6409,7 @@ export default { 244, { "input": [ - 340 + 331 ] } ], @@ -6480,13 +6448,12 @@ export default { }, "LogicFunctionIdInput": { "id": [ - 324 + 291 ], "__typename": [ 1 ] }, - "ID": {}, "AgentIdInput": { "id": [ 3 @@ -6495,98 +6462,12 @@ export default { 1 ] }, - "AgentChatThreadFilter": { - "and": [ - 326 - ], - "or": [ - 326 - ], - "id": [ - 45 - ], - "updatedAt": [ - 327 - ], - "__typename": [ - 1 - ] - }, - "DateFieldComparison": { - "is": [ - 6 - ], - "isNot": [ - 6 - ], - "eq": [ - 4 - ], - "neq": [ - 4 - ], - "gt": [ - 4 - ], - "gte": [ - 4 - ], - "lt": [ - 4 - ], - "lte": [ - 4 - ], - "in": [ - 4 - ], - "notIn": [ - 4 - ], - "between": [ - 328 - ], - "notBetween": [ - 328 - ], - "__typename": [ - 1 - ] - }, - "DateFieldComparisonBetween": { - "lower": [ - 4 - ], - "upper": [ - 4 - ], - "__typename": [ - 1 - ] - }, - "AgentChatThreadSort": { - "field": [ - 330 - ], - "direction": [ - 331 - ], - "nulls": [ - 332 - ], - "__typename": [ - 1 - ] - }, - "AgentChatThreadSortFields": {}, - "SortDirection": {}, - "SortNulls": {}, "EventLogQueryInput": { "table": [ - 334 + 325 ], "filters": [ - 335 + 326 ], "first": [ 21 @@ -6607,7 +6488,7 @@ export default { 1 ], "dateRange": [ - 336 + 327 ], "recordId": [ 1 @@ -6674,7 +6555,7 @@ export default { 1 ], "operationTypes": [ - 341 + 332 ], "__typename": [ 1 @@ -6686,7 +6567,7 @@ export default { 6, { "input": [ - 343, + 334, "AddQuerySubscriptionInput!" ] } @@ -6695,7 +6576,7 @@ export default { 6, { "input": [ - 344, + 335, "RemoveQueryFromEventStreamInput!" ] } @@ -6704,7 +6585,7 @@ export default { 149, { "inputs": [ - 345, + 336, "[CreateNavigationMenuItemInput!]!" ] } @@ -6713,7 +6594,7 @@ export default { 149, { "input": [ - 345, + 336, "CreateNavigationMenuItemInput!" ] } @@ -6722,7 +6603,7 @@ export default { 149, { "inputs": [ - 346, + 337, "[UpdateOneNavigationMenuItemInput!]!" ] } @@ -6731,7 +6612,7 @@ export default { 149, { "input": [ - 346, + 337, "UpdateOneNavigationMenuItemInput!" ] } @@ -6758,7 +6639,7 @@ export default { 121, { "file": [ - 348, + 339, "Upload!" ] } @@ -6767,7 +6648,7 @@ export default { 121, { "file": [ - 348, + 339, "Upload!" ] } @@ -6776,7 +6657,7 @@ export default { 121, { "file": [ - 348, + 339, "Upload!" ] } @@ -6785,7 +6666,7 @@ export default { 121, { "file": [ - 348, + 339, "Upload!" ] } @@ -6794,7 +6675,7 @@ export default { 121, { "file": [ - 348, + 339, "Upload!" ] } @@ -6803,7 +6684,7 @@ export default { 121, { "file": [ - 348, + 339, "Upload!" ], "fieldMetadataId": [ @@ -6816,7 +6697,7 @@ export default { 121, { "file": [ - 348, + 339, "Upload!" ], "fieldMetadataUniversalIdentifier": [ @@ -6829,7 +6710,7 @@ export default { 55, { "input": [ - 349, + 340, "CreateViewFilterGroupInput!" ] } @@ -6842,7 +6723,7 @@ export default { "String!" ], "input": [ - 350, + 341, "UpdateViewFilterGroupInput!" ] } @@ -6869,7 +6750,7 @@ export default { 57, { "input": [ - 351, + 342, "CreateViewFilterInput!" ] } @@ -6878,7 +6759,7 @@ export default { 57, { "input": [ - 352, + 343, "UpdateViewFilterInput!" ] } @@ -6887,7 +6768,7 @@ export default { 57, { "input": [ - 354, + 345, "DeleteViewFilterInput!" ] } @@ -6896,7 +6777,7 @@ export default { 57, { "input": [ - 355, + 346, "DestroyViewFilterInput!" ] } @@ -6905,7 +6786,7 @@ export default { 63, { "input": [ - 356, + 347, "CreateViewInput!" ] } @@ -6918,7 +6799,7 @@ export default { "String!" ], "input": [ - 357, + 348, "UpdateViewInput!" ] } @@ -6945,7 +6826,7 @@ export default { 63, { "input": [ - 358, + 349, "UpsertViewWidgetInput!" ] } @@ -6954,7 +6835,7 @@ export default { 60, { "input": [ - 363, + 354, "CreateViewSortInput!" ] } @@ -6963,7 +6844,7 @@ export default { 60, { "input": [ - 364, + 355, "UpdateViewSortInput!" ] } @@ -6972,7 +6853,7 @@ export default { 6, { "input": [ - 366, + 357, "DeleteViewSortInput!" ] } @@ -6981,7 +6862,7 @@ export default { 6, { "input": [ - 367, + 358, "DestroyViewSortInput!" ] } @@ -6990,7 +6871,7 @@ export default { 53, { "input": [ - 368, + 359, "UpdateViewFieldInput!" ] } @@ -6999,7 +6880,7 @@ export default { 53, { "input": [ - 370, + 361, "CreateViewFieldInput!" ] } @@ -7008,7 +6889,7 @@ export default { 53, { "inputs": [ - 370, + 361, "[CreateViewFieldInput!]!" ] } @@ -7017,7 +6898,7 @@ export default { 53, { "input": [ - 371, + 362, "DeleteViewFieldInput!" ] } @@ -7026,7 +6907,7 @@ export default { 53, { "input": [ - 372, + 363, "DestroyViewFieldInput!" ] } @@ -7035,7 +6916,7 @@ export default { 62, { "input": [ - 373, + 364, "UpdateViewFieldGroupInput!" ] } @@ -7044,7 +6925,7 @@ export default { 62, { "input": [ - 375, + 366, "CreateViewFieldGroupInput!" ] } @@ -7053,7 +6934,7 @@ export default { 62, { "inputs": [ - 375, + 366, "[CreateViewFieldGroupInput!]!" ] } @@ -7062,7 +6943,7 @@ export default { 62, { "input": [ - 376, + 367, "DeleteViewFieldGroupInput!" ] } @@ -7071,7 +6952,7 @@ export default { 62, { "input": [ - 377, + 368, "DestroyViewFieldGroupInput!" ] } @@ -7080,7 +6961,7 @@ export default { 63, { "input": [ - 378, + 369, "UpsertFieldsWidgetInput!" ] } @@ -7089,7 +6970,7 @@ export default { 2, { "input": [ - 381, + 372, "CreateApiKeyInput!" ] } @@ -7098,7 +6979,7 @@ export default { 2, { "input": [ - 382, + 373, "UpdateApiKeyInput!" ] } @@ -7107,7 +6988,7 @@ export default { 2, { "input": [ - 383, + 374, "RevokeApiKeyInput!" ] } @@ -7149,7 +7030,7 @@ export default { 119, { "type": [ - 384, + 375, "AnalyticsType!" ], "name": [ @@ -7262,7 +7143,7 @@ export default { 120, { "input": [ - 385, + 376, "CreateApprovedAccessDomainInput!" ] } @@ -7271,7 +7152,7 @@ export default { 6, { "input": [ - 386, + 377, "DeleteApprovedAccessDomainInput!" ] } @@ -7280,7 +7161,7 @@ export default { 120, { "input": [ - 387, + 378, "ValidateApprovedAccessDomainInput!" ] } @@ -7289,7 +7170,7 @@ export default { 116, { "input": [ - 388, + 379, "CreatePageLayoutTabInput!" ] } @@ -7302,7 +7183,7 @@ export default { "String!" ], "input": [ - 389, + 380, "UpdatePageLayoutTabInput!" ] } @@ -7320,7 +7201,7 @@ export default { 117, { "input": [ - 390, + 381, "CreatePageLayoutInput!" ] } @@ -7333,7 +7214,7 @@ export default { "String!" ], "input": [ - 391, + 382, "UpdatePageLayoutInput!" ] } @@ -7355,7 +7236,7 @@ export default { "String!" ], "input": [ - 392, + 383, "UpdatePageLayoutWithTabsInput!" ] } @@ -7391,7 +7272,7 @@ export default { 78, { "input": [ - 396, + 387, "CreatePageLayoutWidgetInput!" ] } @@ -7404,7 +7285,7 @@ export default { "String!" ], "input": [ - 397, + 388, "UpdatePageLayoutWidgetInput!" ] } @@ -7422,7 +7303,7 @@ export default { 35, { "input": [ - 323, + 322, "LogicFunctionIdInput!" ] } @@ -7431,7 +7312,7 @@ export default { 35, { "input": [ - 398, + 389, "CreateLogicFunctionFromSourceInput!" ] } @@ -7440,7 +7321,7 @@ export default { 158, { "input": [ - 399, + 390, "ExecuteOneLogicFunctionInput!" ] } @@ -7449,7 +7330,7 @@ export default { 6, { "input": [ - 400, + 391, "UpdateLogicFunctionFromSourceInput!" ] } @@ -7458,7 +7339,7 @@ export default { 263, { "input": [ - 402, + 393, "CreateCommandMenuItemInput!" ] } @@ -7467,7 +7348,7 @@ export default { 263, { "input": [ - 403, + 394, "UpdateCommandMenuItemInput!" ] } @@ -7485,7 +7366,7 @@ export default { 34, { "input": [ - 404, + 395, "CreateFrontComponentInput!" ] } @@ -7494,7 +7375,7 @@ export default { 34, { "input": [ - 405, + 396, "UpdateFrontComponentInput!" ] } @@ -7512,7 +7393,7 @@ export default { 49, { "input": [ - 407, + 398, "CreateOneObjectInput!" ] } @@ -7521,7 +7402,7 @@ export default { 49, { "input": [ - 409, + 400, "DeleteOneObjectInput!" ] } @@ -7530,7 +7411,7 @@ export default { 49, { "input": [ - 410, + 401, "UpdateOneObjectInput!" ] } @@ -7539,7 +7420,7 @@ export default { 25, { "input": [ - 412, + 403, "CreateAgentInput!" ] } @@ -7548,7 +7429,7 @@ export default { 25, { "input": [ - 413, + 404, "UpdateAgentInput!" ] } @@ -7557,7 +7438,7 @@ export default { 25, { "input": [ - 325, + 323, "AgentIdInput!" ] } @@ -7579,7 +7460,7 @@ export default { 29, { "createRoleInput": [ - 414, + 405, "CreateRoleInput!" ] } @@ -7588,7 +7469,7 @@ export default { 29, { "updateRoleInput": [ - 415, + 406, "UpdateRoleInput!" ] } @@ -7606,7 +7487,7 @@ export default { 16, { "upsertObjectPermissionsInput": [ - 417, + 408, "UpsertObjectPermissionsInput!" ] } @@ -7615,7 +7496,7 @@ export default { 27, { "upsertPermissionFlagsInput": [ - 419, + 410, "UpsertPermissionFlagsInput!" ] } @@ -7624,7 +7505,7 @@ export default { 26, { "upsertFieldPermissionsInput": [ - 420, + 411, "UpsertFieldPermissionsInput!" ] } @@ -7633,7 +7514,7 @@ export default { 220, { "input": [ - 422, + 413, "UpsertRowLevelPermissionPredicatesInput!" ] } @@ -7664,7 +7545,7 @@ export default { 37, { "input": [ - 425, + 416, "CreateOneFieldMetadataInput!" ] } @@ -7673,7 +7554,7 @@ export default { 37, { "input": [ - 427, + 418, "UpdateOneFieldMetadataInput!" ] } @@ -7682,7 +7563,7 @@ export default { 37, { "input": [ - 429, + 420, "DeleteOneFieldInput!" ] } @@ -7691,7 +7572,7 @@ export default { 59, { "input": [ - 430, + 421, "CreateViewGroupInput!" ] } @@ -7700,7 +7581,7 @@ export default { 59, { "inputs": [ - 430, + 421, "[CreateViewGroupInput!]!" ] } @@ -7709,7 +7590,7 @@ export default { 59, { "input": [ - 431, + 422, "UpdateViewGroupInput!" ] } @@ -7718,7 +7599,7 @@ export default { 59, { "inputs": [ - 431, + 422, "[UpdateViewGroupInput!]!" ] } @@ -7727,7 +7608,7 @@ export default { 59, { "input": [ - 433, + 424, "DeleteViewGroupInput!" ] } @@ -7736,34 +7617,34 @@ export default { 59, { "input": [ - 434, + 425, "DestroyViewGroupInput!" ] } ], "updateMessageFolder": [ - 313, + 312, { "input": [ - 435, + 426, "UpdateMessageFolderInput!" ] } ], "updateMessageFolders": [ - 313, + 312, { "input": [ - 437, + 428, "UpdateMessageFoldersInput!" ] } ], "updateMessageChannel": [ - 305, + 304, { "input": [ - 438, + 429, "UpdateMessageChannelInput!" ] } @@ -7778,34 +7659,34 @@ export default { } ], "updateCalendarChannel": [ - 300, + 299, { "input": [ - 440, + 431, "UpdateCalendarChannelInput!" ] } ], "createWebhook": [ - 320, + 319, { "input": [ - 442, + 433, "CreateWebhookInput!" ] } ], "updateWebhook": [ - 320, + 319, { "input": [ - 443, + 434, "UpdateWebhookInput!" ] } ], "deleteWebhook": [ - 320, + 319, { "id": [ 3, @@ -7814,10 +7695,10 @@ export default { } ], "createChatThread": [ - 289 + 290 ], "sendChatMessage": [ - 294, + 295, { "threadId": [ 3, @@ -7852,6 +7733,46 @@ export default { ] } ], + "renameChatThread": [ + 290, + { + "id": [ + 3, + "UUID!" + ], + "title": [ + 1, + "String!" + ] + } + ], + "archiveChatThread": [ + 290, + { + "id": [ + 3, + "UUID!" + ] + } + ], + "unarchiveChatThread": [ + 290, + { + "id": [ + 3, + "UUID!" + ] + } + ], + "deleteChatThread": [ + 6, + { + "id": [ + 3, + "UUID!" + ] + } + ], "deleteQueuedChatMessage": [ 6, { @@ -7865,7 +7786,7 @@ export default { 288, { "input": [ - 445, + 436, "CreateSkillInput!" ] } @@ -7874,7 +7795,7 @@ export default { 288, { "input": [ - 446, + 437, "UpdateSkillInput!" ] } @@ -7907,7 +7828,7 @@ export default { } ], "evaluateAgentTurn": [ - 298, + 297, { "turnId": [ 3, @@ -7916,7 +7837,7 @@ export default { } ], "runEvaluationInput": [ - 299, + 298, { "agentId": [ 3, @@ -7932,7 +7853,7 @@ export default { 229, { "input": [ - 447, + 438, "GetAuthorizationUrlForSSOInput!" ] } @@ -8186,7 +8107,7 @@ export default { 188, { "input": [ - 448, + 439, "CreateApplicationRegistrationInput!" ] } @@ -8195,7 +8116,7 @@ export default { 7, { "input": [ - 449, + 440, "UpdateApplicationRegistrationInput!" ] } @@ -8222,7 +8143,7 @@ export default { 5, { "input": [ - 451, + 442, "CreateApplicationRegistrationVariableInput!" ] } @@ -8231,7 +8152,7 @@ export default { 5, { "input": [ - 452, + 443, "UpdateApplicationRegistrationVariableInput!" ] } @@ -8249,7 +8170,7 @@ export default { 7, { "file": [ - 348, + 339, "Upload!" ], "universalIdentifier": [ @@ -8320,7 +8241,7 @@ export default { 6, { "input": [ - 454, + 445, "UpdateWorkspaceMemberSettingsInput!" ] } @@ -8354,7 +8275,7 @@ export default { 69, { "data": [ - 455, + 446, "ActivateWorkspaceInput!" ] } @@ -8363,7 +8284,7 @@ export default { 69, { "data": [ - 456, + 447, "UpdateWorkspaceInput!" ] } @@ -8390,7 +8311,7 @@ export default { 6, { "workspaceMigration": [ - 457, + 448, "WorkspaceMigrationInput!" ] } @@ -8425,7 +8346,7 @@ export default { 196, { "input": [ - 460, + 451, "SetupOIDCSsoInput!" ] } @@ -8434,7 +8355,7 @@ export default { 196, { "input": [ - 461, + 452, "SetupSAMLSsoInput!" ] } @@ -8443,7 +8364,7 @@ export default { 192, { "input": [ - 462, + 453, "DeleteSsoInput!" ] } @@ -8452,7 +8373,7 @@ export default { 193, { "input": [ - 463, + 454, "EditSsoInput!" ] } @@ -8483,7 +8404,7 @@ export default { 284, { "input": [ - 464, + 455, "SendEmailInput!" ] } @@ -8509,7 +8430,7 @@ export default { "String!" ], "connectionParameters": [ - 466, + 457, "EmailAccountConnectionParameters!" ], "id": [ @@ -8521,7 +8442,7 @@ export default { 160, { "input": [ - 468, + 459, "UpdateLabPublicFeatureFlagInput!" ] } @@ -8594,7 +8515,7 @@ export default { 71, { "input": [ - 469, + 460, "CreateOneAppTokenInput!" ] } @@ -8649,7 +8570,7 @@ export default { 247, { "file": [ - 348, + 339, "Upload!" ], "applicationUniversalIdentifier": [ @@ -8657,7 +8578,7 @@ export default { "String!" ], "fileFolder": [ - 471, + 462, "FileFolder!" ], "filePath": [ @@ -8766,7 +8687,7 @@ export default { 3 ], "update": [ - 347 + 338 ], "__typename": [ 1 @@ -8873,7 +8794,7 @@ export default { 3 ], "update": [ - 353 + 344 ], "__typename": [ 1 @@ -9029,16 +8950,16 @@ export default { 3 ], "viewFields": [ - 359 + 350 ], "viewFilters": [ - 360 + 351 ], "viewFilterGroups": [ - 361 + 352 ], "viewSorts": [ - 362 + 353 ], "__typename": [ 1 @@ -9143,7 +9064,7 @@ export default { 3 ], "update": [ - 365 + 356 ], "__typename": [ 1 @@ -9178,7 +9099,7 @@ export default { 3 ], "update": [ - 369 + 360 ], "__typename": [ 1 @@ -9254,7 +9175,7 @@ export default { 3 ], "update": [ - 374 + 365 ], "__typename": [ 1 @@ -9318,10 +9239,10 @@ export default { 3 ], "groups": [ - 379 + 370 ], "fields": [ - 380 + 371 ], "__typename": [ 1 @@ -9341,7 +9262,7 @@ export default { 6 ], "fields": [ - 380 + 371 ], "__typename": [ 1 @@ -9510,7 +9431,7 @@ export default { 3 ], "tabs": [ - 393 + 384 ], "__typename": [ 1 @@ -9533,7 +9454,7 @@ export default { 82 ], "widgets": [ - 394 + 385 ], "__typename": [ 1 @@ -9556,7 +9477,7 @@ export default { 3 ], "gridPosition": [ - 395 + 386 ], "position": [ 15 @@ -9605,7 +9526,7 @@ export default { 3 ], "gridPosition": [ - 395 + 386 ], "position": [ 15 @@ -9631,7 +9552,7 @@ export default { 3 ], "gridPosition": [ - 395 + 386 ], "position": [ 15 @@ -9703,7 +9624,7 @@ export default { 3 ], "update": [ - 401 + 392 ], "__typename": [ 1 @@ -9863,7 +9784,7 @@ export default { 3 ], "update": [ - 406 + 397 ], "__typename": [ 1 @@ -9882,7 +9803,7 @@ export default { }, "CreateOneObjectInput": { "object": [ - 408 + 399 ], "__typename": [ 1 @@ -9942,7 +9863,7 @@ export default { }, "UpdateOneObjectInput": { "update": [ - 411 + 402 ], "id": [ 3 @@ -10114,7 +10035,7 @@ export default { }, "UpdateRoleInput": { "update": [ - 416 + 407 ], "id": [ 3 @@ -10169,7 +10090,7 @@ export default { 3 ], "objectPermissions": [ - 418 + 409 ], "__typename": [ 1 @@ -10211,7 +10132,7 @@ export default { 3 ], "fieldPermissions": [ - 421 + 412 ], "__typename": [ 1 @@ -10242,10 +10163,10 @@ export default { 3 ], "predicates": [ - 423 + 414 ], "predicateGroups": [ - 424 + 415 ], "__typename": [ 1 @@ -10305,7 +10226,7 @@ export default { }, "CreateOneFieldMetadataInput": { "field": [ - 426 + 417 ], "__typename": [ 1 @@ -10378,7 +10299,7 @@ export default { 3 ], "update": [ - 428 + 419 ], "__typename": [ 1 @@ -10470,7 +10391,7 @@ export default { 3 ], "update": [ - 432 + 423 ], "__typename": [ 1 @@ -10514,7 +10435,7 @@ export default { 3 ], "update": [ - 436 + 427 ], "__typename": [ 1 @@ -10533,7 +10454,7 @@ export default { 3 ], "update": [ - 436 + 427 ], "__typename": [ 1 @@ -10544,7 +10465,7 @@ export default { 3 ], "update": [ - 439 + 430 ], "__typename": [ 1 @@ -10552,16 +10473,16 @@ export default { }, "UpdateMessageChannelInputUpdates": { "visibility": [ - 306 + 305 ], "isContactAutoCreationEnabled": [ 6 ], "contactAutoCreationPolicy": [ - 308 + 307 ], "messageFolderImportPolicy": [ - 309 + 308 ], "isSyncEnabled": [ 6 @@ -10581,7 +10502,7 @@ export default { 3 ], "update": [ - 441 + 432 ], "__typename": [ 1 @@ -10589,13 +10510,13 @@ export default { }, "UpdateCalendarChannelInputUpdates": { "visibility": [ - 303 + 302 ], "isContactAutoCreationEnabled": [ 6 ], "contactAutoCreationPolicy": [ - 304 + 303 ], "isSyncEnabled": [ 6 @@ -10629,7 +10550,7 @@ export default { 3 ], "update": [ - 444 + 435 ], "__typename": [ 1 @@ -10734,7 +10655,7 @@ export default { 1 ], "update": [ - 450 + 441 ], "__typename": [ 1 @@ -10782,7 +10703,7 @@ export default { 1 ], "update": [ - 453 + 444 ], "__typename": [ 1 @@ -10897,7 +10818,7 @@ export default { }, "WorkspaceMigrationInput": { "actions": [ - 458 + 449 ], "__typename": [ 1 @@ -10905,10 +10826,10 @@ export default { }, "WorkspaceMigrationDeleteActionInput": { "type": [ - 459 + 450 ], "metadataName": [ - 316 + 315 ], "universalIdentifier": [ 1 @@ -11000,7 +10921,7 @@ export default { 1 ], "files": [ - 465 + 456 ], "__typename": [ 1 @@ -11019,13 +10940,13 @@ export default { }, "EmailAccountConnectionParameters": { "IMAP": [ - 467 + 458 ], "SMTP": [ - 467 + 458 ], "CALDAV": [ - 467 + 458 ], "__typename": [ 1 @@ -11064,7 +10985,7 @@ export default { }, "CreateOneAppTokenInput": { "appToken": [ - 470 + 461 ], "__typename": [ 1 @@ -11093,13 +11014,13 @@ export default { 221, { "input": [ - 473, + 464, "LogicFunctionLogsInput!" ] } ], "onAgentChatEvent": [ - 295, + 296, { "threadId": [ 3, diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index e645580fee..04461fa796 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -59,7 +59,9 @@ export type AgentChatThread = { contextWindowTokens?: Maybe; conversationSize: Scalars['Int']; createdAt: Scalars['DateTime']; - id: Scalars['UUID']; + deletedAt?: Maybe; + id: Scalars['ID']; + lastMessageAt?: Maybe; title?: Maybe; totalInputCredits: Scalars['Float']; totalInputTokens: Scalars['Int']; @@ -68,40 +70,6 @@ export type AgentChatThread = { updatedAt: Scalars['DateTime']; }; -export type AgentChatThreadConnection = { - __typename?: 'AgentChatThreadConnection'; - /** Array of edges. */ - edges: Array; - /** Paging information */ - pageInfo: PageInfo; -}; - -export type AgentChatThreadEdge = { - __typename?: 'AgentChatThreadEdge'; - /** Cursor for this node. */ - cursor: Scalars['ConnectionCursor']; - /** The node containing the AgentChatThread */ - node: AgentChatThread; -}; - -export type AgentChatThreadFilter = { - and?: InputMaybe>; - id?: InputMaybe; - or?: InputMaybe>; - updatedAt?: InputMaybe; -}; - -export type AgentChatThreadSort = { - direction: SortDirection; - field: AgentChatThreadSortFields; - nulls?: InputMaybe; -}; - -export enum AgentChatThreadSortFields { - id = 'id', - updatedAt = 'updatedAt' -} - export type AgentIdInput = { /** The id of the agent. */ id: Scalars['UUID']; @@ -1278,26 +1246,6 @@ export enum DatabaseEventAction { UPSERTED = 'UPSERTED' } -export type DateFieldComparison = { - between?: InputMaybe; - eq?: InputMaybe; - gt?: InputMaybe; - gte?: InputMaybe; - in?: InputMaybe>; - is?: InputMaybe; - isNot?: InputMaybe; - lt?: InputMaybe; - lte?: InputMaybe; - neq?: InputMaybe; - notBetween?: InputMaybe; - notIn?: InputMaybe>; -}; - -export type DateFieldComparisonBetween = { - lower: Scalars['DateTime']; - upper: Scalars['DateTime']; -}; - export type DeleteApprovedAccessDomainInput = { id: Scalars['UUID']; }; @@ -2361,6 +2309,7 @@ export type Mutation = { activateSkill: Skill; activateWorkspace: Workspace; addQueryToEventStream: Scalars['Boolean']; + archiveChatThread: AgentChatThread; assignRoleToAgent: Scalars['Boolean']; assignRoleToApiKey: Scalars['Boolean']; authorizeApp: AuthorizeApp; @@ -2410,6 +2359,7 @@ export type Mutation = { deleteApplicationRegistration: Scalars['Boolean']; deleteApplicationRegistrationVariable: Scalars['Boolean']; deleteApprovedAccessDomain: Scalars['Boolean']; + deleteChatThread: Scalars['Boolean']; deleteCommandMenuItem: CommandMenuItem; deleteConnectedAccount: ConnectedAccountDto; deleteCurrentWorkspace: Workspace; @@ -2471,6 +2421,7 @@ export type Mutation = { refreshEnterpriseValidityToken: Scalars['Boolean']; removeQueryFromEventStream: Scalars['Boolean']; removeRoleFromAgent: Scalars['Boolean']; + renameChatThread: AgentChatThread; renewApplicationToken: ApplicationTokenPair; renewToken: AuthTokens; resendEmailVerificationToken: ResendEmailVerificationToken; @@ -2502,6 +2453,7 @@ export type Mutation = { syncMarketplaceCatalog: Scalars['Boolean']; trackAnalytics: Analytics; transferApplicationRegistrationOwnership: ApplicationRegistration; + unarchiveChatThread: AgentChatThread; uninstallApplication: Scalars['Boolean']; updateApiKey?: Maybe; updateApplicationRegistration: ApplicationRegistration; @@ -2579,6 +2531,11 @@ export type MutationAddQueryToEventStreamArgs = { }; +export type MutationArchiveChatThreadArgs = { + id: Scalars['UUID']; +}; + + export type MutationAssignRoleToAgentArgs = { agentId: Scalars['UUID']; roleId: Scalars['UUID']; @@ -2813,6 +2770,11 @@ export type MutationDeleteApprovedAccessDomainArgs = { }; +export type MutationDeleteChatThreadArgs = { + id: Scalars['UUID']; +}; + + export type MutationDeleteCommandMenuItemArgs = { id: Scalars['UUID']; }; @@ -3093,6 +3055,12 @@ export type MutationRemoveRoleFromAgentArgs = { }; +export type MutationRenameChatThreadArgs = { + id: Scalars['UUID']; + title: Scalars['String']; +}; + + export type MutationRenewApplicationTokenArgs = { applicationRefreshToken: Scalars['String']; }; @@ -3248,6 +3216,11 @@ export type MutationTransferApplicationRegistrationOwnershipArgs = { }; +export type MutationUnarchiveChatThreadArgs = { + id: Scalars['UUID']; +}; + + export type MutationUninstallApplicationArgs = { universalIdentifier: Scalars['String']; }; @@ -4063,7 +4036,7 @@ export type Query = { chatMessages: Array; chatStreamCatchupChunks: ChatStreamCatchupChunks; chatThread: AgentChatThread; - chatThreads: AgentChatThreadConnection; + chatThreads: Array; checkUserExists: CheckUserExist; checkWorkspaceInviteHashIsValid: WorkspaceInviteHashValid; commandMenuItem?: Maybe; @@ -4197,13 +4170,6 @@ export type QueryChatThreadArgs = { }; -export type QueryChatThreadsArgs = { - filter?: AgentChatThreadFilter; - paging?: CursorPaging; - sorting?: Array; -}; - - export type QueryCheckUserExistsArgs = { captchaToken?: InputMaybe; email: Scalars['String']; @@ -4793,18 +4759,6 @@ export type Skill = { updatedAt: Scalars['DateTime']; }; -/** Sort Directions */ -export enum SortDirection { - ASC = 'ASC', - DESC = 'DESC' -} - -/** Sort Nulls Options */ -export enum SortNulls { - NULLS_FIRST = 'NULLS_FIRST', - NULLS_LAST = 'NULLS_LAST' -} - export type StandaloneRichTextConfiguration = { __typename?: 'StandaloneRichTextConfiguration'; body: RichTextBody; @@ -6010,6 +5964,13 @@ export type ActivateSkillMutationVariables = Exact<{ export type ActivateSkillMutation = { __typename?: 'Mutation', activateSkill: { __typename?: 'Skill', id: string, name: string, label: string, description?: string | null, icon?: string | null, content: string, isCustom: boolean, isActive: boolean, createdAt: string, updatedAt: string } }; +export type ArchiveChatThreadMutationVariables = Exact<{ + id: Scalars['UUID']; +}>; + + +export type ArchiveChatThreadMutation = { __typename?: 'Mutation', archiveChatThread: { __typename?: 'AgentChatThread', id: string, deletedAt?: string | null, updatedAt: string } }; + export type AssignRoleToAgentMutationVariables = Exact<{ agentId: Scalars['UUID']; roleId: Scalars['UUID']; @@ -6044,6 +6005,13 @@ export type DeactivateSkillMutationVariables = Exact<{ export type DeactivateSkillMutation = { __typename?: 'Mutation', deactivateSkill: { __typename?: 'Skill', id: string, name: string, label: string, description?: string | null, icon?: string | null, content: string, isCustom: boolean, isActive: boolean, createdAt: string, updatedAt: string } }; +export type DeleteChatThreadMutationVariables = Exact<{ + id: Scalars['UUID']; +}>; + + +export type DeleteChatThreadMutation = { __typename?: 'Mutation', deleteChatThread: boolean }; + export type DeleteOneAgentMutationVariables = Exact<{ input: AgentIdInput; }>; @@ -6079,6 +6047,14 @@ export type RemoveRoleFromAgentMutationVariables = Exact<{ export type RemoveRoleFromAgentMutation = { __typename?: 'Mutation', removeRoleFromAgent: boolean }; +export type RenameChatThreadMutationVariables = Exact<{ + id: Scalars['UUID']; + title: Scalars['String']; +}>; + + +export type RenameChatThreadMutation = { __typename?: 'Mutation', renameChatThread: { __typename?: 'AgentChatThread', id: string, title?: string | null, updatedAt: string } }; + export type RunEvaluationInputMutationVariables = Exact<{ agentId: Scalars['UUID']; input: Scalars['String']; @@ -6106,6 +6082,13 @@ export type StopAgentChatStreamMutationVariables = Exact<{ export type StopAgentChatStreamMutation = { __typename?: 'Mutation', stopAgentChatStream: boolean }; +export type UnarchiveChatThreadMutationVariables = Exact<{ + id: Scalars['UUID']; +}>; + + +export type UnarchiveChatThreadMutation = { __typename?: 'Mutation', unarchiveChatThread: { __typename?: 'AgentChatThread', id: string, deletedAt?: string | null, updatedAt: string } }; + export type UpdateOneAgentMutationVariables = Exact<{ input: UpdateAgentInput; }>; @@ -6165,12 +6148,10 @@ export type GetChatMessagesQueryVariables = Exact<{ export type GetChatMessagesQuery = { __typename?: 'Query', chatMessages: Array<{ __typename?: 'AgentMessage', id: string, threadId: string, turnId?: string | null, role: string, status: string, createdAt: string, parts: Array<{ __typename?: 'AgentMessagePart', id: string, messageId: string, orderIndex: number, type: string, textContent?: string | null, reasoningContent?: string | null, toolName?: string | null, toolCallId?: string | null, toolInput?: any | null, toolOutput?: any | null, state?: string | null, providerExecuted?: boolean | null, errorMessage?: string | null, errorDetails?: any | null, sourceUrlSourceId?: string | null, sourceUrlUrl?: string | null, sourceUrlTitle?: string | null, sourceDocumentSourceId?: string | null, sourceDocumentMediaType?: string | null, sourceDocumentTitle?: string | null, sourceDocumentFilename?: string | null, fileMediaType?: string | null, fileFilename?: string | null, fileUrl?: string | null, fileId?: string | null, providerMetadata?: any | null, createdAt: string }> }>, chatStreamCatchupChunks: { __typename?: 'ChatStreamCatchupChunks', chunks: Array, maxSeq: number } }; -export type GetChatThreadsQueryVariables = Exact<{ - paging?: InputMaybe; -}>; +export type GetChatThreadsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetChatThreadsQuery = { __typename?: 'Query', chatThreads: { __typename?: 'AgentChatThreadConnection', edges: Array<{ __typename?: 'AgentChatThreadEdge', cursor: any, node: { __typename?: 'AgentChatThread', id: string, title?: string | null, totalInputTokens: number, totalOutputTokens: number, contextWindowTokens?: number | null, conversationSize: number, totalInputCredits: number, totalOutputCredits: number, createdAt: string, updatedAt: string } }>, pageInfo: { __typename?: 'PageInfo', endCursor?: any | null, hasNextPage?: boolean | null } } }; +export type GetChatThreadsQuery = { __typename?: 'Query', chatThreads: Array<{ __typename?: 'AgentChatThread', id: string, title?: string | null, totalInputTokens: number, totalOutputTokens: number, contextWindowTokens?: number | null, conversationSize: number, totalInputCredits: number, totalOutputCredits: number, deletedAt?: string | null, lastMessageAt?: string | null, createdAt: string, updatedAt: string }> }; export type GetToolIndexQueryVariables = Exact<{ [key: string]: never; }>; @@ -7879,19 +7860,23 @@ export const ViewGroupFragmentFragmentDoc = {"kind":"Document","definitions":[{" export const ViewFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"View"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isCompact"}},{"kind":"Field","name":{"kind":"Name","value":"openRecordIn"}},{"kind":"Field","name":{"kind":"Name","value":"kanbanAggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"kanbanAggregateOperationFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"mainGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"shouldHideEmptyGroups"}},{"kind":"Field","name":{"kind":"Name","value":"anyFieldFilterValue"}},{"kind":"Field","name":{"kind":"Name","value":"calendarFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"calendarLayout"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"createdByUserWorkspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"viewFields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFieldGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldGroupFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFilters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFilterFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFilterGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFilterGroupFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewSorts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewSortFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewGroupFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFieldFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewField"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"viewFieldGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFieldGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFieldGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"viewFields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFilterFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFilter"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"operand"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"viewFilterGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"positionInViewFilterGroup"}},{"kind":"Field","name":{"kind":"Name","value":"subFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFilterGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFilterGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"parentViewFilterGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"logicalOperator"}},{"kind":"Field","name":{"kind":"Name","value":"positionInViewFilterGroup"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewSortFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewSort"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"fieldValue"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}}]} as unknown as DocumentNode; export const SendEmailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendEmail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SendEmailInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sendEmail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; export const ActivateSkillDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ActivateSkill"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activateSkill"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SkillFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SkillFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Skill"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const ArchiveChatThreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ArchiveChatThread"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archiveChatThread"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode; export const AssignRoleToAgentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AssignRoleToAgent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignRoleToAgent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"agentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"roleId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roleId"}}}]}]}}]} as unknown as DocumentNode; export const CreateChatThreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateChatThread"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createChatThread"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode; export const CreateOneAgentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneAgent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateAgentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneAgent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AgentFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AgentFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Agent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"prompt"}},{"kind":"Field","name":{"kind":"Name","value":"modelId"}},{"kind":"Field","name":{"kind":"Name","value":"responseFormat"}},{"kind":"Field","name":{"kind":"Name","value":"roleId"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"modelConfiguration"}},{"kind":"Field","name":{"kind":"Name","value":"evaluationInputs"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const CreateSkillDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSkill"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateSkillInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSkill"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SkillFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SkillFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Skill"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const DeactivateSkillDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeactivateSkill"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deactivateSkill"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SkillFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SkillFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Skill"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const DeleteChatThreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteChatThread"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteChatThread"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}]}}]} as unknown as DocumentNode; export const DeleteOneAgentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneAgent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AgentIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneAgent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AgentFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AgentFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Agent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"prompt"}},{"kind":"Field","name":{"kind":"Name","value":"modelId"}},{"kind":"Field","name":{"kind":"Name","value":"responseFormat"}},{"kind":"Field","name":{"kind":"Name","value":"roleId"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"modelConfiguration"}},{"kind":"Field","name":{"kind":"Name","value":"evaluationInputs"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const DeleteQueuedChatMessageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteQueuedChatMessage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"messageId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteQueuedChatMessage"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"messageId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"messageId"}}}]}]}}]} as unknown as DocumentNode; export const DeleteSkillDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSkill"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteSkill"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SkillFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SkillFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Skill"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const EvaluateAgentTurnDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"EvaluateAgentTurn"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"turnId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"evaluateAgentTurn"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"turnId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"turnId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"turnId"}},{"kind":"Field","name":{"kind":"Name","value":"score"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const RemoveRoleFromAgentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RemoveRoleFromAgent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"removeRoleFromAgent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"agentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}}}]}]}}]} as unknown as DocumentNode; +export const RenameChatThreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RenameChatThread"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"title"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"renameChatThread"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"title"},"value":{"kind":"Variable","name":{"kind":"Name","value":"title"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode; export const RunEvaluationInputDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RunEvaluationInput"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runEvaluationInput"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"agentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"threadId"}},{"kind":"Field","name":{"kind":"Name","value":"agentId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"evaluations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"score"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const SendChatMessageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendChatMessage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"text"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"messageId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"browsingContext"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fileIds"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sendChatMessage"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}},{"kind":"Argument","name":{"kind":"Name","value":"text"},"value":{"kind":"Variable","name":{"kind":"Name","value":"text"}}},{"kind":"Argument","name":{"kind":"Name","value":"messageId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"messageId"}}},{"kind":"Argument","name":{"kind":"Name","value":"browsingContext"},"value":{"kind":"Variable","name":{"kind":"Name","value":"browsingContext"}}},{"kind":"Argument","name":{"kind":"Name","value":"modelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}}},{"kind":"Argument","name":{"kind":"Name","value":"fileIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fileIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"messageId"}},{"kind":"Field","name":{"kind":"Name","value":"queued"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}}]}}]}}]} as unknown as DocumentNode; export const StopAgentChatStreamDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"StopAgentChatStream"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stopAgentChatStream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}}]}]}}]} as unknown as DocumentNode; +export const UnarchiveChatThreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UnarchiveChatThread"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unarchiveChatThread"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode; export const UpdateOneAgentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneAgent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateAgentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneAgent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AgentFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AgentFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Agent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"prompt"}},{"kind":"Field","name":{"kind":"Name","value":"modelId"}},{"kind":"Field","name":{"kind":"Name","value":"responseFormat"}},{"kind":"Field","name":{"kind":"Name","value":"roleId"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"modelConfiguration"}},{"kind":"Field","name":{"kind":"Name","value":"evaluationInputs"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const UpdateSkillDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSkill"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateSkillInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateSkill"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SkillFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SkillFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Skill"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const UploadAiChatFileDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"uploadAiChatFile"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"file"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Upload"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uploadAiChatFile"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"file"},"value":{"kind":"Variable","name":{"kind":"Name","value":"file"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}}]}}]} as unknown as DocumentNode; @@ -7901,7 +7886,7 @@ export const FindOneAgentDocument = {"kind":"Document","definitions":[{"kind":"O export const FindOneSkillDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindOneSkill"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"skill"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SkillFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SkillFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Skill"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const GetAgentTurnsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAgentTurns"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"agentTurns"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"agentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"agentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"threadId"}},{"kind":"Field","name":{"kind":"Name","value":"agentId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"evaluations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"score"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"messages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"parts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"textContent"}},{"kind":"Field","name":{"kind":"Name","value":"reasoningContent"}},{"kind":"Field","name":{"kind":"Name","value":"toolName"}},{"kind":"Field","name":{"kind":"Name","value":"toolCallId"}},{"kind":"Field","name":{"kind":"Name","value":"toolInput"}},{"kind":"Field","name":{"kind":"Name","value":"toolOutput"}},{"kind":"Field","name":{"kind":"Name","value":"errorMessage"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"providerExecuted"}},{"kind":"Field","name":{"kind":"Name","value":"errorDetails"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlUrl"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlTitle"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentMediaType"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentTitle"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentFilename"}},{"kind":"Field","name":{"kind":"Name","value":"fileMediaType"}},{"kind":"Field","name":{"kind":"Name","value":"fileFilename"}},{"kind":"Field","name":{"kind":"Name","value":"fileUrl"}},{"kind":"Field","name":{"kind":"Name","value":"providerMetadata"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetChatMessagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetChatMessages"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chatMessages"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"threadId"}},{"kind":"Field","name":{"kind":"Name","value":"turnId"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"parts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"messageId"}},{"kind":"Field","name":{"kind":"Name","value":"orderIndex"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"textContent"}},{"kind":"Field","name":{"kind":"Name","value":"reasoningContent"}},{"kind":"Field","name":{"kind":"Name","value":"toolName"}},{"kind":"Field","name":{"kind":"Name","value":"toolCallId"}},{"kind":"Field","name":{"kind":"Name","value":"toolInput"}},{"kind":"Field","name":{"kind":"Name","value":"toolOutput"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"providerExecuted"}},{"kind":"Field","name":{"kind":"Name","value":"errorMessage"}},{"kind":"Field","name":{"kind":"Name","value":"errorDetails"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlUrl"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrlTitle"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentMediaType"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentTitle"}},{"kind":"Field","name":{"kind":"Name","value":"sourceDocumentFilename"}},{"kind":"Field","name":{"kind":"Name","value":"fileMediaType"}},{"kind":"Field","name":{"kind":"Name","value":"fileFilename"}},{"kind":"Field","name":{"kind":"Name","value":"fileUrl"}},{"kind":"Field","name":{"kind":"Name","value":"fileId"}},{"kind":"Field","name":{"kind":"Name","value":"providerMetadata"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"chatStreamCatchupChunks"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chunks"}},{"kind":"Field","name":{"kind":"Name","value":"maxSeq"}}]}}]}}]} as unknown as DocumentNode; -export const GetChatThreadsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetChatThreads"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"paging"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CursorPaging"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chatThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"Variable","name":{"kind":"Name","value":"paging"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"totalInputTokens"}},{"kind":"Field","name":{"kind":"Name","value":"totalOutputTokens"}},{"kind":"Field","name":{"kind":"Name","value":"contextWindowTokens"}},{"kind":"Field","name":{"kind":"Name","value":"conversationSize"}},{"kind":"Field","name":{"kind":"Name","value":"totalInputCredits"}},{"kind":"Field","name":{"kind":"Name","value":"totalOutputCredits"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"endCursor"}},{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetChatThreadsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetChatThreads"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chatThreads"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"totalInputTokens"}},{"kind":"Field","name":{"kind":"Name","value":"totalOutputTokens"}},{"kind":"Field","name":{"kind":"Name","value":"contextWindowTokens"}},{"kind":"Field","name":{"kind":"Name","value":"conversationSize"}},{"kind":"Field","name":{"kind":"Name","value":"totalInputCredits"}},{"kind":"Field","name":{"kind":"Name","value":"totalOutputCredits"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode; export const GetToolIndexDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetToolIndex"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getToolIndex"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"objectName"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}}]}}]}}]} as unknown as DocumentNode; export const GetToolInputSchemaDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetToolInputSchema"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"toolName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getToolInputSchema"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"toolName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"toolName"}}}]}]}}]} as unknown as DocumentNode; export const OnAgentChatEventDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnAgentChatEvent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"onAgentChatEvent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"threadId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"threadId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"threadId"}},{"kind":"Field","name":{"kind":"Name","value":"event"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/modules/ai/components/AgentChatProviderContent.tsx b/packages/twenty-front/src/modules/ai/components/AgentChatProviderContent.tsx index df09313e27..70cedeaab9 100644 --- a/packages/twenty-front/src/modules/ai/components/AgentChatProviderContent.tsx +++ b/packages/twenty-front/src/modules/ai/components/AgentChatProviderContent.tsx @@ -5,7 +5,7 @@ import { AgentChatSessionStartTimeEffect } from '@/ai/components/AgentChatSessio import { AgentChatStreamingAutoScrollEffect } from '@/ai/components/AgentChatStreamingAutoScrollEffect'; import { AgentChatStreamingPartsDiffSyncEffect } from '@/ai/components/AgentChatStreamingPartsDiffSyncEffect'; import { AgentChatThreadInitializationEffect } from '@/ai/components/AgentChatThreadInitializationEffect'; -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { Suspense } from 'react'; export const AgentChatProviderContent = ({ diff --git a/packages/twenty-front/src/modules/ai/components/AgentChatStreamingAutoScrollEffect.tsx b/packages/twenty-front/src/modules/ai/components/AgentChatStreamingAutoScrollEffect.tsx index 196937ebf9..6182240eb0 100644 --- a/packages/twenty-front/src/modules/ai/components/AgentChatStreamingAutoScrollEffect.tsx +++ b/packages/twenty-front/src/modules/ai/components/AgentChatStreamingAutoScrollEffect.tsx @@ -1,4 +1,4 @@ -import { agentChatIsScrolledToBottomSelector } from '@/ai/states/agentChatIsScrolledToBottomSelector'; +import { agentChatIsScrolledToBottomSelector } from '@/ai/states/selectors/agentChatIsScrolledToBottomSelector'; import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; import { currentAiChatThreadState } from '@/ai/states/currentAiChatThreadState'; import { scrollAiChatToBottom } from '@/ai/utils/scrollAiChatToBottom'; diff --git a/packages/twenty-front/src/modules/ai/components/AgentChatThreadInitializationEffect.tsx b/packages/twenty-front/src/modules/ai/components/AgentChatThreadInitializationEffect.tsx index 50e601032f..ba802de9ec 100644 --- a/packages/twenty-front/src/modules/ai/components/AgentChatThreadInitializationEffect.tsx +++ b/packages/twenty-front/src/modules/ai/components/AgentChatThreadInitializationEffect.tsx @@ -8,15 +8,15 @@ import { } from '@/ai/states/agentChatDraftsByThreadIdState'; import { agentChatInputState } from '@/ai/states/agentChatInputState'; import { agentChatThreadsLoadingState } from '@/ai/states/agentChatThreadsLoadingState'; -import { agentChatThreadsSelector } from '@/ai/states/agentChatThreadsSelector'; import { agentChatUsageComponentFamilyState } from '@/ai/states/agentChatUsageComponentFamilyState'; +import { agentChatVisibleThreadsSelector } from '@/ai/states/selectors/agentChatVisibleThreadsSelector'; import { currentAiChatThreadState } from '@/ai/states/currentAiChatThreadState'; import { currentAiChatThreadTitleComponentFamilyState } from '@/ai/states/currentAiChatThreadTitleComponentFamilyState'; import { hasInitializedAgentChatThreadsState } from '@/ai/states/hasInitializedAgentChatThreadsState'; import { hasTriggeredCreateForDraftState } from '@/ai/states/hasTriggeredCreateForDraftState'; +import { sortChatThreadsByLastActivityDesc } from '@/ai/utils/sortChatThreadsByLastActivityDesc'; import { useUpdateMetadataStoreDraft } from '@/metadata-store/hooks/useUpdateMetadataStoreDraft'; import { metadataStoreState } from '@/metadata-store/states/metadataStoreState'; -import { type FlatAgentChatThread } from '@/metadata-store/types/FlatAgentChatThread'; import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag'; import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState'; import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; @@ -48,7 +48,9 @@ export const AgentChatThreadInitializationEffect = () => { agentChatUsageComponentFamilyState, ); const store = useStore(); - const agentChatThreads = useAtomStateValue(agentChatThreadsSelector); + const agentChatVisibleThreads = useAtomStateValue( + agentChatVisibleThreadsSelector, + ); const storeEntry = useAtomValue( metadataStoreState.atomFamily('agentChatThreads'), ); @@ -63,17 +65,14 @@ export const AgentChatThreadInitializationEffect = () => { client .query({ query: GetChatThreadsDocument, - variables: { paging: { first: 500 } }, fetchPolicy: 'network-only', }) .then((result) => { - if (!isDefined(result.data?.chatThreads?.edges)) { + if (!isDefined(result.data?.chatThreads)) { return; } - const threads = result.data.chatThreads.edges.map((edge) => edge.node); - - replaceDraft('agentChatThreads', threads); + replaceDraft('agentChatThreads', result.data.chatThreads); applyChanges(); }); }, [ @@ -104,9 +103,8 @@ export const AgentChatThreadInitializationEffect = () => { setHasInitializedAgentChatThreads(true); - const sortedThreads = agentChatThreads.toSorted( - (a: FlatAgentChatThread, b: FlatAgentChatThread) => - new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(), + const sortedThreads = sortChatThreadsByLastActivityDesc( + agentChatVisibleThreads, ); if (sortedThreads.length > 0) { @@ -152,7 +150,7 @@ export const AgentChatThreadInitializationEffect = () => { ); } }, [ - agentChatThreads, + agentChatVisibleThreads, currentAiChatThread, hasAiSettingsPermission, hasInitializedAgentChatThreads, diff --git a/packages/twenty-front/src/modules/ai/components/AiChatEmptyState.tsx b/packages/twenty-front/src/modules/ai/components/AiChatEmptyState.tsx index 736f112732..e9789a56ee 100644 --- a/packages/twenty-front/src/modules/ai/components/AiChatEmptyState.tsx +++ b/packages/twenty-front/src/modules/ai/components/AiChatEmptyState.tsx @@ -5,7 +5,7 @@ import { isDefined } from 'twenty-shared/utils'; import { AiChatSuggestedPrompts } from '@/ai/components/suggested-prompts/AiChatSuggestedPrompts'; import { AGENT_CHAT_NEW_THREAD_DRAFT_KEY } from '@/ai/states/agentChatDraftsByThreadIdState'; import { agentChatErrorComponentFamilyState } from '@/ai/states/agentChatErrorComponentFamilyState'; -import { agentChatHasMessageComponentSelector } from '@/ai/states/agentChatHasMessageComponentSelector'; +import { agentChatHasMessageComponentSelector } from '@/ai/states/selectors/agentChatHasMessageComponentSelector'; import { agentChatMessagesLoadingState } from '@/ai/states/agentChatMessagesLoadingState'; import { agentChatThreadsLoadingState } from '@/ai/states/agentChatThreadsLoadingState'; import { currentAiChatThreadState } from '@/ai/states/currentAiChatThreadState'; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatErrorUnderMessageList.tsx b/packages/twenty-front/src/modules/ai/components/AiChatErrorUnderMessageList.tsx index 7fe01def08..6bc7eae80f 100644 --- a/packages/twenty-front/src/modules/ai/components/AiChatErrorUnderMessageList.tsx +++ b/packages/twenty-front/src/modules/ai/components/AiChatErrorUnderMessageList.tsx @@ -3,8 +3,8 @@ import { AgentMessageRole } from '@/ai/constants/AgentMessageRole'; import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThreadState'; import { agentChatErrorComponentFamilyState } from '@/ai/states/agentChatErrorComponentFamilyState'; import { agentChatIsStreamingComponentFamilyState } from '@/ai/states/agentChatIsStreamingComponentFamilyState'; -import { agentChatMessageComponentFamilySelector } from '@/ai/states/agentChatMessageComponentFamilySelector'; -import { agentChatMessageIdsComponentSelector } from '@/ai/states/agentChatMessageIdsComponentSelector'; +import { agentChatMessageComponentFamilySelector } from '@/ai/states/selectors/agentChatMessageComponentFamilySelector'; +import { agentChatMessageIdsComponentSelector } from '@/ai/states/selectors/agentChatMessageIdsComponentSelector'; import { useAtomComponentFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilySelectorValue'; import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue'; import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatLastMessageWithStreamingState.tsx b/packages/twenty-front/src/modules/ai/components/AiChatLastMessageWithStreamingState.tsx index 21775370e5..bd89b886f1 100644 --- a/packages/twenty-front/src/modules/ai/components/AiChatLastMessageWithStreamingState.tsx +++ b/packages/twenty-front/src/modules/ai/components/AiChatLastMessageWithStreamingState.tsx @@ -2,7 +2,7 @@ import { AiChatMessage } from '@/ai/components/AiChatMessage'; import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThreadState'; import { agentChatErrorComponentFamilyState } from '@/ai/states/agentChatErrorComponentFamilyState'; import { agentChatIsStreamingComponentFamilyState } from '@/ai/states/agentChatIsStreamingComponentFamilyState'; -import { agentChatLastMessageIdComponentSelector } from '@/ai/states/agentChatLastMessageIdComponentSelector'; +import { agentChatLastMessageIdComponentSelector } from '@/ai/states/selectors/agentChatLastMessageIdComponentSelector'; import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue'; import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatMessage.tsx b/packages/twenty-front/src/modules/ai/components/AiChatMessage.tsx index 9fa4fbfcef..42599300a8 100644 --- a/packages/twenty-front/src/modules/ai/components/AiChatMessage.tsx +++ b/packages/twenty-front/src/modules/ai/components/AiChatMessage.tsx @@ -5,7 +5,7 @@ import { AgentMessageRole } from '@/ai/constants/AgentMessageRole'; import { AiChatAssistantMessageRenderer } from '@/ai/components/AiChatAssistantMessageRenderer'; import { AiChatErrorRenderer } from '@/ai/components/AiChatErrorRenderer'; -import { agentChatMessageComponentFamilySelector } from '@/ai/states/agentChatMessageComponentFamilySelector'; +import { agentChatMessageComponentFamilySelector } from '@/ai/states/selectors/agentChatMessageComponentFamilySelector'; import { type AiChatError } from '@/ai/types/AiChatError'; import { LightCopyIconButton } from '@/object-record/record-field/ui/components/LightCopyIconButton'; import { useAtomComponentFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilySelectorValue'; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatNonLastMessageIdsList.tsx b/packages/twenty-front/src/modules/ai/components/AiChatNonLastMessageIdsList.tsx index b949282ab1..0bd93b5b42 100644 --- a/packages/twenty-front/src/modules/ai/components/AiChatNonLastMessageIdsList.tsx +++ b/packages/twenty-front/src/modules/ai/components/AiChatNonLastMessageIdsList.tsx @@ -1,5 +1,5 @@ import { AiChatMessage } from '@/ai/components/AiChatMessage'; -import { agentChatNonLastMessageIdsComponentSelector } from '@/ai/states/agentChatNonLastMessageIdsComponentSelector'; +import { agentChatNonLastMessageIdsComponentSelector } from '@/ai/states/selectors/agentChatNonLastMessageIdsComponentSelector'; import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; export const AiChatNonLastMessageIdsList = () => { diff --git a/packages/twenty-front/src/modules/ai/components/AiChatScrollToBottomButton.tsx b/packages/twenty-front/src/modules/ai/components/AiChatScrollToBottomButton.tsx index fe9afe58b8..269299c29b 100644 --- a/packages/twenty-front/src/modules/ai/components/AiChatScrollToBottomButton.tsx +++ b/packages/twenty-front/src/modules/ai/components/AiChatScrollToBottomButton.tsx @@ -1,4 +1,4 @@ -import { agentChatIsScrolledToBottomSelector } from '@/ai/states/agentChatIsScrolledToBottomSelector'; +import { agentChatIsScrolledToBottomSelector } from '@/ai/states/selectors/agentChatIsScrolledToBottomSelector'; import { scrollAiChatToBottom } from '@/ai/utils/scrollAiChatToBottom'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { styled } from '@linaria/react'; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatStandaloneError.tsx b/packages/twenty-front/src/modules/ai/components/AiChatStandaloneError.tsx index c4c80836a1..00af8e49c9 100644 --- a/packages/twenty-front/src/modules/ai/components/AiChatStandaloneError.tsx +++ b/packages/twenty-front/src/modules/ai/components/AiChatStandaloneError.tsx @@ -3,7 +3,7 @@ import { themeCssVariables } from 'twenty-ui/theme-constants'; import { AiChatErrorRenderer } from '@/ai/components/AiChatErrorRenderer'; import { agentChatErrorComponentFamilyState } from '@/ai/states/agentChatErrorComponentFamilyState'; -import { agentChatHasMessageComponentSelector } from '@/ai/states/agentChatHasMessageComponentSelector'; +import { agentChatHasMessageComponentSelector } from '@/ai/states/selectors/agentChatHasMessageComponentSelector'; import { agentChatIsLoadingState } from '@/ai/states/agentChatIsLoadingState'; import { currentAiChatThreadState } from '@/ai/states/currentAiChatThreadState'; import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue'; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatTabMessageList.tsx b/packages/twenty-front/src/modules/ai/components/AiChatTabMessageList.tsx index 3eced14a1c..bb622e75de 100644 --- a/packages/twenty-front/src/modules/ai/components/AiChatTabMessageList.tsx +++ b/packages/twenty-front/src/modules/ai/components/AiChatTabMessageList.tsx @@ -4,7 +4,7 @@ import { AiChatNonLastMessageIdsList } from '@/ai/components/AiChatNonLastMessag import { AiChatScrollToBottomButton } from '@/ai/components/AiChatScrollToBottomButton'; import { AgentChatScrollToBottomOnDisplayedThreadChangeLayoutEffect } from '@/ai/components/AgentChatScrollToBottomOnDisplayedThreadChangeLayoutEffect'; import { AI_CHAT_SCROLL_WRAPPER_ID } from '@/ai/constants/AiChatScrollWrapperId'; -import { agentChatHasMessageComponentSelector } from '@/ai/states/agentChatHasMessageComponentSelector'; +import { agentChatHasMessageComponentSelector } from '@/ai/states/selectors/agentChatHasMessageComponentSelector'; import { agentChatIsInitialScrollPendingOnThreadChangeState } from '@/ai/states/agentChatIsInitialScrollPendingOnThreadChangeState'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadDeleteConfirmationModal.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadDeleteConfirmationModal.tsx new file mode 100644 index 0000000000..b276771435 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadDeleteConfirmationModal.tsx @@ -0,0 +1,54 @@ +import { Trans, useLingui } from '@lingui/react/macro'; + +import { type AiChatThreadActionsSurface } from '@/ai/types/AiChatThreadActionsSurface'; +import { useDeleteChatThread } from '@/ai/hooks/useDeleteChatThread'; +import { aiChatThreadPendingDeleteFamilyState } from '@/ai/states/aiChatThreadPendingDeleteFamilyState'; +import { getAiChatThreadDeleteModalId } from '@/ai/utils/getAiChatThreadDeleteModalId'; +import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; +import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue'; +import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState'; + +type AiChatThreadDeleteConfirmationModalProps = { + surface: AiChatThreadActionsSurface; +}; + +export const AiChatThreadDeleteConfirmationModal = ({ + surface, +}: AiChatThreadDeleteConfirmationModalProps) => { + const { t } = useLingui(); + const { deleteChatThread } = useDeleteChatThread(); + const aiChatThreadPendingDelete = useAtomFamilyStateValue( + aiChatThreadPendingDeleteFamilyState, + surface, + ); + const setAiChatThreadPendingDelete = useSetAtomFamilyState( + aiChatThreadPendingDeleteFamilyState, + surface, + ); + + const modalInstanceId = getAiChatThreadDeleteModalId(surface); + + const handleDelete = async () => { + if (aiChatThreadPendingDelete === null) return; + + await deleteChatThread(aiChatThreadPendingDelete.threadId); + setAiChatThreadPendingDelete(null); + }; + + return ( + + {aiChatThreadPendingDelete?.threadTitle ?? ''} and + all its messages will be removed. + + } + onConfirmClick={handleDelete} + onClose={() => setAiChatThreadPendingDelete(null)} + confirmButtonText={t`Delete`} + confirmButtonAccent="danger" + /> + ); +}; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdown.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdown.tsx new file mode 100644 index 0000000000..0ffe7bb134 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdown.tsx @@ -0,0 +1,51 @@ +import { useLingui } from '@lingui/react/macro'; +import { useState } from 'react'; +import { IconAdjustments } from 'twenty-ui/display'; +import { LightIconButton } from 'twenty-ui/input'; + +import { AiChatThreadFilterDropdownContent } from '@/ai/components/AiChatThreadFilterDropdownContent'; +import { AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE } from '@/ai/constants/AiChatThreadFilterDropdownPage'; +import { type AiChatThreadActionsSurface } from '@/ai/types/AiChatThreadActionsSurface'; +import { type AiChatThreadFilterDropdownPage } from '@/ai/types/AiChatThreadFilterDropdownPage'; +import { getAiChatThreadFilterDropdownId } from '@/ai/utils/getAiChatThreadFilterDropdownId'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; + +type AiChatThreadFilterDropdownProps = { + surface: AiChatThreadActionsSurface; +}; + +export const AiChatThreadFilterDropdown = ({ + surface, +}: AiChatThreadFilterDropdownProps) => { + const { t } = useLingui(); + const dropdownId = getAiChatThreadFilterDropdownId(surface); + const [page, setPage] = useState( + AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE.ROOT, + ); + + const goToRoot = () => setPage(AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE.ROOT); + + return ( + + } + dropdownComponents={ + + } + /> + ); +}; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownContent.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownContent.tsx new file mode 100644 index 0000000000..48a48eea68 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownContent.tsx @@ -0,0 +1,37 @@ +import { AiChatThreadFilterDropdownGroupByMenu } from '@/ai/components/AiChatThreadFilterDropdownGroupByMenu'; +import { AiChatThreadFilterDropdownLastActivityMenu } from '@/ai/components/AiChatThreadFilterDropdownLastActivityMenu'; +import { AiChatThreadFilterDropdownRootMenu } from '@/ai/components/AiChatThreadFilterDropdownRootMenu'; +import { AiChatThreadFilterDropdownStatusMenu } from '@/ai/components/AiChatThreadFilterDropdownStatusMenu'; +import { AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE } from '@/ai/constants/AiChatThreadFilterDropdownPage'; +import { type AiChatThreadFilterDropdownPage } from '@/ai/types/AiChatThreadFilterDropdownPage'; + +type AiChatThreadFilterDropdownContentProps = { + page: AiChatThreadFilterDropdownPage; + dropdownId: string; + onSelectPage: (page: AiChatThreadFilterDropdownPage) => void; + onBack: () => void; +}; + +export const AiChatThreadFilterDropdownContent = ({ + page, + dropdownId, + onSelectPage, + onBack, +}: AiChatThreadFilterDropdownContentProps) => { + switch (page) { + case AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE.STATUS: + return ; + case AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE.GROUP_BY: + return ; + case AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE.LAST_ACTIVITY: + return ; + case AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE.ROOT: + default: + return ( + + ); + } +}; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownGroupByMenu.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownGroupByMenu.tsx new file mode 100644 index 0000000000..142d02224d --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownGroupByMenu.tsx @@ -0,0 +1,60 @@ +import { useLingui } from '@lingui/react/macro'; +import { IconChevronLeft } from 'twenty-ui/display'; +import { MenuItemSelect } from 'twenty-ui/navigation'; + +import { AGENT_CHAT_THREAD_GROUP_BY } from '@/ai/constants/AgentChatThreadGroupBy'; +import { AGENT_CHAT_THREAD_GROUP_BY_LABELS } from '@/ai/constants/AgentChatThreadGroupByLabels'; +import { agentChatThreadGroupByState } from '@/ai/states/agentChatThreadGroupByState'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; +import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; + +const AGENT_CHAT_THREAD_GROUP_BY_OPTIONS = [ + AGENT_CHAT_THREAD_GROUP_BY.DATE, + AGENT_CHAT_THREAD_GROUP_BY.NONE, +] as const; + +type AiChatThreadFilterDropdownGroupByMenuProps = { + onBack: () => void; +}; + +export const AiChatThreadFilterDropdownGroupByMenu = ({ + onBack, +}: AiChatThreadFilterDropdownGroupByMenuProps) => { + const { t } = useLingui(); + const { closeDropdown } = useCloseDropdown(); + const [agentChatThreadGroupBy, setAgentChatThreadGroupBy] = useAtomState( + agentChatThreadGroupByState, + ); + + return ( + + + } + > + {t`Group by`} + + + {AGENT_CHAT_THREAD_GROUP_BY_OPTIONS.map((option) => ( + { + setAgentChatThreadGroupBy(option); + closeDropdown(); + }} + /> + ))} + + + ); +}; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownLastActivityMenu.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownLastActivityMenu.tsx new file mode 100644 index 0000000000..fb8fa22b68 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownLastActivityMenu.tsx @@ -0,0 +1,64 @@ +import { useLingui } from '@lingui/react/macro'; +import { IconChevronLeft } from 'twenty-ui/display'; +import { MenuItemSelect } from 'twenty-ui/navigation'; + +import { AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER } from '@/ai/constants/AgentChatThreadLastActivityFilter'; +import { AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER_LABELS } from '@/ai/constants/AgentChatThreadLastActivityFilterLabels'; +import { agentChatThreadLastActivityFilterState } from '@/ai/states/agentChatThreadLastActivityFilterState'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; +import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; + +const AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER_OPTIONS = [ + AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER.ONE_DAY, + AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER.THREE_DAYS, + AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER.SEVEN_DAYS, + AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER.THIRTY_DAYS, + AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER.ALL, +] as const; + +type AiChatThreadFilterDropdownLastActivityMenuProps = { + onBack: () => void; +}; + +export const AiChatThreadFilterDropdownLastActivityMenu = ({ + onBack, +}: AiChatThreadFilterDropdownLastActivityMenuProps) => { + const { t } = useLingui(); + const { closeDropdown } = useCloseDropdown(); + const [ + agentChatThreadLastActivityFilter, + setAgentChatThreadLastActivityFilter, + ] = useAtomState(agentChatThreadLastActivityFilterState); + + return ( + + + } + > + {t`Last activity`} + + + {AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER_OPTIONS.map((option) => ( + { + setAgentChatThreadLastActivityFilter(option); + closeDropdown(); + }} + /> + ))} + + + ); +}; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownRootMenu.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownRootMenu.tsx new file mode 100644 index 0000000000..b5ee6965c6 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownRootMenu.tsx @@ -0,0 +1,119 @@ +import { useLingui } from '@lingui/react/macro'; +import { + IconClock, + IconLayoutList, + IconStatusChange, + IconTrash, +} from 'twenty-ui/display'; + +import { AGENT_CHAT_THREAD_FILTER_STATUS } from '@/ai/constants/AgentChatThreadFilterStatus'; +import { AGENT_CHAT_THREAD_FILTER_STATUS_LABELS } from '@/ai/constants/AgentChatThreadFilterStatusLabels'; +import { AGENT_CHAT_THREAD_GROUP_BY } from '@/ai/constants/AgentChatThreadGroupBy'; +import { AGENT_CHAT_THREAD_GROUP_BY_LABELS } from '@/ai/constants/AgentChatThreadGroupByLabels'; +import { AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER } from '@/ai/constants/AgentChatThreadLastActivityFilter'; +import { AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER_LABELS } from '@/ai/constants/AgentChatThreadLastActivityFilterLabels'; +import { AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE } from '@/ai/constants/AiChatThreadFilterDropdownPage'; +import { type AiChatThreadFilterDropdownPage } from '@/ai/types/AiChatThreadFilterDropdownPage'; +import { agentChatThreadFilterStatusState } from '@/ai/states/agentChatThreadFilterStatusState'; +import { agentChatThreadGroupByState } from '@/ai/states/agentChatThreadGroupByState'; +import { agentChatThreadLastActivityFilterState } from '@/ai/states/agentChatThreadLastActivityFilterState'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; +import { MenuItem } from 'twenty-ui/navigation'; + +type AiChatThreadFilterDropdownRootMenuProps = { + dropdownId: string; + onSelectPage: (page: AiChatThreadFilterDropdownPage) => void; +}; + +export const AiChatThreadFilterDropdownRootMenu = ({ + dropdownId, + onSelectPage, +}: AiChatThreadFilterDropdownRootMenuProps) => { + const { t } = useLingui(); + const { closeDropdown } = useCloseDropdown(); + + const [agentChatThreadFilterStatus, setAgentChatThreadFilterStatus] = + useAtomState(agentChatThreadFilterStatusState); + const [agentChatThreadGroupBy, setAgentChatThreadGroupBy] = useAtomState( + agentChatThreadGroupByState, + ); + const [ + agentChatThreadLastActivityFilter, + setAgentChatThreadLastActivityFilter, + ] = useAtomState(agentChatThreadLastActivityFilterState); + + const isAtDefaults = + agentChatThreadFilterStatus === AGENT_CHAT_THREAD_FILTER_STATUS.ACTIVE && + agentChatThreadGroupBy === AGENT_CHAT_THREAD_GROUP_BY.DATE && + agentChatThreadLastActivityFilter === + AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER.ALL; + + const handleClearFilters = () => { + setAgentChatThreadFilterStatus(AGENT_CHAT_THREAD_FILTER_STATUS.ACTIVE); + setAgentChatThreadGroupBy(AGENT_CHAT_THREAD_GROUP_BY.DATE); + setAgentChatThreadLastActivityFilter( + AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER.ALL, + ); + closeDropdown(dropdownId); + }; + + return ( + + + + onSelectPage(AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE.STATUS) + } + /> + + onSelectPage(AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE.GROUP_BY) + } + /> + + onSelectPage(AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE.LAST_ACTIVITY) + } + /> + {!isAtDefaults && ( + <> + + + + )} + + + ); +}; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownStatusMenu.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownStatusMenu.tsx new file mode 100644 index 0000000000..05c1b91d44 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadFilterDropdownStatusMenu.tsx @@ -0,0 +1,60 @@ +import { useLingui } from '@lingui/react/macro'; +import { IconChevronLeft } from 'twenty-ui/display'; +import { MenuItemSelect } from 'twenty-ui/navigation'; + +import { AGENT_CHAT_THREAD_FILTER_STATUS } from '@/ai/constants/AgentChatThreadFilterStatus'; +import { AGENT_CHAT_THREAD_FILTER_STATUS_LABELS } from '@/ai/constants/AgentChatThreadFilterStatusLabels'; +import { agentChatThreadFilterStatusState } from '@/ai/states/agentChatThreadFilterStatusState'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; +import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; + +const AGENT_CHAT_THREAD_FILTER_STATUS_OPTIONS = [ + AGENT_CHAT_THREAD_FILTER_STATUS.ACTIVE, + AGENT_CHAT_THREAD_FILTER_STATUS.ARCHIVED, + AGENT_CHAT_THREAD_FILTER_STATUS.ALL, +] as const; + +type AiChatThreadFilterDropdownStatusMenuProps = { + onBack: () => void; +}; + +export const AiChatThreadFilterDropdownStatusMenu = ({ + onBack, +}: AiChatThreadFilterDropdownStatusMenuProps) => { + const { t } = useLingui(); + const { closeDropdown } = useCloseDropdown(); + const [agentChatThreadFilterStatus, setAgentChatThreadFilterStatus] = + useAtomState(agentChatThreadFilterStatusState); + + return ( + + + } + > + {t`Status`} + + + {AGENT_CHAT_THREAD_FILTER_STATUS_OPTIONS.map((option) => ( + { + setAgentChatThreadFilterStatus(option); + closeDropdown(); + }} + /> + ))} + + + ); +}; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadGroup.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadGroup.tsx index a6e8c0a0ae..6cd884d403 100644 --- a/packages/twenty-front/src/modules/ai/components/AiChatThreadGroup.tsx +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadGroup.tsx @@ -1,9 +1,9 @@ -import { useAiChatThreadClick } from '@/ai/hooks/useAiChatThreadClick'; import { styled } from '@linaria/react'; -import { useLingui } from '@lingui/react/macro'; -import { useContext } from 'react'; -import { IconSparkles } from 'twenty-ui/display'; -import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; +import { type ReactNode } from 'react'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { AiChatThreadListItem } from '@/ai/components/AiChatThreadListItem'; +import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle'; import { type AgentChatThread } from '~/generated-metadata/graphql'; const StyledThreadsList = styled.div` @@ -16,91 +16,33 @@ const StyledDateGroup = styled.div` margin-bottom: ${themeCssVariables.spacing[4]}; `; -const StyledDateHeader = styled.div` - color: ${themeCssVariables.font.color.light}; - font-size: ${themeCssVariables.font.size.xs}; - font-weight: ${themeCssVariables.font.weight.medium}; - margin-bottom: ${themeCssVariables.spacing[1]}; -`; - -const StyledThreadItem = styled.div<{ isSelected?: boolean }>` - align-items: center; - border-left: 3px solid transparent; - border-radius: ${themeCssVariables.border.radius.sm}; - cursor: pointer; - display: flex; - gap: ${themeCssVariables.spacing[2]}; - margin-bottom: ${themeCssVariables.spacing[1]}; - padding: ${themeCssVariables.spacing[1]} 1px; - position: relative; - right: 3px; - transition: all 0.2s ease; - width: calc(100% + 1px); - - &:hover { - background: ${themeCssVariables.background.transparent.light}; - } -`; - -const StyledSparkleIcon = styled.div` - align-items: center; - background: ${themeCssVariables.background.transparent.blue}; - border-radius: ${themeCssVariables.border.radius.sm}; - display: flex; - justify-content: center; - padding: ${themeCssVariables.spacing[1]}; -`; - -const StyledThreadContent = styled.div` - flex: 1; - min-width: 0; -`; - -const StyledThreadTitle = styled.div` - color: ${themeCssVariables.font.color.secondary}; - font-size: ${themeCssVariables.font.size.md}; - font-weight: 500; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -`; - -export const AiChatThreadGroup = ({ - threads, - title, -}: { +type AiChatThreadGroupProps = { + alwaysShowRightIcon?: boolean; + rightIcon?: ReactNode; threads: AgentChatThread[]; title: string; -}) => { - const { theme } = useContext(ThemeContext); - const { t } = useLingui(); - const { handleThreadClick } = useAiChatThreadClick(); +}; +export const AiChatThreadGroup = ({ + alwaysShowRightIcon = false, + rightIcon, + threads, + title, +}: AiChatThreadGroupProps) => { if (threads.length === 0) { return null; } return ( - {title} + {threads.map((thread) => ( - handleThreadClick(thread)} - key={thread.id} - > - - - - - - {thread.title || t`Untitled`} - - - + ))} diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadItemMenu.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadItemMenu.tsx new file mode 100644 index 0000000000..c0595d37fd --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadItemMenu.tsx @@ -0,0 +1,108 @@ +import { useLingui } from '@lingui/react/macro'; +import { + IconArchive, + IconArchiveOff, + IconDotsVertical, + IconPencil, + IconTrash, +} from 'twenty-ui/display'; +import { LightIconButton } from 'twenty-ui/input'; +import { MenuItem } from 'twenty-ui/navigation'; + +import { type AiChatThreadActionsSurface } from '@/ai/types/AiChatThreadActionsSurface'; +import { useChatThreadArchiveActions } from '@/ai/hooks/useChatThreadArchiveActions'; +import { aiChatThreadPendingDeleteFamilyState } from '@/ai/states/aiChatThreadPendingDeleteFamilyState'; +import { getAiChatThreadDeleteModalId } from '@/ai/utils/getAiChatThreadDeleteModalId'; +import { getAiChatThreadItemMenuDropdownId } from '@/ai/utils/getAiChatThreadItemMenuDropdownId'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { useModal } from '@/ui/layout/modal/hooks/useModal'; +import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState'; + +type AiChatThreadItemMenuProps = { + threadId: string; + threadTitle: string; + isArchived: boolean; + surface: AiChatThreadActionsSurface; + onRenameRequested: () => void; +}; + +export const AiChatThreadItemMenu = ({ + threadId, + threadTitle, + isArchived, + surface, + onRenameRequested, +}: AiChatThreadItemMenuProps) => { + const { t } = useLingui(); + const dropdownId = getAiChatThreadItemMenuDropdownId(threadId, surface); + const { closeDropdown } = useCloseDropdown(); + const { openModal } = useModal(); + const { archiveChatThread, unarchiveChatThread } = + useChatThreadArchiveActions(); + const setAiChatThreadPendingDelete = useSetAtomFamilyState( + aiChatThreadPendingDeleteFamilyState, + surface, + ); + + const handleRename = (event: React.MouseEvent) => { + event.stopPropagation(); + closeDropdown(dropdownId); + onRenameRequested(); + }; + + const handleArchive = async (event: React.MouseEvent) => { + event.stopPropagation(); + closeDropdown(dropdownId); + if (isArchived) { + await unarchiveChatThread(threadId); + } else { + await archiveChatThread(threadId); + } + }; + + const handleDelete = (event: React.MouseEvent) => { + event.stopPropagation(); + closeDropdown(dropdownId); + setAiChatThreadPendingDelete({ threadId, threadTitle }); + openModal(getAiChatThreadDeleteModalId(surface)); + }; + + return ( + + } + dropdownComponents={ + + + + + + + + } + /> + ); +}; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadListItem.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadListItem.tsx new file mode 100644 index 0000000000..9d4b743c61 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadListItem.tsx @@ -0,0 +1,163 @@ +import { styled } from '@linaria/react'; +import { useLingui } from '@lingui/react/macro'; +import { useContext } from 'react'; +import { Key } from 'ts-key-enum'; +import { IconArchive, IconSparkles } from 'twenty-ui/display'; +import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; + +import { AiChatThreadItemMenu } from '@/ai/components/AiChatThreadItemMenu'; +import { AI_CHAT_THREAD_ACTIONS_SURFACE } from '@/ai/constants/AiChatThreadActionsSurface'; +import { useAiChatThreadClick } from '@/ai/hooks/useAiChatThreadClick'; +import { useAiChatThreadRename } from '@/ai/hooks/useAiChatThreadRename'; +import { getAiChatThreadItemMenuDropdownId } from '@/ai/utils/getAiChatThreadItemMenuDropdownId'; +import { TextInput } from '@/ui/input/components/TextInput'; +import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; +import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; +import { type AgentChatThread } from '~/generated-metadata/graphql'; + +const StyledThreadItem = styled.div` + align-items: center; + border-left: 3px solid transparent; + border-radius: ${themeCssVariables.border.radius.sm}; + cursor: pointer; + display: flex; + gap: ${themeCssVariables.spacing[2]}; + margin-bottom: ${themeCssVariables.spacing[1]}; + padding: ${themeCssVariables.spacing[1]} 1px; + position: relative; + right: 3px; + transition: all 0.2s ease; + width: calc(100% + 1px); + + &:hover { + background: ${themeCssVariables.background.transparent.light}; + } +`; + +const StyledThreadIcon = styled.div<{ $isArchived: boolean }>` + align-items: center; + background: ${({ $isArchived }) => + $isArchived + ? themeCssVariables.background.transparent.lighter + : themeCssVariables.background.transparent.blue}; + border-radius: ${themeCssVariables.border.radius.sm}; + color: ${({ $isArchived }) => + $isArchived + ? themeCssVariables.font.color.tertiary + : themeCssVariables.color.blue}; + display: flex; + justify-content: center; + padding: ${themeCssVariables.spacing[1]}; +`; + +const StyledThreadContent = styled.div` + flex: 1; + min-width: 0; +`; + +const StyledThreadTitle = styled.div` + color: ${themeCssVariables.font.color.secondary}; + font-size: ${themeCssVariables.font.size.md}; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +`; + +const StyledMenuTrigger = styled.div<{ $isDropdownOpen: boolean }>` + opacity: ${({ $isDropdownOpen }) => ($isDropdownOpen ? 1 : 0)}; + pointer-events: ${({ $isDropdownOpen }) => + $isDropdownOpen ? 'auto' : 'none'}; + position: absolute; + right: ${themeCssVariables.spacing[1]}; + top: 50%; + transform: translateY(-50%); + transition: opacity 150ms; + + ${StyledThreadItem}:hover & { + opacity: 1; + pointer-events: auto; + } +`; + +type AiChatThreadListItemProps = { + thread: AgentChatThread; +}; + +export const AiChatThreadListItem = ({ thread }: AiChatThreadListItemProps) => { + const { theme } = useContext(ThemeContext); + const { t } = useLingui(); + const { handleThreadClick } = useAiChatThreadClick(); + const { + isRenaming, + draftTitle, + setDraftTitle, + startRename, + cancelRename, + commitRename, + } = useAiChatThreadRename(thread); + + const isArchived = Boolean(thread.deletedAt); + const ThreadIcon = isArchived ? IconArchive : IconSparkles; + const displayTitle = thread.title ?? t`Untitled`; + const itemMenuDropdownId = getAiChatThreadItemMenuDropdownId( + thread.id, + AI_CHAT_THREAD_ACTIONS_SURFACE.SIDE_PANEL, + ); + const isDropdownOpen = useAtomComponentStateValue( + isDropdownOpenComponentState, + itemMenuDropdownId, + ); + + return ( + { + if (!isRenaming) { + handleThreadClick(thread); + } + }} + > + + + + + {isRenaming ? ( + event.stopPropagation()} + onFocus={(event) => event.target.select()} + onBlur={() => commitRename(draftTitle)} + onKeyDown={(event) => { + if (event.key === Key.Enter) { + event.preventDefault(); + void commitRename(draftTitle); + } else if (event.key === Key.Escape) { + event.preventDefault(); + cancelRename(); + } + }} + sizeVariant="sm" + fullWidth + autoFocus + aria-label={t`Rename chat`} + /> + ) : ( + {displayTitle} + )} + + event.stopPropagation()} + > + + + + ); +}; diff --git a/packages/twenty-front/src/modules/ai/components/AiChatThreadsList.tsx b/packages/twenty-front/src/modules/ai/components/AiChatThreadsList.tsx index 34845a555a..750fac186b 100644 --- a/packages/twenty-front/src/modules/ai/components/AiChatThreadsList.tsx +++ b/packages/twenty-front/src/modules/ai/components/AiChatThreadsList.tsx @@ -1,15 +1,22 @@ import { styled } from '@linaria/react'; +import { AiChatThreadDeleteConfirmationModal } from '@/ai/components/AiChatThreadDeleteConfirmationModal'; +import { AiChatThreadFilterDropdown } from '@/ai/components/AiChatThreadFilterDropdown'; import { AiChatThreadGroup } from '@/ai/components/AiChatThreadGroup'; +import { AiChatThreadListItem } from '@/ai/components/AiChatThreadListItem'; import { AiChatThreadsListFocusEffect } from '@/ai/components/AiChatThreadsListFocusEffect'; import { AiChatSkeletonLoader } from '@/ai/components/internal/AiChatSkeletonLoader'; +import { AGENT_CHAT_THREAD_GROUP_BY } from '@/ai/constants/AgentChatThreadGroupBy'; +import { AI_CHAT_THREAD_ACTIONS_SURFACE } from '@/ai/constants/AiChatThreadActionsSurface'; import { useChatThreads } from '@/ai/hooks/useChatThreads'; import { useSwitchToNewAiChat } from '@/ai/hooks/useSwitchToNewAiChat'; +import { agentChatThreadGroupByState } from '@/ai/states/agentChatThreadGroupByState'; import { groupThreadsByDate } from '@/ai/utils/groupThreadsByDate'; +import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { t } from '@lingui/core/macro'; import { Key } from 'ts-key-enum'; -import { capitalize } from 'twenty-shared/utils'; import { Button } from 'twenty-ui/input'; import { themeCssVariables } from 'twenty-ui/theme-constants'; import { getOsControlSymbol } from 'twenty-ui/utilities'; @@ -28,11 +35,17 @@ const StyledThreadsContainer = styled.div` padding: ${themeCssVariables.spacing[3]}; `; +const StyledFlatThreadList = styled.div` + display: flex; + flex-direction: column; + gap: ${themeCssVariables.spacing[1]}; +`; + const StyledButtonsContainer = styled.div` border-top: 1px solid ${themeCssVariables.border.color.medium}; display: flex; justify-content: flex-end; - padding: ${themeCssVariables.spacing[2]} 10px; + padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[2]}; `; export const AiChatThreadsList = () => { @@ -48,25 +61,51 @@ export const AiChatThreadsList = () => { }); const { threads, hasNextPage, loading, fetchMoreRef } = useChatThreads(); - - const groupedThreads = groupThreadsByDate(threads); + const agentChatThreadGroupBy = useAtomStateValue(agentChatThreadGroupByState); if (loading && threads.length === 0) { return ; } + const isGroupedByDate = + agentChatThreadGroupBy === AGENT_CHAT_THREAD_GROUP_BY.DATE; + const dateGroups = isGroupedByDate ? groupThreadsByDate(threads) : []; + const shouldRenderDateGroups = isGroupedByDate && dateGroups.length > 0; + const filterDropdown = ( + + ); + return ( <> - {Object.entries(groupedThreads).map(([title, threadsInGroup]) => ( - - ))} + {shouldRenderDateGroups ? ( + dateGroups.map((dateGroup, index) => ( + + )) + ) : ( + <> + + + {threads.map((thread) => ( + + ))} + + + )} {hasNextPage ? (
) : null} @@ -82,6 +121,9 @@ export const AiChatThreadsList = () => { /> + ); }; diff --git a/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatContent.tsx b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatContent.tsx index a4e1e22a99..6613e4b8d4 100644 --- a/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatContent.tsx +++ b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatContent.tsx @@ -2,14 +2,16 @@ import { styled } from '@linaria/react'; import { useLingui } from '@lingui/react/macro'; import { themeCssVariables } from 'twenty-ui/theme-constants'; +import { AiChatThreadDeleteConfirmationModal } from '@/ai/components/AiChatThreadDeleteConfirmationModal'; +import { AiChatThreadFilterDropdown } from '@/ai/components/AiChatThreadFilterDropdown'; import { AiChatSkeletonLoader } from '@/ai/components/internal/AiChatSkeletonLoader'; -import { NavigationDrawerAiChatThreadDateSection } from '@/ai/components/NavigationDrawerAiChatThreadDateSection'; +import { NavigationDrawerAiChatThreadSection } from '@/ai/components/NavigationDrawerAiChatThreadSection'; +import { AGENT_CHAT_THREAD_GROUP_BY } from '@/ai/constants/AgentChatThreadGroupBy'; +import { AI_CHAT_THREAD_ACTIONS_SURFACE } from '@/ai/constants/AiChatThreadActionsSurface'; import { useAiChatThreadClick } from '@/ai/hooks/useAiChatThreadClick'; import { useChatThreads } from '@/ai/hooks/useChatThreads'; +import { agentChatThreadGroupByState } from '@/ai/states/agentChatThreadGroupByState'; import { currentAiChatThreadState } from '@/ai/states/currentAiChatThreadState'; -import { type DateGroupKey } from '@/ai/utils/dateGroupKey'; -import { DATE_GROUP_KEYS } from '@/ai/utils/dateGroupKeys'; -import { getDateGroupTitle } from '@/ai/utils/getDateGroupTitle'; import { groupThreadsByDate } from '@/ai/utils/groupThreadsByDate'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; @@ -22,11 +24,19 @@ const StyledContainer = styled.div` const StyledThreadList = styled.div` display: flex; + flex: 1; flex-direction: column; + min-height: 0; padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[0]}; width: calc(100% - ${themeCssVariables.spacing[2]}); `; +const StyledSectionsContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${themeCssVariables.spacing[3]}; +`; + const StyledEmptyState = styled.div` align-items: center; color: ${themeCssVariables.font.color.light}; @@ -42,6 +52,8 @@ const StyledFetchMoreTrigger = styled.div` width: 100%; `; +const AI_CHAT_RECENTS_NAVIGATION_SECTION_ID = 'AiChatRecents'; + export const NavigationDrawerAiChatContent = () => { const { t } = useLingui(); @@ -49,11 +61,10 @@ export const NavigationDrawerAiChatContent = () => { const { handleThreadClick } = useAiChatThreadClick({ resetNavigationStack: true, }); + const agentChatThreadGroupBy = useAtomStateValue(agentChatThreadGroupByState); const { threads, hasNextPage, loading, fetchMoreRef } = useChatThreads(); - const groupedThreads = groupThreadsByDate(threads); - if (loading && threads.length === 0) { return ( @@ -62,33 +73,54 @@ export const NavigationDrawerAiChatContent = () => { ); } - if (threads.length === 0) { - return ( - - {t`No chat`} - - ); - } + const isGroupedByDate = + agentChatThreadGroupBy === AGENT_CHAT_THREAD_GROUP_BY.DATE; + const dateGroups = isGroupedByDate ? groupThreadsByDate(threads) : []; + const shouldRenderDateGroups = isGroupedByDate && dateGroups.length > 0; + + const filterDropdown = ( + + ); return ( - {DATE_GROUP_KEYS.map((key: DateGroupKey) => { - const threadsInGroup = groupedThreads[key]; - if (threadsInGroup.length === 0) return null; - - return ( - - ); - })} + {shouldRenderDateGroups ? ( + + {dateGroups.map((dateGroup, index) => ( + + ))} + + ) : ( + + )} + {threads.length === 0 ? ( + {t`No chat`} + ) : null} {hasNextPage ? : null} + ); }; diff --git a/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatThreadDateSection.tsx b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatThreadDateSection.tsx deleted file mode 100644 index d82e5404a6..0000000000 --- a/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatThreadDateSection.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { styled } from '@linaria/react'; -import { useLingui } from '@lingui/react/macro'; -import { IconComment } from 'twenty-ui/display'; -import { themeCssVariables } from 'twenty-ui/theme-constants'; - -import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; -import { type AgentChatThread } from '~/generated-metadata/graphql'; -import { beautifyPastDateRelativeToNowShort } from '~/utils/date-utils'; - -const StyledDateSection = styled.section` - margin-bottom: ${themeCssVariables.spacing[4]}; -`; - -const StyledThreadList = styled.div` - display: flex; - flex-direction: column; - gap: ${themeCssVariables.spacing['0.5']}; -`; - -const StyledDateHeader = styled.div` - color: ${themeCssVariables.font.color.light}; - font-size: ${themeCssVariables.font.size.xs}; - font-weight: ${themeCssVariables.font.weight.medium}; - margin-bottom: ${themeCssVariables.spacing[1]}; - padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]}; -`; - -const StyledThreadTimestamp = styled.span` - color: ${themeCssVariables.font.color.light}; - font-size: ${themeCssVariables.font.size.xs}; - font-weight: ${themeCssVariables.font.weight.regular}; - padding-right: ${themeCssVariables.spacing['0.5']}; -`; - -export type NavigationDrawerAiChatThreadDateSectionProps = { - title: string; - threads: AgentChatThread[]; - currentThreadId: string | null; - onThreadClick: (thread: AgentChatThread) => void; -}; - -export const NavigationDrawerAiChatThreadDateSection = ({ - title, - threads, - currentThreadId, - onThreadClick, -}: NavigationDrawerAiChatThreadDateSectionProps) => { - const { t } = useLingui(); - - return ( - - {title} - - {threads.map((thread) => { - const isActive = currentThreadId === thread.id; - const timestamp = beautifyPastDateRelativeToNowShort( - thread.updatedAt ?? thread.createdAt, - ); - return ( - onThreadClick(thread)} - alwaysShowRightOptions - rightOptions={ - {timestamp} - } - /> - ); - })} - - - ); -}; diff --git a/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatThreadItem.tsx b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatThreadItem.tsx new file mode 100644 index 0000000000..3e40b9dec1 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatThreadItem.tsx @@ -0,0 +1,129 @@ +import { styled } from '@linaria/react'; +import { useLingui } from '@lingui/react/macro'; +import { IconArchive, IconComment } from 'twenty-ui/display'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { AiChatThreadItemMenu } from '@/ai/components/AiChatThreadItemMenu'; +import { AI_CHAT_THREAD_ACTIONS_SURFACE } from '@/ai/constants/AiChatThreadActionsSurface'; +import { useAiChatThreadRename } from '@/ai/hooks/useAiChatThreadRename'; +import { getAiChatThreadItemMenuDropdownId } from '@/ai/utils/getAiChatThreadItemMenuDropdownId'; +import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; +import { NavigationDrawerInput } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerInput'; +import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; +import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; +import { type AgentChatThread } from '~/generated-metadata/graphql'; +import { beautifyPastDateRelativeToNowShort } from '~/utils/date-utils'; + +const StyledRightOptions = styled.div` + align-items: center; + display: flex; + height: ${themeCssVariables.spacing[6]}; + justify-content: flex-end; + min-width: ${themeCssVariables.spacing[6]}; + position: relative; +`; + +const StyledTimestamp = styled.span<{ $isDropdownOpen: boolean }>` + color: ${themeCssVariables.font.color.light}; + font-size: ${themeCssVariables.font.size.xs}; + font-weight: ${themeCssVariables.font.weight.regular}; + opacity: ${({ $isDropdownOpen }) => ($isDropdownOpen ? 0 : 1)}; + transition: opacity 150ms; + + .navigation-drawer-item:hover & { + opacity: 0; + } +`; + +const StyledMenuTrigger = styled.div<{ $isDropdownOpen: boolean }>` + opacity: ${({ $isDropdownOpen }) => ($isDropdownOpen ? 1 : 0)}; + pointer-events: ${({ $isDropdownOpen }) => + $isDropdownOpen ? 'auto' : 'none'}; + position: absolute; + right: 0; + top: 0; + transition: opacity 150ms; + + .navigation-drawer-item:hover & { + opacity: 1; + pointer-events: auto; + } +`; + +type NavigationDrawerAiChatThreadItemProps = { + thread: AgentChatThread; + isActive: boolean; + onClick: (thread: AgentChatThread) => void; +}; + +export const NavigationDrawerAiChatThreadItem = ({ + thread, + isActive, + onClick, +}: NavigationDrawerAiChatThreadItemProps) => { + const { t } = useLingui(); + const { + isRenaming, + draftTitle, + setDraftTitle, + startRename, + cancelRename, + commitRename, + } = useAiChatThreadRename(thread); + + const isArchived = Boolean(thread.deletedAt); + const ThreadIcon = isArchived ? IconArchive : IconComment; + const displayLabel = thread.title || t`New chat`; + const timestamp = beautifyPastDateRelativeToNowShort( + thread.lastMessageAt ?? thread.updatedAt ?? thread.createdAt, + ); + const itemMenuDropdownId = getAiChatThreadItemMenuDropdownId( + thread.id, + AI_CHAT_THREAD_ACTIONS_SURFACE.NAV_DRAWER, + ); + const isDropdownOpen = useAtomComponentStateValue( + isDropdownOpenComponentState, + itemMenuDropdownId, + ); + + if (isRenaming) { + return ( + commitRename(value)} + placeholder={t`Chat name`} + /> + ); + } + + return ( + onClick(thread)} + variant={isArchived ? 'tertiary' : 'default'} + alwaysShowRightOptions + rightOptions={ + + + {timestamp} + + + + + + } + /> + ); +}; diff --git a/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatThreadSection.tsx b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatThreadSection.tsx new file mode 100644 index 0000000000..452fc58f60 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAiChatThreadSection.tsx @@ -0,0 +1,79 @@ +import { styled } from '@linaria/react'; +import { type ReactNode } from 'react'; +import { AnimatedExpandableContainer } from 'twenty-ui/layout'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { NavigationDrawerAiChatThreadItem } from '@/ai/components/NavigationDrawerAiChatThreadItem'; +import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper'; +import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle'; +import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection'; +import { type AgentChatThread } from '~/generated-metadata/graphql'; + +const StyledSection = styled.section` + display: flex; + flex-direction: column; +`; + +const StyledThreadList = styled.div` + display: flex; + flex-direction: column; + gap: ${themeCssVariables.spacing['0.5']}; + padding-top: ${themeCssVariables.betweenSiblingsGap}; +`; + +export type NavigationDrawerAiChatThreadSectionProps = { + sectionId: string; + title: string; + threads: AgentChatThread[]; + currentThreadId: string | null; + onThreadClick: (thread: AgentChatThread) => void; + rightIcon?: ReactNode; + alwaysShowRightIcon?: boolean; +}; + +export const NavigationDrawerAiChatThreadSection = ({ + sectionId, + title, + threads, + currentThreadId, + onThreadClick, + rightIcon, + alwaysShowRightIcon = false, +}: NavigationDrawerAiChatThreadSectionProps) => { + const { isNavigationSectionOpen, toggleNavigationSection } = + useNavigationSection(sectionId); + + return ( + + + + + {threads.length > 0 ? ( + + + {threads.map((thread) => ( + + ))} + + + ) : null} + + ); +}; diff --git a/packages/twenty-front/src/modules/ai/components/__stories__/AiChatMessage.stories.tsx b/packages/twenty-front/src/modules/ai/components/__stories__/AiChatMessage.stories.tsx index d13e9a5d9d..3b152f22fe 100644 --- a/packages/twenty-front/src/modules/ai/components/__stories__/AiChatMessage.stories.tsx +++ b/packages/twenty-front/src/modules/ai/components/__stories__/AiChatMessage.stories.tsx @@ -10,7 +10,7 @@ import { ComponentDecorator } from 'twenty-ui/testing'; import { AiChatMessage } from '@/ai/components/AiChatMessage'; -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThreadState'; import { agentChatMessageComponentFamilyState } from '@/ai/states/agentChatMessageComponentFamilyState'; import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; diff --git a/packages/twenty-front/src/modules/ai/components/internal/AiChatContextUsageButton.tsx b/packages/twenty-front/src/modules/ai/components/internal/AiChatContextUsageButton.tsx index 79d749b0d4..f9d9bda623 100644 --- a/packages/twenty-front/src/modules/ai/components/internal/AiChatContextUsageButton.tsx +++ b/packages/twenty-front/src/modules/ai/components/internal/AiChatContextUsageButton.tsx @@ -8,7 +8,7 @@ import { ProgressBar } from 'twenty-ui/feedback'; import { themeCssVariables } from 'twenty-ui/theme-constants'; import { ContextUsageProgressRing } from '@/ai/components/internal/ContextUsageProgressRing'; -import { agentChatHasMessageComponentSelector } from '@/ai/states/agentChatHasMessageComponentSelector'; +import { agentChatHasMessageComponentSelector } from '@/ai/states/selectors/agentChatHasMessageComponentSelector'; import { agentChatUsageComponentFamilyState, type AgentChatLastMessageUsage, diff --git a/packages/twenty-front/src/modules/ai/components/internal/AiChatSkeletonLoader.tsx b/packages/twenty-front/src/modules/ai/components/internal/AiChatSkeletonLoader.tsx index 7db5ebacdc..6d54996062 100644 --- a/packages/twenty-front/src/modules/ai/components/internal/AiChatSkeletonLoader.tsx +++ b/packages/twenty-front/src/modules/ai/components/internal/AiChatSkeletonLoader.tsx @@ -6,7 +6,7 @@ import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; import { AGENT_CHAT_NEW_THREAD_DRAFT_KEY } from '@/ai/states/agentChatDraftsByThreadIdState'; import { agentChatMessagesLoadingState } from '@/ai/states/agentChatMessagesLoadingState'; import { agentChatThreadsLoadingState } from '@/ai/states/agentChatThreadsLoadingState'; -import { agentChatHasMessageComponentSelector } from '@/ai/states/agentChatHasMessageComponentSelector'; +import { agentChatHasMessageComponentSelector } from '@/ai/states/selectors/agentChatHasMessageComponentSelector'; import { currentAiChatThreadState } from '@/ai/states/currentAiChatThreadState'; import { skipMessagesSkeletonUntilLoadedState } from '@/ai/states/skipMessagesSkeletonUntilLoadedState'; import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; diff --git a/packages/twenty-front/src/modules/ai/components/internal/SendMessageButton.tsx b/packages/twenty-front/src/modules/ai/components/internal/SendMessageButton.tsx index 8799ff9527..00c00f9d42 100644 --- a/packages/twenty-front/src/modules/ai/components/internal/SendMessageButton.tsx +++ b/packages/twenty-front/src/modules/ai/components/internal/SendMessageButton.tsx @@ -1,5 +1,5 @@ import { AGENT_CHAT_STOP_EVENT_NAME } from '@/ai/constants/AgentChatStopEventName'; -import { agentChatInputIsEmptySelector } from '@/ai/states/agentChatInputIsEmptySelector'; +import { agentChatInputIsEmptySelector } from '@/ai/states/selectors/agentChatInputIsEmptySelector'; import { agentChatIsLoadingState } from '@/ai/states/agentChatIsLoadingState'; import { agentChatIsStreamingComponentFamilyState } from '@/ai/states/agentChatIsStreamingComponentFamilyState'; import { currentAiChatThreadState } from '@/ai/states/currentAiChatThreadState'; diff --git a/packages/twenty-front/src/modules/ai/constants/AgentChatThreadFilterStatus.ts b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadFilterStatus.ts new file mode 100644 index 0000000000..618631e313 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadFilterStatus.ts @@ -0,0 +1,5 @@ +export const AGENT_CHAT_THREAD_FILTER_STATUS = { + ACTIVE: 'active', + ARCHIVED: 'archived', + ALL: 'all', +} as const; diff --git a/packages/twenty-front/src/modules/ai/constants/AgentChatThreadFilterStatusLabels.ts b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadFilterStatusLabels.ts new file mode 100644 index 0000000000..f33034e6ce --- /dev/null +++ b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadFilterStatusLabels.ts @@ -0,0 +1,13 @@ +import { msg } from '@lingui/core/macro'; +import { type MessageDescriptor } from '@lingui/core'; + +import { type AgentChatThreadFilterStatus } from '@/ai/types/AgentChatThreadFilterStatus'; + +export const AGENT_CHAT_THREAD_FILTER_STATUS_LABELS: Record< + AgentChatThreadFilterStatus, + MessageDescriptor +> = { + active: msg`Active`, + archived: msg`Archived`, + all: msg`All`, +}; diff --git a/packages/twenty-front/src/modules/ai/constants/AgentChatThreadGroupBy.ts b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadGroupBy.ts new file mode 100644 index 0000000000..e6cbf6c04d --- /dev/null +++ b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadGroupBy.ts @@ -0,0 +1,4 @@ +export const AGENT_CHAT_THREAD_GROUP_BY = { + DATE: 'date', + NONE: 'none', +} as const; diff --git a/packages/twenty-front/src/modules/ai/constants/AgentChatThreadGroupByLabels.ts b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadGroupByLabels.ts new file mode 100644 index 0000000000..105f75608a --- /dev/null +++ b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadGroupByLabels.ts @@ -0,0 +1,12 @@ +import { msg } from '@lingui/core/macro'; +import { type MessageDescriptor } from '@lingui/core'; + +import { type AgentChatThreadGroupBy } from '@/ai/types/AgentChatThreadGroupBy'; + +export const AGENT_CHAT_THREAD_GROUP_BY_LABELS: Record< + AgentChatThreadGroupBy, + MessageDescriptor +> = { + date: msg`Date`, + none: msg`None`, +}; diff --git a/packages/twenty-front/src/modules/ai/constants/AgentChatThreadLastActivityFilter.ts b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadLastActivityFilter.ts new file mode 100644 index 0000000000..bdf76890ed --- /dev/null +++ b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadLastActivityFilter.ts @@ -0,0 +1,7 @@ +export const AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER = { + ALL: 'all', + ONE_DAY: '1d', + THREE_DAYS: '3d', + SEVEN_DAYS: '7d', + THIRTY_DAYS: '30d', +} as const; diff --git a/packages/twenty-front/src/modules/ai/constants/AgentChatThreadLastActivityFilterDays.ts b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadLastActivityFilterDays.ts new file mode 100644 index 0000000000..0167253b67 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadLastActivityFilterDays.ts @@ -0,0 +1,12 @@ +import { type AgentChatThreadLastActivityFilter } from '@/ai/types/AgentChatThreadLastActivityFilter'; + +export const AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER_DAYS: Record< + AgentChatThreadLastActivityFilter, + number | null +> = { + all: null, + '1d': 1, + '3d': 3, + '7d': 7, + '30d': 30, +}; diff --git a/packages/twenty-front/src/modules/ai/constants/AgentChatThreadLastActivityFilterLabels.ts b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadLastActivityFilterLabels.ts new file mode 100644 index 0000000000..de8ea53c62 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/constants/AgentChatThreadLastActivityFilterLabels.ts @@ -0,0 +1,15 @@ +import { msg } from '@lingui/core/macro'; +import { type MessageDescriptor } from '@lingui/core'; + +import { type AgentChatThreadLastActivityFilter } from '@/ai/types/AgentChatThreadLastActivityFilter'; + +export const AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER_LABELS: Record< + AgentChatThreadLastActivityFilter, + MessageDescriptor +> = { + all: msg`All`, + '1d': msg`1d`, + '3d': msg`3d`, + '7d': msg`7d`, + '30d': msg`30d`, +}; diff --git a/packages/twenty-front/src/modules/ai/constants/AiChatThreadActionsSurface.ts b/packages/twenty-front/src/modules/ai/constants/AiChatThreadActionsSurface.ts new file mode 100644 index 0000000000..196f016583 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/constants/AiChatThreadActionsSurface.ts @@ -0,0 +1,4 @@ +export const AI_CHAT_THREAD_ACTIONS_SURFACE = { + SIDE_PANEL: 'side-panel', + NAV_DRAWER: 'nav-drawer', +} as const; diff --git a/packages/twenty-front/src/modules/ai/constants/AiChatThreadFilterDropdownPage.ts b/packages/twenty-front/src/modules/ai/constants/AiChatThreadFilterDropdownPage.ts new file mode 100644 index 0000000000..c3098a97ca --- /dev/null +++ b/packages/twenty-front/src/modules/ai/constants/AiChatThreadFilterDropdownPage.ts @@ -0,0 +1,6 @@ +export const AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE = { + ROOT: 'root', + STATUS: 'status', + GROUP_BY: 'groupBy', + LAST_ACTIVITY: 'lastActivity', +} as const; diff --git a/packages/twenty-front/src/modules/ai/states/AgentChatComponentInstanceContext.ts b/packages/twenty-front/src/modules/ai/contexts/AgentChatComponentInstanceContext.ts similarity index 100% rename from packages/twenty-front/src/modules/ai/states/AgentChatComponentInstanceContext.ts rename to packages/twenty-front/src/modules/ai/contexts/AgentChatComponentInstanceContext.ts diff --git a/packages/twenty-front/src/modules/ai/graphql/mutations/archiveChatThread.ts b/packages/twenty-front/src/modules/ai/graphql/mutations/archiveChatThread.ts new file mode 100644 index 0000000000..75190c19b5 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/mutations/archiveChatThread.ts @@ -0,0 +1,11 @@ +import { gql } from '@apollo/client'; + +export const ARCHIVE_CHAT_THREAD = gql` + mutation ArchiveChatThread($id: UUID!) { + archiveChatThread(id: $id) { + id + deletedAt + updatedAt + } + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/mutations/deleteChatThread.ts b/packages/twenty-front/src/modules/ai/graphql/mutations/deleteChatThread.ts new file mode 100644 index 0000000000..f08a49c49f --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/mutations/deleteChatThread.ts @@ -0,0 +1,7 @@ +import { gql } from '@apollo/client'; + +export const DELETE_CHAT_THREAD = gql` + mutation DeleteChatThread($id: UUID!) { + deleteChatThread(id: $id) + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/mutations/renameChatThread.ts b/packages/twenty-front/src/modules/ai/graphql/mutations/renameChatThread.ts new file mode 100644 index 0000000000..efe0953bce --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/mutations/renameChatThread.ts @@ -0,0 +1,11 @@ +import { gql } from '@apollo/client'; + +export const RENAME_CHAT_THREAD = gql` + mutation RenameChatThread($id: UUID!, $title: String!) { + renameChatThread(id: $id, title: $title) { + id + title + updatedAt + } + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/mutations/unarchiveChatThread.ts b/packages/twenty-front/src/modules/ai/graphql/mutations/unarchiveChatThread.ts new file mode 100644 index 0000000000..4206c4a189 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/graphql/mutations/unarchiveChatThread.ts @@ -0,0 +1,11 @@ +import { gql } from '@apollo/client'; + +export const UNARCHIVE_CHAT_THREAD = gql` + mutation UnarchiveChatThread($id: UUID!) { + unarchiveChatThread(id: $id) { + id + deletedAt + updatedAt + } + } +`; diff --git a/packages/twenty-front/src/modules/ai/graphql/queries/getChatThreads.ts b/packages/twenty-front/src/modules/ai/graphql/queries/getChatThreads.ts index 477cf9bb65..e47fd5922e 100644 --- a/packages/twenty-front/src/modules/ai/graphql/queries/getChatThreads.ts +++ b/packages/twenty-front/src/modules/ai/graphql/queries/getChatThreads.ts @@ -1,27 +1,20 @@ import { gql } from '@apollo/client'; export const GET_CHAT_THREADS = gql` - query GetChatThreads($paging: CursorPaging) { - chatThreads(paging: $paging) { - edges { - node { - id - title - totalInputTokens - totalOutputTokens - contextWindowTokens - conversationSize - totalInputCredits - totalOutputCredits - createdAt - updatedAt - } - cursor - } - pageInfo { - endCursor - hasNextPage - } + query GetChatThreads { + chatThreads { + id + title + totalInputTokens + totalOutputTokens + contextWindowTokens + conversationSize + totalInputCredits + totalOutputCredits + deletedAt + lastMessageAt + createdAt + updatedAt } } `; diff --git a/packages/twenty-front/src/modules/ai/hooks/__tests__/useAiChatThreadRename.test.ts b/packages/twenty-front/src/modules/ai/hooks/__tests__/useAiChatThreadRename.test.ts new file mode 100644 index 0000000000..e6742519ce --- /dev/null +++ b/packages/twenty-front/src/modules/ai/hooks/__tests__/useAiChatThreadRename.test.ts @@ -0,0 +1,167 @@ +import { act, renderHook } from '@testing-library/react'; + +import { useAiChatThreadRename } from '@/ai/hooks/useAiChatThreadRename'; +import { useRenameChatThread } from '@/ai/hooks/useRenameChatThread'; +import { type AgentChatThread } from '~/generated-metadata/graphql'; + +jest.mock('@/ai/hooks/useRenameChatThread'); + +const buildThread = ( + overrides: Partial = {}, +): AgentChatThread => + ({ + id: 'thread-1', + title: 'Existing title', + createdAt: '2026-04-01T00:00:00.000Z', + updatedAt: '2026-04-01T00:00:00.000Z', + totalInputTokens: 0, + totalOutputTokens: 0, + contextWindowTokens: null, + conversationSize: 0, + totalInputCredits: 0, + totalOutputCredits: 0, + ...overrides, + }) as AgentChatThread; + +describe('useAiChatThreadRename', () => { + const renameChatThread = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + renameChatThread.mockResolvedValue(true); + (useRenameChatThread as jest.Mock).mockReturnValue({ renameChatThread }); + }); + + it('starts in non-renaming state with the current thread title as draft', () => { + const { result } = renderHook(() => + useAiChatThreadRename(buildThread({ title: 'Existing title' })), + ); + + expect(result.current.isRenaming).toBe(false); + expect(result.current.draftTitle).toBe('Existing title'); + }); + + it('falls back to empty string when the thread title is null', () => { + const { result } = renderHook(() => + useAiChatThreadRename(buildThread({ title: null })), + ); + + expect(result.current.draftTitle).toBe(''); + }); + + it('enters renaming mode and re-seeds draft from current title on startRename', () => { + const { result } = renderHook(() => + useAiChatThreadRename(buildThread({ title: 'Existing title' })), + ); + + act(() => { + result.current.setDraftTitle('Stale draft'); + }); + act(() => { + result.current.startRename(); + }); + + expect(result.current.isRenaming).toBe(true); + expect(result.current.draftTitle).toBe('Existing title'); + }); + + it('exits renaming mode and resets the draft on cancelRename', () => { + const { result } = renderHook(() => + useAiChatThreadRename(buildThread({ title: 'Existing title' })), + ); + + act(() => { + result.current.startRename(); + result.current.setDraftTitle('Edited draft'); + }); + act(() => { + result.current.cancelRename(); + }); + + expect(result.current.isRenaming).toBe(false); + expect(result.current.draftTitle).toBe('Existing title'); + }); + + it('skips renaming when committed title is empty after trim', async () => { + const { result } = renderHook(() => + useAiChatThreadRename(buildThread({ title: 'Existing title' })), + ); + + await act(async () => { + await result.current.commitRename(' '); + }); + + expect(renameChatThread).not.toHaveBeenCalled(); + expect(result.current.isRenaming).toBe(false); + }); + + it('skips renaming when committed title equals the current title', async () => { + const { result } = renderHook(() => + useAiChatThreadRename(buildThread({ title: 'Existing title' })), + ); + + await act(async () => { + await result.current.commitRename('Existing title'); + }); + + expect(renameChatThread).not.toHaveBeenCalled(); + }); + + it('trims surrounding whitespace before comparing to the current title', async () => { + const { result } = renderHook(() => + useAiChatThreadRename(buildThread({ title: 'Existing title' })), + ); + + await act(async () => { + await result.current.commitRename(' Existing title '); + }); + + expect(renameChatThread).not.toHaveBeenCalled(); + }); + + it('renames with the trimmed title when it differs from the current one', async () => { + const { result } = renderHook(() => + useAiChatThreadRename( + buildThread({ id: 'thread-7', title: 'Old title' }), + ), + ); + + await act(async () => { + await result.current.commitRename(' New title '); + }); + + expect(renameChatThread).toHaveBeenCalledWith('thread-7', 'New title'); + expect(result.current.isRenaming).toBe(false); + }); + + it('keeps renaming mode open when the rename mutation reports failure', async () => { + renameChatThread.mockResolvedValueOnce(false); + + const { result } = renderHook(() => + useAiChatThreadRename(buildThread({ id: 'thread-fail', title: 'Old' })), + ); + + act(() => { + result.current.startRename(); + }); + + await act(async () => { + await result.current.commitRename('New title'); + }); + + expect(renameChatThread).toHaveBeenCalledWith('thread-fail', 'New title'); + expect(result.current.isRenaming).toBe(true); + }); + + it('treats a null current title as empty when comparing against committed input', async () => { + const { result } = renderHook(() => + useAiChatThreadRename(buildThread({ id: 'thread-9', title: null })), + ); + + await act(async () => { + await result.current.commitRename('First name'); + }); + + expect(renameChatThread).toHaveBeenCalledWith('thread-9', 'First name'); + }); +}); diff --git a/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts b/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts index 0e62543abf..177efe2ca6 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts @@ -13,20 +13,22 @@ import { AGENT_CHAT_SEND_MESSAGE_EVENT_NAME } from '@/ai/constants/AgentChatSend import { AGENT_CHAT_STOP_EVENT_NAME } from '@/ai/constants/AgentChatStopEventName'; import { SEND_CHAT_MESSAGE } from '@/ai/graphql/mutations/sendChatMessage'; import { STOP_AGENT_CHAT_STREAM } from '@/ai/graphql/mutations/stopAgentChatStream'; +import { useAgentChatModelId } from '@/ai/hooks/useAgentChatModelId'; +import { useGetBrowsingContext } from '@/ai/hooks/useBrowsingContext'; +import { useOptimisticallyUnarchiveOnSend } from '@/ai/hooks/useOptimisticallyUnarchiveOnSend'; import { AGENT_CHAT_NEW_THREAD_DRAFT_KEY, agentChatDraftsByThreadIdState, } from '@/ai/states/agentChatDraftsByThreadIdState'; import { agentChatErrorComponentFamilyState } from '@/ai/states/agentChatErrorComponentFamilyState'; import { agentChatInputState } from '@/ai/states/agentChatInputState'; +import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; import { agentChatSelectedFilesState } from '@/ai/states/agentChatSelectedFilesState'; import { agentChatUploadedFilesState } from '@/ai/states/agentChatUploadedFilesState'; -import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; import { currentAiChatThreadState } from '@/ai/states/currentAiChatThreadState'; -import { useGetBrowsingContext } from '@/ai/hooks/useBrowsingContext'; -import { useAgentChatModelId } from '@/ai/hooks/useAgentChatModelId'; import { useListenToBrowserEvent } from '@/browser-event/hooks/useListenToBrowserEvent'; import { dispatchBrowserEvent } from '@/browser-event/utils/dispatchBrowserEvent'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; @@ -36,7 +38,9 @@ export const useAgentChat = ( ) => { const { modelIdForRequest } = useAgentChatModelId(); const { getBrowsingContext } = useGetBrowsingContext(); + const { applyOptimisticUnarchive } = useOptimisticallyUnarchiveOnSend(); const apolloClient = useApolloClient(); + const { enqueueErrorSnackBar } = useSnackBar(); const setCurrentAiChatThread = useSetAtomState(currentAiChatThreadState); const store = useStore(); @@ -94,6 +98,11 @@ export const useAgentChat = ( const browsingContext = getBrowsingContext(); const messageId = v4(); + const optimisticMessageCreatedAt = new Date().toISOString(); + const rollbackOptimisticUnarchive = applyOptimisticUnarchive( + threadId, + optimisticMessageCreatedAt, + ); const optimisticUserMessage: ExtendedUIMessage = { id: messageId, @@ -103,7 +112,7 @@ export const useAgentChat = ( ...agentChatUploadedFiles, ], metadata: { - createdAt: new Date().toISOString(), + createdAt: optimisticMessageCreatedAt, }, status: 'sent', }; @@ -167,6 +176,8 @@ export const useAgentChat = ( const restoredDraftKey = draftKey === AGENT_CHAT_NEW_THREAD_DRAFT_KEY ? threadId : draftKey; + rollbackOptimisticUnarchive?.(); + setAgentChatInput(contentToSend); setAgentChatDraftsByThreadId((prev) => ({ ...prev, @@ -213,6 +224,7 @@ export const useAgentChat = ( modelIdForRequest, setCurrentAiChatThread, apolloClient, + applyOptimisticUnarchive, ]); useListenToBrowserEvent({ @@ -227,13 +239,17 @@ export const useAgentChat = ( return; } - apolloClient - .mutate({ + try { + await apolloClient.mutate({ mutation: STOP_AGENT_CHAT_STREAM, variables: { threadId }, - }) - .catch(() => {}); - }, [store, apolloClient]); + }); + } catch (error) { + enqueueErrorSnackBar({ + apolloError: CombinedGraphQLErrors.is(error) ? error : undefined, + }); + } + }, [store, apolloClient, enqueueErrorSnackBar]); useListenToBrowserEvent({ eventName: AGENT_CHAT_STOP_EVENT_NAME, diff --git a/packages/twenty-front/src/modules/ai/hooks/useAiChatThreadRename.ts b/packages/twenty-front/src/modules/ai/hooks/useAiChatThreadRename.ts new file mode 100644 index 0000000000..567de5019f --- /dev/null +++ b/packages/twenty-front/src/modules/ai/hooks/useAiChatThreadRename.ts @@ -0,0 +1,45 @@ +import { useState } from 'react'; + +import { useRenameChatThread } from '@/ai/hooks/useRenameChatThread'; +import { type AgentChatThread } from '~/generated-metadata/graphql'; + +export const useAiChatThreadRename = (thread: AgentChatThread) => { + const { renameChatThread } = useRenameChatThread(); + + const [isRenaming, setIsRenaming] = useState(false); + const [draftTitle, setDraftTitle] = useState(thread.title ?? ''); + + const startRename = () => { + setDraftTitle(thread.title ?? ''); + setIsRenaming(true); + }; + + const cancelRename = () => { + setIsRenaming(false); + setDraftTitle(thread.title ?? ''); + }; + + const commitRename = async (nextTitle: string) => { + const trimmed = nextTitle.trim(); + + if (trimmed.length === 0 || trimmed === (thread.title ?? '')) { + setIsRenaming(false); + return; + } + + const succeeded = await renameChatThread(thread.id, trimmed); + + if (succeeded) { + setIsRenaming(false); + } + }; + + return { + isRenaming, + draftTitle, + setDraftTitle, + startRename, + cancelRename, + commitRename, + }; +}; diff --git a/packages/twenty-front/src/modules/ai/hooks/useApplyAgentChatThreadUpdate.ts b/packages/twenty-front/src/modules/ai/hooks/useApplyAgentChatThreadUpdate.ts new file mode 100644 index 0000000000..a38b29e27d --- /dev/null +++ b/packages/twenty-front/src/modules/ai/hooks/useApplyAgentChatThreadUpdate.ts @@ -0,0 +1,17 @@ +import { useUpdateMetadataStoreDraft } from '@/metadata-store/hooks/useUpdateMetadataStoreDraft'; +import { type FlatAgentChatThread } from '@/metadata-store/types/FlatAgentChatThread'; + +type AgentChatThreadDraftUpdate = Partial & { + id: string; +}; + +export const useApplyAgentChatThreadUpdate = () => { + const { updateInDraft, applyChanges } = useUpdateMetadataStoreDraft(); + + const applyAgentChatThreadUpdate = (update: AgentChatThreadDraftUpdate) => { + updateInDraft('agentChatThreads', [update as FlatAgentChatThread]); + applyChanges(); + }; + + return { applyAgentChatThreadUpdate }; +}; diff --git a/packages/twenty-front/src/modules/ai/hooks/useChatThreadArchiveActions.ts b/packages/twenty-front/src/modules/ai/hooks/useChatThreadArchiveActions.ts new file mode 100644 index 0000000000..d7d6acf032 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/hooks/useChatThreadArchiveActions.ts @@ -0,0 +1,55 @@ +import { CombinedGraphQLErrors } from '@apollo/client/errors'; +import { useMutation } from '@apollo/client/react'; + +import { useApplyAgentChatThreadUpdate } from '@/ai/hooks/useApplyAgentChatThreadUpdate'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { + ArchiveChatThreadDocument, + UnarchiveChatThreadDocument, +} from '~/generated-metadata/graphql'; + +export const useChatThreadArchiveActions = () => { + const { applyAgentChatThreadUpdate } = useApplyAgentChatThreadUpdate(); + const { enqueueErrorSnackBar } = useSnackBar(); + + const [archiveMutation] = useMutation(ArchiveChatThreadDocument); + const [unarchiveMutation] = useMutation(UnarchiveChatThreadDocument); + + const archiveChatThread = async (id: string) => { + try { + const { data } = await archiveMutation({ variables: { id } }); + + if (data?.archiveChatThread) { + applyAgentChatThreadUpdate({ + id: data.archiveChatThread.id, + deletedAt: data.archiveChatThread.deletedAt ?? null, + updatedAt: data.archiveChatThread.updatedAt, + }); + } + } catch (error) { + enqueueErrorSnackBar({ + apolloError: CombinedGraphQLErrors.is(error) ? error : undefined, + }); + } + }; + + const unarchiveChatThread = async (id: string) => { + try { + const { data } = await unarchiveMutation({ variables: { id } }); + + if (data?.unarchiveChatThread) { + applyAgentChatThreadUpdate({ + id: data.unarchiveChatThread.id, + deletedAt: data.unarchiveChatThread.deletedAt ?? null, + updatedAt: data.unarchiveChatThread.updatedAt, + }); + } + } catch (error) { + enqueueErrorSnackBar({ + apolloError: CombinedGraphQLErrors.is(error) ? error : undefined, + }); + } + }; + + return { archiveChatThread, unarchiveChatThread }; +}; diff --git a/packages/twenty-front/src/modules/ai/hooks/useChatThreads.ts b/packages/twenty-front/src/modules/ai/hooks/useChatThreads.ts index 91c87c2ce5..f5e42ea44b 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useChatThreads.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useChatThreads.ts @@ -1,27 +1,20 @@ import { useAtomValue } from 'jotai'; -import { useMemo } from 'react'; -import { agentChatThreadsSelector } from '@/ai/states/agentChatThreadsSelector'; +import { agentChatVisibleThreadsSelector } from '@/ai/states/selectors/agentChatVisibleThreadsSelector'; +import { sortChatThreadsByLastActivityDesc } from '@/ai/utils/sortChatThreadsByLastActivityDesc'; import { metadataStoreState } from '@/metadata-store/states/metadataStoreState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; export const useChatThreads = () => { - const agentChatThreads = useAtomStateValue(agentChatThreadsSelector); + const agentChatVisibleThreads = useAtomStateValue( + agentChatVisibleThreadsSelector, + ); const storeEntry = useAtomValue( metadataStoreState.atomFamily('agentChatThreads'), ); - const threads = useMemo( - () => - [...agentChatThreads].sort( - (a, b) => - new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(), - ), - [agentChatThreads], - ); - return { - threads, + threads: sortChatThreadsByLastActivityDesc(agentChatVisibleThreads), hasNextPage: false, loading: storeEntry.status === 'empty', fetchMoreRef: undefined, diff --git a/packages/twenty-front/src/modules/ai/hooks/useDeleteChatThread.ts b/packages/twenty-front/src/modules/ai/hooks/useDeleteChatThread.ts new file mode 100644 index 0000000000..65b8bc7bbe --- /dev/null +++ b/packages/twenty-front/src/modules/ai/hooks/useDeleteChatThread.ts @@ -0,0 +1,66 @@ +import { CombinedGraphQLErrors } from '@apollo/client/errors'; +import { useMutation } from '@apollo/client/react'; +import { useStore } from 'jotai'; + +import { + AGENT_CHAT_NEW_THREAD_DRAFT_KEY, + agentChatDraftsByThreadIdState, +} from '@/ai/states/agentChatDraftsByThreadIdState'; +import { agentChatInputState } from '@/ai/states/agentChatInputState'; +import { agentChatVisibleThreadsSelector } from '@/ai/states/selectors/agentChatVisibleThreadsSelector'; +import { currentAiChatThreadState } from '@/ai/states/currentAiChatThreadState'; +import { sortChatThreadsByLastActivityDesc } from '@/ai/utils/sortChatThreadsByLastActivityDesc'; +import { useUpdateMetadataStoreDraft } from '@/metadata-store/hooks/useUpdateMetadataStoreDraft'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; +import { DeleteChatThreadDocument } from '~/generated-metadata/graphql'; + +export const useDeleteChatThread = () => { + const { removeFromDraft, applyChanges } = useUpdateMetadataStoreDraft(); + const { enqueueErrorSnackBar } = useSnackBar(); + const setCurrentAiChatThread = useSetAtomState(currentAiChatThreadState); + const setAgentChatInput = useSetAtomState(agentChatInputState); + const store = useStore(); + + const [deleteMutation] = useMutation(DeleteChatThreadDocument); + + const deleteChatThread = async (id: string) => { + try { + await deleteMutation({ variables: { id } }); + + removeFromDraft({ key: 'agentChatThreads', itemIds: [id] }); + applyChanges(); + + const isCurrent = store.get(currentAiChatThreadState.atom) === id; + + if (!isCurrent) { + return; + } + + const remaining = sortChatThreadsByLastActivityDesc( + store + .get(agentChatVisibleThreadsSelector.atom) + .filter((thread) => thread.id !== id), + ); + const draftsByThreadId = store.get(agentChatDraftsByThreadIdState.atom); + + if (remaining.length > 0) { + const nextThreadId = remaining[0].id; + + setCurrentAiChatThread(nextThreadId); + setAgentChatInput(draftsByThreadId[nextThreadId] ?? ''); + } else { + setCurrentAiChatThread(AGENT_CHAT_NEW_THREAD_DRAFT_KEY); + setAgentChatInput( + draftsByThreadId[AGENT_CHAT_NEW_THREAD_DRAFT_KEY] ?? '', + ); + } + } catch (error) { + enqueueErrorSnackBar({ + apolloError: CombinedGraphQLErrors.is(error) ? error : undefined, + }); + } + }; + + return { deleteChatThread }; +}; diff --git a/packages/twenty-front/src/modules/ai/hooks/useOptimisticallyUnarchiveOnSend.ts b/packages/twenty-front/src/modules/ai/hooks/useOptimisticallyUnarchiveOnSend.ts new file mode 100644 index 0000000000..8ac9059088 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/hooks/useOptimisticallyUnarchiveOnSend.ts @@ -0,0 +1,47 @@ +import { useStore } from 'jotai'; + +import { useApplyAgentChatThreadUpdate } from '@/ai/hooks/useApplyAgentChatThreadUpdate'; +import { metadataStoreState } from '@/metadata-store/states/metadataStoreState'; +import { type FlatAgentChatThread } from '@/metadata-store/types/FlatAgentChatThread'; + +export const useOptimisticallyUnarchiveOnSend = () => { + const { applyAgentChatThreadUpdate } = useApplyAgentChatThreadUpdate(); + const store = useStore(); + + const applyOptimisticUnarchive = ( + threadId: string, + optimisticUpdatedAt: string, + ): (() => void) | null => { + const entry = store.get(metadataStoreState.atomFamily('agentChatThreads')); + const threads = ( + entry.status === 'draft-pending' ? entry.draft : entry.current + ) as FlatAgentChatThread[]; + const thread = threads.find((t) => t.id === threadId); + + if (!thread?.deletedAt) { + return null; + } + + const previousDeletedAt = thread.deletedAt; + const previousUpdatedAt = thread.updatedAt; + const previousLastMessageAt = thread.lastMessageAt; + + applyAgentChatThreadUpdate({ + id: threadId, + deletedAt: null, + updatedAt: optimisticUpdatedAt, + lastMessageAt: optimisticUpdatedAt, + }); + + return () => { + applyAgentChatThreadUpdate({ + id: threadId, + deletedAt: previousDeletedAt, + updatedAt: previousUpdatedAt, + lastMessageAt: previousLastMessageAt, + }); + }; + }; + + return { applyOptimisticUnarchive }; +}; diff --git a/packages/twenty-front/src/modules/ai/hooks/useRenameChatThread.ts b/packages/twenty-front/src/modules/ai/hooks/useRenameChatThread.ts new file mode 100644 index 0000000000..52128e9638 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/hooks/useRenameChatThread.ts @@ -0,0 +1,44 @@ +import { useMutation } from '@apollo/client/react'; +import { CombinedGraphQLErrors } from '@apollo/client/errors'; + +import { useApplyAgentChatThreadUpdate } from '@/ai/hooks/useApplyAgentChatThreadUpdate'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { RenameChatThreadDocument } from '~/generated-metadata/graphql'; + +export const useRenameChatThread = () => { + const { applyAgentChatThreadUpdate } = useApplyAgentChatThreadUpdate(); + const { enqueueErrorSnackBar } = useSnackBar(); + + const [renameChatThreadMutation] = useMutation(RenameChatThreadDocument); + + const renameChatThread = async ( + id: string, + title: string, + ): Promise => { + try { + const { data } = await renameChatThreadMutation({ + variables: { id, title }, + }); + + if (!data?.renameChatThread) { + return false; + } + + applyAgentChatThreadUpdate({ + id: data.renameChatThread.id, + title: data.renameChatThread.title ?? null, + updatedAt: data.renameChatThread.updatedAt, + }); + + return true; + } catch (error) { + enqueueErrorSnackBar({ + apolloError: CombinedGraphQLErrors.is(error) ? error : undefined, + }); + + return false; + } + }; + + return { renameChatThread }; +}; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatErrorComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatErrorComponentFamilyState.ts index 4367e10293..5ce4ec92ac 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatErrorComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ai/states/agentChatErrorComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { type AiChatError } from '@/ai/types/AiChatError'; import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatFetchedMessagesComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatFetchedMessagesComponentFamilyState.ts index 8937193b67..fbe4dc97e0 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatFetchedMessagesComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ai/states/agentChatFetchedMessagesComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; import { type ExtendedUIMessage } from 'twenty-shared/ai'; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatFirstLiveSeqComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatFirstLiveSeqComponentFamilyState.ts index e8a49d4b55..4098ff98cb 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatFirstLiveSeqComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ai/states/agentChatFirstLiveSeqComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; export const agentChatFirstLiveSeqComponentFamilyState = diff --git a/packages/twenty-front/src/modules/ai/states/agentChatHandleEventCallbackComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatHandleEventCallbackComponentFamilyState.ts index d938e979f3..b621af624d 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatHandleEventCallbackComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ai/states/agentChatHandleEventCallbackComponentFamilyState.ts @@ -1,6 +1,6 @@ import { type AgentChatSubscriptionEvent } from 'twenty-shared/ai'; -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; export const agentChatHandleEventCallbackComponentFamilyState = diff --git a/packages/twenty-front/src/modules/ai/states/agentChatIsStreamingComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatIsStreamingComponentFamilyState.ts index 13db578033..c4309053ef 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatIsStreamingComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ai/states/agentChatIsStreamingComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; export const agentChatIsStreamingComponentFamilyState = diff --git a/packages/twenty-front/src/modules/ai/states/agentChatMessageComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatMessageComponentFamilyState.ts index 46f5a0dec4..2344112d42 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatMessageComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ai/states/agentChatMessageComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; import { type ExtendedUIMessage } from 'twenty-shared/ai'; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatMessagesComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatMessagesComponentFamilyState.ts index 160249be56..47b11c7820 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatMessagesComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ai/states/agentChatMessagesComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; import { type ExtendedUIMessage } from 'twenty-shared/ai'; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatMessagesComponentState.ts b/packages/twenty-front/src/modules/ai/states/agentChatMessagesComponentState.ts index 82de87ab0f..f8811f7228 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatMessagesComponentState.ts +++ b/packages/twenty-front/src/modules/ai/states/agentChatMessagesComponentState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentState } from '@/ui/utilities/state/jotai/utils/createAtomComponentState'; import { type ExtendedUIMessage } from 'twenty-shared/ai'; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatQueuedMessagesComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatQueuedMessagesComponentFamilyState.ts index bd497e5688..82aecda7aa 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatQueuedMessagesComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ai/states/agentChatQueuedMessagesComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; import { type ExtendedUIMessage } from 'twenty-shared/ai'; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatThreadFilterStatusState.ts b/packages/twenty-front/src/modules/ai/states/agentChatThreadFilterStatusState.ts new file mode 100644 index 0000000000..e3bfddf7c5 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/agentChatThreadFilterStatusState.ts @@ -0,0 +1,11 @@ +import { AGENT_CHAT_THREAD_FILTER_STATUS } from '@/ai/constants/AgentChatThreadFilterStatus'; +import { type AgentChatThreadFilterStatus } from '@/ai/types/AgentChatThreadFilterStatus'; +import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; + +export const agentChatThreadFilterStatusState = + createAtomState({ + key: 'agentChatThreadFilterStatusState', + defaultValue: AGENT_CHAT_THREAD_FILTER_STATUS.ACTIVE, + useLocalStorage: true, + localStorageOptions: { getOnInit: true }, + }); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatThreadGroupByState.ts b/packages/twenty-front/src/modules/ai/states/agentChatThreadGroupByState.ts new file mode 100644 index 0000000000..89def85d88 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/agentChatThreadGroupByState.ts @@ -0,0 +1,11 @@ +import { AGENT_CHAT_THREAD_GROUP_BY } from '@/ai/constants/AgentChatThreadGroupBy'; +import { type AgentChatThreadGroupBy } from '@/ai/types/AgentChatThreadGroupBy'; +import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; + +export const agentChatThreadGroupByState = + createAtomState({ + key: 'agentChatThreadGroupByState', + defaultValue: AGENT_CHAT_THREAD_GROUP_BY.DATE, + useLocalStorage: true, + localStorageOptions: { getOnInit: true }, + }); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatThreadLastActivityFilterState.ts b/packages/twenty-front/src/modules/ai/states/agentChatThreadLastActivityFilterState.ts new file mode 100644 index 0000000000..f2912bb6ae --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/agentChatThreadLastActivityFilterState.ts @@ -0,0 +1,11 @@ +import { AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER } from '@/ai/constants/AgentChatThreadLastActivityFilter'; +import { type AgentChatThreadLastActivityFilter } from '@/ai/types/AgentChatThreadLastActivityFilter'; +import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; + +export const agentChatThreadLastActivityFilterState = + createAtomState({ + key: 'agentChatThreadLastActivityFilterState', + defaultValue: AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER.ALL, + useLocalStorage: true, + localStorageOptions: { getOnInit: true }, + }); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatThreadsSelector.ts b/packages/twenty-front/src/modules/ai/states/agentChatThreadsSelector.ts deleted file mode 100644 index bd8de8f3a4..0000000000 --- a/packages/twenty-front/src/modules/ai/states/agentChatThreadsSelector.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { metadataStoreState } from '@/metadata-store/states/metadataStoreState'; -import { type FlatAgentChatThread } from '@/metadata-store/types/FlatAgentChatThread'; -import { createAtomSelector } from '@/ui/utilities/state/jotai/utils/createAtomSelector'; - -export const agentChatThreadsSelector = createAtomSelector< - FlatAgentChatThread[] ->({ - key: 'agentChatThreadsSelector', - get: ({ get }) => { - const storeItem = get(metadataStoreState, 'agentChatThreads'); - - return storeItem.current as FlatAgentChatThread[]; - }, -}); diff --git a/packages/twenty-front/src/modules/ai/states/agentChatUsageComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/agentChatUsageComponentFamilyState.ts index 691aada9e4..9fbfe1ae23 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatUsageComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ai/states/agentChatUsageComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; export type AgentChatLastMessageUsage = { diff --git a/packages/twenty-front/src/modules/ai/states/aiChatThreadPendingDeleteFamilyState.ts b/packages/twenty-front/src/modules/ai/states/aiChatThreadPendingDeleteFamilyState.ts new file mode 100644 index 0000000000..7bbdce9528 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/aiChatThreadPendingDeleteFamilyState.ts @@ -0,0 +1,15 @@ +import { type AiChatThreadActionsSurface } from '@/ai/types/AiChatThreadActionsSurface'; +import { createAtomFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomFamilyState'; + +type AiChatThreadPendingDelete = { + threadId: string; + threadTitle: string; +} | null; + +export const aiChatThreadPendingDeleteFamilyState = createAtomFamilyState< + AiChatThreadPendingDelete, + AiChatThreadActionsSurface +>({ + key: 'aiChatThreadPendingDeleteFamilyState', + defaultValue: null, +}); diff --git a/packages/twenty-front/src/modules/ai/states/currentAiChatThreadTitleComponentFamilyState.ts b/packages/twenty-front/src/modules/ai/states/currentAiChatThreadTitleComponentFamilyState.ts index 5c8793425b..e90b0b148c 100644 --- a/packages/twenty-front/src/modules/ai/states/currentAiChatThreadTitleComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ai/states/currentAiChatThreadTitleComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilyState'; export const currentAiChatThreadTitleComponentFamilyState = diff --git a/packages/twenty-front/src/modules/ai/states/processedToolExecutionPartIdsComponentState.ts b/packages/twenty-front/src/modules/ai/states/processedToolExecutionPartIdsComponentState.ts index 898d77ab71..c59713041b 100644 --- a/packages/twenty-front/src/modules/ai/states/processedToolExecutionPartIdsComponentState.ts +++ b/packages/twenty-front/src/modules/ai/states/processedToolExecutionPartIdsComponentState.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { createAtomComponentState } from '@/ui/utilities/state/jotai/utils/createAtomComponentState'; export const processedToolExecutionPartIdsComponentState = diff --git a/packages/twenty-front/src/modules/ai/states/agentChatHasMessageComponentSelector.ts b/packages/twenty-front/src/modules/ai/states/selectors/agentChatHasMessageComponentSelector.ts similarity index 89% rename from packages/twenty-front/src/modules/ai/states/agentChatHasMessageComponentSelector.ts rename to packages/twenty-front/src/modules/ai/states/selectors/agentChatHasMessageComponentSelector.ts index 0f48533a2f..7bab11845a 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatHasMessageComponentSelector.ts +++ b/packages/twenty-front/src/modules/ai/states/selectors/agentChatHasMessageComponentSelector.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThreadState'; import { createAtomComponentSelector } from '@/ui/utilities/state/jotai/utils/createAtomComponentSelector'; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatInputIsEmptySelector.ts b/packages/twenty-front/src/modules/ai/states/selectors/agentChatInputIsEmptySelector.ts similarity index 100% rename from packages/twenty-front/src/modules/ai/states/agentChatInputIsEmptySelector.ts rename to packages/twenty-front/src/modules/ai/states/selectors/agentChatInputIsEmptySelector.ts diff --git a/packages/twenty-front/src/modules/ai/states/agentChatIsScrolledToBottomSelector.ts b/packages/twenty-front/src/modules/ai/states/selectors/agentChatIsScrolledToBottomSelector.ts similarity index 100% rename from packages/twenty-front/src/modules/ai/states/agentChatIsScrolledToBottomSelector.ts rename to packages/twenty-front/src/modules/ai/states/selectors/agentChatIsScrolledToBottomSelector.ts diff --git a/packages/twenty-front/src/modules/ai/states/agentChatLastMessageIdComponentSelector.ts b/packages/twenty-front/src/modules/ai/states/selectors/agentChatLastMessageIdComponentSelector.ts similarity index 89% rename from packages/twenty-front/src/modules/ai/states/agentChatLastMessageIdComponentSelector.ts rename to packages/twenty-front/src/modules/ai/states/selectors/agentChatLastMessageIdComponentSelector.ts index c6feed8a16..63d7deac08 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatLastMessageIdComponentSelector.ts +++ b/packages/twenty-front/src/modules/ai/states/selectors/agentChatLastMessageIdComponentSelector.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThreadState'; import { createAtomComponentSelector } from '@/ui/utilities/state/jotai/utils/createAtomComponentSelector'; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatMessageComponentFamilySelector.ts b/packages/twenty-front/src/modules/ai/states/selectors/agentChatMessageComponentFamilySelector.ts similarity index 91% rename from packages/twenty-front/src/modules/ai/states/agentChatMessageComponentFamilySelector.ts rename to packages/twenty-front/src/modules/ai/states/selectors/agentChatMessageComponentFamilySelector.ts index 25398ecddd..a5498d516f 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatMessageComponentFamilySelector.ts +++ b/packages/twenty-front/src/modules/ai/states/selectors/agentChatMessageComponentFamilySelector.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThreadState'; import { createAtomComponentFamilySelector } from '@/ui/utilities/state/jotai/utils/createAtomComponentFamilySelector'; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatMessageIdsComponentSelector.ts b/packages/twenty-front/src/modules/ai/states/selectors/agentChatMessageIdsComponentSelector.ts similarity index 89% rename from packages/twenty-front/src/modules/ai/states/agentChatMessageIdsComponentSelector.ts rename to packages/twenty-front/src/modules/ai/states/selectors/agentChatMessageIdsComponentSelector.ts index b6daaf34ac..e495005a7b 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatMessageIdsComponentSelector.ts +++ b/packages/twenty-front/src/modules/ai/states/selectors/agentChatMessageIdsComponentSelector.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThreadState'; import { createAtomComponentSelector } from '@/ui/utilities/state/jotai/utils/createAtomComponentSelector'; diff --git a/packages/twenty-front/src/modules/ai/states/agentChatNonLastMessageIdsComponentSelector.ts b/packages/twenty-front/src/modules/ai/states/selectors/agentChatNonLastMessageIdsComponentSelector.ts similarity index 91% rename from packages/twenty-front/src/modules/ai/states/agentChatNonLastMessageIdsComponentSelector.ts rename to packages/twenty-front/src/modules/ai/states/selectors/agentChatNonLastMessageIdsComponentSelector.ts index c074f9424d..c236be6692 100644 --- a/packages/twenty-front/src/modules/ai/states/agentChatNonLastMessageIdsComponentSelector.ts +++ b/packages/twenty-front/src/modules/ai/states/selectors/agentChatNonLastMessageIdsComponentSelector.ts @@ -1,4 +1,4 @@ -import { AgentChatComponentInstanceContext } from '@/ai/states/AgentChatComponentInstanceContext'; +import { AgentChatComponentInstanceContext } from '@/ai/contexts/AgentChatComponentInstanceContext'; import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMessagesComponentFamilyState'; import { agentChatDisplayedThreadState } from '@/ai/states/agentChatDisplayedThreadState'; import { createAtomComponentSelector } from '@/ui/utilities/state/jotai/utils/createAtomComponentSelector'; diff --git a/packages/twenty-front/src/modules/ai/states/selectors/agentChatVisibleThreadsSelector.ts b/packages/twenty-front/src/modules/ai/states/selectors/agentChatVisibleThreadsSelector.ts new file mode 100644 index 0000000000..cac3bee93a --- /dev/null +++ b/packages/twenty-front/src/modules/ai/states/selectors/agentChatVisibleThreadsSelector.ts @@ -0,0 +1,50 @@ +import { AGENT_CHAT_THREAD_FILTER_STATUS } from '@/ai/constants/AgentChatThreadFilterStatus'; +import { AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER_DAYS } from '@/ai/constants/AgentChatThreadLastActivityFilterDays'; +import { agentChatThreadFilterStatusState } from '@/ai/states/agentChatThreadFilterStatusState'; +import { agentChatThreadLastActivityFilterState } from '@/ai/states/agentChatThreadLastActivityFilterState'; +import { metadataStoreState } from '@/metadata-store/states/metadataStoreState'; +import { type FlatAgentChatThread } from '@/metadata-store/types/FlatAgentChatThread'; +import { createAtomSelector } from '@/ui/utilities/state/jotai/utils/createAtomSelector'; + +const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; + +export const agentChatVisibleThreadsSelector = createAtomSelector< + FlatAgentChatThread[] +>({ + key: 'agentChatVisibleThreadsSelector', + get: ({ get }) => { + const storeItem = get(metadataStoreState, 'agentChatThreads'); + const allThreads = storeItem.current as FlatAgentChatThread[]; + const filterStatus = get(agentChatThreadFilterStatusState); + const lastActivityFilter = get(agentChatThreadLastActivityFilterState); + const lastActivityDays = + AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER_DAYS[lastActivityFilter]; + + const cutoffMs = + lastActivityDays !== null + ? Date.now() - lastActivityDays * MILLISECONDS_PER_DAY + : null; + + return allThreads.filter((thread) => { + switch (filterStatus) { + case AGENT_CHAT_THREAD_FILTER_STATUS.ACTIVE: + if (thread.deletedAt) return false; + break; + case AGENT_CHAT_THREAD_FILTER_STATUS.ARCHIVED: + if (!thread.deletedAt) return false; + break; + case AGENT_CHAT_THREAD_FILTER_STATUS.ALL: + break; + } + + if (cutoffMs !== null) { + const lastActivityMs = new Date( + thread.lastMessageAt ?? thread.updatedAt, + ).getTime(); + if (lastActivityMs < cutoffMs) return false; + } + + return true; + }); + }, +}); diff --git a/packages/twenty-front/src/modules/ai/types/AgentChatThreadFilterStatus.ts b/packages/twenty-front/src/modules/ai/types/AgentChatThreadFilterStatus.ts new file mode 100644 index 0000000000..2f577c2648 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/types/AgentChatThreadFilterStatus.ts @@ -0,0 +1,4 @@ +import { type AGENT_CHAT_THREAD_FILTER_STATUS } from '@/ai/constants/AgentChatThreadFilterStatus'; + +export type AgentChatThreadFilterStatus = + (typeof AGENT_CHAT_THREAD_FILTER_STATUS)[keyof typeof AGENT_CHAT_THREAD_FILTER_STATUS]; diff --git a/packages/twenty-front/src/modules/ai/types/AgentChatThreadGroupBy.ts b/packages/twenty-front/src/modules/ai/types/AgentChatThreadGroupBy.ts new file mode 100644 index 0000000000..703c318da1 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/types/AgentChatThreadGroupBy.ts @@ -0,0 +1,4 @@ +import { type AGENT_CHAT_THREAD_GROUP_BY } from '@/ai/constants/AgentChatThreadGroupBy'; + +export type AgentChatThreadGroupBy = + (typeof AGENT_CHAT_THREAD_GROUP_BY)[keyof typeof AGENT_CHAT_THREAD_GROUP_BY]; diff --git a/packages/twenty-front/src/modules/ai/types/AgentChatThreadLastActivityFilter.ts b/packages/twenty-front/src/modules/ai/types/AgentChatThreadLastActivityFilter.ts new file mode 100644 index 0000000000..25804ef7af --- /dev/null +++ b/packages/twenty-front/src/modules/ai/types/AgentChatThreadLastActivityFilter.ts @@ -0,0 +1,4 @@ +import { type AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER } from '@/ai/constants/AgentChatThreadLastActivityFilter'; + +export type AgentChatThreadLastActivityFilter = + (typeof AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER)[keyof typeof AGENT_CHAT_THREAD_LAST_ACTIVITY_FILTER]; diff --git a/packages/twenty-front/src/modules/ai/types/AiChatThreadActionsSurface.ts b/packages/twenty-front/src/modules/ai/types/AiChatThreadActionsSurface.ts new file mode 100644 index 0000000000..1e364e4532 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/types/AiChatThreadActionsSurface.ts @@ -0,0 +1,4 @@ +import { type AI_CHAT_THREAD_ACTIONS_SURFACE } from '@/ai/constants/AiChatThreadActionsSurface'; + +export type AiChatThreadActionsSurface = + (typeof AI_CHAT_THREAD_ACTIONS_SURFACE)[keyof typeof AI_CHAT_THREAD_ACTIONS_SURFACE]; diff --git a/packages/twenty-front/src/modules/ai/types/AiChatThreadFilterDropdownPage.ts b/packages/twenty-front/src/modules/ai/types/AiChatThreadFilterDropdownPage.ts new file mode 100644 index 0000000000..3c3a6b069b --- /dev/null +++ b/packages/twenty-front/src/modules/ai/types/AiChatThreadFilterDropdownPage.ts @@ -0,0 +1,4 @@ +import { type AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE } from '@/ai/constants/AiChatThreadFilterDropdownPage'; + +export type AiChatThreadFilterDropdownPage = + (typeof AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE)[keyof typeof AI_CHAT_THREAD_FILTER_DROPDOWN_PAGE]; diff --git a/packages/twenty-front/src/modules/ai/utils/__tests__/groupThreadsByDate.test.ts b/packages/twenty-front/src/modules/ai/utils/__tests__/groupThreadsByDate.test.ts index f56546dff3..db6367bf4e 100644 --- a/packages/twenty-front/src/modules/ai/utils/__tests__/groupThreadsByDate.test.ts +++ b/packages/twenty-front/src/modules/ai/utils/__tests__/groupThreadsByDate.test.ts @@ -2,13 +2,25 @@ import { type AgentChatThread } from '~/generated-metadata/graphql'; import { groupThreadsByDate } from '@/ai/utils/groupThreadsByDate'; describe('groupThreadsByDate', () => { - const today = new Date(); - const yesterday = new Date(today); - yesterday.setDate(today.getDate() - 1); - const twoDaysAgo = new Date(today); - twoDaysAgo.setDate(today.getDate() - 2); + const today = new Date('2026-04-26T12:00:00'); - const baseThread: Omit = { + const getDateDaysAgo = (daysAgo: number) => { + const date = new Date(today); + date.setDate(today.getDate() - daysAgo); + + return date; + }; + + const yesterday = getDateDaysAgo(1); + const twoDaysAgo = getDateDaysAgo(2); + const sevenDaysAgo = getDateDaysAgo(7); + const eightDaysAgo = getDateDaysAgo(8); + const fourteenDaysAgo = getDateDaysAgo(14); + + const baseThread: Omit< + AgentChatThread, + 'updatedAt' | 'id' | 'lastMessageAt' + > = { title: 'Test Thread', createdAt: twoDaysAgo.toISOString(), totalInputTokens: 0, @@ -19,26 +31,107 @@ describe('groupThreadsByDate', () => { totalOutputCredits: 0, }; - const threads: AgentChatThread[] = [ - { ...baseThread, id: '1', updatedAt: today.toISOString() }, - { ...baseThread, id: '2', updatedAt: yesterday.toISOString() }, - { ...baseThread, id: '3', updatedAt: twoDaysAgo.toISOString() }, - ]; - - it('groups threads into today, yesterday, and older', () => { - const result = groupThreadsByDate(threads); - expect(result.today).toHaveLength(1); - expect(result.today[0].id).toBe('1'); - expect(result.yesterday).toHaveLength(1); - expect(result.yesterday[0].id).toBe('2'); - expect(result.older).toHaveLength(1); - expect(result.older[0].id).toBe('3'); + const buildThread = (id: string, lastMessageAt: Date): AgentChatThread => ({ + ...baseThread, + id, + lastMessageAt: lastMessageAt.toISOString(), + updatedAt: lastMessageAt.toISOString(), }); - it('returns empty arrays if no threads', () => { - const result = groupThreadsByDate([]); - expect(result.today).toEqual([]); - expect(result.yesterday).toEqual([]); - expect(result.older).toEqual([]); + it('groups threads into Today, Yesterday, Previous 7 days, and month sections', () => { + const monthFormatter = new Intl.DateTimeFormat(undefined, { + month: 'long', + year: 'numeric', + }); + + const threads: AgentChatThread[] = [ + buildThread('1', today), + buildThread('2', yesterday), + buildThread('3', twoDaysAgo), + buildThread('4', sevenDaysAgo), + buildThread('5', eightDaysAgo), + buildThread('6', fourteenDaysAgo), + ]; + + const result = groupThreadsByDate(threads, today); + + expect(result).toHaveLength(4); + expect(result[0]).toMatchObject({ + id: 'today', + title: 'Today', + threads: [{ id: '1' }], + }); + expect(result[1]).toMatchObject({ + id: 'yesterday', + title: 'Yesterday', + threads: [{ id: '2' }], + }); + expect(result[2]).toMatchObject({ + id: 'previous-7-days', + title: 'Previous 7 days', + threads: [{ id: '3' }, { id: '4' }], + }); + expect(result[3]).toMatchObject({ + id: 'month:2026-4', + title: monthFormatter.format(eightDaysAgo), + threads: [{ id: '5' }, { id: '6' }], + }); + }); + + it('returns no groups if no threads', () => { + expect(groupThreadsByDate([], today)).toEqual([]); + }); + + it('falls back to updatedAt when lastMessageAt is null', () => { + const thread: AgentChatThread = { + ...baseThread, + id: '1', + lastMessageAt: null, + updatedAt: yesterday.toISOString(), + }; + + const [group] = groupThreadsByDate([thread], today); + + expect(group.id).toBe('yesterday'); + }); + + describe('timezone handling', () => { + it('keeps a thread last touched a few hours ago in Today across DST/midnight boundaries', () => { + const todayLate = new Date('2026-04-26T23:30:00'); + const todayEarly = new Date('2026-04-26T00:15:00'); + + const result = groupThreadsByDate( + [buildThread('late', todayLate), buildThread('early', todayEarly)], + today, + ); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + id: 'today', + threads: [{ id: 'late' }, { id: 'early' }], + }); + }); + + it('places a thread last touched late yesterday in the Yesterday bucket', () => { + const yesterdayJustBeforeMidnight = new Date('2026-04-25T23:59:00'); + + const [group] = groupThreadsByDate( + [buildThread('y', yesterdayJustBeforeMidnight)], + today, + ); + + expect(group.id).toBe('yesterday'); + }); + + it('does not slip a thread from yesterday into Today when local times differ by hours', () => { + const yesterdayMorning = new Date('2026-04-25T08:00:00'); + + const [group] = groupThreadsByDate( + [buildThread('y', yesterdayMorning)], + today, + ); + + expect(group.id).toBe('yesterday'); + }); }); }); diff --git a/packages/twenty-front/src/modules/ai/utils/dateGroupKey.ts b/packages/twenty-front/src/modules/ai/utils/dateGroupKey.ts deleted file mode 100644 index c12765471e..0000000000 --- a/packages/twenty-front/src/modules/ai/utils/dateGroupKey.ts +++ /dev/null @@ -1 +0,0 @@ -export type DateGroupKey = 'today' | 'yesterday' | 'older'; diff --git a/packages/twenty-front/src/modules/ai/utils/dateGroupKeys.ts b/packages/twenty-front/src/modules/ai/utils/dateGroupKeys.ts deleted file mode 100644 index ede6d99754..0000000000 --- a/packages/twenty-front/src/modules/ai/utils/dateGroupKeys.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { type DateGroupKey } from '@/ai/utils/dateGroupKey'; - -export const DATE_GROUP_KEYS: readonly DateGroupKey[] = [ - 'today', - 'yesterday', - 'older', -]; diff --git a/packages/twenty-front/src/modules/ai/utils/getAiChatThreadDeleteModalId.ts b/packages/twenty-front/src/modules/ai/utils/getAiChatThreadDeleteModalId.ts new file mode 100644 index 0000000000..e9ba99eb31 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/utils/getAiChatThreadDeleteModalId.ts @@ -0,0 +1,5 @@ +import { type AiChatThreadActionsSurface } from '@/ai/types/AiChatThreadActionsSurface'; + +export const getAiChatThreadDeleteModalId = ( + surface: AiChatThreadActionsSurface, +) => `delete-chat-thread-modal-${surface}`; diff --git a/packages/twenty-front/src/modules/ai/utils/getAiChatThreadFilterDropdownId.ts b/packages/twenty-front/src/modules/ai/utils/getAiChatThreadFilterDropdownId.ts new file mode 100644 index 0000000000..35bc9e70fe --- /dev/null +++ b/packages/twenty-front/src/modules/ai/utils/getAiChatThreadFilterDropdownId.ts @@ -0,0 +1,5 @@ +import { type AiChatThreadActionsSurface } from '@/ai/types/AiChatThreadActionsSurface'; + +export const getAiChatThreadFilterDropdownId = ( + surface: AiChatThreadActionsSurface, +) => `ai-chat-thread-filter-${surface}`; diff --git a/packages/twenty-front/src/modules/ai/utils/getAiChatThreadItemMenuDropdownId.ts b/packages/twenty-front/src/modules/ai/utils/getAiChatThreadItemMenuDropdownId.ts new file mode 100644 index 0000000000..8b42607805 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/utils/getAiChatThreadItemMenuDropdownId.ts @@ -0,0 +1,6 @@ +import { type AiChatThreadActionsSurface } from '@/ai/types/AiChatThreadActionsSurface'; + +export const getAiChatThreadItemMenuDropdownId = ( + threadId: string, + surface: AiChatThreadActionsSurface, +) => `ai-chat-thread-item-menu-${surface}-${threadId}`; diff --git a/packages/twenty-front/src/modules/ai/utils/getDateGroupTitle.ts b/packages/twenty-front/src/modules/ai/utils/getDateGroupTitle.ts deleted file mode 100644 index 58ac9a796a..0000000000 --- a/packages/twenty-front/src/modules/ai/utils/getDateGroupTitle.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { t } from '@lingui/core/macro'; - -import { type DateGroupKey } from '@/ai/utils/dateGroupKey'; - -export const getDateGroupTitle = (key: DateGroupKey): string => { - switch (key) { - case 'today': - return t`Today`; - case 'yesterday': - return t`Yesterday`; - case 'older': - return t`Older`; - } -}; diff --git a/packages/twenty-front/src/modules/ai/utils/groupThreadsByDate.ts b/packages/twenty-front/src/modules/ai/utils/groupThreadsByDate.ts index e1ffd59ff8..084a3c718a 100644 --- a/packages/twenty-front/src/modules/ai/utils/groupThreadsByDate.ts +++ b/packages/twenty-front/src/modules/ai/utils/groupThreadsByDate.ts @@ -1,29 +1,83 @@ +import { t } from '@lingui/core/macro'; +import { differenceInCalendarDays } from 'date-fns'; + import { type AgentChatThread } from '~/generated-metadata/graphql'; -import { type DateGroupKey } from '@/ai/utils/dateGroupKey'; +export type AgentChatThreadDateGroup = { + id: string; + title: string; + threads: AgentChatThread[]; +}; + +const getLocalDayDifference = (date: Date, today: Date) => + differenceInCalendarDays(today, date); + +const getMonthGroupId = (date: Date) => + `month:${date.getFullYear()}-${date.getMonth() + 1}`; + +const formatMonthGroupTitle = (date: Date) => + new Intl.DateTimeFormat(undefined, { + month: 'long', + year: 'numeric', + }).format(date); + +const getThreadDateGroup = ( + threadActivityAt: Date, + today: Date, +): Omit => { + const localDayDifference = getLocalDayDifference(threadActivityAt, today); + + if (localDayDifference === 0) { + return { + id: 'today', + title: t`Today`, + }; + } + + if (localDayDifference === 1) { + return { + id: 'yesterday', + title: t`Yesterday`, + }; + } + + if (localDayDifference >= 2 && localDayDifference <= 7) { + return { + id: 'previous-7-days', + title: t`Previous 7 days`, + }; + } + + return { + id: getMonthGroupId(threadActivityAt), + title: formatMonthGroupTitle(threadActivityAt), + }; +}; export const groupThreadsByDate = ( threads: AgentChatThread[], -): Record => { - const today = new Date(); - const yesterday = new Date(today); - yesterday.setDate(yesterday.getDate() - 1); + today = new Date(), +): AgentChatThreadDateGroup[] => { + const groupedThreadsByDate = new Map(); - return threads.reduce>( - (acc, thread) => { - const threadDate = new Date(thread.updatedAt); - const threadDateString = threadDate.toDateString(); + for (const thread of threads) { + const threadDateGroup = getThreadDateGroup( + new Date(thread.lastMessageAt ?? thread.updatedAt), + today, + ); + const existingThreadDateGroup = groupedThreadsByDate.get( + threadDateGroup.id, + ); - if (threadDateString === today.toDateString()) { - acc.today.push(thread); - } else if (threadDateString === yesterday.toDateString()) { - acc.yesterday.push(thread); - } else { - acc.older.push(thread); - } + if (existingThreadDateGroup !== undefined) { + existingThreadDateGroup.threads.push(thread); + } else { + groupedThreadsByDate.set(threadDateGroup.id, { + ...threadDateGroup, + threads: [thread], + }); + } + } - return acc; - }, - { today: [], yesterday: [], older: [] }, - ); + return [...groupedThreadsByDate.values()]; }; diff --git a/packages/twenty-front/src/modules/ai/utils/sortChatThreadsByLastActivityDesc.ts b/packages/twenty-front/src/modules/ai/utils/sortChatThreadsByLastActivityDesc.ts new file mode 100644 index 0000000000..95f6258624 --- /dev/null +++ b/packages/twenty-front/src/modules/ai/utils/sortChatThreadsByLastActivityDesc.ts @@ -0,0 +1,14 @@ +type ThreadWithLastActivity = { + lastMessageAt?: string | Date | null; + updatedAt: string | Date; +}; + +const getLastActivityMs = (thread: ThreadWithLastActivity): number => + new Date(thread.lastMessageAt ?? thread.updatedAt).getTime(); + +export const sortChatThreadsByLastActivityDesc = < + T extends ThreadWithLastActivity, +>( + threads: T[], +): T[] => + threads.toSorted((a, b) => getLastActivityMs(b) - getLastActivityMs(a)); diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx index 0bc99db50a..9c8a3b9f2a 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx @@ -92,15 +92,15 @@ const StyledItem = styled.button` border-radius: ${themeCssVariables.border.radius.sm}; box-sizing: border-box; color: ${({ active, isSoon, variant }) => { + if (variant === 'tertiary') { + return themeCssVariables.font.color.tertiary; + } if (active === true) { return themeCssVariables.font.color.primary; } if (isSoon) { return themeCssVariables.font.color.light; } - if (variant === 'tertiary') { - return themeCssVariables.font.color.tertiary; - } return themeCssVariables.font.color.secondary; }}; cursor: ${({ isSoon, isDragging }) => @@ -129,7 +129,10 @@ const StyledItem = styled.button` &:hover { background: ${themeCssVariables.background.transparent.light}; - color: ${themeCssVariables.font.color.primary}; + color: ${({ variant }) => + variant === 'tertiary' + ? themeCssVariables.font.color.tertiary + : themeCssVariables.font.color.primary}; } &:hover .keyboard-shortcuts { diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777682000000-add-deleted-at-to-agent-chat-thread.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777682000000-add-deleted-at-to-agent-chat-thread.ts new file mode 100644 index 0000000000..0fd60a11df --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777682000000-add-deleted-at-to-agent-chat-thread.ts @@ -0,0 +1,27 @@ +import { QueryRunner } from 'typeorm'; + +import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator'; +import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface'; + +@RegisteredInstanceCommand('2.2.0', 1777682000000) +export class AddDeletedAtToAgentChatThreadFastInstanceCommand + implements FastInstanceCommand +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."agentChatThread" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_AGENT_CHAT_THREAD_ID_DELETED_AT" ON "core"."agentChatThread" ("id", "deletedAt")`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DROP INDEX "core"."IDX_AGENT_CHAT_THREAD_ID_DELETED_AT"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."agentChatThread" DROP COLUMN "deletedAt"`, + ); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts index 752b17e203..fd2a7dfb71 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts @@ -20,6 +20,7 @@ import { AddProviderExecutedToAgentMessagePartFastInstanceCommand } from 'src/da import { BackfillPageLayoutWidgetPositionSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-1/2-1-instance-command-slow-1795000002000-backfill-page-layout-widget-position'; import { AddCacheTokensToAgentChatThreadFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777455269302-add-cache-tokens-to-agent-chat-thread'; import { AddLogoToApplicationFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777539664664-add-logo-to-application'; +import { AddDeletedAtToAgentChatThreadFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777682000000-add-deleted-at-to-agent-chat-thread'; export const INSTANCE_COMMANDS = [ AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand, @@ -42,4 +43,5 @@ export const INSTANCE_COMMANDS = [ BackfillPageLayoutWidgetPositionSlowInstanceCommand, AddCacheTokensToAgentChatThreadFastInstanceCommand, AddLogoToApplicationFastInstanceCommand, + AddDeletedAtToAgentChatThreadFastInstanceCommand, ]; diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/ai-chat.module.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/ai-chat.module.ts index d3ee6498c1..faa15e22f5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/ai-chat.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/ai-chat.module.ts @@ -1,14 +1,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { SortDirection } from '@ptc-org/nestjs-query-core'; -import { - NestjsQueryGraphQLModule, - PagingStrategies, -} from '@ptc-org/nestjs-query-graphql'; -import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; -import { PermissionFlagType } from 'twenty-shared/constants'; - import { TokenModule } from 'src/engine/core-modules/auth/token/token.module'; import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; import { WorkspaceDomainsModule } from 'src/engine/core-modules/domain/workspace-domains/workspace-domains.module'; @@ -19,8 +11,6 @@ import { ToolProviderModule } from 'src/engine/core-modules/tool-provider/tool-p import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; -import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { AiAgentExecutionModule } from 'src/engine/metadata-modules/ai/ai-agent-execution/ai-agent-execution.module'; import { AiBillingModule } from 'src/engine/metadata-modules/ai/ai-billing/ai-billing.module'; import { AiGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/ai/interceptors/ai-graphql-api-exception.interceptor'; @@ -32,7 +22,6 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache import { DashboardToolsModule } from 'src/modules/dashboard/tools/dashboard-tools.module'; import { WorkflowToolsModule } from 'src/modules/workflow/workflow-tools/workflow-tools.module'; -import { AgentChatThreadDTO } from './dtos/agent-chat-thread.dto'; import { AgentChatThreadEntity } from './entities/agent-chat-thread.entity'; import { StreamAgentChatJob } from './jobs/stream-agent-chat.job'; import { AgentChatResolver } from './resolvers/agent-chat.resolver'; @@ -54,33 +43,6 @@ import { SystemPromptBuilderService } from './services/system-prompt-builder.ser UserWorkspaceEntity, WorkspaceEntity, ]), - NestjsQueryGraphQLModule.forFeature({ - imports: [ - NestjsQueryTypeOrmModule.forFeature([AgentChatThreadEntity]), - PermissionsModule, - ], - resolvers: [ - { - EntityClass: AgentChatThreadEntity, - DTOClass: AgentChatThreadDTO, - pagingStrategy: PagingStrategies.CURSOR, - read: { - defaultSort: [ - { field: 'updatedAt', direction: SortDirection.DESC }, - ], - one: { disabled: true }, - many: { name: 'chatThreads' }, - }, - create: { disabled: true }, - update: { disabled: true }, - delete: { disabled: true }, - guards: [ - WorkspaceAuthGuard, - SettingsPermissionGuard(PermissionFlagType.AI), - ], - }, - ], - }), AiAgentExecutionModule, BillingModule, ThrottlerModule, diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/dtos/agent-chat-thread.dto.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/dtos/agent-chat-thread.dto.ts index edf8562971..2d69961b2a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/dtos/agent-chat-thread.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/dtos/agent-chat-thread.dto.ts @@ -1,32 +1,8 @@ -import { UnauthorizedException } from '@nestjs/common'; -import { Field, Float, HideField, Int, ObjectType } from '@nestjs/graphql'; - -import { - Authorize, - FilterableField, - IDField, -} from '@ptc-org/nestjs-query-graphql'; -import { isDefined } from 'twenty-shared/utils'; - -import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; -import { type AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request'; +import { Field, Float, HideField, ID, Int, ObjectType } from '@nestjs/graphql'; @ObjectType('AgentChatThread') -@Authorize({ - authorize: (context: { req?: AuthenticatedRequest }) => { - const userWorkspaceId = context?.req?.userWorkspaceId; - - if (!isDefined(userWorkspaceId)) { - throw new UnauthorizedException( - 'userWorkspaceId is required to query chat threads', - ); - } - - return { userWorkspaceId: { eq: userWorkspaceId } }; - }, -}) export class AgentChatThreadDTO { - @IDField(() => UUIDScalarType) + @Field(() => ID) id: string; @Field({ nullable: true }) @@ -55,10 +31,15 @@ export class AgentChatThreadDTO { @Field() createdAt: Date; - @FilterableField() @Field() updatedAt: Date; + @Field(() => Date, { nullable: true }) + deletedAt: Date | null; + + @Field(() => Date, { nullable: true }) + lastMessageAt: Date | null; + @HideField() userWorkspaceId: string; } diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity.ts index b2698d2c95..4f174d6a55 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity.ts @@ -17,6 +17,7 @@ import type { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspac import { EntityRelation } from 'src/engine/workspace-manager/workspace-migration/types/entity-relation.interface'; @Entity({ name: 'agentChatThread', schema: 'core' }) +@Index('IDX_AGENT_CHAT_THREAD_ID_DELETED_AT', ['id', 'deletedAt']) export class AgentChatThreadEntity { @PrimaryGeneratedColumn('uuid') id: string; @@ -75,6 +76,9 @@ export class AgentChatThreadEntity { @OneToMany(() => AgentMessageEntity, (message) => message.thread) messages: EntityRelation; + @Column({ type: 'timestamptz', nullable: true }) + deletedAt: Date | null; + @CreateDateColumn({ type: 'timestamptz' }) createdAt: Date; diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/jobs/stream-agent-chat.job.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/jobs/stream-agent-chat.job.ts index c4f8827f90..eed7f8dcd2 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/jobs/stream-agent-chat.job.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/jobs/stream-agent-chat.job.ts @@ -449,6 +449,15 @@ export class StreamAgentChatJob { return; } + const threadStatus = await this.threadRepository.findOne({ + where: { id: threadId }, + select: ['id', 'deletedAt'], + }); + + if (!threadStatus || threadStatus.deletedAt) { + return; + } + const userMessage = await userMessagePromise; await this.agentChatService.addMessage({ diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat.resolver.ts index 6547664b81..7601d6525d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat.resolver.ts @@ -59,6 +59,11 @@ export class AgentChatResolver { private readonly threadRepository: Repository, ) {} + @Query(() => [AgentChatThreadDTO]) + async chatThreads(@AuthUserWorkspaceId() userWorkspaceId: string) { + return this.agentChatService.getThreadsForUser(userWorkspaceId); + } + @Query(() => AgentChatThreadDTO) async chatThread( @Args('id', { type: () => UUIDScalarType }) id: string, @@ -140,6 +145,13 @@ export class AgentChatResolver { ); } + if (isDefined(thread.deletedAt)) { + await this.agentChatService.unarchiveThread({ + threadId, + userWorkspaceId, + }); + } + if (isDefined(thread.activeStreamId)) { const queuedMessage = await this.agentChatService.queueMessage({ threadId, @@ -147,6 +159,7 @@ export class AgentChatResolver { id: messageId, fileIds: fileIds ?? undefined, workspaceId: workspace.id, + userWorkspaceId, }); await this.eventPublisherService.publish({ @@ -201,6 +214,75 @@ export class AgentChatResolver { return true; } + @Mutation(() => AgentChatThreadDTO) + async renameChatThread( + @Args('id', { type: () => UUIDScalarType }) id: string, + @Args('title') title: string, + @AuthUserWorkspaceId() userWorkspaceId: string, + ): Promise { + return this.agentChatService.updateThreadTitle({ + threadId: id, + userWorkspaceId, + title, + }); + } + + @Mutation(() => AgentChatThreadDTO) + async archiveChatThread( + @Args('id', { type: () => UUIDScalarType }) id: string, + @AuthUserWorkspaceId() userWorkspaceId: string, + ): Promise { + await this.cancelActiveStreamIfAny(id, userWorkspaceId); + + return this.agentChatService.archiveThread({ + threadId: id, + userWorkspaceId, + }); + } + + @Mutation(() => AgentChatThreadDTO) + async unarchiveChatThread( + @Args('id', { type: () => UUIDScalarType }) id: string, + @AuthUserWorkspaceId() userWorkspaceId: string, + ): Promise { + return this.agentChatService.unarchiveThread({ + threadId: id, + userWorkspaceId, + }); + } + + @Mutation(() => Boolean) + async deleteChatThread( + @Args('id', { type: () => UUIDScalarType }) id: string, + @AuthUserWorkspaceId() userWorkspaceId: string, + ): Promise { + await this.cancelActiveStreamIfAny(id, userWorkspaceId); + + await this.agentChatService.hardDeleteThread({ + threadId: id, + userWorkspaceId, + }); + + return true; + } + + private async cancelActiveStreamIfAny( + threadId: string, + userWorkspaceId: string, + ): Promise { + const thread = await this.threadRepository.findOne({ + where: { id: threadId, userWorkspaceId }, + }); + + if (!isDefined(thread) || !isDefined(thread.activeStreamId)) { + return; + } + + const redis = this.redisClientService.getClient(); + + await redis.publish(getCancelChannel(threadId), 'cancel'); + } + @Mutation(() => Boolean) async deleteQueuedChatMessage( @Args('messageId', { type: () => UUIDScalarType }) messageId: string, @@ -261,4 +343,16 @@ export class AgentChatResolver { totalOutputCredits(@Parent() thread: AgentChatThreadEntity): number { return toDisplayCredits(thread.totalOutputCredits); } + + @ResolveField('lastMessageAt', () => Date, { nullable: true }) + async lastMessageAt( + @Parent() + thread: AgentChatThreadEntity & { lastMessageAt?: Date | null }, + ): Promise { + if (thread.lastMessageAt !== undefined) { + return thread.lastMessageAt; + } + + return this.agentChatService.getLastMessageAtForThread(thread.id); + } } diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat-streaming.service.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat-streaming.service.ts index 2941080574..4a716d479b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat-streaming.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat-streaming.service.ts @@ -100,6 +100,11 @@ export class AgentChatStreamingService { workspaceId: workspace.id, }); + await this.agentChatService.notifyThreadActivityUpdated( + threadId, + userWorkspaceId, + ); + const previousMessages = await this.loadMessagesFromDB( threadId, userWorkspaceId, @@ -139,6 +144,15 @@ export class AgentChatStreamingService { workspaceId: string, hasTitle: boolean, ): Promise { + const threadStatus = await this.threadRepository.findOne({ + where: { id: threadId }, + select: ['id', 'deletedAt'], + }); + + if (!threadStatus || threadStatus.deletedAt) { + return; + } + const queuedMessages = await this.agentChatService.getQueuedMessages(threadId); diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service.ts index 4f5fbfa7bb..f0adbeac82 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/agent-chat.service.ts @@ -1,8 +1,8 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { ExtendedUIMessage } from 'twenty-shared/ai'; -import { In, Repository } from 'typeorm'; +import { In, IsNull, Not, Repository } from 'typeorm'; import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'; import type { UIDataTypes, UIMessagePart, UITools } from 'ai'; @@ -16,30 +16,39 @@ import { } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message.entity'; import { AgentTurnEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-turn.entity'; import { mapUIMessagePartsToDBParts } from 'src/engine/metadata-modules/ai/ai-agent-execution/utils/mapUIMessagePartsToDBParts'; +import { AgentChatThreadEntity } from 'src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity'; import { AiException, AiExceptionCode, } from 'src/engine/metadata-modules/ai/ai.exception'; -import { AgentChatThreadEntity } from 'src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity'; import { WorkspaceEventBroadcaster } from 'src/engine/subscriptions/workspace-event-broadcaster/workspace-event-broadcaster.service'; import { AgentTitleGenerationService } from './agent-title-generation.service'; -const serializeThreadForBroadcast = (thread: AgentChatThreadEntity) => ({ +const serializeThreadForBroadcast = ( + thread: AgentChatThreadEntity, + lastMessageAt: Date | null, +) => ({ id: thread.id, title: thread.title, totalInputTokens: thread.totalInputTokens, totalOutputTokens: thread.totalOutputTokens, + totalCacheReadTokens: thread.totalCacheReadTokens, + totalCacheCreationTokens: thread.totalCacheCreationTokens, contextWindowTokens: thread.contextWindowTokens, conversationSize: thread.conversationSize, totalInputCredits: thread.totalInputCredits, totalOutputCredits: thread.totalOutputCredits, - createdAt: thread.createdAt.toISOString(), - updatedAt: thread.updatedAt.toISOString(), + deletedAt: thread.deletedAt, + lastMessageAt, + createdAt: thread.createdAt, + updatedAt: thread.updatedAt, }); @Injectable() export class AgentChatService { + private readonly logger = new Logger(AgentChatService.name); + constructor( @InjectRepository(AgentChatThreadEntity) private readonly threadRepository: Repository, @@ -78,7 +87,7 @@ export class AgentChatService { recordId: savedThread.id, recipientUserWorkspaceIds: [userWorkspaceId], properties: { - after: serializeThreadForBroadcast(savedThread), + after: serializeThreadForBroadcast(savedThread, null), }, }, ], @@ -105,6 +114,53 @@ export class AgentChatService { return thread; } + async getThreadsForUser( + userWorkspaceId: string, + ): Promise<(AgentChatThreadEntity & { lastMessageAt: Date | null })[]> { + const rankedThreads = await this.threadRepository + .createQueryBuilder('thread') + .select('thread.id', 'id') + .addSelect('MAX(message.createdAt)', 'last_message_at') + .leftJoin('thread.messages', 'message') + .where('thread.userWorkspaceId = :userWorkspaceId', { userWorkspaceId }) + .groupBy('thread.id') + .orderBy('last_message_at', 'DESC', 'NULLS LAST') + .addOrderBy('thread.updatedAt', 'DESC') + .getRawMany<{ id: string; last_message_at: Date | null }>(); + + if (rankedThreads.length === 0) { + return []; + } + + const rankedThreadIds = rankedThreads.map( + (rankedThread) => rankedThread.id, + ); + + const threads = await this.threadRepository.find({ + where: { id: In(rankedThreadIds), userWorkspaceId }, + }); + + const threadById = new Map(threads.map((thread) => [thread.id, thread])); + + return rankedThreads.flatMap((rankedThread) => { + const thread = threadById.get(rankedThread.id); + + return thread + ? [{ ...thread, lastMessageAt: rankedThread.last_message_at ?? null }] + : []; + }); + } + + async getLastMessageAtForThread(threadId: string): Promise { + const result = await this.messageRepository + .createQueryBuilder('message') + .select('MAX(message.createdAt)', 'last_message_at') + .where('message.threadId = :threadId', { threadId }) + .getRawOne<{ last_message_at: Date | null }>(); + + return result?.last_message_at ?? null; + } + async addMessage({ threadId, uiMessage, @@ -198,12 +254,14 @@ export class AgentChatService { id, fileIds, workspaceId, + userWorkspaceId, }: { threadId: string; text: string; id?: string; fileIds?: string[]; workspaceId: string; + userWorkspaceId: string; }): Promise { const messageValues = { ...(id ? { id } : {}), @@ -246,6 +304,8 @@ export class AgentChatService { await this.messagePartRepository.insert(parts); + await this.notifyThreadActivityUpdated(threadId, userWorkspaceId); + return { id: savedMessageId, ...messageValues, @@ -311,6 +371,188 @@ export class AgentChatService { return savedTurnId; } + async updateThreadTitle({ + threadId, + userWorkspaceId, + title, + }: { + threadId: string; + userWorkspaceId: string; + title: string; + }): Promise { + const trimmed = title.trim(); + + if (trimmed.length === 0) { + throw new AiException( + 'Chat thread title cannot be empty', + AiExceptionCode.INVALID_CHAT_THREAD_TITLE, + ); + } + + const result = await this.threadRepository.update( + { id: threadId, userWorkspaceId }, + { title: trimmed }, + ); + + if (result.affected === 0) { + throw new AiException( + 'Thread not found', + AiExceptionCode.THREAD_NOT_FOUND, + ); + } + + const updated = await this.getThreadById(threadId, userWorkspaceId); + + await this.broadcastThreadUpdated(updated, ['title'], userWorkspaceId); + + return updated; + } + + async archiveThread({ + threadId, + userWorkspaceId, + }: { + threadId: string; + userWorkspaceId: string; + }): Promise { + const thread = await this.getThreadById(threadId, userWorkspaceId); + + if (thread.deletedAt) { + return thread; + } + + const deletedAt = new Date(); + + const result = await this.threadRepository.update( + { id: threadId, userWorkspaceId, deletedAt: IsNull() }, + { deletedAt, activeStreamId: null }, + ); + + if ((result.affected ?? 0) === 0) { + return thread; + } + + thread.deletedAt = deletedAt; + thread.activeStreamId = null; + + await this.broadcastThreadUpdated(thread, ['deletedAt'], userWorkspaceId); + + return thread; + } + + async unarchiveThread({ + threadId, + userWorkspaceId, + }: { + threadId: string; + userWorkspaceId: string; + }): Promise { + const thread = await this.getThreadById(threadId, userWorkspaceId); + + if (!thread.deletedAt) { + return thread; + } + + const result = await this.threadRepository.update( + { id: threadId, userWorkspaceId, deletedAt: Not(IsNull()) }, + { deletedAt: null }, + ); + + if ((result.affected ?? 0) === 0) { + return thread; + } + + thread.deletedAt = null; + + await this.broadcastThreadUpdated(thread, ['deletedAt'], userWorkspaceId); + + return thread; + } + + async hardDeleteThread({ + threadId, + userWorkspaceId, + }: { + threadId: string; + userWorkspaceId: string; + }): Promise { + const thread = await this.threadRepository.findOne({ + where: { id: threadId, userWorkspaceId }, + }); + + if (!thread) { + throw new AiException( + 'Thread not found', + AiExceptionCode.THREAD_NOT_FOUND, + ); + } + + const result = await this.threadRepository.delete({ + id: threadId, + userWorkspaceId, + }); + + if ((result.affected ?? 0) === 0) { + this.logger.warn( + `hardDeleteThread: thread ${threadId} vanished between fetch and delete`, + ); + + return; + } + + await this.workspaceEventBroadcaster.broadcast({ + workspaceId: thread.workspaceId, + events: [ + { + type: 'deleted', + entityName: 'agentChatThread', + recordId: threadId, + recipientUserWorkspaceIds: [userWorkspaceId], + properties: { + before: serializeThreadForBroadcast(thread, null), + }, + }, + ], + }); + } + + async notifyThreadActivityUpdated( + threadId: string, + userWorkspaceId: string, + ): Promise { + const thread = await this.getThreadById(threadId, userWorkspaceId); + + await this.broadcastThreadUpdated( + thread, + ['lastMessageAt'], + userWorkspaceId, + ); + } + + private async broadcastThreadUpdated( + thread: AgentChatThreadEntity, + updatedFields: string[], + userWorkspaceId: string, + ): Promise { + const lastMessageAt = await this.getLastMessageAtForThread(thread.id); + + await this.workspaceEventBroadcaster.broadcast({ + workspaceId: thread.workspaceId, + events: [ + { + type: 'updated', + entityName: 'agentChatThread', + recordId: thread.id, + recipientUserWorkspaceIds: [userWorkspaceId], + properties: { + updatedFields, + after: serializeThreadForBroadcast(thread, lastMessageAt), + }, + }, + ], + }); + } + async generateTitleIfNeeded({ threadId, messageContent, @@ -336,21 +578,11 @@ export class AgentChatService { await this.threadRepository.update(threadId, { title }); - await this.workspaceEventBroadcaster.broadcast({ - workspaceId, - events: [ - { - type: 'updated', - entityName: 'agentChatThread', - recordId: threadId, - recipientUserWorkspaceIds: [thread.userWorkspaceId], - properties: { - updatedFields: ['title'], - after: serializeThreadForBroadcast({ ...thread, title }), - }, - }, - ], - }); + await this.broadcastThreadUpdated( + { ...thread, title }, + ['title'], + thread.userWorkspaceId, + ); return title; } diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai.exception.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai.exception.ts index 24030daa94..d84b3149e6 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai.exception.ts @@ -11,6 +11,7 @@ export enum AiExceptionCode { AGENT_EXECUTION_FAILED = 'AGENT_EXECUTION_FAILED', INVALID_AGENT_INPUT = 'INVALID_AGENT_INPUT', THREAD_NOT_FOUND = 'THREAD_NOT_FOUND', + INVALID_CHAT_THREAD_TITLE = 'INVALID_CHAT_THREAD_TITLE', MESSAGE_NOT_FOUND = 'MESSAGE_NOT_FOUND', API_KEY_NOT_CONFIGURED = 'API_KEY_NOT_CONFIGURED', USER_WORKSPACE_ID_NOT_FOUND = 'USER_WORKSPACE_ID_NOT_FOUND', @@ -32,6 +33,8 @@ const getAiExceptionUserFriendlyMessage = (code: AiExceptionCode) => { return msg`Invalid agent input.`; case AiExceptionCode.THREAD_NOT_FOUND: return msg`Chat thread not found.`; + case AiExceptionCode.INVALID_CHAT_THREAD_TITLE: + return msg`Chat thread title cannot be empty.`; case AiExceptionCode.MESSAGE_NOT_FOUND: return msg`Chat message not found.`; case AiExceptionCode.API_KEY_NOT_CONFIGURED: diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/utils/ai-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/metadata-modules/ai/utils/ai-graphql-api-exception-handler.util.ts index 3d80b615f9..34c9968478 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/utils/ai-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/utils/ai-graphql-api-exception-handler.util.ts @@ -21,6 +21,7 @@ export const aiGraphqlApiExceptionHandler = (error: Error) => { case AiExceptionCode.ROLE_NOT_FOUND: throw new NotFoundError(error); case AiExceptionCode.INVALID_AGENT_INPUT: + case AiExceptionCode.INVALID_CHAT_THREAD_TITLE: throw new UserInputError(error); case AiExceptionCode.AGENT_ALREADY_EXISTS: throw new ConflictError(error); diff --git a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts index 46c9f52c94..cf92a1ad08 100644 --- a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts +++ b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts @@ -1,6 +1,7 @@ /* oxlint-disable no-restricted-imports */ export { IconNumber123 as Icon123, + IconAdjustments, IconAlertCircle, IconAlertTriangle, IconAlignBoxLeftTop, @@ -10,7 +11,6 @@ export { IconApi, IconApps, IconAppWindow, - IconWindow, IconArchive, IconArchiveOff, IconArrowBackUp, @@ -427,6 +427,7 @@ export { IconVideo, IconWand, IconWebhook, + IconWindow, IconWorld, IconX, } from '@tabler/icons-react'; diff --git a/packages/twenty-ui/src/display/index.ts b/packages/twenty-ui/src/display/index.ts index 701f7fe7d9..64d3edbed5 100644 --- a/packages/twenty-ui/src/display/index.ts +++ b/packages/twenty-ui/src/display/index.ts @@ -81,6 +81,7 @@ export { IllustrationIconWrapper } from './icon/components/IllustrationIconWrapp export type { TablerIconsProps } from './icon/components/TablerIcons'; export { Icon123, + IconAdjustments, IconAlertCircle, IconAlertTriangle, IconAlignBoxLeftTop, @@ -90,7 +91,6 @@ export { IconApi, IconApps, IconAppWindow, - IconWindow, IconArchive, IconArchiveOff, IconArrowBackUp, @@ -507,6 +507,7 @@ export { IconVideo, IconWand, IconWebhook, + IconWindow, IconWorld, IconX, } from './icon/components/TablerIcons';