Files
twenty/packages/twenty-shared
Félix Malfait 5d88cf7f2f feat(server): add role management tools for AI chat (#23613)
Adds role management tools to the AI chat so the agent can create and
configure roles, including row-level permissions.

## What

A `RoleToolProvider` in `tool-provider/providers/`, mirroring
`webhook-tool.provider.ts`, registered in `tool-provider.module.ts` and
gated behind `PermissionFlagType.ROLES` via
`PermissionsService.checkRolesPermissions` (same pattern as the
VIEWS/WORKFLOWS gating). Tools:

- `list_roles` — global record permissions, settings access, per-object
overrides, permission flags, assignability; optionally includes
row-level rules
- `create_role`, `update_role`, `delete_role`
- `assign_role_to_workspace_member` — via
`UserRoleService.assignRoleToManyUserWorkspace`, which goes through
role-target
- `upsert_object_permissions` — per-object overrides, e.g. read-only on
a given object
- `upsert_row_level_permission_rules` — reuses
`RowLevelPermissionPredicateService.upsertRowLevelPermissionPredicates`
and the predicate-group service, so the agent can express rules like
"members with this role only see records where the owner field matches
the current user" (a predicate with `workspaceMemberFieldMetadataId`
pointing at the workspaceMember `id` field, resolved to the current user
at query time)

Everything routes through the existing role services and DTOs
(`RoleService`, `ObjectPermissionService`, `UserRoleService`, the
row-level predicate services) rather than reimplementing them. A new
`ToolCategory.ROLE` is added to `twenty-shared`, along with its label in
the exhaustive switch in `build-tool-catalog-section.util.ts`.

## Safeguards

- Any mutation on a role with `isEditable: false` is rejected. That
covers the Admin role, which is created non-editable, and matches what
Settings blocks.
- Deleting the role the caller is currently acting under is rejected,
since deletion would rebind them to the workspace default role.
- Setting `canUpdateAllSettings: false` on the caller's own role is
rejected unless that role keeps an explicit ROLES permission flag.
- Changing your own role via `assign_role_to_workspace_member` is
rejected, checked both by workspace member id and by resolved user
workspace id.

The tool-layer checks are deliberate pre-checks: the migration
validators and services enforce the same rules downstream
(`validate-role-is-editable.util.ts`, default-role deletion, last-admin
unassignment, write-without-read consistency), but catching them early
gives the model a named, actionable message instead of a build failure
report. Where the deeper layer does reject, `formatValidationErrors`
expands the migration exception so the underlying per-entity errors
reach the model rather than a generic summary.

Worth flagging for reviewers: the self-lockout protection currently
lives only at the tool layer. A human admin can still strip settings
access from their own role through Settings/GraphQL. Closing that would
mean changing `RoleService`/`UserRoleService` behavior for the human
path, which felt like a separate decision than what this change is
scoped to.

## Notes

`ToolCategory.ROLE` is intentionally left out of
`WORKFLOW_AGENT_REGISTRY_TOOL_CATEGORIES`, so workflow agents don't get
these tools; only the chat surface and the MCP/tool-index paths that
share the registry do.

## Testing

- 24 unit tests in `providers/__tests__/role-tool.provider.spec.ts`,
covering permission gating, descriptor exposure, each safeguard, the
N+1-free list path, and validation-error surfacing
- 333 tests pass across the tool-provider, role, object-permission and
ai suites
- `npx nx lint:diff-with-main twenty-server` and `npx nx typecheck
twenty-server` are clean


---
_Generated by [Claude
Code](https://claude.ai/code/session_0131sLKVsRuaDoaKCxFM8g4Z)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23613?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. -->
2026-07-31 11:49:23 +00:00
..