Commit Graph

89 Commits

Author SHA1 Message Date
martmull cb5d64fefc Add twenty-exa application to internal app ci (#21882)
renamed exa to twenty-exa
add twenty-exa to ci check

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21882?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-22 11:36:33 +02:00
nitin 6237598a30 Fix meeting bot CalendarEvent field visibility and editability (#21883)
- Add the meeting bot preference field to the CalendarEvent record page
fields view.
- Use a Standard-app ownership gate for record field read-only logic.
- Allow app-owned and workspace-custom fields on system objects to
follow isUIEditable and permissions.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21883?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-22 11:24:32 +02:00
martmull 584c567a7f Add twenty-discord on internal ci apps (#21928)
add twenty-discord to ci check

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21928?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-22 11:05:43 +02:00
nitin dd6fbbe854 Rename meeting bot app variables from RECALL_BOT_* to MEETING_BOT_* (#21878)
sorry recall :)

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21878?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-20 00:32:49 +05:30
nitin c2e3296d85 Add transcript tab to calendar record page (#21792)
Adds a read-only "Transcript" tab to the CalendarEvent record page,
contributed by the Twenty Meeting Bot app. Renders the diarized
transcript stored on `CallRecording.transcript`, with placeholder states
for pending and failed transcription.

Front-end only — the transcription pipeline (request/download/reconcile
+ PENDING/FAILED markers) already landed on main.

Deferred: live-mount verification via `yarn twenty dev`.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21792?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-19 23:35:35 +05:30
Abdullah. 8899360ebe fix(security): refresh undici across lockfiles (6.x → 6.27.0, 7.x → 7.28.0) (#21870) 2026-06-19 19:15:47 +02:00
martmull a870e034a6 Add twenty slack to internal application ci (#21849)
- adds `twenty-slack` to internal application ci 
- unify config with twenty-last-contact app
- add base oxlint config to show error twenty-shared is used in internal
app

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21849?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-19 14:39:42 +00:00
Raphaël Bosi 88967a6e47 Add Update fields select to People Data Labs enrichment functions (#21801)
## What

The People Data Labs enrichment logic functions (`enrich-person`,
`enrich-company`, `enrich-people`, `enrich-companies`) now expose an
**`Update fields`** select instead of the `overrideExistingValues`
boolean, and always return the enriched data in their output.

`Update fields` options:
- **Yes and overwrite**: persist, overwriting existing standard fields
- **Yes and don't overwrite** (default): persist, filling standard
fields only when empty
- **No**: write nothing to the record (no CRM fields, no PDL metadata,
no company creation)

## Why

The functions previously only persisted data. With `No`, they can now
fetch from PDL and return the result without modifying the record, so
downstream workflow steps can consume it. Every matched result now
carries a `data` object with the mapped record fields (standard + `pdl*`
values), and the bulk functions also declare their `results[]` array in
the output schema.

Billing is unchanged: a successful PDL match is still charged in all
modes, since the API cost is incurred regardless of persistence.

## Notes

- Default behavior is preserved (unset input means fill-empty +
persist).
- Typecheck, lint, and the full unit suite (368 tests) pass.
2026-06-19 14:39:49 +02:00
martmull 576f88b5c5 Update ci internal applications (#21837)
Fix `twenty-last-contact` ci

- add tests
- add tsgo
- update package commands

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21837?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-19 13:42:15 +02:00
martmull 5a6c02a7aa Create CI workflow for internal apps (#21791)
as title

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21791?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:08:52 +02:00
nitin a7afff7465 fix call recording bot automatic leave activate after bug (#21820)
hardcoded activate_after -- 0 on everyone_left_timeout was getting
rejected by recall with a 400 (activate_after can't be 0). pulled it
into a named const, set to 1.

its weird -- recall doc says default is 0 -- but its erroring out for us
if we send zero. looks like the 0 default only applies when you leave
the field out, not when you pass it explicitly(did not verify). keeping
it as lowest possible value (1) for now.

what the property does -- after the meet starts, how long before the bot
starts watching the "everyone left" timeout. 1s is basically immediate,
which is what we wanted with 0 anyway.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21820?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-19 13:07:21 +05:30
Rashad Karanouh 0e97e1a908 feat(partners): add partner-application-triage and partner-meeting-recap skills (#21819)
## What

Adds two Twenty partner-pipeline skills to
`twenty-partners/src/skills/`, plus a patch version bump.

### `twenty-partner-application-triage`
Ranks the partner-application backlog by net-new value and surfaces a
short chase-list of high-value applicants who haven't booked a call.
Read-only against the live partners workspace. Ships `rank.py` as its
scoring helper.

### `twenty-partner-meeting-recap`
After partner calls, pulls Fireflies meetings, matches each to an
existing Partner by attendee email/domain, writes a recap
(transcript-first, Fireflies summary as fallback), and injects it as a
Note linked to the partner via `NoteTarget`. Skips leads/discovery calls
(no Partner match) and meetings whose content isn't ready yet. Optional
`--prune` deletes the Fireflies recording once its recap is safely in
the CRM (confirmed first).

## Version
`twenty-partners` 0.5.4 → **0.5.5** (patch: additive skill docs, no app
behaviour change).

## Notes
- Both skills read credentials from `~/.twenty/credentials.env`; no
secrets committed.
- All GraphQL queries/mutations are the proven ones used against the
live workspace.
2026-06-19 09:35:47 +02:00
Abdullah. bebe03e453 fix(security): bump tar to 7.5.16 across lockfiles (PAX file smuggling) (#21813)
## fix(security): bump tar to 7.5.16 across lockfiles (PAX file
smuggling)

Resolves [#1472 1474 1476 1479 1481 1483 1485 1487 1489 1491 1493 1496
1498 1505](https://github.com/twentyhq/twenty/security/dependabot/1472
1474 1476 1479 1481 1483 1485 1487 1489 1491 1493 1496 1498 1505).

### What

`tar` (`node-tar`) `<= 7.5.15` applies a PAX size override to
intermediary GNU long-name/long-link headers, causing a tar-parser
interpretation differential (file smuggling). Patched in `7.5.16`.

### Why these alerts

The advisory is scanned across many independent Yarn projects, so it
surfaced as one alert per lockfile: the root `yarn.lock` plus 13
`packages/twenty-apps/**` lockfiles (each pulls `tar` transitively).

### How

- Refreshed `tar` to `7.5.16` in the root and all 13 app lockfiles —
they hold `tar` via `^7.5.x` ranges that already permit it, so this is
an in-range lockfile refresh (no override) via `yarn up -R tar`.
- The root additionally had `tar@7.5.15` exact-pinned by
`@mintlify/previewing`, which has **no upstream fix** (latest `4.0.1163`
still pins `7.5.15`). Added a scoped resolution
`@mintlify/previewing/tar -> ^7.5.16`, **extending the existing scoped
tar resolutions** already used for `@electron/rebuild` and
`@electron/node-gyp`.

### Not included

`seed-dependencies/yarn.lock` (alert #1500) is intentionally excluded:
that lockfile and its checksum constants are already modified by the
open form-data PR, so its `tar` bump will follow separately to avoid a
conflict.

### Verification

- No `tar <= 7.5.15` remains across the root or any app lockfile.
- `yarn install --immutable` passes.
2026-06-19 08:49:07 +02:00
Abdullah. 26b4d6caed fix(security): bump form-data to 4.0.6 (CRLF injection) (#21808)
Resolves [Dependabot Alert
#1473](https://github.com/twentyhq/twenty/security/dependabot/1473),
[#1475](https://github.com/twentyhq/twenty/security/dependabot/1475),
[#1477](https://github.com/twentyhq/twenty/security/dependabot/1477),
[#1478](https://github.com/twentyhq/twenty/security/dependabot/1478),
[#1480](https://github.com/twentyhq/twenty/security/dependabot/1480),
[#1482](https://github.com/twentyhq/twenty/security/dependabot/1482),
[#1484](https://github.com/twentyhq/twenty/security/dependabot/1484),
[#1486](https://github.com/twentyhq/twenty/security/dependabot/1486),
[#1488](https://github.com/twentyhq/twenty/security/dependabot/1488),
[#1490](https://github.com/twentyhq/twenty/security/dependabot/1490),
[#1492](https://github.com/twentyhq/twenty/security/dependabot/1492),
[#1494](https://github.com/twentyhq/twenty/security/dependabot/1494),
[#1495](https://github.com/twentyhq/twenty/security/dependabot/1495),
[#1497](https://github.com/twentyhq/twenty/security/dependabot/1497),
[#1499](https://github.com/twentyhq/twenty/security/dependabot/1499),
[#1501](https://github.com/twentyhq/twenty/security/dependabot/1501) and
[#1506](https://github.com/twentyhq/twenty/security/dependabot/1506).
2026-06-19 08:46:32 +02:00
nitin f1d4d6aeaf Converge Recall transcript artifacts on meeting start (#21788)
## what

- converge stuck call recordings after the meeting starts, not based on
scheduled end
- replace the pending-transcript cron with Recall transcript artifact
reconciliation
- list existing Recall transcripts before creating one, so we avoid
duplicate requests
- keep a local pending marker as an idempotency latch for
stale/redelivered events



<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21788?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-18 19:04:25 +05:30
martmull 41b7c53dbf Bump call recording app version (#21787)
as title

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21787?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-18 13:01:21 +00:00
nitin 06cefb1dac Configure Recall bot server variables (#21774)
## What changed

- Added server variables for Recall bot leave behavior
- Added `RECALL_BOT_JOIN_EARLY_MINUTES` so the bot can join slightly
before meeting start
- Defaults stay aligned with Recall where applicable
- Kept descriptions more human-friendly in app config + README


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21774?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-18 14:58:25 +02:00
nitin 640a8e6ca6 Add post-call recording ingestion and billing (#21758)
## Summary

- Add post-call Recall recording ingestion for transcripts, audio, and
video
- Request/retrieve async transcripts and reconcile stale pending
transcript markers
- Complete call recordings atomically once all artifacts and billable
timestamps are available
- Charge `CALL_RECORDING` usage once per completed recording based on
recording duration
- Add Recall recording/media API helpers, transcript marker utilities,
and audio/video field identifiers
- Update generated metadata/SDK files and billing usage operation
support
- Add unit coverage for ingestion, completion, charging, Recall API
behavior, and reconciliation flows



<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21758?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-18 14:01:18 +05:30
nitin d4e0b78d20 Add stale Recall bot reconciliation (#21720)
This PR adds a scheduled reconciliation pass for the Twenty Meeting Bot
app so call recording state does not depend only on event-driven updates
from calendar changes and Recall webhooks.
Why we need this -- 

- A call recording row can be created, but the process can fail before
the Recall bot id is written back.
- Recall webhooks can be missed or delivered late, leaving Twenty stuck
in an older local state.
- A bot can disappear from Recall, leaving Twenty with a stale
externalBotId.
- A cancellation can fail locally, leaving an app-managed Recall bot
that would still join the meeting.

What this adds -- 

- A cron logic function that heals botless scheduled call recordings.
- A convergence pass that pulls Recall bot state for stale local rows,
including SCHEDULED rows.
- Orphaned bot cleanup for app-managed Recall bots that are no longer
claimed by an open call recording.
- Guards so destructive bot cleanup does not affect bots claimed by
another app registration.
- Tests for the new stale-state, missed-webhook, and orphan cleanup
behavior.

Not included -- 

- Media ingestion.
- Transcript pipeline.
- Billing.
- Marking call recordings as COMPLETED.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21720?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-17 15:42:03 +00:00
Rashad Karanouh a7760c04ab Partners app: profile picture (additive file field), derived region & deployment, scope cleanup (0.5.4) (#21709)
## Summary (twenty-partners app, v0.5.4)

- **Profile picture upload (additive)**: `profilePicture` stays a URL
(LINKS) — existing partners keep their picture — and a new
`profilePictureFile` (FILES) field is added for uploads. The read logic
functions (`list-available-partners`, `get-partner-by-slug`) select both
and **prefer the uploaded file, falling back to the legacy URL**,
returning the existing `{ primaryLinkUrl }` shape so the public
directory and the website are unchanged.
- **Region** auto-derived from the partner's country on application
creation (static lookup).
- **Deployment expertise** derived: defaults to `CLOUD`, adds
`SELF_HOST` when the partner covers Hosting & Infrastructure.
- **Partner.website** now set from the submitted domain.
- Removed 5 unused `partnerScope` categories (0 production usage); seed
remapped.
- Removed one-off data scripts (`import-from-tft`,
`migrate-partner-scope`, `partner-scope-map`).

Rebased on `main` (includes #21615 company-reuse).

## Why additive, not a field-type change

Twenty treats a field's `type` as **immutable**: an app upgrade silently
ignores a LINKS→FILES change (`fieldMetadata.type` is `toCompare: false`
in the server's flat-entity config). An in-place flip would leave the
column LINKS on prod while the display queries asked for a FILES `url`,
**breaking the partner directory**. The additive `profilePictureFile`
upgrades cleanly with no data loss; existing URLs keep working via the
legacy field + fallback. Removing the 5 unused enum options is also a
clean upgrade (0 records use them).

## Deploy notes

- Version `0.5.4`. Fully additive schema change → installs in place, no
data migration required.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21709?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-17 10:04:23 +00:00
martmull 306a1454aa Update Connection provider path (#21678)
## Before

After connecting to oAuth linear app connection:

<img width="1512" height="851" alt="image"
src="https://github.com/user-attachments/assets/39b94aaf-648f-46a6-8f4d-deb1cb7e22c5"
/>

## After

Redirects to Linear

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21678?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-16 13:53:13 +00:00
nitin 5cb8a091fc Add Recall webhook status handler to the meeting bot app (#21659)
Adds a `recall-webhook` logic function (`POST /webhook/recall`,
unauthenticated) to the `twenty-meeting-bot` app. It verifies the
Recall/Svix `whsec_` signature over the raw body, parses bot lifecycle
events, matches the corresponding `CallRecording` (by
`twentyCallRecordingId` metadata, falling back to `externalBotId`), and
updates lifecycle fields — `status`, `externalBotId`,
`externalRecordingId`, and `startedAt`/`endedAt` (only when unset) —
guarded against stale out-of-order events that would move the status
backwards. Adds the required `RECALL_WEBHOOK_SECRET` server variable.

This opens the real provider test path: install the app → schedule a bot
through the existing calendar-event flow → point a Recall webhook
endpoint at Twenty → bot lifecycle events update the matching
`CallRecording`.

Unit tests cover signature verification, status mapping, the downgrade
guard, metadata/bot-id matching, and timestamp fill.

Deferred to later PRs:
- transcript/media ingestion, file uploads, and the completion charge
(so `COMPLETED` is never set here)
- repair/reconcile cron jobs

Also flips `DEFAULT_RECALL_REGION` to `eu-central-1` (separate commit)
to match the Recall account region.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21659?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-16 10:38:15 +00:00
Raphaël Bosi e83fa2108d Add single-record People Data Labs enrich functions (company & person) (#21650)
Adds two single-record enrichment logic functions to the People Data
Labs app — `enrich-company` and `enrich-person` — that call PDL's
single-record Enrichment endpoints (`/company/enrich`,
`/person/enrich`). Each function declares both a workflow-action trigger
and an AI-tool trigger, so the same function is usable as a workflow
step and as an AI tool. They take a single `{ recordId,
overrideExistingValues? }` and return a single `EnrichResult`.

The new functions replace the previous `enrich-company-tool` /
`enrich-person-tool` AI-tool functions (which delegated to the bulk
endpoints), avoiding duplicate near-identical tools for the LLM. The
bulk `enrich-companies` / `enrich-people` workflow actions are
unchanged.

Implementation reuses the existing enrichment machinery: the
single-record adapters spread the existing company/person adapters and
only override `enrichBatch`, so identifier extraction, TTL guard, field
mapping (fill-only-if-empty), billing, and error backoff all carry over.
A new `post-pdl-single-enrich` util posts params directly and classifies
the response via the existing `parsePdlItem`.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21650?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-16 08:54:38 +00:00
nitin 12b1dba986 Add call recording scheduling backend (#21629)
This PR adds the backend scheduling slice for call recording. It wires
the `twenty-meeting-bot` internal app to reconcile calendar events,
calendar-channel associations, and workspace member auto-record
preference changes, then schedule, cancel, or reschedule Recall bots
based on the resulting policy.

It also adds the needed calendar-channel owner lookup support, generated
metadata updates, app config/default role updates, unit tests, and CI
for the internal app.

Coming next:
- Recall webhook handling and signature validation
- Stale-state convergence for failed Recall cleanup/recreate cases
- Media, transcript, audio, and video ingestion
- Billing charge flow
- Frontend/settings UI for recording controls

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21629?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-16 08:47:09 +00:00
Rashad Karanouh 065b6efe11 fix(twenty-partners): reuse existing company by domain in partner-application handler (#21615)
## Problem

Partner applications **502** for any applicant whose company is already
in the CRM.

The `submit-partner-application` logic function dedupes applicants
**only by person email**. When no person matches that email, it takes
the create path and calls `createCompany` unconditionally. But
`Company.domainName` has a **UNIQUE index**, so whenever a company with
the applicant's domain already exists — which is common, since the **TFT
import seeds companies** — the mutation throws `"duplicate entry"`. The
handler's `catch` returns `{ ok: false }`, and the website
`/api/partner-application` route surfaces it as a **502**. The applicant
can never be submitted.

Real case that surfaced this: an applicant whose company (`BKG
Integration UG`, domain `bkg-integration.de`) was already present from
the TFT import with no Partner/Person attached.

## Fix

Extract `findOrCreateCompanyId`:
- Look the company up by **exact domain** (`domainName.primaryLinkUrl
eq`) and **reuse** it when found.
- Only `createCompany` when no domain matches.
- The matched company is **never renamed** — the existing CRM name wins
over the applicant's free-text `companyName`.

Person-email dedup is unchanged (already handled upstream in the
handler).

### Known limitation
Matches **active** rows only. A *soft-deleted* company still holds the
unique index and would re-collide; clear those with `yarn purge:prod`.
Noted inline.

## Tests
Adds an integration test: pre-seed a company by domain → submit an
application with the same domain → assert the partner reuses the same
company id and the company name is untouched.

## Version
`twenty-partners` 0.5.1 → **0.5.2** (patch: bug fix, no schema change).

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21615?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-16 07:17:29 +00:00
Rashad Karanouh 09694b2f3b feat(partners): add twenty-partner-match skill (#21601)
## Summary

- Adds `twenty-partner-match` — a Claude Code skill that closes the
partner pipeline loop: query validated partners from the API, score and
explain candidates against a lead's match criteria, pause for human
validation, then generate and open all intro emails in Gmail (1 client
notification + 2N partner emails for N confirmed partners)
- Updates `twenty-partner-design-doc` to distinguish **default
zero-inference mode** (strict, 1-page brief) from `--full` inference
mode, and adds **Step 8** which always produces
`partner-match-criteria.md` alongside the brief
- Updates `design-doc-doctrine.md` with the full zero-inference /
full-mode doctrine so the Claude Code skill and a future in-product
`defineSkill` stay in sync

## Skill chain

```
/twenty-lead-intro-call-summary → /twenty-partner-design-doc → /twenty-partner-match
```

`/twenty-partner-match` chains back into the earlier skills if
`partner-match-criteria.md` is missing, and applies critical review if
the brief is thin before querying the API.

## Credentials

The skill reads `~/.twenty/credentials.env` for API keys (never
committed). See `SKILL.md` for setup instructions.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21601?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-15 14:16:23 +00:00
nitin 8a866dba54 Add call recording schema and meeting bot scaffold (#21584)
## Summary
- add 2.13 upgrade commands for call recording request status and
dropping CalendarEvent recordingPreference
- remove the recording preference from the core CalendarEvent standard
object
- add a scaffold-generated twenty-meeting-bot app with logo and the
CalendarEvent meetingBotPreference field

## Tests
- yarn install
- yarn lint
- yarn twenty dev:typecheck
- git diff --check


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21584?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-15 15:33:12 +02:00
Charles Bochet a3fe9efb69 chore(apps): bump twenty-sdk & twenty-client-sdk to 2.13.0, vitest to 4 (#21553)
## What

Bumps **all 14 `twenty-apps`** (internal, examples, community) to the
freshly published SDK **2.13.0**, and upgrades `vitest` 3 → 4 so `vite`
resolves to 8 (rolldown).

- `twenty-sdk` / `twenty-client-sdk`: `2.10.1` → `2.13.0` (each app's
original spec format preserved — plain, `^`, and `npm:…@`).
- `vitest`: `^3.x` → `^4.0.0` in the 13 apps that use it
(`call-recording` has no vitest).

## Why

Each app's `yarn.lock` had open Dependabot esbuild alerts — high
`GHSA-gv7w-rqvm-qjhr` and low `GHSA-g7r4-m6w7-qqqr`, both fixed in
esbuild `0.28.1`.

The SDK bump alone does **not** clear them: the advisories fire on *any*
esbuild `< 0.28.1`, and each app pulled a vulnerable `esbuild@0.27.7`
transitively via **vite** (through the `vitest` devDependency),
independent of the SDK. Bumping `vitest` to 4 resolves `vite@8`
(rolldown), which drops the esbuild dependency entirely.
`twenty-partners` additionally needed a recursive esbuild re-resolution
(its `tsx` dep had `esbuild@~0.28.0` pinned at the still-vulnerable
`0.28.0`).

After this change, **all 14 lockfiles resolve esbuild `0.28.1` only** —
zero copies `< 0.28.1`.

## Test

- All 14 lockfiles verified free of esbuild `< 0.28.1`.
- vitest 4 + vite 8 confirmed working: `people-data-labs` runs **334
tests across 83 files, all passing**.
- `twenty-for-twenty`'s suite fails only because its global setup
requires a live Twenty server (`/healthz`) — environmental, would fail
identically under vitest 3.


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21553?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-14 20:50:57 +02:00
martmull 1934fcc261 Add last contact twenty app (#21464)
Adds a last contact at column in people object
- backfill at installation
- update last contact when receiving an email or a calendar event
- cron to update last contact with recently passed calendar event

@Bonapara can you check the app logo?

<img width="909" height="464" alt="image"
src="https://github.com/user-attachments/assets/ca1c01a5-9838-4cf0-b0b8-d66a7f88b5fc"
/>

---------

Co-authored-by: Thomas des Francs <tdesfrancs@gmail.com>
Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
2026-06-12 13:42:18 +00:00
Raphaël Bosi e3cfbbffb5 Bill People Data Labs enrichments in Twenty credits (#21481)
Adds per-enrichment billing to the People Data Labs app. Each
**matched** record charges the workspace in Twenty credits via
`chargeCredits` (`twenty-sdk/billing`), following the same pattern as
the exa app.

- Person match: **336,000 micro-credits** ($0.336 — PDL list price $0.28
+ 20% margin)
- Company match: **120,000 micro-credits** ($0.12 — PDL list price $0.10
+ 20% margin)

> **Note:** the 20% margin is a first draft, not final — it's a single
constant (`src/constants/billing-margin-multiplier.ts`) and easy to
adjust once we settle on pricing.

PDL only consumes a credit on a successful match, so `not_found`,
errors, and skipped records are free. The charge is emitted once per PDL
batch call (≤100 records) with `operationType: CODE_EXECUTION`,
`quantity` = match count, and `resourceContext` `pdl/person` /
`pdl/company`, at the moment PDL responds — a match whose record write
later fails is still billed since the PDL cost was already incurred.
Billing failures are non-fatal and never break an enrichment.

Prices and margin live as constants in `src/constants/` for easy
retuning. No SDK bump needed (`twenty-sdk@2.10.1` already ships
`./billing`).
2026-06-12 13:13:17 +00:00
Raphaël Bosi a0e3c43234 People data labs app: remove navigation menu items (#21478)
Removes the People Data Labs app navigation menu — the "People Data
Labs" folder and its two view entries (Enriched People, Enriched
Companies) — from the sidebar, along with the now-unused navigation menu
item identifiers.

The "Enriched (PDL)" view definitions are kept and remain available on
the Person and Company objects; they just no longer appear as a folder
in the navigation menu.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21478?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-12 08:24:06 +00:00
Raphaël Bosi 2538239e05 People data labs: update app logo (#21479)
Replaces the People Data Labs internal app icon with the new logo.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21479?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-06-12 10:26:11 +02:00
Rashad Karanouh adbd78767e feat(partners): lock admin-managed + ownership fields on Partner role (#21471)
## What

Tightens the **Partner** self-service role's field-level permissions so
a partner can edit its own profile but not admin/ops-controlled or
ownership fields. All locks are `canUpdateFieldValue: false` on the
Partner object.

**Admin-managed scalar fields (7):** `slug`, `validationStage`,
`reviewed`, `ranking`, `partnerTier`, `applicationNotes`, `lastMatchAt`

**Ownership relation FKs (2):** `partnerUser`, `company`

## Why

- The 7 scalar fields are admin/ops-controlled (validation, ranking,
tiering, internal notes) — a partner must not be able to self-promote or
alter ops data.
- `partnerUser` is the **RLS pivot**: the row-level predicate scopes a
partner to records where `partnerUser IS <their workspace member>`. If a
partner could clear or repoint it, they'd drop their own record out of
scope (an orphan only admins can see). It is already locked on
Opportunity; this brings Partner in line.
- `company` is read-only at the object level for partners, so its FK
link must not be repointable from the Partner side either.

The remaining Partner relations (`opportunities`, `persons`,
`partnerContents`) need no lock — they are already protected by
inverse-side field locks or object-level read-only / no-access rules.

## Scope

- One source file: `src/roles/partner.role.ts` (9 field-permission
entries).
- No schema changes — additive permission tightening; upgrades cleanly
via `deploy` + `install`.
- Version: patch bump `0.5.0 → 0.5.1`.
2026-06-12 07:24:15 +00:00
Rashad Karanouh f7463886a6 feat(partners): partner role row-level security (RLS) with scoped edits (#21386)
## Summary
Adds an external **Partner** self-service role that sees and edits only
its own
records via row-level security (RLS), so a validated partner can sign in
and manage
just the deals they're matched on.

## What's included
- **`partnerUser` relation** on Partner, Person, Company, Opportunity (+
inverse
  relations on Workspace Member) — the login member a record belongs to.
- **RLS predicates** scoping each of those objects to "partnerUser IS
the current
workspace member", plus a self-scope on Workspace Member so member-typed
relations
resolve without exposing the internal team roster. Applied out-of-band
via
  `yarn rls:configure` (the app manifest cannot ship RLS predicates).
- **Assign / unassign cascade** (`on-opportunity-partner-assigned` logic
function):
assigning a Partner to an Opportunity stamps `partnerUser` onto the
Opportunity +
its Company + People; removing the Partner clears it (and cascades to
the
  Company/People when no other deal of that member still uses them).
- **Partner role permissions**
  - Partner profile: full read/update.
- Opportunity: read all; **update `stage` and `amount` only** (every
other
    user-facing field locked).
  - Company / Person: read-only.
  - Workspace Member: read-only, RLS-scoped to self.
- **`partnerUser` column** added to the Validated Partners view so the
login member
  can be assigned inline.

## Install / upgrade note
After install or reinstall, run `yarn rls:configure` (`:prod` variant
for prod) to
(re)apply the RLS predicates and verify the field-permission locks.
Manifest sync
handles object/field permissions; predicates are applied by this script.

## Platform gaps found (for the eng team)
1. **Manifest sync doesn't invalidate the roles-permissions Redis
cache.** Permission
changes deployed via `yarn twenty dev --once` persist to the DB but
aren't reflected
   in the cached snapshot used for enforcement until

`engine:workspace:metadata:permissions:roles-permissions:<workspaceId>:{data,hash}`
   is flushed. Relevant on any real workspace when permissions change.
2. **Locking a server-injected field silently breaks all updates.** The
`*.updateOne`
pre-query hook writes `updatedBy` into every update, so
`canUpdateFieldValue:false`
on `updatedBy` makes the permission check reject *every* record update
with
`PERMISSION_DENIED`. Field-permission lock lists must exclude
server-managed/injected
fields (`updatedBy`; and `position`, co-written with `stage` on kanban
drag).

## Version
Minor bump → `0.5.0` (new role, new fields, new behaviour;
backwards-compatible).

## Testing
- Verified locally as a partner user: edits own profile; edits
Opportunity stage +
amount; Company/Person read-only; sees only matched deals; unassigning a
partner
  removes the deal (and its company/people) from the partner's view.
- `yarn rls:configure` passes (5 predicates upserted; 24 Opportunity
fields locked,
  stage + amount editable).
- Lint clean.
2026-06-11 11:37:35 +00:00
Raphaël Bosi 6756ea628d Add People Data Labs enrichment logic functions (#21254)
## Add People Data Labs enrichment logic functions

Implements the **enrichment logic functions** that call the PDL API and
map responses onto standard + `pdl*` fields.

- **Logic functions** — shared core powers bulk workflow actions
(`enrich-person` / `enrich-company`) and single-record AI tools. Each
guards a TTL, matches via identifier, fills standard fields only when
empty, always writes `pdl*` fields, and normalizes SELECT values against
field options.
- **Data model** — PDL employer now links via the **standard `company`
relation** (find-or-create, fill-only-if-empty); removed the dedicated
`pdlCurrentCompany` relation, six `pdlJobCompany*` scalars, and the
`pdl*` indexes.

**Not functional yet:** the post-install workflow seeding
(`seed-enrichment-workflow.ts`) is left in place but blocked — it needs
workflow-builder mutations that the CRUD-only app SDK schema doesn't
expose.

**Deferred:** metering/billing and auto-enrichment triggers.



https://github.com/user-attachments/assets/a570a484-ae79-4c57-a274-e068fdab78e4



https://github.com/user-attachments/assets/6edfb5e1-cada-4892-87e0-b485eb359692
2026-06-11 09:06:20 +00:00
Charles Bochet 6e147a548b security: clear twenty-apps & seed-dependencies CVE alerts (#21410)
Clears the Oneleet/dependency CVE alerts from the `twenty-apps`
example/internal app lockfiles and the application-package
`seed-dependencies` template — all via parent/direct dependency
upgrades, **no `resolutions` overrides**.

## Lock refresh (non-breaking, within existing ranges)
- **postcss** 8.5.8/8.5.9 → 8.5.15 — CVE-2026-41305 — postcard,
hello-world, self-hosting
- **ip-address** 10.1.0 → 10.2.0 — CVE-2026-42338 — postcard,
hello-world, self-hosting, twenty-for-twenty
- **yaml** 1.10.2 → 1.10.3 — CVE-2026-33532 — call-recording

## seed-dependencies (direct/parent bumps)
- **uuid** `^10.0.0 → ^11.1.1` (direct) — CVE-2026-41907
- **body-parser** `^1.20.4 → ^1.20.5`, which pulls **qs** 6.15.2 —
CVE-2026-8723
- **socks** 2.8.3 → 2.8.9 (refresh), which pulls **ip-address** 10.2.0 —
CVE-2026-42338

## twenty-for-twenty
- **resend** 6.12.0 → 6.12.4 (refresh): 6.12.4 drops the `svix` dep that
pulled the vulnerable **uuid** 10.0.0, leaving only uuid 13.0.2 —
CVE-2026-41907

All flagged packages were transitive (except the direct seed-deps
`uuid`); no app source changes.
2026-06-10 17:02:51 +02:00
Charles Bochet a825dcf2cc security: clear 8 Dependabot alerts via transitive/parent bumps (no resolutions) (#21409)
Clears 8 Dependabot alerts via in-range transitive/parent bumps and one
dead-dependency removal. **No `resolutions` overrides** were used —
every fix is a real version bump within existing semver ranges or a
parent upgrade.

### Root `yarn.lock`
- **react-router** 6.30.3 → 6.30.4 (open redirect via protocol-relative
URL) — pulled through react-router-dom, ranges unchanged — alert #1382
- **yaml** 2.8.1 → 2.9.0 (stack overflow on deeply nested collections) —
alert #734
- **uuid** `^13.0.0` → 13.0.2 in twenty-sdk + create-twenty-app (buffer
bounds check) — alert #1164
- **ip-address** `^9.0.5` dropped by bumping **socks** 2.8.3 → 2.8.9
(now depends on `ip-address ^10.1.1`, which is unaffected) — alert #1171

### `seed-dependencies` lockfile
- **uuid** `^10.0.0` → `^11.1.1` (direct dep; removed now-redundant
`@types/uuid` since uuid v11 ships its own types) — alert #1287
- **ip-address** `^9.0.5` dropped via the same socks bump — alert #1170

### `twenty-for-twenty` lockfile
- **resend** bumped to 6.12.4 (`^6.12.0` range kept), which drops its
`svix@1.90.0 → uuid@^10` transitive chain — alert #1278

### `twenty-companion`
- Removed the unused **simplemde** dependency. The note editor loads
SimpleMDE from a CDN `<script>` tag and never imports the npm package;
`easymde` (its maintained fork) is already a dependency — alert #690

### Not addressed here
The remaining alerts can't be closed without `resolutions` overrides
(deliberately avoided in this PR) or a larger migration:
- **qs** (#1305, #1304), **lodash** (#824 high / #823 / #385), **ws**
(#1238), **postcss** (#1061) — vulnerable copies are pinned exact /
bundled by parents (express, body-parser, @nestjs/*, next,
styled-components, zapier) with no in-range patch.
- **webpack-dev-server** (#1237/#692/#691) — pinned by
`@electron-forge/plugin-webpack` (still on v4); dev-tooling only.
- **uuid <11.1.1** (#1289) — spread across `^3`/`^8`/`^9` transitive
ranges; reaching v11 is a breaking jump.
- **apollo-server-core** (#735/#736) — requires an Apollo Server 3 → 4
migration.
2026-06-10 17:02:18 +02:00
Marie 6971d6fa95 Prevent self-hosting app from re-matching/re-creating people on no-op updates (#21406)
The match-telemetry-event-with-people logic function triggers on
selfHostingUser.* (both created and updated) and unconditionally wrote
personId back to the record on every run. Since the handler's own write
produces an updated event — and the telemetry webhook also updates
unrelated fields (name, serverUrl, etc.) on returning signups — the
matching logic re-ran unnecessarily, querying people and writing on each
pass.

This adds an early-return guard so the handler only does work when
there's actually something to match:

Skip when the selfHostingUser is already linked (personId set) and the
primary email hasn't changed.
Still (re)match on first link and on genuine email changes — the
legitimate reasons for listening on updated.
The guard reads before.email.primaryEmail via an 'before' in properties
narrowing so it stays type-safe across the create/update event union.
2026-06-10 14:46:50 +00:00
Rashad Karanouh 99bd1daaec feat(twenty-partners): website field + restructured partner & opportunity views (#21385)
## What

Twenty Partners app — partner enrichment + a simpler, partner-centric
view structure.

**Partner**
- New `website` (LINKS) field.
- Icons for the opportunity `partner` relation and `matchStatus` fields
(were the default "123").

**Partner views** (Partners folder → Applications / Validated / All)
- **Applications**: grouped by validation stage, showing Application +
Potential; columns Categories / Skills / Type of Team / Languages /
Country / LinkedIn.
- **Validated**: grouped by availability; columns harmonized with
Applications, plus Partner Tier.
- Wider Name / Categories columns.

**Opportunity pipeline** (Pipeline folder, simplified by partner
presence)
- `OPP without partner` / `OPP with partner` tables (filtered on partner
*is empty* / *is not empty*), `OPP all`, and a kanban **board** grouped
by match status.
- Sort by match status (uses the option position, i.e. pipeline order).
- Removes the previous matching views (waiting-for-match,
matches-overview, partner-deals).

## Version
Patch bump `0.4.2 → 0.4.3` (relative to main).
2026-06-10 09:26:01 +00:00
Charles Bochet ca4fc5615f security: refresh lodash + picomatch in twenty-apps lockfiles (#21378)
## What

Clears the 4 remaining High alerts in the standalone `twenty-apps`
lockfiles (hello-world, call-recording). Both are transitive and already
in-range, so a plain lockfile refresh picks up the patched releases — no
resolutions.

| Package | From → To | Requested by | Advisory |
|---|---|---|---|
| lodash | 4.17.x → 4.18.1 | `@genql/runtime` (`^4.17.20`),
`twenty-client-sdk` (`^4.17.21`) | GHSA-r5fr-rjxr-66jc |
| picomatch | 4.0.x → 4.0.4 | `tinyglobby` (`^4.0.3`) |
GHSA-c2c7-rcm5-vvqj |

Only the two app `yarn.lock` files change. These are isolated
example/internal apps (not in the root workspace), in the same family as
the already-merged #21371 / #21374.
2026-06-09 19:11:02 +02:00
Charles Bochet e51efef7c8 security(apps): bump twenty-sdk to 2.10.1 for the 3 remaining pre-2.0 apps (tmp, undici) (#21374)
## Summary

Completes the follow-up flagged in #21344, which deliberately deferred
the **three apps pinning a pre-2.0 `twenty-sdk`** (a major jump that
needed per-app validation). These were the last `twenty-apps/*`
lockfiles still carrying the `tmp` + `undici` Dependabot clusters:

| App | SDK before | SDK after |
|---|---|---|
| `examples/hello-world` | `0.9.0` | `2.10.1` |
| `internal/call-recording` | `0.6.3-alpha` | `2.10.1` |
| `internal/self-hosting` | `1.22.0-canary.6` | `2.10.1` |

Bumping to `twenty-sdk@2.10.1` drops the two vulnerable transitive deps
these apps still inherited (via `inquirer ^10 → external-editor`, and
`@genql/cli`):

| Vuln dep | Advisory | Source |
|---|---|---|
| `tmp@0.0.33` |
[GHSA-ph9p-34f9-6g65](https://github.com/advisories/GHSA-ph9p-34f9-6g65)
/ CVE-2026-44705 (path traversal) | `inquirer ^10 → external-editor` |
| `undici@5.29.0` |
[GHSA-vrm6-8vpv-qv8q](https://github.com/advisories/GHSA-vrm6-8vpv-qv8q)
/ CVE-2026-1526 (websocket OOM) | `@genql/cli` |

## Changes

- Bump `twenty-sdk` (and `twenty-client-sdk` where pinned) to `2.10.1`
in all 3 apps + regenerate each lockfile.
- `hello-world` and `self-hosting` migrate transparently (typecheck
clean).
- `internal/call-recording` needed source changes for the 2.x API:
- `twenty-sdk/clients` → `twenty-client-sdk/core` +
`twenty-client-sdk/metadata` (5 files); added `twenty-client-sdk`
dependency.
- `defineRole` `permissionFlags` → `permissionFlagUniversalIdentifiers`
(`SystemPermissionFlag`) — real runtime fix (old key is silently ignored
in 2.x).

## Verification

Per-app after regen: **`tmp@0.0.33` = 0**, **`undici@5` = 0** in every
lockfile; `oxlint` passes with **0 errors**. Root `yarn.lock` untouched;
all other undici in the repo is already ≥ patched (`6.26.0` / `7.24.8`).
2026-06-09 17:24:09 +02:00
Charles Bochet 123db9e3be security: bump vite to 7.3.5 in twenty-apps lockfiles (GHSA-v2wj-q39q-566r) (#21371)
## What

The standalone apps under `packages/twenty-apps/*` each ship **their own
`yarn.lock`** (they're not part of the root workspace). Three of them
still pinned the vulnerable transitive `vite@7.3.1`:

- `examples/hello-world`
- `examples/postcard`
- `internal/call-recording`

`vite <= 7.3.1` is affected by three advisories, all first patched in
**7.3.2**:

| Advisory | Summary | Open Dependabot alerts |
|----------|---------|------------------------|
|
[GHSA-v2wj-q39q-566r](https://github.com/advisories/GHSA-v2wj-q39q-566r)
(CVE-2026-39364) | `server.fs.deny` bypassed with queries | #894, #892,
#891 |
|
[GHSA-4w7w-66w2-5vf9](https://github.com/advisories/GHSA-4w7w-66w2-5vf9)
| Path traversal in optimized-deps `.map` handling | #901, #899, #898 |
|
[GHSA-p9ff-h696-f583](https://github.com/advisories/GHSA-p9ff-h696-f583)
| Arbitrary file read via dev-server WebSocket | #908, #906, #905 |

The root `yarn.lock` was already remediated separately (vite 7.3.2 /
8.0.16); these three sub-package lockfiles were the only ones still
flagged open.

## How

Ran `yarn up -R vite` per app to re-resolve vite within the existing
range; it lands on **7.3.5**.

## Scope

- **Lockfile-only**, 3 apps. No `package.json` changes.
- Each lockfile diff is 3 lines (version / resolution / checksum).
- Verified no vite resolution below the patched thresholds remains
anywhere in the repo.
2026-06-09 16:54:53 +02:00
Charles Bochet a48c158a66 security(apps): bump twenty-sdk to 2.10.1 across twenty-apps (tmp, undici) (#21344)
## 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`.
2026-06-08 21:31:14 +02:00
Rashad Karanouh bf75ab8982 feat(twenty-partners): notify Discord on new partner application (#21313)
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.
2026-06-08 18:20:18 +00:00
Charles Bochet 5a2523f533 chore(apps): bump vitest to 3.2.6 in twenty-apps projects (GHSA-5xrq-8626-4rwp) (#21336)
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.
2026-06-08 19:36:42 +02:00
Charles Bochet 0403762516 security: refresh twenty-apps lockfiles for vulnerable transitive deps (#21316)
## 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.
2026-06-08 16:45:19 +02:00
Raphaël Bosi 36b654bab3 Scaffold people data labs enrichment app (#21175)
# Scaffold People Data Labs enrichment app

Defines the data model for enriching **Person** and **Company** with
People Data
Labs data. **Scaffold only** — the enrichment logic (the "mapper")
follows
separately; see the package README.

## Included
- **Fields** on Person & Company (PDL base data set).
- **Enums as SELECT / MULTI_SELECT** validated against PDL canonical
files (v34.1).
- **Standard-field mapping**: no `pdl*` shadow where a standard field
exists.
- **Location → ADDRESS**; **relation** `pdlCurrentCompany` ↔
`pdlCurrentEmployees`.
- **Metadata**: `pdlId`, `pdlLikelihood`, `pdlEnrichmentStatus`,
`pdlLastEnrichedAt`, `pdlRawPayload`.
- Shared option constants + helper, indexes, and a view per object.
2026-06-04 15:54:38 +00:00
Etienne 15eaabdbc1 fix(ai) - optimize crud tools (#21133)
- **Add delete many**, `delete_many_{object}` added alongside the
existing `delete_one_{object}`.
- **Uniformize naming**, crud module, type names, and MCP helper
constants renamed for consistency.
- **Optimize tool schema (learn phase)**
  - `find_many(_companies)`: **7 158 → 2 700 tokens**
  - `find_one(_company)`: **280 → 126 tokens**
  -  ....
- Main mechanism: `reused: 'ref'` (line 7 of
`to-tool-json-schema.util.ts`). Zod walks the schema tree, tracks which
Zod schema instances appear more than once, and emits each reused
instance exactly once in `$defs`, replacing all subsequent occurrences
with a `$ref`. Works because filter and value schemas are now extracted
as shared objects.

- **Optimize system prompt (tool catalog)**, DATABASE_CRUD section
restructured to list operation patterns (`find_many_{object}`, …) once +
objects once, instead of the full N×M cross-product of tool names.
- **Optimize execute_tool**, shared record-properties schema (same
`$defs` deduplication applies at call time); introduced `upsert_many`;
added `selectedFields` to `find_*` so the agent only fetches the fields
it needs.
2026-06-03 17:57:40 +00:00
Rashad Karanouh c2ad3f3614 (partners): bump app version 0.3.3 -> 0.3.4 (#21167)
Forgot to bump the version when merging #21162 from `0.3.3` to `0.3.4`
2026-06-03 07:34:28 +00:00
Rashad Karanouh ff5d082e7c feat(partners): remove Project Budget Typical field, rework partner views & nav order (#21162)
## Summary

Partners-app changes spanning the Partner object, its data scripts,
table views, and sidebar navigation.

### Remove the "Project Budget Typical" field
Dropped the `projectBudgetTypical` currency field from the Partner
object and every reference to it:
- `get-partner-by-slug` and `list-available-partners` logic-function
selections
- the seed script (type, write mapping, and per-partner data)
- the `import-from-tft` mapping (also dropping the now-unused
`partnerBudgetAverage` TFT source selection)

`projectBudgetMin` is intentionally kept.

### Rework partner views
- **Partners** (all-partners) view: replaced the **Deployment
Expertise** column with **Categories** (the `partnerScope` field).
- **Validated partners** view: added a **Languages Spoken** column.
- Set view `position`s so the in-object view switcher orders **Validated
→ Applications → Partners**.

### Navigation order
Reordered the "Partners" folder navigation items so the sidebar reads
**Validated partners → Partner applications → Partners** (Partner
content stays last).

### Also included
The previously-pushed fix that excludes partners with an empty slug from
the available-partners list.

## Notes
- No deploy/sync performed. The view-column and navigation-ordering
changes take effect once the app manifest is synced (`yarn twenty dev
--once` locally).
- The `deploymentExpertise` field itself is unchanged — only its column
was removed from the all-partners view.
2026-06-02 20:19:07 +00:00