feat: agent roleUniversalIdentifier for manifest-driven role assignment (#23206)
## Summary - Adds optional `roleUniversalIdentifier` on `AgentManifest` / `defineAgent` so apps can declaratively assign a role to an agent (same config shape as `defaultRoleUniversalIdentifier`). - Wires `agentUniversalIdentifier` as a sync many-to-one FK on `roleTarget`, and emits a deterministic `roleTarget` from the agent during app sync (create / update / delete). - Enables app agents (e.g. Slack assistant) to get a role on install without postInstall hooks or manual admin assignment. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23206?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
} from 'typeorm';
|
||||
|
||||
import { ApiKeyEntity } from 'src/engine/core-modules/api-key/api-key.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
|
||||
@@ -50,6 +51,13 @@ export class RoleTargetEntity extends SyncableEntity {
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
agentId: string | null;
|
||||
|
||||
@ManyToOne(() => AgentEntity, {
|
||||
onDelete: 'CASCADE',
|
||||
nullable: true,
|
||||
})
|
||||
@JoinColumn({ name: 'agentId' })
|
||||
agent: Relation<AgentEntity> | null;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
apiKeyId: string | null;
|
||||
|
||||
|
||||
+8
-1
@@ -56,7 +56,12 @@ export class RoleTargetService {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { flatRoleTargetMaps, flatApplicationMaps, flatRoleMaps } =
|
||||
const {
|
||||
flatRoleTargetMaps,
|
||||
flatApplicationMaps,
|
||||
flatRoleMaps,
|
||||
flatAgentMaps,
|
||||
} =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
@@ -64,6 +69,7 @@ export class RoleTargetService {
|
||||
'flatRoleTargetMaps',
|
||||
'flatApplicationMaps',
|
||||
'flatRoleMaps',
|
||||
'flatAgentMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
@@ -88,6 +94,7 @@ export class RoleTargetService {
|
||||
createRoleTargetInput,
|
||||
flatRoleTargetMaps,
|
||||
flatRoleMaps,
|
||||
flatAgentMaps,
|
||||
workspaceId,
|
||||
flatApplication: flatApplication ?? workspaceCustomFlatApplication,
|
||||
});
|
||||
|
||||
+15
-1
@@ -3,6 +3,7 @@ import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { resolveEntityRelationUniversalIdentifiers } from 'src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util';
|
||||
import { type FlatRoleTarget } from 'src/engine/metadata-modules/flat-role-target/types/flat-role-target.type';
|
||||
import { findFlatRoleTargetFromForeignKey } from 'src/engine/metadata-modules/flat-role-target/utils/find-flat-role-target-from-foreign-key.util';
|
||||
@@ -13,12 +14,16 @@ export const fromCreateRoleTargetInputToFlatRoleTargetToCreate = ({
|
||||
workspaceId,
|
||||
flatRoleTargetMaps,
|
||||
flatRoleMaps,
|
||||
flatAgentMaps,
|
||||
flatApplication,
|
||||
}: {
|
||||
createRoleTargetInput: CreateRoleTargetInput;
|
||||
workspaceId: string;
|
||||
flatApplication: FlatApplication;
|
||||
} & Pick<AllFlatEntityMaps, 'flatRoleTargetMaps' | 'flatRoleMaps'>): {
|
||||
} & Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatRoleTargetMaps' | 'flatRoleMaps' | 'flatAgentMaps'
|
||||
>): {
|
||||
flatRoleTargetToCreate: FlatRoleTarget;
|
||||
flatRoleTargetsToDelete: FlatRoleTarget[];
|
||||
} => {
|
||||
@@ -34,12 +39,21 @@ export const fromCreateRoleTargetInputToFlatRoleTargetToCreate = ({
|
||||
},
|
||||
);
|
||||
|
||||
const agentUniversalIdentifier =
|
||||
targetMetadataForeignKey === 'agentId'
|
||||
? findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatAgentMaps,
|
||||
flatEntityId: targetId,
|
||||
}).universalIdentifier
|
||||
: null;
|
||||
|
||||
const flatRoleTargetToCreate: FlatRoleTarget = {
|
||||
id: v4(),
|
||||
roleId,
|
||||
roleUniversalIdentifier,
|
||||
userWorkspaceId: null,
|
||||
agentId: null,
|
||||
agentUniversalIdentifier,
|
||||
apiKeyId: null,
|
||||
createdAt: now.toISOString(),
|
||||
updatedAt: now.toISOString(),
|
||||
|
||||
Reference in New Issue
Block a user