Commit graph

11603 commits

Author SHA1 Message Date
Weiko
3268a86f4b
Skip backfill record page layouts for missing standard objects (#19799) 2026-04-17 13:09:11 +00:00
neo773
68746e22a0
Send Email Tool: Don't persist message on SMTP only connections (#19756)
Previously this blocked users who only had SMTP configured to send
outbound emails, this fixes it by making messageChannel and persist
layer conditional
2026-04-17 12:56:38 +00:00
Charles Bochet
4ed6fcd19e
chore: move TABLE_WIDGET view type migration to 1.23 fast instance command (#19797)
## Summary

- Relocates `AddTableWidgetViewTypeFastInstanceCommand` from `1-22/` to
`1-23/` and bumps its `@RegisteredInstanceCommand` version from `1.22.0`
to `1.23.0`. The original timestamp `1775752190522` is preserved so the
command slots chronologically into the existing 1.23 sequence;
auto-discovered via `@RegisteredInstanceCommand`, no module wiring
change needed.
- Same pattern as #19792 (move
`pageLayoutWidget.conditionalAvailabilityExpression` to 1.23).
2026-04-17 12:44:35 +00:00
Weiko
e70269b9d3
Fix: aggregate Calculate not updating in dashboard Table widgets (#19796)
## Context
Picking an aggregate option (Count, Sum, Percentage Not Empty, etc.) in
a dashboard Table widget footer did nothing visually — the value never
appeared or updated

## Fix
RecordTableWidget was missing RecordIndexTableContainerEffect, which
reactively syncs currentView.viewFields[].aggregateOperation from the
Apollo cache into the viewFieldAggregateOperationState jotai atom that
the footer reads

<img width="612" height="261" alt="Screenshot 2026-04-17 at 14 17 47"
src="https://github.com/user-attachments/assets/b4409b0e-82a6-4614-bc09-653be738134a"
/>
2026-04-17 12:34:11 +00:00
github-actions[bot]
f94ee2d495
i18n - translations (#19795)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-04-17 14:15:46 +02:00
Paul Rastoin
bb464b2ffb
Forbid other app role extension (#19783)
# Introduction
Even though this would not possible through API at the moment, from
neither API metadata or manifest ( as manifest `permissionsFlag`
declarations etc are done from within a declared role )
Prevent any app to create permissions entities over another app role
from the validation engine itself

## `isEditable`
We might wanna deprecate this column at some point from the entity it
self as now the grain would rather be `what app owns that role ?`

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-04-17 12:00:37 +00:00
Abdul Rahman
a93f23a150
Fix AI chat dropzone persisting when dragging file out without dropping (#19794)
Issue link:
https://discord.com/channels/1130383047699738754/1494248499351519232

### Before


https://github.com/user-attachments/assets/ec4ba8cc-b9e7-4b77-8b8f-b254e11edb19



### After



https://github.com/user-attachments/assets/2905536b-6a31-41e8-9f25-8768fd224b6a
2026-04-17 11:43:02 +00:00
Charles Bochet
beeb8b7406
chore: move pageLayoutWidget.conditionalAvailabilityExpression migration to 1.23 fast instance command (#19792)
## Summary

- Replaces the standalone TypeORM migration
`1775654781000-addConditionalAvailabilityExpressionToPageLayoutWidget.ts`
with a registered fast instance command under
`packages/twenty-server/src/database/commands/upgrade-version-command/1-23/`,
so the `pageLayoutWidget.conditionalAvailabilityExpression` column is
created through the unified upgrade pipeline.
- Uses `ADD COLUMN IF NOT EXISTS` / `DROP COLUMN IF EXISTS` so the new
instance command is a safe no-op for environments that already applied
the previous TypeORM migration.
- Keeps the original timestamp `1775654781000` so the command slots
chronologically into the existing 1.23 sequence; auto-discovered via
`@RegisteredInstanceCommand`, no module wiring needed.

## Context

Reported error when creating a new workspace on `main`:

> column PageLayoutWidgetEntity.conditionalAvailabilityExpression does
not exist

Aligns this column addition with the rest of the 1.23 schema changes
that already use the instance-command pattern.
2026-04-17 11:40:05 +00:00
Weiko
5cd8b7899d
shouldIncludeRecordPageLayouts deprecation (#19774)
## Context
Deprecating shouldIncludeRecordPageLayouts in preparation for page
layout release.

See new workspace with standard page layout from standard app
<img width="570" height="682" alt="Screenshot 2026-04-16 at 18 35 23"
src="https://github.com/user-attachments/assets/bf7fa621-d40d-4c29-8d96-537c58b3eb40"
/>
2026-04-17 11:32:10 +00:00
Charles Bochet
76ea0f37ed
Surface structured validation errors during application install (#19787)
## Summary
- Add `WorkspaceMigrationGraphqlApiExceptionInterceptor` to
`MarketplaceResolver` and `ApplicationInstallResolver` so validation
failures during app install return `METADATA_VALIDATION_FAILED` with
structured `extensions.errors` instead of generic
`INTERNAL_SERVER_ERROR`
- Update SDK `installTarballApp()` to pass the full GraphQL error object
(including extensions) through the install flow
- Add `formatInstallValidationErrors` utility to format structured
validation errors for CLI output
- Add integration test verifying structured error responses for invalid
navigation menu items and view fields

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 11:29:33 +00:00
Weiko
7aa60fd20b Revert "Fix"
This reverts commit 70603a1af6.
2026-04-17 13:15:57 +02:00
Weiko
70603a1af6 Fix 2026-04-17 13:15:04 +02:00
Marie
b31f84fbb8
fix(server): workspace member permissions and profile onboarding (#19786)
Some checks are pending
CD deploy main / deploy-main (push) Waiting to run
CI Create App E2E minimal / changed-files-check (push) Waiting to run
CI Create App E2E minimal / create-app-e2e-minimal (push) Blocked by required conditions
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Blocked by required conditions
CI Create App / changed-files-check (push) Waiting to run
CI Create App / create-app-test (lint) (push) Blocked by required conditions
CI Create App / create-app-test (test) (push) Blocked by required conditions
CI Create App / create-app-test (typecheck) (push) Blocked by required conditions
CI Create App / ci-create-app-status-check (push) Blocked by required conditions
CI Docs / changed-files-check (push) Waiting to run
CI Docs / docs-lint (push) Blocked by required conditions
CI Emails / changed-files-check (push) Waiting to run
CI Emails / emails-test (push) Blocked by required conditions
CI Emails / ci-emails-status-check (push) Blocked by required conditions
CI Example App Hello World / changed-files-check (push) Waiting to run
CI Example App Hello World / example-app-hello-world (push) Blocked by required conditions
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Blocked by required conditions
CI Example App Postcard / ci-example-app-postcard-status-check (push) Blocked by required conditions
CI Example App Postcard / changed-files-check (push) Waiting to run
CI Example App Postcard / example-app-postcard (push) Blocked by required conditions
Push translations to Crowdin / Extract and upload translations (push) Waiting to run
## Summary

Aligns **workspace member** editing and **onboarding** with how the
product is actually used: profile and other “settings” fields go through
**`updateWorkspaceMemberSettings`**, while **`/graphql`** record APIs
follow **object-level** permissions for the `workspaceMember` object.

## Product behaviour

### Completing “Create profile” onboarding

Users who must create a profile (empty name at sign-up) get
`ONBOARDING_CREATE_PROFILE_PENDING` set. The onboarding UI saves the
name with **`updateWorkspaceMemberSettings`**, not with a workspace
record **`updateOne`**.

**Before:** The server only cleared the pending flag on
**`workspaceMember.updateOne`**, so the flag could stay set and
onboarding appeared stuck.

**After:** Clearing the profile step runs when
**`updateWorkspaceMemberSettings`** persists an update that includes a
**name** (same rules as before: non-empty name parts). Onboarding can
advance normally after **Continue** on Create profile.

### Two ways to change workspace member data

| Path | Typical use | Who can change what |
|------|----------------|---------------------|
| **`updateWorkspaceMemberSettings`** (metadata API) | Standard member
fields the app treats as “my profile / preferences” (name,
avatar-related settings, locale, time zone, etc.) | **Always** your
**own** workspace member. Changing **another** member still requires
**Workspace members** in role settings (`WORKSPACE_MEMBERS`). Custom
fields are **not** allowed on this endpoint (unchanged). |
| **`/graphql`** record mutations on **`workspaceMember`** | Custom
fields, integrations, anything that goes through the generic record API
| **`WorkspaceMember`** is special-cased in permissions: **read** stays
**on** for everyone, but **update / create / delete** require
**`WORKSPACE_MEMBERS`**, including updating **your own** row via
`/graphql`. So a **Member** without that permission cannot fix their
name through **`updateWorkspaceMember`**; they use **Settings** /
**`updateWorkspaceMemberSettings`** instead. |

This matches **`WorkspaceRolesPermissionsCacheService`**: for the
workspace member object, `canReadObjectRecords` is always true;
`canUpdateObjectRecords` (and delete-related flags) follow
**`WORKSPACE_MEMBERS`**.

### Hooks and delete side-effects

- Removed **`workspaceMember.updateOne`** pre-query hook and
**`WorkspaceMemberPreQueryHookService`**: they duplicated the same rules
the permission cache already enforces for `/graphql`.
- **`WorkspaceMember.deleteOne`** pre-hook still tells users to remove
members via the dedicated flow; the post-hook only runs the
**`deleteUserWorkspace`** side-effect when a member row is actually
removed—**no** extra settings-permission check there, since only callers
that already passed **object** delete permission can remove the row.

## Tests

- **`workspace-members.integration-spec.ts`**: clarifies and extends
coverage so **`/graphql`** **`updateOne`** is denied for **own** record
on a **standard** name field and on a **custom** field when the role
lacks **`WORKSPACE_MEMBERS`**.

## Implementation notes

- **`OnboardingService.completeOnboardingProfileStepIfNameProvided`**
centralises the “clear profile pending if name present” logic;
**`UserResolver.updateWorkspaceMemberSettings`** calls it after save,
using the typed update payload’s **`name`** (no cast).
- **`UserWorkspaceService.updateUserWorkspaceLocaleForUserWorkspace`**:
drops a redundant **`coreEntityCacheService.invalidate`**;
**`updateWorkspaceMemberSettings`** still invalidates the user-workspace
cache after the mutation.
2026-04-17 09:58:34 +00:00
github-actions[bot]
ba1195d92e
i18n - translations (#19784)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-04-17 09:34:28 +02:00
Abdul Rahman
fcba0ca30a
Add search to add column dropdown (#19763)
https://github.com/user-attachments/assets/4a64fff0-6495-4651-934b-43f4ad0dc966
2026-04-17 07:19:07 +00:00
github-actions[bot]
d7453303b8
chore: sync AI model catalog from models.dev (#19782)
Automated daily sync of `ai-providers.json` from
[models.dev](https://models.dev).

This PR updates pricing, context windows, and model availability based
on the latest data.
New models meeting inclusion criteria (tool calling, pricing data,
context limits) are added automatically.
Deprecated models are detected based on cost-efficiency within the same
model family.

**Please review before merging** — verify no critical models were
incorrectly deprecated.

Co-authored-by: FelixMalfait <6399865+FelixMalfait@users.noreply.github.com>
2026-04-17 08:32:09 +02:00
Paul Rastoin
75235f4621
Validate universalIdentifier uniqueness among application and its dependencies (#19767)
# Introduction
Gracefully validating that when creating an entity its
`universalIdentifier` is available within the all application metadata
maps context ( current app + twenty standard, currently the only managed
dependencies )
2026-04-16 16:59:12 +00:00
Paul Rastoin
bf410ae438
upgrade:status command (#19584)
## Introduction
Introducing a new command in order to determine the curent twenty
instance and workspaces status as it's not stored in database but a
derivation of each current curors


## `upgrade:status` all healthy
<img width="1376" height="1202" alt="image"
src="https://github.com/user-attachments/assets/e90d6987-07d2-4b6b-b573-105249aca325"
/>

## `upgrade:status` Nearly use cases
<img width="1442" height="1304" alt="image"
src="https://github.com/user-attachments/assets/c336cb9d-eb9d-4c7d-9392-ec1ef54a7326"
/>

## `upgrade:status --failed-only`
<img width="1442" height="940" alt="image"
src="https://github.com/user-attachments/assets/93a3dfdb-0d2f-4a01-b185-118e5cf0a078"
/>

## `upgrade:status -w aa8fdcb1-8ee1-4012-98af-44a97caa7411 -w
20202020-1c25-4d02-bf25-6aeccf7ea419 -w
20202020-1c25-4d02-bf25-6aeccf7ea412`
<img width="1486" height="928" alt="image"
src="https://github.com/user-attachments/assets/ec1b1abc-46e8-4e36-9799-ab3a4b85e410"
/>
2026-04-16 16:05:02 +00:00
Etienne
aecbc89a3f
Fix slow db query issue (#19770)
https://github.com/twentyhq/twenty/pull/19586#discussion_r3074136617
2026-04-16 15:37:58 +00:00
Thomas Trompette
446a3923f2
Add workspace id in job logs (#19764)
Cannot currently investigate spikes
2026-04-16 15:28:37 +00:00
Félix Malfait
4f4f723ed0
Fix MCP discovery: path-aware well-known URL and protocol version (#19766)
## Summary

Adding `https://api.twenty.com/mcp` as an MCP server in Claude fails
with `Couldn't reach the MCP server` before OAuth can start. Two
independent bugs cause this:

1. **Missing path-aware well-known route.** The latest MCP spec
instructs clients to probe `/.well-known/oauth-protected-resource/mcp`
before `/.well-known/oauth-protected-resource`. Only the root path was
registered, so the path-aware request fell through to
`ServeStaticModule` and returned the SPA's `index.html` with HTTP 200.
Strict clients (Claude.ai) tried to parse it as JSON and gave up. Fixed
by registering both paths on the same handler.
2. **Stale protocol version.** Server advertised `2024-11-05`, which
predates Streamable HTTP. We've implemented Streamable HTTP (SSE
response format was added in #19528), so bumped to `2025-06-18`.

Reproduction before the fix:

```
$ curl -s -o /dev/null -w "%{http_code} %{content_type}\n" https://api.twenty.com/.well-known/oauth-protected-resource/mcp
200 text/html; charset=UTF-8
```

After the fix this returns `application/json` with the RFC 9728 metadata
document.

Note: this is separate from #19755 (host-aware resource URL for
multi-host deployments).

## Test plan

- [x] `npx jest oauth-discovery.controller` — 2/2 tests pass, including
one asserting both routes are registered
- [x] `npx nx lint:diff-with-main twenty-server` passes
- [ ] After deploy, `curl
https://api.twenty.com/.well-known/oauth-protected-resource/mcp` returns
JSON (not HTML)
- [ ] Adding `https://api.twenty.com/mcp` in Claude reaches the OAuth
authorization screen

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 17:32:45 +02:00
Charles Bochet
4103efcb84
fix: replace slow deep-equal with fastDeepEqual to resolve CPU bottleneck (#19771)
## Summary

- Replaced the `deep-equal` npm package with the existing
`fastDeepEqual` from `twenty-shared/utils` across 5 files in the server
and shared packages
- `deep-equal` was causing severe CPU overhead in the record update hot
path (`executeMany` → `formatTwentyOrmEventToDatabaseBatchEvent` →
`objectRecordChangedValues` → `deepEqual`, called **per field per
record**)
- `fastDeepEqual` is ~100x faster for plain JSON database records since
it skips unnecessary prototype chain inspection and edge-case handling
- Removed the now-unnecessary `LARGE_JSON_FIELDS` branching in
`objectRecordChangedValues` since all fields now use the fast
implementation
2026-04-16 17:23:50 +02:00
Félix Malfait
d3df58046c
chore(server): drop api-host branch in OAuth discovery (#19768)
## Summary

Follow-up to #19755. Simplifies `OAuthDiscoveryController` by dropping
the `authorization_endpoint → frontend base URL` branch that was there
to make `api.twenty.com/mcp` paste-able in MCP clients.

We've decided not to support pasting `api.twenty.com/mcp` — users can
paste `app.twenty.com/mcp`, `<workspace>.twenty.com/mcp`, or a custom
domain, all of which serve both frontend and API. On those hosts,
`authorization_endpoint` was already pointed at the same host as
`issuer`, which is what we want.

## Change

- Remove `isApiHost` helper and the `authorizeBase` branch — use
`issuer` for `authorization_endpoint`.
- Drop now-unused `TwentyConfigService` and `DomainServerConfigService`
injections.
- Drop duplicate `DomainServerConfigModule` import from
`application-oauth.module.ts` (the module is no longer needed).

Net diff: +1 / -22 across 2 files.

## Breaking change

MCP clients configured with `https://api.twenty.com/mcp` will stop
working. They should be reconfigured with the host matching the
workspace they're connecting to (`<workspace>.twenty.com/mcp`,
`app.twenty.com/mcp`, or a custom domain).

## Test plan

- [x] `yarn jest --testPathPatterns="mcp-auth.guard"` → 2/2 passing
(unchanged)
- [x] `tsc --noEmit` clean on modified files
- [ ] Manual verification on staging: `app.twenty.com/mcp` and
`<workspace>.twenty.com/mcp` OAuth flow still works end-to-end

Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 17:01:55 +02:00
Félix Malfait
cb6953abe3
fix(server): make OAuth discovery and MCP auth metadata host-aware (#19755)
## Summary

OAuth discovery metadata (RFC 9728 protected-resource, RFC 8414
authorization-server) and the MCP `WWW-Authenticate` header were
hardcoded to `SERVER_URL`. This breaks MCP clients that paste any URL
other than `api.twenty.com/mcp` — the metadata declares `resource:
https://api.twenty.com/mcp`, which doesn't match the URL the client
connected to, so the client rejects it and the OAuth flow never starts.

Reproduced with Claude's MCP integration: pasting
`<workspace>.twenty.com/mcp`, `app.twenty.com/mcp`, or a custom domain
returned *"Couldn't reach the MCP server"* because discovery returned a
resource URL for a different host.

Related memory: MCP clients POST to the URL the user entered, not the
discovered resource URL — so every paste-able hostname has to advertise
`resource` for that same hostname.

## What the server now does

`WorkspaceDomainsService.getValidatedRequestBaseUrl(req)` resolves the
canonical base URL for the host the request came in on, validated
against the set of hosts we actually serve:

- `SERVER_URL` (e.g. `api.twenty.com`) — API host
- default base URL (e.g. `app.twenty.com`) — the `DEFAULT_SUBDOMAIN`
base
- `FRONTEND_URL` bare host
- any `<workspace>.twenty.com` subdomain (DB lookup)
- any workspace `customDomain` where `isCustomDomainEnabled = true`
- any registered `publicDomain`

An unrecognized / spoofed Host falls back to
`DomainServerConfigService.getBaseUrl()`. **We never reflect arbitrary
Host values into the response.**

Callers updated:

- `OAuthDiscoveryController.getProtectedResourceMetadata` — echoes the
validated host into `resource` and `authorization_servers`.
- `OAuthDiscoveryController.getAuthorizationServerMetadata` — uses the
validated host for `issuer` and `*_endpoint`, **except**
`authorization_endpoint`: when the request came in via `SERVER_URL`
(API-only, no `/authorize` route), we keep that one pointed at the
default frontend base URL.
- `McpAuthGuard` — sets `WWW-Authenticate: Bearer
resource_metadata=\"<validatedBase>/.well-known/oauth-protected-resource\"`
on 401s, so the MCP client's follow-up discovery fetch lands on the same
host it started on.

## Security

- Workspace identity is already bound to the JWT via per-workspace
signing secrets (`jwtWrapperService.generateAppSecret(tokenType,
workspaceId)`). Host-aware discovery does not weaken that.
- Custom domains are only accepted once `isCustomDomainEnabled = true`
(i.e. after DNS verification), so an attacker can't register a
custom-domain mapping on a workspace and have discovery reflect it
before it's been proven.
- Unknown / spoofed Hosts fall through to the default base URL.

## Drive-by

Fixed a duplicate `DomainServerConfigModule` import in
`application-oauth.module.ts` while adding `WorkspaceDomainsModule`.

## Companion infra change required for custom domains

Customer custom domains (`crm.acme.com/mcp`) also require an
ingress-level fix to exclude `/mcp`, `/oauth`, and `/.well-known` from
the `/s\$uri` rewrite applied when `X-Twenty-Public-Domain: true`.
Shipping that in a twenty-infra PR (will cross-link here).

## Test plan

- [x] 14 new tests in
`WorkspaceDomainsService.getValidatedRequestBaseUrl` covering: missing
Host, SERVER_URL, base URL, FRONTEND_URL, workspace subdomain, unknown
subdomain fallback, enabled custom domain, disabled custom domain,
public domain, completely unrecognized host, lowercase coercion,
malformed Host, single-workspace mode fallback, DB throwing → fallback
- [x] New `oauth-discovery.controller.spec.ts` covering both endpoints
across api / app / workspace-subdomain / custom-domain hosts, plus
`cli_client_id` propagation
- [x] Rewrote `mcp-auth.guard.spec.ts` to cover `WWW-Authenticate` for
all four host types (api, workspace subdomain, custom domain, spoofed
fallback)
- [x] `yarn jest
--testPathPatterns=\"workspace-domains.service|oauth-discovery.controller|mcp-auth.guard\"`
→ 41/41 passing
- [x] `tsc --noEmit` clean on all modified files
- [ ] Manual verification against staging: connect Claude to
`api.twenty.com/mcp`, `app.twenty.com/mcp`,
`<workspace>.twenty.com/mcp`, and a custom domain and confirm OAuth flow
completes on each

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 16:47:42 +02:00
github-actions[bot]
9bc803d0c7
i18n - translations (#19765)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-04-16 16:17:27 +02:00
Thomas Trompette
cd31d9e0de
Add test tab to tool step (#19760)
So we can use those as variables.
When the function input is updated, invalidate the input using an
effect.

<img width="770" height="635" alt="Capture d’écran 2026-04-16 à 14 45
41"
src="https://github.com/user-attachments/assets/cbf0b3e4-baad-424d-8f08-06eb1028abdb"
/>
2026-04-16 14:00:21 +00:00
github-actions[bot]
c0fef0be08
i18n - translations (#19762)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-04-16 15:14:16 +02:00
Abdul Rahman
270069c3e3
Add search to Fields dropdown (#19750)
Issue link:
https://discord.com/channels/1130383047699738754/1489198502998315100


https://github.com/user-attachments/assets/7d0a859e-c33e-4f9e-bdb3-5867fc0dd80f
2026-04-16 12:59:01 +00:00
Paul Rastoin
2413af0ba9
[run-instance-commands] Preserve fast slow sequentiality (#19757)
# Introduction
The command was wrongly running all fast and then all slow ignoring
instance commands segment
Leading to 
```ts
1.23.0_AddGlobalObjectContextToCommandMenuItemAvailabilityTypeFastInstanceCommand_1776090711153 executed successfully
[Nest] 32679  - 04/16/2026, 1:21:07 PM     LOG [InstanceCommandRunnerService] 1.23.0_DropWorkspaceVersionColumnFastInstanceCommand_1785000000000 executed successfully
[Nest] 32679  - 04/16/2026, 1:21:07 PM     LOG [InstanceCommandRunnerService] 1.22.0_BackfillWorkspaceIdOnIndirectEntitiesSlowInstanceCommand_1775758621018 executed successfully
[Nest] 32679  - 04/16/2026, 1:21:07 PM     LOG [RunInstanceCommandsCommand] Instance commands completed
```

No prod/self host impact as 1.23 hasn't been released yet
2026-04-16 12:14:47 +00:00
Raphaël Bosi
2b5b8a8b13
Link command menu items to specific page layout (#19706)
- Add a `pageLayoutId` foreign key to `CommandMenuItem`, allowing
command menu items to be scoped to a specific page layout instead of
being globally available
- Filter command menu items by the current page layout on the frontend.
Items with a `pageLayoutId` only appear when viewing that layout, while
items without one remain globally visible
- Create an effect to track the current page layout ID
- Include a seed example: a "Show Notification" command pinned to the
Star history standalone page layout

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-04-16 12:07:36 +00:00
github-actions[bot]
7af82fb6a4
i18n - translations (#19758)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-04-16 13:48:47 +02:00
Félix Malfait
9beb1ca326
Support Select All for workflow manual triggers (#19734)
## Summary
- Move record fetching and payload building from the enrichment hook
into `TriggerWorkflowVersionEngineCommand`, following the same
component-based pattern as `DeleteRecordsCommand` and
`RestoreRecordsCommand`
- The enrichment hook now only stores workflow metadata (`trigger`,
`availabilityType`, `availabilityObjectMetadataId`); the component uses
`useLazyFetchAllRecords` for exclusion mode (Select All) with full
pagination
- `buildTriggerWorkflowVersionPayloads` is now a pure function accepting
`selectedRecords: ObjectRecord[]` instead of reading from the Jotai
store

Fixes the issue introduced by #19718 which blocked Select All with a
warning toast instead of implementing it.

## Test plan
- [ ] Select individual records → run workflow trigger from command menu
→ works as before
- [ ] Click Select All → run workflow trigger from command menu →
fetches all matching records and runs the workflow
- [ ] Select All with some records deselected → correctly excludes those
records
- [ ] Global workflows (no object context) → run without payload as
before
- [ ] Bulk record triggers → payload wraps records in `{namePlural:
[records]}`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 11:33:03 +00:00
Lazare Rossillon
d54092b0e2
fix: add missing LayoutRenderingProvider in SettingsApplicationCustomTab (#19679)
## Summary

- `SettingsApplicationCustomTab` renders `FrontComponentRenderer` which
calls `useFrontComponentExecutionContext` →
`useLayoutRenderingContext()`, but the settings page never provided a
`LayoutRenderingProvider`
- Every other render site (side panel, command menu, record pages) wraps
`FrontComponentRenderer` with this provider — it was just missed here
- Opening the "Custom" tab in Settings → Applications crashes with:
`LayoutRenderingContext Context not found`
- Fix: wrap with `LayoutRenderingProvider` using `DASHBOARD` layout type
and no target record, matching the pattern used in
`SidePanelFrontComponentPage`

## Test plan

- [ ] Open Settings → Applications → any app with a custom settings tab
- [ ] Click the "Custom" tab — should render the front component without
crashing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2026-04-16 10:01:28 +00:00
github-actions[bot]
60701c2cf9
i18n - translations (#19754)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-04-16 12:03:48 +02:00
martmull
381f3ba7d9
Fix app design 1/2 (#19735)
comply with
https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=96977-349627&m=dev

## After
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 40
37"
src="https://github.com/user-attachments/assets/6d80191a-79a9-4f0f-aa4f-0e447fff4f6d"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 40
22"
src="https://github.com/user-attachments/assets/4f763272-027e-4246-b455-7d46babf7d8c"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 39
11"
src="https://github.com/user-attachments/assets/b9b35e18-8068-447e-821d-5ec28bb5bd16"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 39
05"
src="https://github.com/user-attachments/assets/57d9318a-902f-4fd7-a2a3-5795ebe0b9dc"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 39
02"
src="https://github.com/user-attachments/assets/78a33fa8-6bdd-484e-a82d-bd0f7592a623"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 38
58"
src="https://github.com/user-attachments/assets/f7987aed-c6e1-4032-a611-86817655137d"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 38
55"
src="https://github.com/user-attachments/assets/d1c451ab-1d2d-41e4-a059-cf4303ecabe7"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 38
48"
src="https://github.com/user-attachments/assets/593cae36-2320-443f-a955-93b211a6ee3f"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 37
40"
src="https://github.com/user-attachments/assets/c9f602b1-8de3-4e82-a3a6-344594a0c153"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 37
34"
src="https://github.com/user-attachments/assets/b54ddddf-5dda-46c8-ace3-cffe6015825a"
/>

## before

<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 42
18"
src="https://github.com/user-attachments/assets/c0976a0a-0124-48ec-8e7c-78627cea7063"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 42
16"
src="https://github.com/user-attachments/assets/d2db926c-4040-411d-9091-8b60e7c519e6"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 42
13"
src="https://github.com/user-attachments/assets/2d69f2ff-f26e-4249-91a3-2cf3d261e840"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 42
07"
src="https://github.com/user-attachments/assets/1028aabc-77ac-4c51-a8c3-9a194faba87f"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 42
01"
src="https://github.com/user-attachments/assets/1caa9f5e-3eaa-433c-9d3b-e0f094f16e8e"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 41
56"
src="https://github.com/user-attachments/assets/f42b6976-3a8f-4591-9283-bda79bdb424b"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 41
53"
src="https://github.com/user-attachments/assets/93d00df8-0091-4dfa-9ac0-f6f376be5962"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 41
43"
src="https://github.com/user-attachments/assets/9deae7e5-39c1-4518-a463-6d79bc5bf132"
/>
<img width="1512" height="909" alt="Capture d’écran 2026-04-16 à 09 41
37"
src="https://github.com/user-attachments/assets/3e21b521-c47d-482c-ad41-66abfe973772"
/>
2026-04-16 09:47:44 +00:00
neo773
64470baa1e
fix compute folders to update util (#19749)
This regressed with the migration work

Type checker should've caught it, but didn't because of `Partial`
changed it to `Pick` instead to avoid future cases

/closes https://github.com/twentyhq/twenty/issues/19745
2026-04-16 09:09:15 +00:00
Paul Rastoin
f5e8c05267
Add logs before and after instance slow data migration (#19753)
As it can take sometime, would result in not seeing any logs until
2026-04-16 09:08:37 +00:00
Raphaël Bosi
48c540eb6f
Add event forwarding stories to the front component renderer (#19721)
Add Storybook stories and example components to test event forwarding
through the front component renderer: form events (text input, checkbox,
focus/blur, submit), keyboard events (key/code/modifiers), and host API
calls (navigate, snackbar, progress, close panel)
2026-04-16 08:55:49 +00:00
Paul Rastoin
5583254f58
Remove workspace-migrations deadcode (#19752)
Was a previous twenty version upgrade command util
2026-04-16 08:52:58 +00:00
Abdullah.
97832af778
[Website] Resolve animations breaking due to renderer context being lost. (#19747)
Resolves the following two issues. The reason we had these issues was
because the maxLimit of renderers was reached and the browser started
losing context before restoring it. Now, we make sure the context that
is lost belongs to visuals that are not currently in the viewport and
when those visuals come back into viewport, they're loaded again.

https://github.com/twentyhq/core-team-issues/issues/2372

https://github.com/twentyhq/core-team-issues/issues/2373
2026-04-16 08:50:56 +00:00
Abdul Rahman
7ce3e2b065
Fix spacing between workspace domain cards (#19742)
https://discord.com/channels/1130383047699738754/1492113564138344468
2026-04-16 08:43:54 +00:00
Abdul Rahman
a49d8386aa
Fix calendar event "Not shared" content alignment and background (#19743)
Issue link:
https://discord.com/channels/1130383047699738754/1491712714848866424

<img width="1287" height="419" alt="Screenshot 2026-04-16 at 9 50 34 AM"
src="https://github.com/user-attachments/assets/ba2143df-7957-4aee-8994-80c0392b6086"
/>
2026-04-16 08:38:09 +00:00
Charles Bochet
e420ee8746
Release v1.22.0 for twenty-sdk, twenty-client-sdk, and create-twenty-app (#19751)
## Summary
- Bump `twenty-sdk` from `1.22.0-canary.6` to `1.22.0`
- Bump `twenty-client-sdk` from `1.22.0-canary.6` to `1.22.0`
- Bump `create-twenty-app` from `1.22.0-canary.6` to `1.22.0`
2026-04-16 08:29:50 +00:00
Thomas Trompette
7adab8884b
Add isUnique update in query + invalidate cache on rollback (#19746)
Fix isUnique not sent in field update mutation: Added isUnique and
settings to the Pick type in useUpdateOneFieldMetadataItem, which
previously silently dropped these properties from the GraphQL payload.

Invalidate cache on failed migration rollback: Added invalidateCache()
call in the migration runner's catch block so that a failed migration
(e.g., unique index creation failing due to duplicate data) regenerates
the Redis hash, preventing stale metadata from persisting in frontend
localStorage indefinitely. Was
2026-04-16 07:44:59 +00:00
Thomas des Francs
6101a4f113
Update website hero to use shared local avatars and logos (#19736)
## Summary
- import shared company logos and people avatars into
`packages/twenty-website-new/public/images/shared`
- expand the shared asset registry with the new local logo and avatar
paths
- update the home hero data to use local shared assets for matched
people and company icons
- replace synthetic or mismatched hero avatars with better-fitting named
or anonymous local assets
- prefer local shared company logos in hero visual components before
falling back to remote icons

## Testing
- Not run (not requested)

---------

Co-authored-by: Abdullah <125115953+mabdullahabaid@users.noreply.github.com>
2026-04-16 06:58:48 +00:00
Thomas des Francs
da8fe7f6f7
Homepage 3 cards finished (#19732)
& various fixes
2026-04-16 06:42:25 +00:00
github-actions[bot]
cddc47b61f
i18n - translations (#19731)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-04-15 19:08:24 +02:00
Charles Bochet
446c39d1c0
Fix silent failures in logic function route trigger execution (#19698)
## Summary

Route-triggered logic functions were returning empty 500 responses with
zero server-side logging when the Lambda build chain failed. This PR
makes those failures observable and returns meaningful HTTP responses to
API clients.

- **Observability** — Log errors (with stack traces) at each layer of
the execution chain: `LambdaDriver` (deps-layer fetch, SDK-layer fetch,
invocation), `LogicFunctionExecutorService`, and `RouteTriggerService`.
- **Typed exceptions** — Replace raw `throw error` sites with
`LogicFunctionException` carrying an appropriate code and
`userFriendlyMessage` (new codes: `LOGIC_FUNCTION_EXECUTION_FAILED`,
`LOGIC_FUNCTION_LAYER_BUILD_FAILED`).
- **Correct HTTP semantics** — `RouteTriggerService` maps inner
exception codes to the right `RouteTriggerExceptionCode` so
`LOGIC_FUNCTION_NOT_FOUND` returns 404 and `RATE_LIMIT_EXCEEDED` returns
429 (new code + filter case) instead of a generic 500.
- **User-facing messages** — Forward the inner
`CustomException.userFriendlyMessage` when wrapping into
`RouteTriggerException`, without leaking raw internal error text into
the public exception message.
- **Infra** — Bump Lambda ephemeral storage from 2048 to 4096 MB to
prevent `ENOSPC` errors during yarn install layer builds (root cause of
the original silent failures).
2026-04-15 16:49:43 +00:00
Abdul Rahman
5eda10760c
Fix navbar folder opening lag by deferring navigation until expand animation completes (#19686)
### Before


https://github.com/user-attachments/assets/7d57aa55-8d4c-43e1-8835-f40206e5e453



### After


https://github.com/user-attachments/assets/2457c8ee-fcb9-41ae-a8f7-08f8c7b4a233

---------

Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
2026-04-15 16:31:33 +00:00
Aryan Ghugare
b9605a3003
Refactor SnackBar duration handling and progress bar visibility logic (#19712)
## Summary

Fixes error snackbars disappearing too quickly by **disabling
auto-dismiss for error variants by default**. Error snackbars now remain
visible until the user closes them.

Closes #19694

## What changed

- Error snackbars no longer default to a 6s timeout (they only
auto-dismiss if an explicit `duration` is provided).
- Non-error snackbars keep the existing default auto-dismiss behavior
(6s).
- Progress bar animation/visibility is tied to auto-dismiss (no progress
bar when there’s no duration).

**Files**
-
packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx

## How to test

1. Trigger a long error snackbar (example: spreadsheet/CSV import error
with a long message).
2. Confirm the snackbar **stays visible** until clicking **Close**.
3. Trigger a success/info snackbar and confirm it **still
auto-dismisses** after ~6s.

## Notes

- Call sites that explicitly pass `options.duration` for error snackbars
will continue to auto-dismiss (intentional).

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-04-15 16:22:28 +00:00