Commit Graph

3318 Commits

Author SHA1 Message Date
Raphaël Bosi f48adb5f07 Migrate page layout services (#16443)
Last step of the page layout migration:
- Migrate services
- Write integration tests
2025-12-10 13:49:44 +00:00
Abdul Rahman 1839f8e946 Fix: Persist hide empty groups setting on backend (BREAKING: deploy server first) (#16385)
Closes #13754 and [core team issue
#414](https://github.com/twentyhq/core-team-issues/issues/414)
2025-12-10 19:10:29 +05:30
Baptiste Devessier 20f2ca6864 Prevent seeding empty configuration for dashboard widgets (#16458)
We used to seed widgets with empty configuration when the fields they
required weren't defined. I suggest we only seed properly defined
widgets.
2025-12-10 14:40:02 +01:00
Paul Rastoin 20879c8147 Standard views tooling (#16436)
# 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.
2025-12-10 14:29:01 +01:00
Paul Rastoin 7b98804ec1 Fix flat field metadata date type (#16445)
# Introduction
Following https://github.com/twentyhq/twenty/pull/16420
2025-12-09 19:53:36 +01:00
Charles Bochet 83fc434c5d Fix workspace invite onboarding loop (#16444)
- 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
2025-12-09 19:18:17 +01:00
Charles Bochet aa519e7d81 Fix messaging errors on message folder list (#16404)
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.
2025-12-09 17:01:25 +01:00
Charles Bochet a18203934c Fix flat entity maps date serialization (#16420)
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
2025-12-09 16:41:50 +01:00
nitin 7af3da9393 [Dashboards] add ratio aggregate on aggregate chart (#16413)
closes https://github.com/twentyhq/core-team-issues/issues/1960
video QA


https://github.com/user-attachments/assets/e90cee44-5edc-430d-b003-03ab4ea921f7
2025-12-09 15:23:21 +01:00
Thomas Trompette bd49fd8d5f Remove object relation schema from step output (#16428)
Fix https://github.com/twentyhq/core-team-issues/issues/1931

On trigger database event and step record crud, we still display the
full object for relation in the variable picker (Account Owner in
screenshot).
<img width="259" height="189" alt="Capture d’écran 2025-12-09 à 14 43
06"
src="https://github.com/user-attachments/assets/1d7fdebf-9183-4197-929a-7b7d719c465a"
/>

But as we do not enrich the data anymore, those fields are actually
empty. We should instead display AccountOwnerId and, if the user needs
the full Account owner, a search can be used.
<img width="259" height="189" alt="Capture d’écran 2025-12-09 à 14 42
37"
src="https://github.com/user-attachments/assets/87b46762-3204-4d25-9b6a-077d6b8567bd"
/>
2025-12-09 14:10:12 +00:00
Paul Rastoin e758f78caf Finalize standard index as code declaration (#16429)
# Introduction
Following https://github.com/twentyhq/twenty/pull/16426
Related https://github.com/twentyhq/core-team-issues/issues/1995
2025-12-09 14:05:27 +00:00
Thomas Trompette 3ba7a070d9 Fix is empty and non empty filter for numbers (#16424)
isNonEmptyString does not work for a number. We need to properly handle
null and undefined
2025-12-09 14:55:14 +01:00
Paul Rastoin e8d4f11d8e Standard index as code introduction + type refactor (#16426)
# 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
2025-12-09 14:52:25 +01:00
Paul Rastoin 4996f3dd28 Finalize twenty standard app as workspace migration object and fields (#16353)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1995
In this PR we're fixing the remaining object/fields validation errors
resulting from standard objects and fields now passing a validation that
wasn't when using the sync metadata

## Key Changes
- **Field naming**: Renamed `iCalUID` to `iCalUid` for consistent
camelCase convention across calendar events
- **Enum standardization**: Uppercased enum values for message channels
(email→EMAIL), message participants (from→FROM, to→TO, cc→CC, bcc→BCC),
and message direction (incoming→INCOMING, outgoing→OUTGOING)
- **Label simplification**: Removed example values from workspace member
number format labels for cleaner UI
- **Migration infrastructure**: Added `isSystemBuild` flag throughout
field metadata service pipeline to allow system-level updates of
standard fields that bypass normal restrictions

## Migrating the existing data
We've created an upgrade command that will identify using the existing
object and field standard id field that needs to be updated, even though
the sync metadata still in usage could have fix them ( and the goal is
to deprecate it by the end of the sprint )
We will call the updateOneField for each of them, we're passing by the
field service in order to battle test what are going to be the temporary
way to handle standard migrations when we will start deprecating the
sync metadata but haven't still refactored the v2 workspace migration to
be workspace agnostic

## Twenty eng migration
Tested the whole migration + upgrade on twenty eng
Here are generated workspace migration
Records are handled natively gracefully too

### ICalUid
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": "iCalUID",
            "to": "iCalUid",
            "property": "name"
          }
        ]
      }
    ],
    "workspaceId": ""
  }
}
```

### Incoming Outgoing
None as already caps in database somehow
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [],
    "workspaceId": ""
  }
}
```

### EMAIL
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": "'email'",
            "to": "'EMAIL'",
            "property": "defaultValue"
          },
          {
            "from": [
              {
                "color": "green",
                "id": "",
                "label": "Email",
                "position": 0,
                "value": "email"
              },
              {
                "color": "blue",
                "id": "",
                "label": "SMS",
                "position": 1,
                "value": "sms"
              }
            ],
            "to": [
              {
                "color": "green",
                "id": "",
                "label": "Email",
                "position": 0,
                "value": "EMAIL"
              },
              {
                "color": "blue",
                "id": "",
                "label": "SMS",
                "position": 1,
                "value": "SMS"
              }
            ],
            "property": "options"
          }
        ]
      }
    ],
    "workspaceId": "e"
  }
}
```

### MessageParticipantRole
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": "'from'",
            "to": "'FROM'",
            "property": "defaultValue"
          },
          {
            "from": [
              {
                "color": "green",
                "id": "",
                "label": "From",
                "position": 0,
                "value": "from"
              },
              {
                "color": "blue",
                "id": "",
                "label": "To",
                "position": 1,
                "value": "to"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Cc",
                "position": 2,
                "value": "cc"
              },
              {
                "color": "red",
                "id": "",
                "label": "Bcc",
                "position": 3,
                "value": "bcc"
              }
            ],
            "to": [
              {
                "color": "green",
                "id": "",
                "label": "From",
                "position": 0,
                "value": "FROM"
              },
              {
                "color": "blue",
                "id": "",
                "label": "To",
                "position": 1,
                "value": "TO"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Cc",
                "position": 2,
                "value": "CC"
              },
              {
                "color": "red",
                "id": "",
                "label": "Bcc",
                "position": 3,
                "value": "BCC"
              }
            ],
            "property": "options"
          }
        ]
      }
    ],
    "workspaceId": ""
  }
}
```

### Workspace member number format labels
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": [
              {
                "color": "turquoise",
                "id": "",
                "label": "System",
                "position": 0,
                "value": "SYSTEM"
              },
              {
                "color": "blue",
                "id": "",
                "label": "Commas and dot (1,234.56)",
                "position": 1,
                "value": "COMMAS_AND_DOT"
              },
              {
                "color": "green",
                "id": "",
                "label": "Spaces and comma (1 234,56)",
                "position": 2,
                "value": "SPACES_AND_COMMA"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Dots and comma (1.234,56)",
                "position": 3,
                "value": "DOTS_AND_COMMA"
              },
              {
                "color": "purple",
                "id": "",
                "label": "Apostrophe and dot (1'234.56)",
                "position": 4,
                "value": "APOSTROPHE_AND_DOT"
              }
            ],
            "to": [
              {
                "color": "turquoise",
                "id": "",
                "label": "System",
                "position": 0,
                "value": "SYSTEM"
              },
              {
                "color": "blue",
                "id": "",
                "label": "Commas and dot",
                "position": 1,
                "value": "COMMAS_AND_DOT"
              },
              {
                "color": "green",
                "id": "",
                "label": "Spaces and comma",
                "position": 2,
                "value": "SPACES_AND_COMMA"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Dots and comma",
                "position": 3,
                "value": "DOTS_AND_COMMA"
              },
              {
                "color": "purple",
                "id": "",
                "label": "Apostrophe and dot",
                "position": 4,
                "value": "APOSTROPHE_AND_DOT"
              }
            ],
            "property": "options"
          }
        ]
      }
    ],
    "workspaceId": ""
  }
}
```
2025-12-09 14:09:48 +01:00
Etienne be91d870f9 Fix - revert standard sync logic on index + fix command (#16421) 2025-12-09 12:54:01 +00:00
Etienne ec2b6ae691 Fix storybook test (#16416) 2025-12-09 10:29:23 +00:00
Abdul Rahman ae2919e564 fix: migrate wildcard routes to named parameters (#16380)
Closes #16189

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-12-09 09:52:32 +01:00
github-actions[bot] 0b8984116b i18n - translations (#16408)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-08 20:20:32 +01:00
Abdul Rahman 77e592502c fix: improve CRON schedule validation and display (#16360)
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
2025-12-09 00:09:11 +05:30
github-actions[bot] f80b2f2c38 i18n - docs translations (#16406)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-08 19:22:30 +01:00
Etienne 07cfaa78ef Fix unique standard field (#16371)
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>
2025-12-08 18:05:28 +00:00
github-actions[bot] 9efeb180d6 i18n - docs translations (#16402)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-08 17:22:01 +01:00
Thomas Trompette ac89b5aff6 Improve object record changed performances (#16398)
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
2025-12-08 17:06:53 +01:00
Weiko 859004f4fc Refactor global datasource part 2 (#16399)
## 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
2025-12-08 17:05:00 +01:00
Raphaël Bosi 5fb7e76005 Migrate page layout to v2 (#16364) 2025-12-08 14:11:24 +00:00
Charles Bochet 2790d5dd93 Message fixes (#16389)
In this PR:
- add more logs to troubleshoot issues in production
- use messageChannelSyncStatus in messageSync service instead of making
a manual update
2025-12-08 14:56:43 +01:00
neo773 5f4c7e016c refactor imap gql resolver (#16375) 2025-12-08 14:54:24 +01:00
martmull 7f1e69740a 1895 extensibility v1 application tokens (#16365)
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
2025-12-08 10:42:46 +01:00
Félix Malfait 1f9a6b067a fix: make impersonation audit logging non-blocking (#16390)
## 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
2025-12-08 08:48:44 +00:00
Paul Rastoin 3e57aa14d3 Refactor page layout tab and widgets migration (#16367)
# 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
2025-12-06 10:23:24 +01:00
Félix Malfait 223082a4da refactor(twenty-server): consolidate AI tool provider architecture (#16355)
## 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
2025-12-06 06:51:47 +01:00
Paul Rastoin c8f541618d Refactor validate build and run for configuration to be less verbose and more reliable (#16343)
# 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
2025-12-05 15:41:56 +00:00
neo773 fee3e6b7a1 fix folder actions (#16344)
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-12-05 16:19:50 +01:00
Thomas Trompette de4e78e5b2 Fix workflow relation variables (#16362)
display many to one relations instead of one to many
2025-12-05 16:11:37 +01:00
github-actions[bot] a9033a3045 i18n - docs translations (#16359)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-05 15:21:01 +01:00
Marie 7ce22d5c7e breaking (soft) - Migrate viewGroup.fieldMetadataId -> view.mainGroupByFieldMetadataId (2/3) (#16277)
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)
2025-12-05 14:00:03 +00:00
github-actions[bot] 17d88a074b i18n - translations (#16356)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-05 14:40:04 +01:00
Raphaël Bosi 077be7644c Migrate page layout widget to v2 of the API (#16323) 2025-12-05 13:04:06 +00:00
Charles Bochet 56e66315cd Fix messaging direct import (#16354)
Mainly clarifying naming (scheduleMessageImport was actually tagging as
PENDING)
+ tagging as SCHEDULED in case of direct import (we skip PENDING state)
2025-12-05 10:48:09 +01:00
Charles Bochet 10b5e156e5 Improvement on messaging (#16351)
In this PR:
- change messaging / calendar stale duration check to 30minutes (cron is
running every 1h, duration check was 1h, so evaluation was flaky)
- when temporary error (throttling), preserve syncStageStartedAt as this
is necessary to assess exponential throttling
2025-12-05 00:04:07 +01:00
Félix Malfait 2803292521 feat: add Metadata Builder agent for data model management (#16350)
## Summary

This PR adds a new **Metadata Builder** AI agent that specializes in
managing the workspace data model (creating objects, adding fields,
etc.).

## Changes

### New Files
- `data-model-manager-role.ts` - New standard role with `DATA_MODEL`
permission flag
- `metadata-builder-agent.ts` - New standard agent for data model
management

### Modified Files
- **ChatToolsProviderService**: Refactored to consolidate all
permission-based tools into a single `getChatTools()` method. Now
injects both workflow tools and metadata tools based on permissions.
- **AgentChatRoutingService**: Updated to use the new consolidated
`getChatTools()` method
- **AiChatModule**: Added imports for `ObjectMetadataModule` and
`FieldMetadataModule`
- **Router system prompt**: Added metadata-builder agent selection rules
with clear distinction between schema operations vs data operations
- **Metadata tools factories**: Improved error messages to show detailed
validation errors instead of generic messages

### Refactoring
- Renamed `index.ts` files to `standard-agent-definitions.ts` and
`standard-role-definitions.ts` to follow naming conventions
- Renamed exports from `standardAgentDefinitions` to
`STANDARD_AGENT_DEFINITIONS` (SCREAMING_SNAKE_CASE)

## Key Features

1. **Metadata Builder Agent** can:
   - Create new custom objects
   - Add fields to existing objects
   - Update object and field properties
   - Create relations between objects

2. **Permission-based tool injection**: Tools are automatically injected
based on the `DATA_MODEL` permission flag

3. **Improved routing**: The router now correctly distinguishes between:
   - "Create an object called Project" → metadata-builder (schema)
   - "Create a company called Acme" → data-manipulator (data)

4. **Better error messages**: Validation errors now show detailed
messages like:
   ```
   Validation errors:
   [objectMetadata] Name must be in camelCase format
   [objectMetadata] Label is required
   ```
2025-12-04 23:26:29 +01:00
Félix Malfait 34d7d82099 refactor(mcp): call metadata services directly instead of REST layer (#16349)
## Summary

Refactors MCP metadata tools to call underlying services directly
instead of going through the REST layer. This makes MCP a pure
presentation layer.

### Changes

**Created:**
-
`packages/twenty-server/src/engine/metadata-modules/metadata-tools/metadata-tools.module.ts`
- Module that exports MetadataToolsFactory
-
`packages/twenty-server/src/engine/metadata-modules/metadata-tools/services/metadata-tools.factory.ts`
- Factory that generates 8 metadata tools using Zod schemas:
- `get-object-metadata`, `create-object-metadata`,
`update-object-metadata`, `delete-object-metadata`
- `get-field-metadata`, `create-field-metadata`,
`update-field-metadata`, `delete-field-metadata`

**Modified:**
-
`packages/twenty-server/src/engine/api/mcp/services/mcp-metadata.service.ts`
- Uses new factory instead of REST-based services
- `packages/twenty-server/src/engine/api/mcp/mcp.module.ts` - Imports
MetadataToolsModule, removes old service imports

**Deleted:**
-
`packages/twenty-server/src/engine/api/mcp/services/tools/create.tools.service.ts`
-
`packages/twenty-server/src/engine/api/mcp/services/tools/update.tools.service.ts`
-
`packages/twenty-server/src/engine/api/mcp/services/tools/delete.tools.service.ts`
-
`packages/twenty-server/src/engine/api/mcp/services/tools/get.tools.service.ts`
-
`packages/twenty-server/src/engine/api/mcp/services/tools/mcp-metadata-tools.service.ts`

### Architecture Improvement

**Before:**
```
MCP Tool → MetadataQueryBuilderFactory → RestApiService → GraphQL API → Service
```

**After:**
```
MCP Tool → Service (ObjectMetadataService / FieldMetadataService)
```

This follows the pattern established by `direct-record-tools.factory.ts`
and workflow tools.
2025-12-04 22:21:16 +01:00
github-actions[bot] aa0471ca1f i18n - translations (#16348)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-04 21:21:31 +01:00
Félix Malfait 9cecbaebc3 refactor(workflow-tools): reorganize to one file per tool with co-located schemas (#16313)
## Summary

Reorganizes workflow tools to improve maintainability and
discoverability by having one file per tool with co-located input
schemas.

## Changes

- Create individual tool files in `tools/` directory (11 files)
- Co-locate input schemas with their tool implementations
- Add shared types file for dependencies and context
- Simplify workspace service to aggregate tool factories
- Remove centralized `schemas/` directory

## New Structure

```
workflow-tools/
├── services/
│   └── workflow-tool.workspace-service.ts
├── tools/
│   ├── activate-workflow-version.tool.ts
│   ├── compute-step-output-schema.tool.ts
│   ├── create-complete-workflow.tool.ts
│   ├── create-draft-from-workflow-version.tool.ts
│   ├── create-workflow-version-edge.tool.ts
│   ├── create-workflow-version-step.tool.ts
│   ├── deactivate-workflow-version.tool.ts
│   ├── delete-workflow-version-edge.tool.ts
│   ├── delete-workflow-version-step.tool.ts
│   ├── update-workflow-version-positions.tool.ts
│   └── update-workflow-version-step.tool.ts
├── types/
│   └── workflow-tool-dependencies.type.ts
└── workflow-tools.module.ts
```

## Benefits

- **Co-location**: Schema and tool logic are in the same file
- **Single responsibility**: Each file handles one tool
- **Easier maintenance**: Changes to a tool only touch one file
- **Better discoverability**: File names match tool names
2025-12-04 21:06:49 +01:00
Weiko 1991ee850e Fix seeding perf + batch role targets creation (#16337) 2025-12-04 20:29:48 +01:00
github-actions[bot] ddece3aac8 i18n - translations (#16342)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-04 19:01:28 +01:00
github-actions[bot] aede5bab34 i18n - translations (#16338)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-12-04 18:21:26 +01:00
Paul Rastoin 28cdb02fbb Twenty standard application Objects and fields as allFlatEntityMaps ID non-agnostic (#16298)
# Introduction

Related to https://github.com/twentyhq/core-team-issues/issues/1995
This PR introduces the basis of the `twentyStandard` application as code
on demand, it's highly tied to `ids` where it will becomes workspace
agnostic following the builder and runner `universalIdentifier` refactor
later.

The goal here to allow computing the `allFlatEntityMaps` `to` of the
`twentyStandard` application on a empty workspace ( workspace creation
). Allowing installing the twenty standard app through a workspace
migration instead of passing by the sync metadata

Nothing done will be run in production for the moment if it's not the
small validation refactor we've introduced

Please note that everything introduced here will be replaced at some
point by a twenty app instance when the twenty sdk is mature enough to
handle of the edge cases we need here

## How we've proceeded
We've been iterating over every workspace entity both objects and their
fields, and transpiled them to flatEntity.
Being sure we migrate the defaultValue, settings and so on accordingly.
We've also compute all the ids in prior of the whole entities
computation so we don't face any hoisting issue.

## Current state
At the moment only handling all of the 29 standard objects and their
fields
Settings a unique universalIdentifier for all of them

Will come views, agent role targets and so on later

## `workspace:compute-twenty-standard-migration` command
This command allow generating a workspace migration that will result in
installing the twenty standard app in an empty workspace
It's temporary and aims to allow debugging for the moment we might not
keep it in the future as it is right now
It contains debug writeFileSync which is expected no worries greptile

## `LabelFieldMetadataIdentifierId`
Small refactor allowing defining the label identifier field metadata id
of a uuid field metadata type for system object, as some of our standard
object don't have a name field and don't aim to
Also please note that we might remove this build options later in the
sake of the currently installed universal identifier application that we
could compare with the deterministic twenty standard one

## `runFlatFieldMetadataValidators`
Deprecated this pattern which was redundant and not v2 friendly pattern

## Current errors that will address in upcoming PR
Current standard objects and fields metadata does not pass the
validation that we have in place, as historically the sync metadata
would directly consume the repositories and would just ignore the
validation. This is about to change.
Will handle the below errors in dedicated PRs as they will required
upgrade commands in order to migrate the data, or will handle that from
the sync metadata instead still to be determined but nothing critical
here

- camel case field metadata name
- options label invalid format

```json
{
  "status": "fail",
  "report": {
    "fieldMetadata": [
      {
        "status": "fail",
        "errors": [
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Name should be in camelCase",
            "userFriendlyMessage": {
              "id": "P+jdmX",
              "message": "Name should be in camelCase"
            },
            "value": "iCalUID"
          }
        ],
        "flatEntityMinimalInformation": {
          "id": "68dd83cd-92c8-4233-bb28-47939bab6124",
          "name": "iCalUID",
          "objectMetadataId": "11c16ab6-9176-439e-a2db-a12c5a58a524"
        },
        "type": "create_field"
      },
      {
        "status": "fail",
        "errors": [
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"email\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "email"
              }
            },
            "value": "email"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"sms\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "sms"
              }
            },
            "value": "sms"
          }
        ],
        "flatEntityMinimalInformation": {
          "id": "e3caaf2a-e07d-4146-8dfc-9eef904e82c9",
          "name": "type",
          "objectMetadataId": "4b777de5-4c7b-4af4-9b92-655c0f87512b"
        },
        "type": "create_field"
      },
      {
        "status": "fail",
        "errors": [
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"incoming\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "incoming"
              }
            },
            "value": "incoming"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"outgoing\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "outgoing"
              }
            },
            "value": "outgoing"
          }
        ],
        "flatEntityMinimalInformation": {
          "id": "d96233a4-93be-45ea-9548-3b50f3c700cf",
          "name": "direction",
          "objectMetadataId": "480a648a-d2e5-482a-992f-ef053e1b4bb0"
        },
        "type": "create_field"
      },
      {
        "status": "fail",
        "errors": [
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"from\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "from"
              }
            },
            "value": "from"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"to\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "to"
              }
            },
            "value": "to"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"cc\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "cc"
              }
            },
            "value": "cc"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Value must be in UPPER_CASE and follow snake_case \"bcc\"",
            "userFriendlyMessage": {
              "id": "UBPzFQ",
              "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"",
              "values": {
                "sanitizedValue": "bcc"
              }
            },
            "value": "bcc"
          }
        ],
        "flatEntityMinimalInformation": {
          "id": "961c598e-67c3-452d-8bb2-b92c0bc64404",
          "name": "role",
          "objectMetadataId": "8af8a13c-ff97-4cd3-b70d-52a7dc2924b4"
        },
        "type": "create_field"
      },
      {
        "status": "fail",
        "errors": [
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Label must not contain a comma",
            "userFriendlyMessage": {
              "id": "k731jp",
              "message": "Label must not contain a comma"
            },
            "value": "Commas and dot (1,234.56)"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Label must not contain a comma",
            "userFriendlyMessage": {
              "id": "k731jp",
              "message": "Label must not contain a comma"
            },
            "value": "Spaces and comma (1 234,56)"
          },
          {
            "code": "INVALID_FIELD_INPUT",
            "message": "Label must not contain a comma",
            "userFriendlyMessage": {
              "id": "k731jp",
              "message": "Label must not contain a comma"
            },
            "value": "Dots and comma (1.234,56)"
          }
        ],
        "flatEntityMinimalInformation": {
          "id": "7fa20caf-2597-42e3-84e5-15a91b125b9b",
          "name": "numberFormat",
          "objectMetadataId": "a6974302-9e72-461c-aa09-9390f4ff16fc"
        },
        "type": "create_field"
      }
    ],
    "objectMetadata": [],
    "view": [],
    "viewField": [],
    "viewGroup": [],
    "index": [],
    "serverlessFunction": [],
    "cronTrigger": [],
    "databaseEventTrigger": [],
    "routeTrigger": [],
    "viewFilter": [],
    "role": [],
    "roleTarget": [],
    "agent": []
  }
}
```
2025-12-04 17:39:12 +01:00
Paul Rastoin b2d785de4b Page layout tab v2 (#16319)
# Introduction
Migrating `pageLayoutTab` to the v2 engine
- Types and constants
- Builder and validate
- Runner

Introduced a new `StrictSyncableEntity` that enforces that
`universalIdentifier` and `applicationId` are defined
As these entities are brand new we could enforce this rule already
This still requires a migration command to associate the existing
entities to custom workspace application instance and define
universalIdentifier

Handled retro-comp of the migration through a migration as upgrade
command fallback

---------

Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
2025-12-04 17:37:16 +01:00
Etienne 1981cf0ed6 Query complexity - Fix rest metadata api limit (#16336) 2025-12-04 17:24:25 +01:00