The current repository implementation require the caller to pass the
permissionConfig which is not the case in messaging and calendar custom
resolver. We are bypassing the permission as fetching the role in the
caller will likely not be needed anymore in the new datasource / orm
implementation
## Context
Following https://github.com/twentyhq/twenty/pull/16399
Now using the new global orm manager everywhere and returning a
GlobalDatasource/WorkspaceDatasource based on a feature flag.
This means we now need to wrap all our ORM calls within
executeInWorkspaceContext callback (at least for now) so the global
datasource can dynamically hydrate its context via the new store (the
global datasource does not store anything related to workspaces as it is
now a unique singleton). If feature flag is off it still uses local data
stored in the workspace datasource.
## Context
SyncMetadata for role update was broken when those roles had permission
flags in them.
This PR https://github.com/twentyhq/twenty/issues/16365 introduced a
change in all existing standard roles which probably triggered the
update of faulty roles for the first time. Down the line, some
properties from flat were not omit before querying the DB (in this case
permissionFlagIds does not exist as a column in role table)
The fix is definitely not great, this also probably broke the update of
permissionFlags for existing STANDARD roles but it's not subject to
change (It actually never changed. Again, what has been updated was not
the permission flag but a new boolean introduced 2 days ago which forced
the "UPDATE" path in the sync which was probably never triggered before,
or at least not for roles with permission flags which are "quite" new).
Also we are about to remove this whole code in favor of the new
migration v2
# Introduction
Early returning in `validateBuildAndRun` as the runner even if passed an
empty workspace migration actions array be invalidating dependency
related flat entity maps.
When we will refactor the runner to consume the generic flat entity maps
tools such as
`addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow` we
will be able to remove the dependency cache invalidation
## Summary
This PR significantly simplifies the AI chat architecture by removing
complex routing/planning mechanisms and introduces clickable record
links in AI responses.
## Changes
### AI Chat Architecture Simplification
- **Removed** the entire `ai-chat-router` module (~850 lines) including:
- Strategy decider service
- Plan generator service
- Complex routing logic
- **Removed** agent execution planning services (~700 lines):
- `agent-execution.service.ts`
- `agent-plan-executor.service.ts`
- `agent-tool-generator.service.ts`
- **Added** centralized `ToolRegistryService` for tool management:
- Builds searchable tool index (database, action, workflow tools)
- Provides tool lookup by name
- Supports agent search for loading expertise
- **Added** `ChatExecutionService` as simple replacement:
- Includes full tool catalog in system prompt
- Pre-loads common tools (find/create/update for company, person,
opportunity, task, note)
- Uses `load_tools` mechanism for dynamic tool activation
- Enables native web search by default
### Record References in AI Responses
- Added `recordReferences` field to tool outputs for create, find, and
update operations
- Implemented `[[record:objectName:recordId:displayName]]` syntax for AI
to reference records
- Created `RecordLink` component that renders clickable chips with
object icons
- Integrated record link parsing into the markdown renderer
- Users can now click directly on created/found records in AI responses
### Workflow Agent Fixes
- Fixed cache invalidation issue when creating agents in workflows
- Added default prompt for workflow-created agents to prevent validation
errors
- Relaxed agent validation to only check properties being updated (not
all required properties)
### Code Quality Improvements
- Extracted `getRecordDisplayName` utility that mirrors frontend's
`getLabelIdentifierFieldValue` logic
- Uses object metadata to determine the correct label identifier field
- Handles `FULL_NAME` composite type for person/workspaceMember objects
- Shared across create, find, and update record services
## Net Impact
- **~1,200 lines deleted** (complex routing/planning code)
- **~500 lines added** (simpler tool registry + record links)
- Significantly reduced code complexity
- Better tool discovery through full catalog in system prompt
- Improved UX with clickable record references
## Testing
- Typecheck passes
- Lint passes
- Manual testing of AI chat with record creation and linking
# Introduction
In this PR we've introduced the type safe generation tooling for
standard view, view fields, view filters and view groups.
Also generated the ids before such as what was done for object and
fields, it's still temporary as will be replaced by universalIdentifier
mapping afterwards
## Task views
In this PR only implemented the `task` views and view groups, filters
and fields in order to battle test all the generation tools
Will create a dedicated PR that will implement all the remaining views.
- The issue is that the listener is not triggered if they are no change
to the workspaceMember (which happens when name is already filled
through GoogleSSO)
Fixes https://github.com/twentyhq/twenty/issues/16440
This is a known quirk of many Google APIs, including Gmail. Some error
responses use numeric code fields (e.g., 403), while others may return
them as strings (e.g., "403"). This depends on which internal service
returns the error and the language client you’re using.
Changes:
- as we store date in redis as serialized, let's make all flatEntity
dates as string. This requires changing FlatEntity types and making sure
that entity are converted to flatEntity and flatEntity to dtos
# Introduction
Related https://github.com/twentyhq/core-team-issues/issues/1995
In this PR we're introducing the standard index tools for their
declaration
Also adding a common typing in order to have a simily consistency across
standard builders
Will implement remaining standard index builder in an other PR
Fixes multiple issues with CRON schedule input validation and execution
time display.
### Issues Fixed
1. **UTC label placement** - Added "UTC" suffix to specific times (e.g.,
"at 09:30 UTC") but not to interval descriptions (e.g., "every hour")
2. **Upcoming execution time calculation** - Fixed incorrect execution
times for malformed CRON expressions by implementing auto-correction
### Changes
- Created `normalizeCronExpression` utility to standardize cron
expressions before parsing
- Updated `formatTime` to support optional UTC suffix
- Enhanced `getHoursDescription` to append UTC to specific times
- Added comprehensive test coverage (102 tests passing)
### Before
- `"1 /3 * * *"` showed daily executions at same time (incorrect)
- `"9 * * *"` showed same time repeated 3 times (incorrect)
- No UTC labels on schedule descriptions (confusing)
### After
- All malformed expressions auto-corrected and show correct execution
times
- UTC labels clearly indicate timezone for specific times
- User-friendly error messages for truly invalid patterns
Closes#15870
Fixes https://github.com/twentyhq/twenty/issues/15925
- update field metadata update logic
- uniformize the way index are named
- command to migrate v1-named unique index
- add integration testing
---------
Co-authored-by: prastoin <paul@twenty.com>
Using deepEqual, average over 3 calls:
- version trigger 1.53ms
- version steps 38.3ms
- run state 372.8 ms
Using stringified hash, average over 3 calls:
- version trigger 0.07ms
- version steps 0.74 ms
- run state 0.521ms
Short fastDeepEqual
- version trigger 0.028ms
- version steps 0.197ms
- run state 0.214ms
Lib fastDeepEqual
- version trigger 0.038ms
- version steps 0.187ms
- run state 0.230ms
## Context
Deprecating TwentyORMManager in favor of TwentyORMGlobalManager
(temporarily, as this will simplify the ultimate goal to later replace
all usages with the new TwentyORMGlobalManagerV2 which will have a
similar signature)
This means this PR had to refactor a bit of code to pass down the
workspaceId when not available directly as it is now a requirement,
meaning we also deprecated scopedWorkspaceContextFactory to have a less
obscure way to fetch the workspaceId and have something more
declarative.
Step 3 will be to update TwentyORMGlobalManager to use a featureFlag
toggling and use the new GlobalWorkspaceOrmManager internally using the
new cache service
Step 4 will be to remove the feature flag and pg_pool patch
In this PR:
- add more logs to troubleshoot issues in production
- use messageChannelSyncStatus in messageSync service instead of making
a manual update
First PR to implement application tokens
- add new application role in twenty-server
- move duplicated constants and types to twenty-shared
- will add role configuration utils into twenty-sdk in another PR
## Summary
This PR makes the Clickhouse audit logging in the impersonation flow
**non-blocking** (fire-and-forget).
## Problem
When the Clickhouse server is unavailable or slow, the impersonation
feature becomes blocked because all audit logging calls use `await`,
causing the entire impersonation flow to hang.
## Solution
Remove the `await` keyword from all audit logging calls in the
impersonation flow. The `ClickHouseService.insert()` method already
handles errors internally (catches and logs them), so there's no risk of
unhandled promise rejections.
## Changes
- **`impersonation.service.ts`**: 4 audit calls made non-blocking
- **`auth.resolver.ts`**: 5 audit calls made non-blocking
- **`auth.service.ts`**: 2 audit calls made non-blocking
## Testing
- Impersonation flow continues to work when Clickhouse is available
- Impersonation flow no longer blocks when Clickhouse is unavailable
# Introduction
Making columns nullable instead of required + metadata on the fly
migration in typeorm migration that could affect other breaking change
migrations to be run
## Summary
Consolidates the AI tool provider architecture by creating a single
`ToolProviderService` as the entry point for all tool generation. This
removes multiple intermediate services and simplifies the codebase.
## Changes
### New Architecture
- **`ToolProviderService`**: Single service for all tool generation
with:
- `getTools(spec)` - Get tools by category with permissions
- `getToolByType(type)` - Get specific tool for workflow execution
- **`ToolCategory` enum**: Declarative specification of tool types:
- `DATABASE_CRUD` - Record CRUD operations
- `ACTION` - HTTP requests, email sending, article search
- `WORKFLOW` - Workflow management tools
- `METADATA` - Object/field metadata tools
- `NATIVE_MODEL` - Model-specific tools (e.g., web search)
- **`ToolSpecification` type**: Clean API for requesting tools with
permissions
### Removed
- `AiToolsModule` - No longer needed
- `ToolService` - Logic inlined into ToolProviderService
- `ToolAdapterService` - Logic inlined into ToolProviderService
- `ToolRegistryService` - Logic inlined into ToolProviderService
### Updated
- All consumers (agents, chat, MCP, workflows) now use
`ToolProviderService`
- Test files updated accordingly
## Stats
- **547 insertions, 1146 deletions** (net ~600 lines removed)
- 4 services deleted
- 1 module deleted
## Testing
- [x] Typecheck passes
- [x] Lint passes
# Introduction
Refactored the api `validateBuildAndRunWorkspaceMigration` to be require
less configuration but to infer required args dynamically depending on
provided metadata maps to compare
## `inferDeletionFromMissingEntities`
Is not dynamically computed avoiding any miss configuration issue and
any missleading devxp
## Maps computation
Making only one call to redis to build both dependency and to be
compared entity maps. It does not matter to avoid passing a about to
compared flat entity maps to could also be a depedency, it's handled
directly in the builder setup optimistic cache logic
Please note that the flat maps used for the service input transpilation
might differ from the one that we will dynamically compute and inject in
the builder. Leading to do 2 redis calls but also race condition prone
validation error
We prefer that this occurs at the builder rather than at the runner
level as the pg instance is not cache and reflect the real state of a
given workspace
In a nutshell, there's a possible race condition between cache
invalidation and computation in both service input transpilers and
builder but we're totally ok with that
Should be merged once https://github.com/twentyhq/twenty/pull/16206 has
been released + command run to prod
In this PR
- Remove usage of viewGroup.fieldMetadataId, both in BE and FE states.
- But we still need to properly populate it until we fully remove
viewGroup.fieldMetadataId from db and ORM entity (upcoming 3rd PR out of
3). fieldMetadataId was removed from CoreViewGroup type and
CreateViewGroupInput and is determined BE-side based on the associated
view's mainGroupByFieldMetadataId. **I expect this means a downtime on
viewGroup creation, until both FE and BE are deployed and cache is
flushed.** This seems acceptable to me as it only regards viewGroup
creation.
- this information is replaced by view.mainGroupByFieldMetadataID
- Handle view group creation, update and deletion in the BE as a
side-effect of a view creation, update or deletion. Optimistic effects
are still used
- Add validation at view creation or update regarding
mainGroupByFieldMetadata
Left to do in 3rd PR
- Remove viewGroup.fieldMetadataId from db and ORM entity
- Restore feature allowing to update an existing grouped view's group by
field (already OK on BE side but need to rebuild FE optimistic)
Mainly clarifying naming (scheduleMessageImport was actually tagging as
PENDING)
+ tagging as SCHEDULED in case of direct import (we skip PENDING state)