- Added new `useUpdateManyRecords` hook for batch record updates with
optimistic cache updates
- Added `updateMessageFoldersSyncStatus` hook for managing message
folder sync state
- Redesigned Message Folders List with BreadCrumb and Animations
- Batch Gmail API calls using `googleapis-batcher` for folder processing
- Add concurrency limit for Microsoft Graph folder processing
- Skip IMAP folder sync when no new messages (checks UIDVALIDITY/MODSEQ)
- Refactored `syncMessageFolders` to return folder state directly,
avoiding extra DB round-trips
- Refactored `processPendingFolderActions` to reuse state instead of
querying DB again
- Add unique index on message folders entity
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
- Ability to display the details of a field
- The field can be edited (relations edition will be supported later)
- For now, the widget configuration stores the name of the field instead
of its fieldMetadataId. A hook resolves the fieldMetadataId from the
list of fields and the provided name. This will be replaced once we
migrate the configuration to the backend.
## Demo
https://github.com/user-attachments/assets/ab7efbda-66b2-46c1-b641-c350977c31dd
## Remaining to do
- Edition of relations
Since we now store rich text value in blocknote rather than markdown,
variables need to be resolved accordingly.
Replacing the variable tag pattern
`{"type":"variableTag","attrs":\{"variable":"(\{\{[^{}]+\}\})"\}\}` by a
blocknote text `{"type":"text","text":"${escapedText}"}`
Fixes https://github.com/twentyhq/twenty/issues/16583
To test :
- build a workflow that creates a note/ sends an email with a variable
in the body
- make sure the result is properly formatted once run
---------
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
# Introduction
In this pull-request we introduce a service dedicated to the
twenty-standard app installation, we will later be able to re-use
existing logic to be more generic and allow any app installation.
For the moment sticking to this usage
https://github.com/twentyhq/core-team-issues/issues/1995
## Encountered issues
- We decided not to migrate deprecated fields ( also they will become
custom field for any existing workspace having them in the future )
- duplicate criteria
- wrong search index declaration
- forgotten isSearchable
- Attachement seed
- Restored standardId
## Note
For the moment we're still searching through standardId for code that
run on both existing and new workspaces.
For code running on new workspace exclusively we're searching using
universalIdentifier
We will standardize universalIdentifier usage later when we've migratred
all the existing workspaces
## Workspace creation
Will handle workspace creation the same way in another PR
Related https://github.com/twentyhq/twenty/pull/15065
## TODO
- [ ] Double all frontend hardcoded queries to not refer to deprecated
fields especially attachments
# Introduction
Creating dedicated folders and module for both `page-layout-tab` and
`page-layout-widget`
The addition diff with deletion is due to the module being added
## Summary
Fixes a bug where `BillingUsageService.billUsage()` only sent the first
event from the `billingEvents` array to Stripe, silently ignoring all
subsequent events.
## Bug Description
The `billUsage()` method accepts an array of `BillingUsageEvent[]` but
was only processing `billingEvents[0]`, causing:
- Customers to be undercharged for their usage
- Revenue loss due to unbilled events
- Incorrect usage tracking
## Fix
Changed the implementation to use `Promise.all()` to send all events in
the array concurrently to Stripe.
## Before
```typescript
await this.stripeBillingMeterEventService.sendBillingMeterEvent({
eventName: billingEvents[0].eventName, // Only first event
value: billingEvents[0].value,
stripeCustomerId: workspaceStripeCustomer.stripeCustomerId,
dimensions: billingEvents[0].dimensions,
});
```
## After
```typescript
await Promise.all(
billingEvents.map((event) =>
this.stripeBillingMeterEventService.sendBillingMeterEvent({
eventName: event.eventName,
value: event.value,
stripeCustomerId: workspaceStripeCustomer.stripeCustomerId,
dimensions: event.dimensions,
}),
),
);
```
This PR updates the `isNameAvailable` function in
`getRemoteTableLocalName` to use parameterized queries instead of string
interpolation when querying the information_schema.
**Changes:**
- Replaced template literal interpolation with PostgreSQL's `$1`, `$2`
placeholder syntax
- Parameters are now passed as a separate array argument to
`dataSource.query()`
This follows best practices for database queries.
## Summary
Fixes a database connection leak in `WorkspaceDataSourceService` where
`QueryRunner.release()` was not being called when schema operations
failed.
## Problem
The `createWorkspaceDBSchema` and `deleteWorkspaceDBSchema` methods use
TypeORM's QueryRunner but didn't wrap the operations in
try-catch-finally blocks. When schema operations fail (e.g., permission
denied, schema conflicts), the `QueryRunner.release()` method was never
called.
**Impact:** Failed schema operations leak database connections, which
can exhaust the connection pool and cause the application to hang or
crash under load.
## Solution
Wrap both methods in try-finally blocks to ensure
`queryRunner.release()` is always called, regardless of whether the
operation succeeds or fails.
## Changes
- `createWorkspaceDBSchema`: Wrapped in try-finally to ensure connection
release
- `deleteWorkspaceDBSchema`: Wrapped in try-finally to ensure connection
release
## Summary
Instead of throwing an error at server startup when LOCAL code
interpreter is configured in production, we now return a DisabledDriver
that only throws when the code interpreter is actually used.
## Changes
- Created `DisabledDriver` class that implements `CodeInterpreterDriver`
but throws an error only when `execute()` is called
- Added `DISABLED` to the `CodeInterpreterDriverType` enum
- Updated the factory to return a `DISABLED` driver config instead of
throwing when LOCAL is used in production
- Updated the module to handle the new `DISABLED` driver type
## Motivation
Many users don't need the code interpreter feature and want to deploy to
production without configuring E2B. Previously, the server would crash
at startup if `CODE_INTERPRETER_TYPE=LOCAL` was set in production.
**Before:** Server crashes at startup in production if
`CODE_INTERPRETER_TYPE=LOCAL`
**After:** Server starts fine. The error only occurs if someone actually
tries to **use** the code interpreter feature, at which point they get a
clear error message explaining how to configure E2B.
## Summary
This PR adds the `lingui/no-unlocalized-strings` ESLint rule to detect
untranslated strings and fixes translation issues across multiple
components.
## Changes
### ESLint Configuration (`eslint.config.react.mjs`)
- Added comprehensive `ignore` patterns for non-translatable strings
(CSS values, HTML attributes, technical identifiers)
- Added `ignoreNames` for props that don't need translation (className,
data-*, aria-*, etc.)
- Added `ignoreFunctions` for console methods, URL APIs, and other
non-user-facing functions
- Disabled rule for debug files, storybook, and test files
### Components Fixed (~19 files)
- Object record components (field inputs, pickers, merge dialogs)
- Settings components (accounts, admin panel)
- Serverless function components
- Record table and title cell components
## Status
🚧 **Work in Progress** - ~124 files remaining to fix
This PR is being submitted as draft to allow progressive fixing of
remaining translation issues.
## Testing
- Run `npx eslint "src/**/*.tsx"` in `packages/twenty-front` to check
remaining issues
Resolves [Code Scanning Alert
180](https://github.com/twentyhq/twenty/security/code-scanning/180).
- Normalize unexpected GraphQL errors in convertExceptionToGraphql to a
generic "Internal Server Error" instead of exposing exception.name
directly to clients.
- Only attach stack and response (original error message) in
development, so production responses don’t leak internal class names,
implementation details, or stack traces, while observability is
preserved via `ExceptionHandlerService`/Sentry.
- Keep behavior consistent with `convertHttpExceptionToGraphql`, which
also only exposes detailed response and stack information when `NODE_ENV
=== DEVELOPMENT`.
Title: "feat: Add second, minute & hour resolution options to relative
date Filter action"
---
## Summary
This PR enables support for smaller time units — **Seconds, Minutes, and
Hours** — in the *Relative Date* filter used in workflows, rather than
being limited to days only.
---
## What Changed
This PR extends the relative date filter to include support for the
following units:
✔️ `SECOND`
✔️ `MINUTE`
✔️ `HOUR`
✔️ (Existing: `DAY`, `WEEK`, `MONTH`, etc.)
Changes include:
- Adding `SECOND`, `MINUTE`, and `HOUR` options to the internal relative
date unit enum/constant.
- Updating utility functions and parsers to correctly interpret and
evaluate these new units.
- Enhancing existing tests and adding new tests to cover second, minute,
and hour relative filters.
---
## Testing
New and updated tests include:
- Unit tests for serialization of relative filter values including
seconds, minutes, and hours.
- Workflow filter evaluation tests that verify minute/hour resolution
behaves correctly.
Tests are included in the changeset.
---
## Backward Compatibility
This change is fully backward compatible:
- All existing relative date filters using days or larger units behave
exactly as before.
- Adding finer units does not alter existing stored data or workflow
definitions.
---
## Issue Reference
Fixes: **twentyhq/twenty#15525**
<img width="1909" height="896" alt="image"
src="https://github.com/user-attachments/assets/328d03dc-ca0b-4c3f-84e5-58961c178398"
/>
---------
Co-authored-by: Guillim <guillim@users.noreply.github.com>
Co-authored-by: guillim <guigloo@msn.com>
# Introduction
@bosiraphael has to introduce async validators and feature flag
contextual validator
In this way in this PR we make all entity validators asyncable
Also added an `additionalCacheDataMaps` to the low level args validators
In this PR,
- current basic E2E tests are fixed, and some were added, covering some
basic scenarios
- some tests avec been commented out, until we decide whether they are
worth fixing
The next steps are
- evaluate the flakiness of the tests. Once they've proved not to be
flaky, we should add more tests + re-write the current ones not using
aria-label (cf @lucasbordeau indication).
- We will add them back to the development flow
## Summary
This PR enforces that all custom exceptions must provide a
`userFriendlyMessage`, ensuring end users always see readable error
messages.
## Changes
### Core Changes
- **`CustomException` simplified**: Removed the `ForceFriendlyMessage`
generic parameter - `userFriendlyMessage` is now always required
- **Type safety**: The constructor now requires `{ userFriendlyMessage:
MessageDescriptor }` (no longer optional)
### Updated Files
- **74+ exception classes** updated to provide default user-friendly
messages using Lingui `msg` macro
- Each exception class has a sensible fallback message (e.g., `msg\`An
authentication error occurred.\``)
- Exception classes that had code-specific message maps retain their
behavior
## Benefits
- **Compile-time enforcement**: Forgetting to add a user-friendly
message now causes a TypeScript error
- **Better UX**: End users always see a localized, human-readable error
message
- **Simpler API**: No more boolean generic parameter to think about
## Testing
- `npx nx run twenty-server:typecheck` passes
- `npx nx run twenty-server:lint` passes
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Enforces `userFriendlyMessage` on `CustomException` and updates all
exception classes to supply localized default messages, with
filters/tests adjusted accordingly.
>
> - **Core**:
> - Enforce required `userFriendlyMessage` in `CustomException` (remove
optional generic; constructor now requires `{ userFriendlyMessage:
MessageDescriptor }`).
> - **Exceptions**:
> - Update ~70+ exception classes to set default localized messages via
Lingui `msg` maps and pass them in constructors (e.g., `AuthException`,
`ObjectMetadataException`, `FieldMetadataException`, etc.).
> - Add fallback messages where needed (e.g., `INTERNAL_SERVER_ERROR` or
domain-specific defaults).
> - **HTTP/GraphQL Filters**:
> - Ensure fallbacks create `UnknownException` with `msg` for
user-friendly text in REST/GraphQL exception filters.
> - **Tests**:
> - Adjust unit tests to pass `userFriendlyMessage` to exceptions.
> - Update Jest snapshots to include `extensions.userFriendlyMessage` or
message objects where applicable.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
221004fdfc. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Closes [89](https://github.com/twentyhq/core-team-issues/issues/89)
## Problem
When users attempt to create or update a record with a duplicate value
for a unique field (e.g., duplicate email or domain name), they receive
a generic error message: "This record already exists. Please check your
data and try again." This provides no actionable way to locate and view
the existing conflicting record, forcing users to manually search for
it.
## Solution
This PR enhances duplicate key constraint error handling to
automatically detect the conflicting record and display a "View existing
record" link in the error notification. When clicked, users are
navigated directly to the existing record's detail page.
## Backend Changes
### 1. PostgreSQL Error Parsing
(`parse-postgres-constraint-error.util.ts`)
- Extracts structured information from PostgreSQL `QueryFailedError`
messages.
### 2. Conflicting Record Lookup (`find-conflicting-record.util.ts`)
- Queries the database to find the existing record with the conflicting
value
### 3. Error Handling Orchestration
(`handle-duplicate-key-error.util.ts`)
- Parses PostgreSQL error to extract column name and conflicting value
- Attempts to find the conflicting record
- Enriches `TwentyORMException` with `conflictingRecordId` and
`conflictingObjectNameSingular` if found
### 4. Exception Computation Updates (`compute-twenty-orm-exception.ts`)
- Made function `async` and added optional `entityManager` and
`internalContext` parameters
- Needed to support async database queries for conflicting record lookup
### 5. GraphQL Error Handler
(`twenty-orm-graphql-api-exception-handler.util.ts`)
- **Changes**: Enhanced `DUPLICATE_ENTRY_DETECTED` case to include
`conflictingRecordId` and `conflictingObjectNameSingular` in GraphQL
error extensions
## Frontend Changes
### 1. Error Extraction Utility
(`get-conflicting-record-from-apollo-error.util.ts`)
- Accesses GraphQL error extensions
- Validates that both `conflictingRecordId` and
`conflictingObjectNameSingular` exist and are strings
- Returns `null` if validation fails
### 2. SnackBar Enhancement (`useSnackBar.ts`)
- Extracts conflicting record info from Apollo error
- Constructs URL using `getAppPath` utility
- Adds link object to snackbar options with text "View existing record"
<img width="931" height="858" alt="image"
src="https://github.com/user-attachments/assets/28137dc7-18ab-4ffe-b669-1f2d4ec264d1"
/>
# Introduction
In this pullrequest have been migrated to the flat standard entities:
Role, Agent and RoleTargets.
## What happens
- Removed createStandardMorph tool util in favor of dynamic typing of
`createMorphOrRelationStandardField`
- Implemented a command to remove standard agents and their role that
has been removed in https://github.com/twentyhq/twenty/pull/16513 also
added a default role target to data manipulator role to the only
remaining agent
- Implemented an agent deleteMany service handler
## Description
This PR fixes an issue where the GraphQL schema was being incorrectly
cached and shared across different API keys within the same workspace.
This resulted in the `createdBy` field (Actor) from the first API key's
request being erroneously attributed to subsequent requests made by
different API keys.
## Changes
- Updated the `@graphql-yoga/nestjs` patch to include the request's
`Authorization` header in the schema cache key generation logic.
- This ensures that every unique authentication token (and thus every
unique API key) generates a distinct cache entry, preventing schema
context collisions.
Closes#15093
- moves applicationRoleId to application entity
- add new `APPLICATION` FieldActorSource and `APPLICATION`
JwtTokenTypeEnum value
- create a new token with applicationId when executing a function
- when applicationId is in token, check for application.defaultRole
permissions
-use twenty-shared types in `twenty-sdk/application`
- create a new import from generate called "Twenty" that you can use
directly without having to set TWENTY_API_KEY AND TWENTY_API_URL (keep
metadata or core parameter only)
- provide to serverless unique one time BEARER TOKEN to run it
Result
<img width="977" height="566" alt="image"
src="https://github.com/user-attachments/assets/e78428a0-5b13-4975-aa13-58ee3b32450c"
/>
<img width="910" height="596" alt="image"
src="https://github.com/user-attachments/assets/6ec72bf5-7655-4093-a45e-ad269595a324"
/>
<img width="741" height="568" alt="image"
src="https://github.com/user-attachments/assets/7683944c-fd79-4417-8fb2-8e4815cc112f"
/>
Fixes https://github.com/twentyhq/core-team-issues/issues/1382
Current issue : all step output schemas are computed and stored on
backend side. Which means that, when the database schema is updated -
like a field creation - steps needs to be deleted an recreated. Which is
invisible to users.
Solution : schema generation is moved on frontend side
1. Coming on the page the first time, the schema is populated for all
steps except a few ones that are handled differently (Code, Webhook,
http node, Agent)
2. A separated state allow to determine if a step needs a recomputation.
3. The user only needs a refresh to see the whole schema re-computed
Follow-up:
- check if remaining backend steps could be moved to runtime
computation. But Code will still require storage.
- Clean backend service that is not used anymore
## Summary
- Add code interpreter tool that enables AI to execute Python code for
data analysis, CSV processing, and chart generation
- Support for both local (development) and E2B (sandboxed production)
execution drivers
- Real-time streaming of stdout/stderr and generated files
- Frontend components for displaying code execution results with
expandable sections
## Code Quality Improvements
- Extract `getMimeType` to shared utility to reduce code duplication
between drivers
- Fix security issue: escape single quotes/backslashes in E2B driver env
variable injection
- Add `buildExecutionState` helper to reduce duplicated state object
construction
- Add `DEFAULT_CODE_INTERPRETER_TIMEOUT_MS` constant for consistency
- Fix lingui linting warning and TypeScript theme errors in frontend
## Test Plan
- [ ] Test code interpreter with local driver in development
- [ ] Test code interpreter with E2B driver in production environment
- [ ] Verify streaming output displays correctly in chat UI
- [ ] Verify generated files (charts, CSVs) are uploaded and
downloadable
- [ ] Test file upload flow (CSV, Excel) triggers code interpreter
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Updates generated i18n catalogs for Polish and pseudo-English, adding
strings for code execution/output (code interpreter) and various UI
messages, with minor text adjustments.
>
> - **Localization**:
> - **Generated catalogs**: Refresh `locales/generated/pl-PL.ts` and
`locales/generated/pseudo-en.ts`.
> - Add strings for code execution/output (e.g., code, copy code/output,
running/waiting states, download files, generated files, Python code
execution).
> - Include new UI texts (errors, prompts, menus) and minor text
corrections.
> - No changes to `pt-BR`; other files unchanged functionally.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
befc13d02c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Closes#16557
Tiptap Editor (which the Send Email Node uses) , creates a content json
with type 'hardBreak' for line breaks.
The was no rederer defined for this `hardBreak` node type, so the
`renderNode` function was ignoring that node (returning null).
**Fix :** Added a renderer for `hardBreak` node type.
## Introduction
When migrating a v1 index name to v2 it might collide with an existing
v2 index
In this case we remove both the v1 metadata and pg index
In case of a metadata and pg_index desync this might occur too late in
the process that's why we have two fallback
One computing the v1 deletion from the metadata and another one in the
catch block of the v1 to migration transaction commit