From 6070dbf16a354c1a7a50c8aa408e1fb68f3b4f80 Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Sun, 18 Jan 2026 17:31:25 +0100 Subject: [PATCH] Identify agent (#17221) # Introduction Related to https://github.com/twentyhq/core-team-issues/issues/1989 1/ Migration, applicationId and universalIdentifier are required on entity ( save point migration + upgrade command fallback pattern ) 2/ Backfill using previous standard ids ## Test tested prod extract --- .../1-16-identify-agent-metadata.command.ts | 168 ++++++++++++++++++ ...ation-id-not-nullable-migration.command.ts | 71 ++++++++ .../1-16-upgrade-version-command.module.ts | 8 + .../upgrade.command.ts | 7 + ...alIdentifierAndApplicationIdNotNullable.ts | 63 +++++++ ...ntifierAndApplicationIdNotNullable.util.ts | 23 +++ .../ai/ai-agent/entities/agent.entity.ts | 4 +- ...ansform-agent-entity-to-flat-agent.util.ts | 3 +- 8 files changed, 344 insertions(+), 3 deletions(-) create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-agent-universal-identifier-and-application-id-not-nullable-migration.command.ts create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/common/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.ts create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/utils/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.util.ts diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command.ts new file mode 100644 index 0000000000..7dcb059f8b --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command.ts @@ -0,0 +1,168 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { isDefined } from 'twenty-shared/utils'; +import { IsNull, Repository } from 'typeorm'; +import { v4 } from 'uuid'; + +import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; +import { ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity'; +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; +import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util'; +import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; +import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; +import { STANDARD_AGENT } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-agent.constant'; + +@Command({ + name: 'upgrade:1-16:identify-agent-metadata', + description: 'Identify standard agent metadata', +}) +export class IdentifyAgentMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + constructor( + @InjectRepository(WorkspaceEntity) + protected readonly workspaceRepository: Repository, + @InjectRepository(AgentEntity) + private readonly agentRepository: Repository, + protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, + protected readonly dataSourceService: DataSourceService, + protected readonly applicationService: ApplicationService, + protected readonly workspaceCacheService: WorkspaceCacheService, + ) { + super(workspaceRepository, twentyORMGlobalManager, dataSourceService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + this.logger.log( + `Running identify standard agent metadata for workspace ${workspaceId}`, + ); + + const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { workspaceId }, + ); + + await this.identifyStandardAgent({ + workspaceId, + twentyStandardApplicationId: twentyStandardFlatApplication.id, + dryRun: options.dryRun ?? false, + }); + + await this.identifyCustomAgents({ + workspaceId, + workspaceCustomApplicationId: workspaceCustomFlatApplication.id, + dryRun: options.dryRun ?? false, + }); + + const relatedMetadataNames = getMetadataRelatedMetadataNames('agent'); + const relatedCacheKeysToInvalidate = relatedMetadataNames.map( + getMetadataFlatEntityMapsKey, + ); + + this.logger.log( + `Invalidating caches: flatAgentMaps ${relatedCacheKeysToInvalidate.join(' ')}`, + ); + if (!options.dryRun) { + await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ + 'flatAgentMaps', + ...relatedCacheKeysToInvalidate, + ]); + } + } + + private async identifyStandardAgent({ + workspaceId, + twentyStandardApplicationId, + dryRun, + }: { + workspaceId: string; + twentyStandardApplicationId: string; + dryRun: boolean; + }): Promise { + const helperAgent = await this.agentRepository.findOne({ + select: { + id: true, + name: true, + universalIdentifier: true, + applicationId: true, + }, + where: { + workspaceId, + name: 'helper', + isCustom: false, + }, + withDeleted: true, + }); + + if (!isDefined(helperAgent)) { + this.logger.warn( + `Standard agent "helper" not found for workspace ${workspaceId}, skipping standard agent identification`, + ); + + return; + } + + if (isDefined(helperAgent.applicationId)) { + this.logger.warn( + `Standard agent "helper" already has applicationId set, skipping`, + ); + + return; + } + + this.logger.log( + ` - Standard agent "helper" (id=${helperAgent.id}) -> universalIdentifier=${STANDARD_AGENT.helper.universalIdentifier}`, + ); + + if (!dryRun) { + await this.agentRepository.save({ + id: helperAgent.id, + universalIdentifier: STANDARD_AGENT.helper.universalIdentifier, + applicationId: twentyStandardApplicationId, + }); + } + } + + private async identifyCustomAgents({ + workspaceId, + workspaceCustomApplicationId, + dryRun, + }: { + workspaceId: string; + workspaceCustomApplicationId: string; + dryRun: boolean; + }): Promise { + const remainingCustomAgents = await this.agentRepository.find({ + select: { + id: true, + universalIdentifier: true, + applicationId: true, + }, + where: { + workspaceId, + applicationId: IsNull(), + }, + withDeleted: true, + }); + + const customUpdates = remainingCustomAgents.map((agentEntity) => ({ + id: agentEntity.id, + universalIdentifier: agentEntity.universalIdentifier ?? v4(), + applicationId: workspaceCustomApplicationId, + })); + + this.logger.log( + `Found ${customUpdates.length} custom agent(s) to update for workspace ${workspaceId}`, + ); + + if (!dryRun) { + await this.agentRepository.save(customUpdates); + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-agent-universal-identifier-and-application-id-not-nullable-migration.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-agent-universal-identifier-and-application-id-not-nullable-migration.command.ts new file mode 100644 index 0000000000..0c361307d6 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-agent-universal-identifier-and-application-id-not-nullable-migration.command.ts @@ -0,0 +1,71 @@ +import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { DataSource, Repository } from 'typeorm'; + +import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; +import { makeAgentUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.util'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; + +@Command({ + name: 'upgrade:1-16:make-agent-universal-identifier-and-application-id-not-nullable-migration', + description: + 'Make universalIdentifier and applicationId columns NOT NULL on agent table', +}) +export class MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + private hasRunOnce = false; + + constructor( + @InjectRepository(WorkspaceEntity) + protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, + protected readonly dataSourceService: DataSourceService, + @InjectDataSource() + private readonly coreDataSource: DataSource, + ) { + super(workspaceRepository, twentyORMGlobalManager, dataSourceService); + } + + override async runOnWorkspace({ + options, + }: RunOnWorkspaceArgs): Promise { + if (this.hasRunOnce) { + this.logger.warn( + 'Skipping has already been run once MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', + ); + + return; + } + + if (options.dryRun) { + return; + } + + const queryRunner = this.coreDataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + await makeAgentUniversalIdentifierAndApplicationIdNotNullableQueries( + queryRunner, + ); + + await queryRunner.commitTransaction(); + this.logger.log( + 'Successfully run MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', + ); + this.hasRunOnce = true; + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error( + `Rolling back MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`, + ); + } finally { + await queryRunner.release(); + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-upgrade-version-command.module.ts index 28d18c0d7c..f79a94aa8b 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-upgrade-version-command.module.ts @@ -3,11 +3,13 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { BackfillOpportunityOwnerFieldCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-backfill-opportunity-owner-field.command'; import { BackfillStandardPageLayoutsCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-backfill-standard-page-layouts.command'; +import { IdentifyAgentMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command'; import { IdentifyFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-field-metadata.command'; import { IdentifyObjectMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-object-metadata.command'; import { IdentifyViewFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-field-metadata.command'; import { IdentifyViewFilterMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-filter-metadata.command'; import { IdentifyViewMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-metadata.command'; +import { MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-agent-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-field-metadata-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-object-metadata-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-field-universal-identifier-and-application-id-not-nullable-migration.command'; @@ -16,6 +18,7 @@ import { MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand import { UpdateTaskOnDeleteActionCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-update-task-on-delete-action.command'; import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module'; @@ -33,6 +36,7 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace imports: [ TypeOrmModule.forFeature([ WorkspaceEntity, + AgentEntity, FieldMetadataEntity, ObjectMetadataEntity, ViewEntity, @@ -52,11 +56,13 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace UpdateTaskOnDeleteActionCommand, BackfillOpportunityOwnerFieldCommand, BackfillStandardPageLayoutsCommand, + IdentifyAgentMetadataCommand, IdentifyFieldMetadataCommand, IdentifyObjectMetadataCommand, IdentifyViewMetadataCommand, IdentifyViewFieldMetadataCommand, IdentifyViewFilterMetadataCommand, + MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, @@ -67,11 +73,13 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace UpdateTaskOnDeleteActionCommand, BackfillOpportunityOwnerFieldCommand, BackfillStandardPageLayoutsCommand, + IdentifyAgentMetadataCommand, IdentifyFieldMetadataCommand, IdentifyObjectMetadataCommand, IdentifyViewMetadataCommand, IdentifyViewFieldMetadataCommand, IdentifyViewFilterMetadataCommand, + MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts index 2f7567877b..8c2d973e49 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts @@ -24,11 +24,13 @@ import { FixNanPositionValuesInNotesCommand } from 'src/database/commands/upgrad import { MigratePageLayoutWidgetConfigurationCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.command'; import { BackfillOpportunityOwnerFieldCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-backfill-opportunity-owner-field.command'; import { BackfillStandardPageLayoutsCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-backfill-standard-page-layouts.command'; +import { IdentifyAgentMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command'; import { IdentifyFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-field-metadata.command'; import { IdentifyObjectMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-object-metadata.command'; import { IdentifyViewFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-field-metadata.command'; import { IdentifyViewFilterMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-filter-metadata.command'; import { IdentifyViewMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-metadata.command'; +import { MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-agent-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-field-metadata-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-object-metadata-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-field-universal-identifier-and-application-id-not-nullable-migration.command'; @@ -77,11 +79,13 @@ export class UpgradeCommand extends UpgradeCommandRunner { protected readonly updateTaskOnDeleteActionCommand: UpdateTaskOnDeleteActionCommand, protected readonly backfillOpportunityOwnerFieldCommand: BackfillOpportunityOwnerFieldCommand, protected readonly backfillStandardPageLayoutsCommand: BackfillStandardPageLayoutsCommand, + protected readonly identifyAgentMetadataCommand: IdentifyAgentMetadataCommand, protected readonly identifyFieldMetadataCommand: IdentifyFieldMetadataCommand, protected readonly identifyObjectMetadataCommand: IdentifyObjectMetadataCommand, protected readonly identifyViewMetadataCommand: IdentifyViewMetadataCommand, protected readonly identifyViewFieldMetadataCommand: IdentifyViewFieldMetadataCommand, protected readonly identifyViewFilterMetadataCommand: IdentifyViewFilterMetadataCommand, + protected readonly makeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, protected readonly makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, protected readonly makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, protected readonly makeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, @@ -124,11 +128,14 @@ export class UpgradeCommand extends UpgradeCommandRunner { this.updateTaskOnDeleteActionCommand, this.backfillOpportunityOwnerFieldCommand, this.backfillStandardPageLayoutsCommand, + this.identifyAgentMetadataCommand, this.identifyFieldMetadataCommand, this.identifyObjectMetadataCommand, this.identifyViewMetadataCommand, this.identifyViewFieldMetadataCommand, this.identifyViewFilterMetadataCommand, + this + .makeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, this .makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, this diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.ts new file mode 100644 index 0000000000..97d87162ad --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.ts @@ -0,0 +1,63 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm'; + +import { makeAgentUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.util'; + +export class MakeAgentUniversalIdentifierAndApplicationIdNotNullable1768213174274 + implements MigrationInterface +{ + name = 'MakeAgentUniversalIdentifierAndApplicationIdNotNullable1768213174274'; + + public async up(queryRunner: QueryRunner): Promise { + const savepointName = + 'sp_make_agent_universal_identifier_and_application_id_not_nullable'; + + try { + await queryRunner.query(`SAVEPOINT ${savepointName}`); + + await makeAgentUniversalIdentifierAndApplicationIdNotNullableQueries( + queryRunner, + ); + + await queryRunner.query(`RELEASE SAVEPOINT ${savepointName}`); + } catch (e) { + try { + await queryRunner.query(`ROLLBACK TO SAVEPOINT ${savepointName}`); + await queryRunner.query(`RELEASE SAVEPOINT ${savepointName}`); + } catch (rollbackError) { + // eslint-disable-next-line no-console + console.error( + 'Failed to rollback to savepoint in MakeAgentUniversalIdentifierAndApplicationIdNotNullable1768213174274', + rollbackError, + ); + throw rollbackError; + } + + // eslint-disable-next-line no-console + console.error( + 'Swallowing MakeAgentUniversalIdentifierAndApplicationIdNotNullable1768213174274 error', + e, + ); + } + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."agent" DROP CONSTRAINT "FK_259c48f99f625708723414adb5d"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_0cc4d03dbcc269e77ba4d297fb"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."agent" ALTER COLUMN "applicationId" DROP NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "core"."agent" ALTER COLUMN "universalIdentifier" DROP NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0cc4d03dbcc269e77ba4d297fb" ON "core"."agent" ("workspaceId", "universalIdentifier") `, + ); + await queryRunner.query( + `ALTER TABLE "core"."agent" ADD CONSTRAINT "FK_259c48f99f625708723414adb5d" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } +} diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.util.ts b/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.util.ts new file mode 100644 index 0000000000..0395b996b9 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.util.ts @@ -0,0 +1,23 @@ +import { type QueryRunner } from 'typeorm'; + +export const makeAgentUniversalIdentifierAndApplicationIdNotNullableQueries = + async (queryRunner: QueryRunner): Promise => { + await queryRunner.query( + `ALTER TABLE "core"."agent" DROP CONSTRAINT "FK_259c48f99f625708723414adb5d"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_0cc4d03dbcc269e77ba4d297fb"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."agent" ALTER COLUMN "universalIdentifier" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "core"."agent" ALTER COLUMN "applicationId" SET NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0cc4d03dbcc269e77ba4d297fb" ON "core"."agent" ("workspaceId", "universalIdentifier") `, + ); + await queryRunner.query( + `ALTER TABLE "core"."agent" ADD CONSTRAINT "FK_259c48f99f625708723414adb5d" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/entities/agent.entity.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/entities/agent.entity.ts index 3b4255cc29..487d6edc3d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/entities/agent.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/entities/agent.entity.ts @@ -14,7 +14,7 @@ import { DEFAULT_SMART_MODEL, ModelId, } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const'; -import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface'; +import { SyncableEntityRequired } from 'src/engine/workspace-manager/types/syncable-entity-required.interface'; @Entity('agent') @Index('IDX_AGENT_ID_DELETED_AT', ['id', 'deletedAt']) @@ -23,7 +23,7 @@ import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-enti where: '"deletedAt" IS NULL', }) export class AgentEntity - extends SyncableEntity + extends SyncableEntityRequired implements Required { @PrimaryGeneratedColumn('uuid') diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-agent/utils/transform-agent-entity-to-flat-agent.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-agent/utils/transform-agent-entity-to-flat-agent.util.ts index d161f15706..6c1626a730 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-agent/utils/transform-agent-entity-to-flat-agent.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-agent/utils/transform-agent-entity-to-flat-agent.util.ts @@ -19,7 +19,8 @@ export const transformAgentEntityToFlatAgent = ( responseFormat: agentEntity.responseFormat, workspaceId: agentEntity.workspaceId, isCustom: agentEntity.isCustom, - universalIdentifier: agentEntity.standardId || agentEntity.id, + // TODO remove once MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand has been run once + universalIdentifier: agentEntity.universalIdentifier ?? agentEntity.id, applicationId: agentEntity.applicationId, modelConfiguration: agentEntity.modelConfiguration, evaluationInputs: agentEntity.evaluationInputs,