## Problem
On workspaces with a sizeable AI chat history, the whole app was
freezing during navigation, including Settings (one navigation click
measured ~6.5s).
## Root cause
`AgentChatProvider` is mounted app-wide in `AppRouterProviders`, so its
effects run on every page.
On every render it would:
1. auto-select the most recently active thread
(`AgentChatThreadInitializationEffect`),
2. fetch that thread's **full message history**
(`AgentChatMessagesFetchEffect`),
3. run `AgentChatStreamingPartsDiffSyncEffect` →
`updateStreamingPartsWithDiff`, which loops over every message doing
`isDeeplyEqual(existing, incoming)` + `structuredClone`.
A large thread would produce multi-second freeze on every interaction,
app-wide. (Confirmed via a Chrome CPU profile)
## Fix
Don't run the agent-chat **message runtime** until the chat is actually
opened.
## Note
There is still room for improvement, opening AI chats would still be
very slow.
## Problem
Syncing a `twenty-sdk` app against a server (`twenty dev`) **crashes the
server process**. The metadata migration completes, then the server-side
`GqlTypeGenerator` regenerates typed clients via the vendored genql
codegen in `twenty-client-sdk`, which throws and exits node:
```
ConfigError: Couldn't find plugin for AST format "estree".
Plugins must be explicitly added to the standalone bundle.
at .../packages/twenty-client-sdk/dist/generate.cjs
Node.js v24.5.0 ← process exits
```
The CLI sees `ECONNRESET`; the app row still persists because the crash
happens after the metadata commit. Any app sync takes the server down.
## Root cause
The genql codegen formatter
[`prettify.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-client-sdk/src/generate/genql/helpers/prettify.ts)
was vendored (in #21339) targeting **prettier 2.8**: a synchronous
`format()` and `prettier/parser-typescript`, which in 2.8 bundles the
estree printer. `package.json` pins `prettier ^2.8.8`, but the monorepo
actually resolves/bundles **prettier 3.8.3** (`^2.8.8` is silently
unsatisfied — no 2.8.x is nested for this package, and the subpath
imports are bundled from the hoisted 3.8.3). Under prettier 3 the estree
printer must be added explicitly (`prettier/plugins/estree`) and
`format()` is async — so the codegen throws.
`prettier/parser-typescript` / `parser-graphql` don't even exist in
prettier 3 (only `prettier/plugins/*`), so the declared `^2.8.8` was
already inconsistent with what runs.
## Fix
- `prettify`: switch to the prettier-3 entrypoints
`prettier/plugins/{graphql,typescript,estree}`, `await` the async
`format()`, and fall back to the unformatted (still valid) code on any
failure so cosmetic formatting can never crash codegen again.
- `RenderContext.toCode` + `clientTasks`: propagate the now-async
`prettify` (await the four `toCode` call sites).
- Bump the declared `prettier` dependency `^2.8.8 → ^3.8.3` to match
what is actually used (only consumer; minimal lockfile diff).
## Verification (local, source server on :3000)
- `twenty dev --once` now completes: `Registering application → Syncing
manifest → Generating API client → ✓ Synced` with the **server staying
up**.
- The app integration test passes (full re-sync of 6 metadata objects +
`MetadataApiClient`/`CoreApiClient` CRUD through the generated genql
runtime).
- `nx build twenty-client-sdk` (incl. `tsgo` typecheck) passes.
Release note: this is a v2.11 blocker — without it, installing/syncing
any app crashes the server.
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>
## Problem
Since the `twenty-ui` → `twenty-ui-deprecated` / `twenty-new-ui` →
`twenty-ui` rename (#21315), many deprecated components render with
**compacted height** — e.g. dropdown menu items collapse from 32px to
16px, and chips from ~24px to 16px.
## Root cause
The new `twenty-ui` (formerly `twenty-new-ui`) ships a global reset in
`packages/twenty-ui/src/styles/base/reset.scss`:
```css
*, *::before, *::after {
box-sizing: border-box;
}
```
This is bundled into `twenty-ui/style.css`, which the app imports in
`index.tsx`. #21315 did not change the `import 'twenty-ui/style.css'`
line, but it changed what `twenty-ui` resolves to (old → new), so this
**global `border-box` reset now applies app-wide**.
Several deprecated components were authored against the **content box**,
e.g. `StyledMenuItemBase`:
```css
height: calc(32px - 2 * var(--vertical-padding));
padding: var(--vertical-padding) var(--horizontal-padding);
```
With `content-box` the padding sits *outside* the declared height → 32px
total. Under the new `border-box` reset the padding is folded *inside* →
16px total. (`Chip` uses `height: spacing[4]` + outside padding — same
failure mode.)
Verified in the running app: the collapsed menu item computes
`box-sizing: border-box`, matched by the rule `*, ::before, ::after {
box-sizing: border-box }`; `height` resolves to `calc(32px - 2 * 8px) =
16px`.
## Fix
Add `box-sizing: content-box` to the affected deprecated components. A
class selector outranks the universal `*` reset, so this restores their
intended sizing **without touching the global reset** (which the new
`twenty-ui` components rely on).
Affected: `StyledMenuItemBase` (and its hoverable variant),
`MenuItemSelect`, `MenuItemSuggestion`, `Chip`.
Replaces #21279 and #21282 with one clean PR from `main`.
Generalizes the settings primary-bar / secondary-bar card chrome to the
record index, record show and standalone pages via a shared
`PageCardLayout` + `PageCardHeader` (the side panel sits as a sibling of
the content card), and applies the new flat design direction: square
corners on the card, side panel and loading skeletons.
Iterating toward the new design (Figma node 102282-221623); the
confirmed direction and the explicit "remove rounded corners" change are
in, remaining designer specifics to follow.
## Summary
Propagates the just-published **`twenty-sdk@2.10.1`** security patch
into the `twenty-apps/*` mini-apps, clearing the bulk of the
nested-lockfile Dependabot alerts (the `tmp` + `undici` clusters).
Each app carries its own `yarn.lock`, so the fix only reaches them once
they bump the SDK. `2.10.1` drops the two vulnerable transitive deps
every app inherited:
| Vuln dep | Source | Fixed by |
|---|---|---|
| `tmp@0.0.33` (GHSA-ph9p / GHSA-52f5) | `inquirer ^10 →
external-editor` | `inquirer ^14` → `@inquirer/editor@5` (no
external-editor) |
| `undici@<6.24` (5 GHSAs) | `@genql/cli` | vendored genql codegen
(`@genql/cli` removed) |
## Changes
Bumps `twenty-sdk` **and** `twenty-client-sdk` (whichever each app pins
— several pin both) to `2.10.1` and regenerates each lockfile.
**10 apps updated** (all on the v2 line — minor bump, low risk):
`twenty-slack`, `twenty-discord`, `twenty-linear`, `twenty-partners`,
`twenty-fireflies`, `people-data-labs`, `twenty-for-twenty`, `exa`,
`github-connector`, `postcard`.
Verified per-app after regen: **`tmp@0.0.33` = 0** and **`undici@5` =
0** in every updated lockfile.
## Deliberately excluded
Three apps pin a **pre-2.0** SDK, where `→ 2.10.1` is a major jump that
risks breaking the app and needs per-app validation:
- `examples/hello-world` (`0.9.0`)
- `internal/call-recording` (`0.6.3-alpha`)
- `internal/self-hosting` (`1.22.0-canary.6`)
These still carry one `tmp`/`undici` alert each and should be handled in
a follow-up.
## Related
- `twenty-sdk@2.10.1` release (tag `sdk/v2.10.1`) — backport of #21339
(undici) + #21340 (tmp) from `main`.
## Summary
Removes all transitive **`tar@6.2.1`** from the dependency tree,
resolving [Dependabot alert
#400](https://github.com/twentyhq/twenty/security/dependabot/400)
([GHSA-34x7-hfp2-rc4v](https://github.com/isaacs/node-tar/security/advisories/GHSA-34x7-hfp2-rc4v)
/ CVE-2026-24842 — node-tar hardlink path traversal, high/8.2).
The alert had been dismissed as `no_bandwidth`, but `tar@6.2.1` was
still in the lockfile. I confirmed **6.2.1 is genuinely exploitable** by
running the advisory's PoC (the hardlink escaped the extraction dir to a
parent-directory file); `7.5.16` blocks it. There is **no patched 6.x
release** — the fix only exists in `7.5.7+`.
## Approach
Upgrade the build tooling that pulled tar v6 to the majors that depend
on tar v7, rather than forcing tar onto v6-era consumers:
| Package | Change | Mechanism |
|---|---|---|
| `node-gyp` | 10.2.0 / 7.1.2 / 9.4.1 → **12.4.0** | resolution |
| `cacache` | 18 → **20.0.4** | resolution |
| `make-fetch-happen` | → **15.0.6** | resolution |
| `mintlify` (twenty-docs) | `latest` → **^4.2.594**
(`@mintlify/previewing` → tar 7.5.15) | direct dep bump |
| `@electron/rebuild`, `@electron/node-gyp`, `pacote` → `tar` | →
**^7.5.16** | scoped resolution |
The last row covers the two subtrees with **no upstream tar-v7
release**: `@electron/rebuild` (+ electron's `node-gyp` fork) in
`twenty-companion`, and `pacote@11/15` via `zapier-platform-cli` in
`twenty-zapier`.
All `tar` now resolves to **7.5.13 / 7.5.15 / 7.5.16**; `node_modules`
verified free of tar v6.
## Validation done
- `yarn install` completes cleanly (constraints pass, only pre-existing
`enableScripts: false` + peer-dep warnings).
- Installed `node_modules` contains zero tar v6.
## Validation still needed before merge ⚠️
- The scoped overrides force tar v7 onto packages written for the v6
API. Resolution is consistent, but **runtime not exercised**
(`enableScripts: false` skips native builds at install). Please
validate:
- `twenty-companion` electron `make` / native rebuild
- `twenty-zapier` build/push
- If either breaks, drop the scoped overrides and accept those two
**dev/build-only** clusters as residual — they extract only trusted
archives at build time, so the CVE (which needs attacker-controlled
input) isn't reachable there.
- `mintlify` is pinned (not `latest`) because `.yarnrc.yml`'s
`npmMinimalAgeGate: 3d` quarantines the true latest. Pinning is arguably
healthier, but it's a deliberate behavior change.
## Note
twenty-server's own runtime tarball extraction
(`extract-tarball-securely.util.ts`) was already on patched tar **and**
rejects all hardlink/symlink entries — so this PR addresses the
remaining build-tooling exposure, not a live runtime hole.
Large `yarn.lock` churn is expected: the node-gyp/cacache major bumps
refresh npm-internals tree-wide.
## What
Vendors a narrowed copy of
[`@genql/cli@3.0.5`](https://github.com/remorses/genql) (MIT) into
`packages/twenty-client-sdk/src/generate/genql/` and repoints the two
client generators at it, then removes `@genql/cli` from
`twenty-client-sdk`, `twenty-sdk` and `create-twenty-app`.
## Why
`@genql/cli` was used **only** to generate the typed GraphQL client from
an SDL string. It is unmaintained and pulls in vulnerable/abandoned
transitives — `undici@5` (**30 Dependabot alerts**), `native-fetch`,
`listr`, `yargs`, etc. None of these were ever executed by Twenty: the
sole consumer of `undici`/`native-fetch` is `@genql/cli`'s live-endpoint
schema-introspection path, and Twenty always passes a schema string,
never an endpoint.
Removing the package eliminates the dependency at the source — for
Twenty and for scaffolded end-user apps.
## What changed vs upstream
The vendored copy (`genql/README.md` + `genql/LICENSE`) keeps the
`render/` and `runtime/` trees verbatim and narrows the orchestration:
- **Dropped the endpoint/introspection path** (`schema/fetchSchema.ts`)
— the only `undici`/`native-fetch`/`qs` consumer.
- **Dropped `listr`** — generation tasks run as plain sequential `async`
functions (file contents unchanged).
- **Replaced `fs-extra`/`mkdirp`/`rimraf`** with `node:fs`.
- **Runtime templates are imported as `?raw`** and bundled, instead of
read from `node_modules` at generation time.
- **Kept `prettier@^2.8` and `@graphql-tools/*`** so the generated
output is byte-for-byte identical.
## Verification
- **Byte-identical output**: regenerating the metadata client from its
committed schema produces a recursive-diff-clean result vs the previous
`@genql/cli` output (including the copied `runtime/` folder). The core
client generates and esbuild-bundles cleanly.
- The public `twenty-client-sdk/generate` barrel API is unchanged
(twenty-server / twenty-sdk consumers unaffected).
- `undici@^5`, `native-fetch`, `@genql/cli`, `listr`, `yargs@^15` and
`subscriptions-transport-ws@0.9` are gone from `yarn.lock` (net −364
lines).
- `twenty-client-sdk` and `twenty-sdk` typecheck, lint and build;
`twenty-client-sdk` tests pass (9/9).
## Notes
- The vendored folder is excluded from `oxlint`/`oxfmt` (it is
third-party code, with `@ts-nocheck` on the verbatim renderers,
mirroring the generated output).
- Stacks conceptually on #21334 (drops `@genql/runtime`); the two are
independent and only overlap trivially in `yarn.lock`. `@genql/runtime`
is intentionally left for that PR.
The **central fix** for the `tmp` Dependabot alerts in
`packages/twenty-apps/*` — so apps don't each need a per-app
`resolutions` entry.
### Root cause
Every app depends on `twenty-sdk`, whose inquirer chain pulls the
vulnerable `tmp`:
```
twenty-sdk → inquirer ^10 → @inquirer/prompts 7.x → @inquirer/editor 4.x → external-editor → tmp@0.0.33
```
`@inquirer/editor 5.x` dropped `external-editor` (and thus old `tmp`),
and it's only reached via `@inquirer/prompts 8.x`, which requires
**inquirer ≥ 13.4.3**. So `^12` isn't enough — bump to **`^14`**
(latest):
```
inquirer 14 → @inquirer/prompts 8.5.2 → @inquirer/editor 5.2.2 (no external-editor)
```
### Verified
- The SDK uses the classic `inquirer.prompt([...])` API (uninstall / add
/ remote commands) — **typechecks cleanly under inquirer 14**.
- After the bump, the SDK's subtree resolves `@inquirer/editor@5.2.2`
(the lingering `external-editor` in this repo's lockfile is from *other*
consumers — `nx`/`zapier` — handled separately).
### Propagation
Fixes it **once** for every app using the SDK, with no per-app
`package.json` additions. Existing `twenty-apps/*` clear their `tmp`
alert once a new `twenty-sdk` is published and they bump to it;
newly-scaffolded apps are clean immediately.
(The root-workspace `tmp` from `nx`/`zapier` is handled by
twenty#21338.)
Adds an `on-partner-application-created` logic function triggered on the
`partner.created` database event. When the website application form
creates a new Partner, it posts a rich embed to a Discord channel
(applicant, company, country, languages, partner scope, skills) with a
deep link to the record.
## How it works
- Fires only on genuine form submissions — discriminates via
`createdBy.source === 'APPLICATION'`, which excludes seed/import (`API`)
and manual UI (`MANUAL`) creation.
- Runs out-of-band on the worker (database event trigger), so it adds
**no latency** to the applicant's submission, and the linked Person
already exists by the time it runs.
- Best-effort: a Discord failure never fails the trigger (wrapped in
`try/catch`, 8s timeout).
## Configuration (per workspace — Settings → Apps → Twenty Partners →
Variables)
- `DISCORD_WEBHOOK_URL` (secret) — the incoming webhook URL. **The
feature is a no-op when unset.**
- `PARTNER_APP_FRONTEND_URL` — workspace front-end base URL for the
record deep link (e.g. `https://partners.twenty.com`).
## Notes
- New logic function + two application variables; version bumped to
**0.4.0** (minor).
- Unit tests cover the source-guard branches, the on/off switch, the
embed contents/ordering, and best-effort failure handling.
- The website and the existing `submit-partner-application` handler are
untouched.
Resolves the **root** `tmp` Dependabot alert (`tmp < 0.2.6`, #1308).
In the root workspace, `tmp` is a transitive dep of `nx` (`~0.2.1`) and
`zapier-platform-cli` (exact `0.2.1`) — dev/CLI tooling that
**exact-pins old tmp with no fixed parent to upgrade to** (verified:
even latest `zapier-platform-cli@15.19.0` still pins `0.2.1`). So it's
pinned to the patched **0.2.7** via a root `resolutions` entry — the
correct tool for un-dedupe-able transitive pins. Not in the prod image.
### Why the `twenty-apps/*` alerts are not fixed here
Those come from a different source — `twenty-sdk → inquirer ^10 →
@inquirer/editor 4.x → external-editor → tmp@0.0.33`. Rather than add a
`resolutions` block to every app's `package.json` (which doesn't scale —
every newly-scaffolded app would need it), they'll be fixed
**centrally** by bumping `inquirer` in `twenty-sdk` (`^10 → ^12`, which
reaches `@inquirer/editor 5.x` that dropped external-editor). Separate
PR — apps inherit the fix on the next SDK release with no manual
additions.
Resolves the **vitest Critical** Dependabot alerts
(`GHSA-5xrq-8626-4rwp`, vitest `< 3.2.6`) — #1422–#1433.
Each `packages/twenty-apps/*` project is an **independent yarn project**
with its own `package.json` + `yarn.lock` (not part of the root
workspace). 12 of them declared `vitest: ^3.1.1` and locked an older
3.2.x. This bumps the range to `^3.2.6` and refreshes each lockfile to
**3.2.6** (latest 3.x, published 2026-06-01).
Projects updated: `community/github-connector`,
`examples/{hello-world,postcard}`,
`internal/{exa,people-data-labs,self-hosting,twenty-discord,twenty-fireflies,twenty-for-twenty,twenty-linear,twenty-partners,twenty-slack}`.
- Dev-scope only (test runner); no runtime impact.
- The **root workspace already uses vitest 4.x** (≥ the fix) and is
intentionally untouched.
- Verified: no `vitest < 3.2.6` remains in any `twenty-apps` lockfile.
## What
Removes the `@genql/runtime` dependency from `twenty-client-sdk` and
`twenty-sdk`. It was declared but **never imported** in source.
## Why
The genql codegen (`@genql/cli` `generate()`) inlines a **fully
self-contained runtime** into every generated client — see the committed
`twenty-client-sdk/src/metadata/generated/runtime/` (all relative
imports) and the generated `index.ts` which imports from `./runtime`,
not `@genql/runtime`. So the `@genql/runtime` package was dead weight in
the dep graph.
Dropping it prunes its abandoned, vulnerable transitive deps **at the
source**:
- `ws@^6` (old)
- `subscriptions-transport-ws@0.9.x`
- `isomorphic-unfetch`
- `zen-observable-ts`
- `graphql-query-batcher`
- `lodash`
None are used by Twenty — the generated client makes plain `fetch`
GraphQL requests and has no `ws`-based subscriptions.
## Verification
- `@genql/runtime` is gone from `node_modules` and `yarn.lock` (103
lockfile lines removed); the remaining
`subscriptions-transport-ws@0.11.0` is a different, maintained version
pulled by an unrelated package.
- `twenty-client-sdk` and `twenty-sdk` typecheck.
- `twenty-client-sdk` unit tests pass (9/9).
- With `@genql/runtime` physically removed from `node_modules`,
`generate()` still emits a complete, self-contained client (`index.ts`
imports `./runtime`).
## Scope
`@genql/cli` (the codegen, which pulls `undici`) is intentionally
**not** touched here — it is still required for client generation and
will be addressed separately.
Bumps `@nestjs` packages to clear the scanner findings they pin on the
prod image. All within-major bumps, past the repo's `npmMinimalAgeGate:
3d`.
## Changes
| Package | From → To | Clears |
|---|---|---|
| `@nestjs/common` | 11.1.16 → **11.1.24** | `file-type@21.3.0` → 21.3.4
|
| `@nestjs/core` | ^11.1.18 → **^11.1.24** | (path-to-regexp 8.4.2) |
| `@nestjs/platform-express` | 11.1.16 → **11.1.24** |
`path-to-regexp@8.3.0` → 8.4.2 |
| `@nestjs/serve-static` | 5.0.4 → **5.0.5** | `path-to-regexp@8.3.0` →
8.4.2 |
| `@nestjs/testing` | 11.1.16 → **11.1.24** | — |
Verified in the regenerated lockfile: **`file-type@21.3.0` and
`path-to-regexp@8.3.0` are gone**. `twenty-server:typecheck` passes
locally.
## Not in scope
- **`lodash@4.17.21`** and **`ws@8.16.0`** are pinned by
**`@nestjs/graphql@12.1.1`** (and lodash also by
`@nestjs/config@3.3.0`). Bumping graphql 12→13 would clear them, but
it's blocked by a **316-line custom patch** implementing Twenty's
multi-schema scoping (`resolverSchemaScope`, `computeReachableTypes`)
welded to 12.1.1's compiled internals — a dedicated effort, not a
routine bump. (Twenty uses the Yoga driver, so it's *not* an Apollo
migration.)
- `@nestjs/config` 3→4 alone wouldn't clear `lodash` (graphql still pins
it), so deferred with the graphql work.
- `path-to-regexp@0.1.12` is express 4.x's own — separate from @nestjs.
I think these screenshots were accidentally committed at the repo root
in #21033. They are not referenced anywhere in the codebase, so removing
them to keep the repo clean.
Signed-off-by: Parship Chowdhury <parshipchowdhury@gmail.com>
This PR migrates the p-limit library to Native graph SDK batching fixing
the concurrency and rate limit issues in production seen for some larger
accounts
## Summary
- Adds a server-side guard that rejects deletion of standard fields
(`isCustom: false`) in the `deleteOneField` path
- The UI already prevents this, but the API had no enforcement, allowing
standard fields to be deleted via direct GraphQL calls
Without this guard, deleting a standard field like `jobTitle` cascades
to drop dependent generated columns (e.g. `searchVector`), leaving the
object in a broken state where all subsequent queries fail with "Data
validation error."
## Test plan
- [x] Call `deleteOneField` with a standard field ID → should return
`FIELD_MUTATION_NOT_ALLOWED` error
- [x] Call `deleteOneField` with a custom field ID → should succeed as
before
- [x] UI deactivation of standard fields still works (deactivate !=
delete)
## What
On the very first load (full browser refresh), the navigation skeleton
didn't match the real `NavigationDrawer` width: it rendered an 8px-wider
panel (an 8px wrapper padding on top of the 220px animated container)
and right-aligned 204/196px item rows, so the menu visibly shifted and
resized once the app finished loading.
This makes every navigation skeleton mirror the real drawer geometry: a
single `NAVIGATION_DRAWER_CONSTRAINTS.default`-wide (220px), border-box
panel with the drawer's own padding, left-aligned, and skeleton bars
that fill the content width like the real nav items (`width: 100%`). The
same fill-width fix is applied to the in-drawer section skeletons so
every navigation skeleton matches the real menu width.
## Verification
- `tsgo` typecheck, `oxlint`, and `oxfmt` all clean on the changed
files.
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
## Description
Promotes the next-gen UI library (formerly `twenty-new-ui`) to the name
**`twenty-ui`** (v0.1.0, publishable) and renames the old package to
**`twenty-ui-deprecated`**. Rewrites ~1,730 `twenty-ui` imports →
`twenty-ui-deprecated`, updates all configs/CI/Docker/deps, and migrates
twenty-front's `Toggle` to the new package (first consumer) as a
drop-in.
## Next steps
- Wire the `ui/v*` publish dispatch (`cd-deploy-tag.yaml` +
`.yarnrc.yml`), then tag `ui/v0.1.0` to publish.
- Continue migrating components from `twenty-ui-deprecated` →
`twenty-ui`.
Hardens the `prod-twenty` server image. Built `--target twenty-server`
and walked it to verify each change.
- **Node 24.15.0 → 24.16.0** (all stages + `.nvmrc`): 24.15.0 links
OpenSSL **3.5.5** (CVE-2026-31798), 24.16.0 links **3.5.6** — the proper
fix (deleting headers only hid it; the binary still linked the vuln
lib).
- **Remove the bundled npm CLI** (`ip-address`): app uses yarn via
corepack, never npm; npm still bundles `ip-address@10.1.0` and its
latest 10.2.0 is itself unfixed — no upgrade path.
- **Remove vendored `example/` apps** (`passport-microsoft/example`
ships a `package-lock.json` for an old Express demo, never
installed/run; not in our lockfile).
- **node-forge → 1.4.0** (Critical CVE-2026-33606) via `yarn dedupe` —
lockfile-only, no phantom dep, no root resolution.
Verified on the built image: node 24.16.0 / openssl 3.5.6, npm CLI +
example dirs absent, node-forge@1.4.0 only.
**Not included (need CI/QA):** real deps pinned inside
`@nestjs/*`/`express` (`lodash@4.17.21`, `file-type`, `path-to-regexp`,
`ws`, `qs`) need parent bumps or scoped resolutions; standalone
`undici@5.29.0` (5→7), `apollo-server-core@3` (EOL), `typeorm`, etc.
(`axios` already patched.)
## Summary
- The `twenty_queue_jobs_waiting_total` gauge was summing all queues
into a single value without a `queue` label, making the Grafana "Jobs
Waiting by Queue" panel show a single aggregated line instead of
per-queue breakdown.
- Uses `getMeter()` directly to call `observableResult.observe(count, {
queue: queueName })` per queue, matching the `by (queue)` grouping the
dashboard already expects.
## Test plan
- [x] Deploy and verify the Grafana "Jobs Waiting by Queue" panel
displays separate series per queue
- [x] Confirm Prometheus scrape returns
`twenty_queue_jobs_waiting_total{queue="..."}` with distinct queue
labels
Clears the `uuid` "missing buffer bounds check in v3/v5/v6" advisory —
patched in **11.1.1**. Bumps `twenty-server`, `twenty-shared`,
`twenty-front` from 9 → `^11.1.1`.
### Why 11 and not 13
uuid **11.1.x still ships a CommonJS build**, so jest loads it with **no
config changes**. uuid went **ESM-only at v12+**, which would otherwise
force `transformIgnorePatterns` workarounds across the jest projects
(and broke server/integration/storybook CI on the earlier 13 attempt).
11.1.1 is the actual patched version, so this is the minimal fix.
### Changes
- `uuid` → `^11.1.1` in the three workspaces (lockfile regenerated under
hardened mode)
- one test (`useCreateManyRecords.test.tsx`): pin the mocked `v4` to its
string-returning overload — uuid's types declare a `Uint8Array` overload
that `jest.mocked` resolves to (present in v11 too, unrelated to ESM).
All usages are named imports, so no source migration. typecheck passes
(server/shared/front); affected specs pass. **No jest config changes.**
Fixes the **high-severity** `serialize-javascript` RCE advisory
(RegExp.flags / Date.prototype.toISOString, patched in **7.0.5**).
- Bumps the direct dep in `twenty-website` `^6.0.2 → ^7.0.5`.
- Only consumer is `src/lib/seo/JsonLd.tsx` (default-export API,
unchanged in v7 — the major only drops old Node support).
- `twenty-website` typecheck passes; lockfile regenerated under hardened
mode (`--immutable --check-cache` clean).
Clears the Electron advisory batch (use-after-free, IPC scoping, origin
handling, ASAR integrity, …) — patched in **39.8.5+**, resolves to
**39.8.10**.
- `twenty-companion` is the standalone desktop companion app
(electron-forge; @electron-forge 7.8 supports Electron 39). Main-process
code only uses basic `require('electron')` APIs, stable across 36→39.
- Lockfile + manifest only; gate-safe; hardened install clean.
⚠️ **Verification caveat:** there's no CI job that builds/tests
twenty-companion, so this isn't exercised by CI, and I couldn't verify
runtime locally (electron-forge packaging downloads the ~100MB Electron
binary / needs a display, and the repo's `enableScripts: false` skips
the binary). **Recommend a manual smoke test** (`yarn make`/`start` in
twenty-companion) before relying on the bump.
## What
The standalone apps under `packages/twenty-apps/*` each ship **their own
`yarn.lock`** (they're not part of the root workspace), and those
lockfiles still pulled vulnerable transitive versions of `axios`,
`undici`, `tmp`, `qs`, `ws`, `brace-expansion`, `uuid` (via `twenty-sdk`
/ `twenty-client-sdk`). This was ~130 of the open Dependabot alerts —
none of them reachable from the root-lockfile PRs.
Ran `yarn up -R` per app to re-resolve the vulnerable transitives within
their existing ranges, across all 13 flagged apps:
- **`axios` → 1.17.0** — clears the entire proxy-auth-leak / ReDoS /
config-merge MITM advisory set (the 56 axios alerts)
- **`qs`, `brace-expansion`, `uuid`** → patched
- **`undici`, `ws`** → patched on the in-range majors (older majors that
parents pin exactly remain, same situation as the root lockfile)
## Scope
- **Lockfile-only**, 13 apps. No `package.json` changes.
- Test **fixtures** (`packages/twenty-apps/fixtures/*`) intentionally
left untouched — Dependabot didn't flag them and they back snapshot
tests.
## What
Two standalone dep-set lockfiles bundled for the logic-function /
application-package sandbox carried vulnerable transitive versions.
Within-range bumps only (these dep sets are handed to user logic
functions, so no breaking majors):
**`seed-dependencies`**
- `nodemailer` `^8.0.4 → ^8.0.5` (resolves 8.0.10) — SMTP CRLF command
injection (direct dep)
- `brace-expansion`, `picomatch`, `lodash` → patched
**`common-layer-dependencies`**
- `lodash`, `brace-expansion` → patched
## Left for follow-up (can't be fixed within-range)
- `qs` — pinned transitively by `body-parser`, stays at 6.14.2 (needs
body-parser bump)
- `ip-address` 9.x → 10.x and `uuid` 10.x → 11.x — major bumps; left out
since these are exposed to user functions and would be breaking
`axios` here is already on the patched `^1.16.1`.
## Summary
- Resolves#11977
- When looking into the deleted records from People tab (or any object
list), the record detail header showing 0/(total records) instead of the
correct position among deleted records only, e.g. 1/3 or 3/7. So, this
PR makes the count match what users see in the deleted-records list.
- Also normal records showing `0/N` in the header when opened from a
list view (e.g. `0/48` -> `2/48`).
## Approach
I tried to keep the change small and avoid extra server requests:
- when a user came from a deleted-records view, we tell our existing
queries to include soft-deleted records.
- for the position number, we use the record list the user already had
open (from the index view they came from) instead of apollo cache, which
didn’t include records, especially deleted ones, but also normal
records.
- normal list behavior is not changed on the server side.
## Test plan
- Open people/company, delete a record
- Use the side menu -> “see deleted records”
- open a deleted record’s details
- confirm the header showing the correct position and total (e.g. 1/2,
not 0/100)
- for normal list: open People (normal list, not deleted) -> click a
record -> open full page -> confirm header shows correct position and
total (e.g. `2/48`, not `0/48`)
## Screenshots
### Before:
<img width="1513" height="309" alt="Screenshot 2026-06-07 135204"
src="https://github.com/user-attachments/assets/4754f1a7-8315-4a7a-815f-dda977b09331"
/>
<img width="1514" height="261" alt="Screenshot 2026-06-07 141735"
src="https://github.com/user-attachments/assets/dd5b1834-5d84-49fe-8d20-633428d73502"
/>
### After:
<img width="1511" height="224" alt="Screenshot 2026-06-07 134946"
src="https://github.com/user-attachments/assets/9450af7d-84b9-40bb-95e9-5a8665cc0923"
/>
<img width="1514" height="288" alt="Screenshot 2026-06-07 135045"
src="https://github.com/user-attachments/assets/029ae632-ad7e-451e-8170-a4e4e71ac6f9"
/>
<img width="1512" height="229" alt="Screenshot 2026-06-07 141642"
src="https://github.com/user-attachments/assets/576f4cad-a9e9-4380-aa67-e5f0e976a193"
/>
---------
Signed-off-by: Parship Chowdhury <parshipchowdhury@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
The root lockfile carries redundant standalone descriptors (`ws@8.21.0`,
`postcss@8.5.15`, `nanoid@3.3.12`) left over from the transitive-dep
security bump (#21310). Under `enableHardenedMode` + `yarn install
--immutable --check-cache` (how CI runs it), these trip `YN0028` — which
is currently **failing the danger-js check on every new PR**.
A mutable install merges them into their existing descriptor groups: **2
insertions, 37 deletions, no version changes, no security downgrades**.
`yarn install --immutable --check-cache` passes again afterward.
This unblocks danger-js across all open PRs.
## What
Companion to the direct-dependency security PR (#21309). Bumps
**transitive** vulnerable packages via `yarn up -R` (re-resolve to
newest within existing ranges). **Lockfile-only — no root `package.json`
/ resolutions changes.**
### Cleared
| Package | Fix |
|---|---|
| `brace-expansion` | numeric-range / zero-step DoS |
| `follow-redirects` | auth header leak to cross-domain |
| `diff` | ReDoS in parse/applyPatch |
| `@protobufjs/utf8` | overlong UTF-8 decoding |
| `lodash` | — |
## Out of scope
Vulnerable copies that are **exact-pinned by third-party parents**, so
they can only be fixed once those parents ship patched versions —
they're not direct deps of any workspace, can't be added per-package,
and a sibling dependency can't override a parent's exact pin (only a
root resolution could, which we're intentionally not adding):
- `postcss` ← `next`, `styled-components`
- `fast-xml-parser` ← `@aws-sdk/xml-builder`
- `undici`, `ws`, `tmp`, `picomatch`, `webpack-dev-server`,
`ip-address`, `unhead`, `yeoman-environment`, `@tootallnate/once`,
`ajv`, `react-router` (need breaking major bumps or coordinated
multi-package updates; mostly dev/build tooling, not shipped runtime)
> Split from the direct-deps PR (#21309) per the agreed split-by-group
plan.
## What
Adds a `LOG` driver to the emailing-domain feature, selected via a new
`EMAILING_DOMAIN_DRIVER` config variable (defaults to `AWS_SES`, so
production behavior is unchanged).
The LOG driver:
- resolves domains to `VERIFIED` instantly (no DNS / SES setup)
- logs each `sendEmail` and returns a synthetic `messageId` instead of
calling SES
It also dev-seeds a pre-verified domain per workspace
(`<workspaceId>.dev.twenty.local`) so the feature works out of the box.
## Why
The emailing-domain feature currently ships only the AWS SES driver, so
the verify → send flow can't be exercised locally (or in CI) without
real AWS credentials. This unblocks local development and review of
anything built on emailing domains.
## Usage
```
EMAILING_DOMAIN_DRIVER=LOG
```
The seeded `*.dev.twenty.local` domain is already verified; sends are
logged (`[log-driver] sendEmail ...`).
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
This reworks the /product hero into a single scroll-driven story. It
opens on a collaborative CRM intro — an autoplay cursor ("Alice") tours
real app-preview surfaces (Companies → an Anthropic record, clicking
through its tabs → People → Notes) — then, as you scroll, the light
intro morphs into a dark AI section via one continuous color wipe that
flows up through the (transparent) navigation menu. The AI section
presents four "build" pillars (pipeline board, follow-up tasks, sales
dashboard, email-sequence workflow), each playing an agentic preamble
(thinking + tool steps) and skeleton-loading state before revealing the
finished artifact as a finale. All preview surfaces — table, kanban,
record, dashboard charts, and workflow — are rendered for real and
matched to twenty-front's design (step-8 chart palette, default
opportunity stages, etc.) rather than static images.
Beyond the core morph, this PR layers in the coherence and polish work:
- the intro halftone reuses the AI section's pattern tinted to the brand
blue and scoped to the visual;
- the workflow finale draws its edges/labels in as a staged build;
- AI response copy is kept consistent with the visuals (matching stage
names and task counts);
- the ARR line chart fades in cleanly instead of flashing a partial
draw;
- the experience resets sensibly on scroll — the intro cursor tour
restarts when you scroll back to it, and the AI section returns to its
first tab once you're back at the top.
Here is the video showcasing the experience:
https://github.com/user-attachments/assets/c20708ed-4435-4f1a-a1c6-308b99305a97
Awaiting confirmation about the experience from @Bonapara before
merging.
Reduces output tokens for all 13 metadata tools by (~49%) based on
production sampling data.
GET tools (field + object metadata)
System fields are now returned as compact {id, name, type} instead of
the full ~20-key payload (opt-in includeFullSystemFields to get full
payload). System objects are similarly compacted to {id, nameSingular,
namePlural}.
Internal fields the agent never uses (searchVector, deletedAt, position,
updatedBy) are excluded entirely.
workspaceId and applicationId are hoisted into a response envelope
instead of being repeated on every record.
Null/default-false properties are stripped from custom field and object
payloads (e.g. options: null, settings: null, isUIReadOnly: false).
CUD tools (create/update/delete)
Create and update field tools now return {id, name, type, label} instead
of the full DTO.
Create and update object tools now return {id, nameSingular,
labelSingular} instead of the full DTO.
Delete tools return {id, success: true} instead of the full DTO of the
deleted entity.
Validation errors are grouped by message — e.g. 10 fields failing the
same check produce one line with all names instead of 10 identical
lines.
Learn schemas (all tools)
UUID pattern regex stripped from JSON schemas (keeps format: "uuid").
$schema and additionalProperties: false stripped from all generated
schemas.
All Zod .describe() annotations and tool descriptions shortened.
Skill & tool description updates:
All references to the removed list_object_metadata_items tool replaced
with get_object_metadata / get_field_metadata across skill instructions,
dashboard tools, view filter/sort tools, and MCP server instructions.
## What
Bumps the transitive dev dependency
`@babel/plugin-transform-modules-systemjs` from `7.25.9` → `7.29.7`
(lockfile-only).
## Why
Resolves Dependabot alert **#1182** — **GHSA-fv7c-fp4j-7gwp** /
**CVE-2026-44728** (high severity):
> `@babel/plugin-transform-modules-systemjs` generates arbitrary code
when compiling malicious input.
- Vulnerable range: `>= 7.12.0, <= 7.29.3`
- First patched: `7.29.4`
## How
It's pulled in transitively via `@babel/preset-env` with the range
`^7.25.9`, which `7.29.7` satisfies — so **only `yarn.lock` changes**,
no `package.json` edits needed. The diff is confined to the `@babel/*`
subtree (13 helper packages updated alongside it).
## Notes
- Scope is `development` only.
- The alert is currently marked *dismissed* on the security tab; opening
this anyway to actually remove the vulnerable version from the lockfile.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Summary
- Add `ci-new-ui.yaml` workflow mirroring `ci-ui.yaml` for
`twenty-new-ui` (lint, typecheck, test, Storybook build, Storybook test
with screenshot capture)
- Update `visual-regression-dispatch.yaml` to watch both `CI UI` and `CI
New UI` workflows, with dispatches for three Argos projects:
1. **Self-hosted twenty-ui pixel diff** (existing, unchanged)
2. **Self-hosted twenty-new-ui pixel diff** (new) -- standard regression
against main
3. **Self-hosted twenty-ui vs twenty-new-ui comparison** (new) --
cross-package visual parity
- Add "Visual regression" documentation section to
`twenty-new-ui/README.md` with local dev workflow (argos-tunnel via
super CLI + `storybook:visual-diff`)
- Resolve open question 4 in README (Argos confirmed as visual
regression tool)
## Companion PR
Requires companion PR on `twentyhq/ci-privileged` for the new
`visual-regression-cloud` dispatch handler, upload script, and
post-comment logic.
## Manual setup
After merging both PRs, create two Argos projects on
argos.twenty-internal.com:
- `twenty-new-ui` (pixel diff)
- `twenty-ui-vs-new-ui` (cross-package comparison, auto-approved branch:
main)
Add secrets `ARGOS_TOKEN_NEW_UI` and `ARGOS_TOKEN_COMPARISON` to
ci-privileged.
## Test plan
- [ ] Verify `CI New UI` workflow triggers on twenty-new-ui changes
- [ ] Verify screenshot artifact `argos-screenshots-twenty-new-ui` is
uploaded
- [ ] Verify dispatch sends `visual-regression-cloud` events to
ci-privileged
- [ ] Verify existing `CI UI` → self-hosted Argos flow is unchanged
## What does this PR do?
Fixes a bug where `NavigationMenuItem.userWorkspaceId` was being
compared/set to `WorkspaceMember.id` instead of the correct
`UserWorkspace.id`, causing the favorites functionality to not work
correctly.
Fixes#21291
## Problem
The `isFavorite` check in `ViewPickerOptionDropdown` and
`createManyNavigationMenuItems` calls in multiple files were using
`currentWorkspaceMemberId` (which is `WorkspaceMember.id` from the
`workspace_*` schema) instead of the correct `UserWorkspace.id` (from
the `core` schema).
This caused:
- `isFavorite` to always return `false` for user favorites
- Navigation menu items to be created with incorrect `userWorkspaceId`
## Root Cause
In `useNavigationMenuItemsData.ts`:
- `currentWorkspaceMemberId` was derived from
`currentWorkspaceMember?.id` (WorkspaceMember.id)
- But `NavigationMenuItem.userWorkspaceId` expects a `UserWorkspace.id`
- These are two different entities from different schemas (core vs
workspace)
## Solution
1. Added `currentUserWorkspaceId` to the `useNavigationMenuItemsData`
hook return type
2. `currentUserWorkspaceId` is derived from
`currentWorkspaceMember?.userWorkspaceId`
3. Updated all comparisons and assignments to use
`currentUserWorkspaceId` when dealing with `userWorkspaceId`
## Files Changed
-
`packages/twenty-front/src/modules/navigation-menu-item/display/hooks/useNavigationMenuItemsData.ts`
- Added `currentUserWorkspaceId` to return type
-
`packages/twenty-front/src/modules/views/view-picker/components/ViewPickerOptionDropdown.tsx`
- Fixed `isFavorite` check and `createManyNavigationMenuItems` call
-
`packages/twenty-front/src/modules/command-menu-item/engine-command/record/single-record/components/AddToFavoritesSingleRecordCommand.tsx`
- Fixed `createManyNavigationMenuItems` call
-
`packages/twenty-front/src/modules/navigation-menu-item/edit/hooks/useNavigationMenuItemEditController.ts`
- Fixed `targetUserWorkspaceId` assignment
## Testing
- No existing tests directly cover the `useNavigationMenuItemsData` hook
- The fix is a simple type/field correction that should not affect other
components
- CI will verify TypeScript compilation and linting
## Checklist
- [x] I have read the
[CONTRIBUTING.md](https://github.com/twentyhq/twenty/blob/main/.github/CONTRIBUTING.md)
file
- [x] Changes are tested locally (TypeScript compilation)
- [x] Commit message follows repository conventions
- [x] PR is linked to the relevant issue (#21291)
---------
Co-authored-by: Mani bharadwaj <Manibharadwaj@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
The **APIs & Webhooks** settings page (`SettingsPath.ApiWebhooks`) is
gated by the wrong permission flag. Its route sits in the
`PermissionFlagType.WORKSPACE` group in `SettingsRoutes.tsx`, but
everything else about the page is gated on `API_KEYS_AND_WEBHOOKS`:
- The **nav item** is hidden behind `API_KEYS_AND_WEBHOOKS`
(`useSettingsNavigationItems.tsx`).
- All its **sub-routes** — new/detail API key, new/detail webhook, and
the GraphQL & REST playgrounds — already live under the
`API_KEYS_AND_WEBHOOKS` wrapper.
So a role with **"API Keys & Webhooks"** enabled but **without
"Workspace"** sees the nav item (and the **"Set up MCP"** button in
*Settings → AI*, which links to `/settings/api-webhooks#mcp`), but on
arrival `SettingsProtectedRouteWrapper` finds no `WORKSPACE` flag and
redirects them to the **Profile** page. The entry points are visible;
the destination is unreachable.
## Root cause
The route was grouped under the `WORKSPACE` wrapper while its nav item
and sub-pages are gated on `API_KEYS_AND_WEBHOOKS` — the page's route
gate and its nav gate disagree.
## Changes
- `SettingsRoutes.tsx` — move the `SettingsPath.ApiWebhooks` route out
of the `WORKSPACE` group and into the existing `API_KEYS_AND_WEBHOOKS`
group, alongside its own sub-routes.
This is the same class of fix as #21239 (*gate the AI settings page on
`AI_SETTINGS`, not the chat flag*).
## Test plan
- [ ] Role with **only "API Keys & Webhooks"** (`API_KEYS_AND_WEBHOOKS`,
no `WORKSPACE`): *Settings → APIs & Webhooks* is reachable; the nav item
and the *Settings → AI* "Set up MCP" link both land on the page instead
of redirecting to Profile.
- [ ] Role with **"Workspace" but not "API Keys & Webhooks"**: the APIs
& Webhooks nav item stays hidden and the route is not reachable (was
previously reachable — now consistent with the nav).
- [ ] Admin (both flags): unchanged.
## What
Renames the historical TypeORM migrations directory so it's obvious at a
glance the path is frozen.
- `packages/twenty-server/src/database/typeorm/core/migrations/common/`
→ `…/typeorm/core/legacy-typeorm-migrations-do-not-add/common/`
- `packages/twenty-server/src/database/typeorm/core/migrations/billing/`
→ `…/typeorm/core/legacy-typeorm-migrations-do-not-add/billing/`
- `packages/twenty-server/src/database/typeorm/core/migrations/utils/` —
**left in place** (those SQL helpers are still imported by current
instance/workspace commands, see e.g.
`1-21-workspace-command-1775500002000-add-global-key-value-pair-unique-index.command.ts`)
- Updates `core.datasource.ts` globs to the new path and adds an inline
comment explaining the dir is frozen
- Adds a `README.md` at the new dir pointing readers at
`UPGRADE_COMMANDS.md` and the active `upgrade-version-command/` tree
## Why
The TypeORM migration system was replaced by fast/slow instance commands
+ workspace commands (PR #19356), but `typeorm/core/migrations/common/`
still looked structurally identical to an active migrations folder, with
new files merging in as recently as last week. New contributors — and AI
agents — kept inferring it was the active path and adding TypeORM
`MigrationInterface` files. See #21286 for the most recent instance.
This is recommendation **1b** from #21286
(`https://github.com/twentyhq/twenty/pull/21286#discussion_r3369356792`).
A renamed folder is the single strongest signal — no one instinctively
adds to a `do-not-add` directory.
## Notes
- Imports from `src/database/typeorm/core/migrations/utils/…` keep
working because `utils/` did not move.
- No behavior change at runtime: TypeORM loads the same files via
`_typeorm_migrations`, just from the new path.
## Test plan
- [ ] CI green
- [ ] `nx build twenty-server` succeeds
- [ ] Fresh `nx database:reset twenty-server` from a clean DB still
replays the legacy TypeORM migrations (i.e. `_typeorm_migrations` rows
get inserted at boot/init)
- [ ] Spot-check that an instance command that imports from
`migrations/utils/` still resolves at build time (e.g.
`1-21-workspace-command-1775500002000-add-global-key-value-pair-unique-index.command.ts`)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <FelixMalfait@users.noreply.github.com>