Compare commits

..
Author SHA1 Message Date
WeikoandGitHub 941f8ede2d Merge branch 'main' into c--introduce-permission-context-into-logic-functions 2026-05-29 10:17:26 +02:00
3041ed3b6e chore: sync AI model catalog from models.dev (#21041)
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 <[email protected]>
2026-05-29 09:12:25 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Félix MalfaitWeikoclaude[bot] <41898282+claude[bot]@users.noreply.github.com>Félix Malfait
6d550611d2 chore(deps): bump typescript from 5.9.2 to 5.9.3 (#20991)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.2
to 5.9.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/microsoft/TypeScript/releases">typescript's
releases</a>.</em></p>
<blockquote>
<h2>TypeScript 5.9.3</h2>
<p>Note: this tag was recreated to point at the correct commit. The npm
package contained the correct content.</p>
<p>For release notes, check out the <a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-5-9/">release
announcement</a></p>
<ul>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+5.9.0%22+is%3Aclosed+">fixed
issues query for Typescript 5.9.0 (Beta)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+5.9.1%22+is%3Aclosed+">fixed
issues query for Typescript 5.9.1 (RC)</a>.</li>
<li><em>No specific changes for TypeScript 5.9.2 (Stable)</em></li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+5.9.3%22+is%3Aclosed+">fixed
issues query for Typescript 5.9.3 (Stable)</a>.</li>
</ul>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/microsoft/TypeScript/commit/c63de15a992d37f0d6cec03ac7631872838602cb"><code>c63de15</code></a>
Bump version to 5.9.3 and LKG</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/8428ca4cc8a7ecc9ac18dd0258016228814f5eaf"><code>8428ca4</code></a>
🤖 Pick PR <a
href="https://redirect.github.com/microsoft/TypeScript/issues/62438">#62438</a>
(Fix incorrectly ignored dts file fr...) into release-5.9 (#...</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/a131cac6831aa6532ea963d0cb3131b957cad980"><code>a131cac</code></a>
🤖 Pick PR <a
href="https://redirect.github.com/microsoft/TypeScript/issues/62351">#62351</a>
(Add missing Float16Array constructo...) into release-5.9 (#...</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/04243333584a5bfaeb3434c0982c6280fe87b8d5"><code>0424333</code></a>
🤖 Pick PR <a
href="https://redirect.github.com/microsoft/TypeScript/issues/62423">#62423</a>
(Revert PR 61928) into release-5.9 (<a
href="https://redirect.github.com/microsoft/TypeScript/issues/62425">#62425</a>)</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/bdb641a4347af822916fb8cdb9894c9c2d2421dd"><code>bdb641a</code></a>
🤖 Pick PR <a
href="https://redirect.github.com/microsoft/TypeScript/issues/62311">#62311</a>
(Fix parenthesizer rules for manuall...) into release-5.9 (#...</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/0d9b9b92e2aca2f75c979a801abbc21bff473748"><code>0d9b9b9</code></a>
🤖 Pick PR <a
href="https://redirect.github.com/microsoft/TypeScript/issues/61978">#61978</a>
(Restructure CI to prepare for requi...) into release-5.9 (#...</li>
<li><a
href="https://github.com/microsoft/TypeScript/commit/2dce0c58af51cf9a9068365dc2f756c61b82b597"><code>2dce0c5</code></a>
Intentionally regress one buggy declaration output to an older version
(<a
href="https://redirect.github.com/microsoft/TypeScript/issues/62163">#62163</a>)</li>
<li>See full diff in <a
href="https://github.com/microsoft/TypeScript/compare/v5.9.2...v5.9.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typescript&package-manager=npm_and_yarn&previous-version=5.9.2&new-version=5.9.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <[email protected]>
Co-authored-by: Weiko <[email protected]>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <[email protected]>
2026-05-29 08:39:35 +02:00
Félix MalfaitandGitHub 3c97d9648b fix(address): show saved address in record detail when street1 is null (#21033)
## Fixes #20084

### Problem
A saved address is visible in the **table view** but shows **"Empty"**
in the **record detail page** when `addressStreet1` is `null`.

This reproduces with the default seed data out of the box — e.g.
**Google** (city "Mountain View", no street), **Microsoft** (Redmond),
**Meta** (Menlo Park) — which is why several users reported hitting it
immediately.

### Root cause
The frontend zod schema required `addressStreet1` to be a **non-null**
string:

```ts
// isFieldAddressValue.ts
export const addressSchema = z.object({
  addressStreet1: z.string(),          // ← required non-null
  addressStreet2: z.string().nullable(),
  ...
});
```

…but the backend composite type marks it `isRequired: false`
(`address.composite-type.ts`), and the DB column is nullable. So the API
legitimately returns `addressStreet1: null` when only other subfields
are filled.

The two views diverge on how they render:

- **Record detail** gates the value behind `useIsFieldEmpty()` →
`isFieldValueEmpty()`, which for addresses calls
`isFieldAddressValue()`. With `addressStreet1: null` the `safeParse`
**fails**, so `isFieldValueEmpty` returns `true` and the `"Empty"`
placeholder is shown (`RecordInlineCellDisplayMode`).
- **Table view** (`RecordTableCellDisplayMode`) renders
`AddressFieldDisplay` directly with **no** empty check, so the address
stays visible.

This was a latent mismatch since the address guard was introduced.

### Fix
Make `addressStreet1` nullable to match the backend and the other
subfields:

- `addressSchema` → `addressStreet1: z.string().nullable()`
- `FieldAddressValue.addressStreet1` → `string | null`
- `FieldAddressDraftValue.addressStreet1` → `string | null` (keeps the
input/draft type consistent; the text input already renders `?? ''`)

The change is strictly more permissive — persisting and the settings
default-value form still accept string values; they now also accept
`null`.

### Tests
- `isFieldAddressValue.test.ts` — guard returns `true` for
`addressStreet1: null` with other subfields filled.
- `isFieldValueEmpty.test.ts` — new address coverage: empty address is
empty; **`street1: null` + city filled is NOT empty**; normal address is
not empty. (Added an `addressFieldDefinition` mock.)

Both new assertions were confirmed to **fail before the fix** and pass
after.

### Verification
- `npx jest isFieldValueEmpty isFieldAddressValue
normalize-address-field-value-for-persist` → 17 passed
- `npx nx typecheck twenty-front` → pass
- `npx nx lint:diff-with-main twenty-front` → 0 warnings, 0 errors
2026-05-29 08:38:47 +02:00
Félix MalfaitandGitHub 3afdabb93e fix(dashboards): isolate pie chart slice labels per widget (#21034)
## Summary

Fixes [#21014](https://github.com/twentyhq/twenty/issues/21014).

When two pie chart widgets shared the same group-by field (and therefore
the same slice ids) but used different aggregation operators (e.g.
`count` vs `sum`), the arc-link labels would mirror between the two
charts — both ending up showing either the count or the sum values,
depending on render order. Center metrics stayed correct.

**Root cause.** Nivo's `ArcLinkLabelsLayer` and `ArcsLayer` (from
`@nivo/arcs`) wire `react-spring`'s `useTransition` with `keys: e =>
e.id`. When two `<ResponsivePie>` instances render with overlapping ids,
the transitioned data bleeds across charts. The center metric is
unaffected because it's computed by a separate hook
(`usePieChartCenterMetricData`).

**Fix.** Namespace the Nivo-computed slice id per widget by passing an
`id` accessor to `<ResponsivePie>`:
```tsx
id={(datum) => `${id}:${String(datum.id)}`}
```
Lookups inside the widget switch to `datum.data.id` (the original,
un-namespaced id stored on the raw datum), so value/percentage
formatting, the custom tooltip, and the legend hover-dim behavior all
keep working.

Touched files:
- `GraphWidgetPieChart.tsx` — add `id` accessor
- `CustomArcsLayer.tsx` — compare legend highlight against
`datum.data.id`
- `getPieChartFormattedValue.ts`, `getPieChartTooltipData.ts` — match on
`datum.data.id`
- Tests for both utils get a regression case covering the namespaced
computed id

## Test plan

- [ ] `npx jest getPieChartFormattedValue` 
- [ ] `npx jest getPieChartTooltipData` 
- [ ] `npx tsc --noEmit` 
- [ ] Manual: dashboard with two pies on the same group-by field, one
`count` and one `sum`, "Display data label" on for both — confirm each
chart shows its own metric on the slices, and the central total is
unchanged.
- [ ] Manual: hover a legend item — the matching slice in that chart
stays solid while the others dim, and the sibling chart is not affected.
- [ ] Manual: clicking a slice still drills into the correctly filtered
view.
2026-05-29 05:51:10 +00:00
25b0e0d091 fix: correct typo occurence -> occurrence in metadata-event-emitter.ts (#21036)
## Summary

Fixes a spelling typo in
`packages/twenty-server/src/engine/subscriptions/metadata-event/metadata-event-emitter.ts`:

- Variable name `occurence` → `occurrence` (4 references on lines 101,
103, 114, 115)

## Changes

-
`packages/twenty-server/src/engine/subscriptions/metadata-event/metadata-event-emitter.ts`
— rename misspelled variable

Co-authored-by: james <[email protected]>
2026-05-29 05:48:57 +00:00
a43e5c3fb3 i18n - translations (#21032)
Created by Github action

---------

Co-authored-by: github-actions <[email protected]>
2026-05-28 22:15:29 +02:00
996cdaf3ff refactor(agents): split tool resolution into native and action rails (#20331)
## Summary

Splits AI agent tool resolution into two independent rails:

- **Native tools** — capabilities baked into the model SDK
(Anthropic/OpenAI `web_search`, xAI `web`/`x` provider options). Bound
by `NativeToolBinderService`, controlled by per-agent
`modelConfiguration` toggles. Opaque to Twenty — executed on the model
provider's servers.
- **Action tools** — registry-scoped tools from `ToolRegistryService`
(code interpreter, send email, record CRUD, etc.). Permission-gated via
the agent's role. Executed on Twenty's server.

Both rails merge into a single `ToolSet` at call time. When both
surfaces expose a search tool the model picks at runtime — coexistence
is intentional (relevant once Exa returns as an action, see below).

## Notable changes worth calling out

**Contract change: `AgentAsyncExecutorService.executeAgent` no longer
accepts `rolePermissionConfig`.** Workflow agents now scope exclusively
by the agent's own permission-tab role (`unionOf: [agentRoleId]`). The
previous role-merging path (caller role intersected with agent role) is
removed. No agent role → no registry tools (fail-closed by design).

**`NativeToolBinderService` relocated** from
`core-modules/tool-provider/native/` →
`metadata-modules/ai/ai-models/services/`. The binder needs SDK-package
knowledge, which lives in `ai-models`. Old location created a backwards
module dependency.

**`NATIVE_MODEL_TOOLS_BY_SDK_PACKAGE` is exhaustive over
`AiSdkPackage`** (`Record<>`, not `Partial<Record<>>`). Adding a new SDK
without thinking about native tools now fails the build. SDKs without
native tools (Bedrock, Google, Mistral, Azure, OpenAI-compatible) get
explicit `{}` entries.

**Discriminated union `kind: 'sdk-tool' | 'provider-option'`** lets one
registry describe both function tools (Anthropic/OpenAI) and runtime
sources (xAI). Follows the local `tool-provider` convention from #19321.

## Deferred to follow-ups

- **Exa web search is dropped from this PR** (along with its
`WEB_SEARCH_TOOL` permission flag and the Exa-specific gating). Exa
comes back as an **action/app tool** once apps can define permission
flags through the SDK — ongoing work in #20481.
- **xAI native search currently errors.** xAI deprecated its Live Search
API (the `web`/`x` provider-option sources this rail maps to), so xAI
returns `410` when native search is actually exercised. The code path
itself is clear — it's only hit if you test xAI native tools. Fixed
separately alongside the broader xAI model fixes.

## Conscious non-decisions

- **No "twenty-native" category.** `native` is reserved for
model/provider SDK features; everything Twenty-owned is just a
tool/action.
- **Coexistence over precedence.** No rule forcing an action search tool
to override native search (or vice-versa) — when both exist, it's the
user's choice in workflow agents and the model's choice in chat.

---------

Co-authored-by: Félix Malfait <[email protected]>
Co-authored-by: Félix Malfait <[email protected]>
2026-05-28 22:08:05 +02:00
Raphaël BosiandGitHub 1d84695fb0 Fix: focus stack overwritten when auto-opening title cell on new record (#21029)
Fixes https://github.com/twentyhq/twenty/issues/20894

In `PageChangeEffect` `resetFocusStackToFocusItem` ran right after
`openNewRecordTitleCell`, wiping the title cell entry. Typing in the
auto-opened breadcrumb input (e.g. new workflow) triggered global
shortcuts and ignored Enter / Escape / Tab.

Reordered so the page reset runs first, then the title cell push lands
on top.

## Before


https://github.com/user-attachments/assets/d3c0c266-a493-46b8-b99b-32f4381b8664


## After


https://github.com/user-attachments/assets/3a886386-f438-46d2-a6ea-9ee6908d6df2
2026-05-28 19:23:40 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Weiko
64e0b76d00 chore(deps): bump js-cookie from 3.0.5 to 3.0.7 (#20992)
Bumps [js-cookie](https://github.com/js-cookie/js-cookie) from 3.0.5 to
3.0.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/js-cookie/js-cookie/releases">js-cookie's
releases</a>.</em></p>
<blockquote>
<h2>v3.0.7</h2>
<ul>
<li>Prevent cookie attribute injection: CVE-2026-46625 (eb3c40e)</li>
<li>Add <code>Partitioned</code> attribute to readme (b994768)</li>
<li>Publish to npm registry via trusted publisher exclusively
(4dc71be)</li>
<li>Ensure consistent behaviour for <code>get('name')</code> +
<code>get()</code> (1953d30)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/js-cookie/js-cookie/commit/17bacba0171dd022728d8fdeba3203c60791bf58"><code>17bacba</code></a>
Craft v3.0.7 release</li>
<li><a
href="https://github.com/js-cookie/js-cookie/commit/adb823cb7e95ead47f3af4d4951e589acbde2077"><code>adb823c</code></a>
Fix release workflow halting at <code>git tag</code></li>
<li><a
href="https://github.com/js-cookie/js-cookie/commit/5f9e759b07d2752e8407a3a43fb5f879bf384c5e"><code>5f9e759</code></a>
May remove Git user config from release workflow</li>
<li><a
href="https://github.com/js-cookie/js-cookie/commit/6ac921184c7b3b7d9431c88707f56521acd72ab4"><code>6ac9211</code></a>
Fix release workflow not able to push commit + tag</li>
<li><a
href="https://github.com/js-cookie/js-cookie/commit/2278bc55e1804c4c2d9bd2110a9b449949a52751"><code>2278bc5</code></a>
Fix missing package version bump</li>
<li><a
href="https://github.com/js-cookie/js-cookie/commit/eb3c40e89731e99b8970faaf35ddad249c6c0020"><code>eb3c40e</code></a>
Prevent cookie attribute injection</li>
<li><a
href="https://github.com/js-cookie/js-cookie/commit/f6f157f430d707d2ffd0c9c9138227a6cea564e5"><code>f6f157f</code></a>
Bump globals from 17.5.0 to 17.6.0</li>
<li><a
href="https://github.com/js-cookie/js-cookie/commit/f409d022da50a0c6fa8724f087fbc50fab9a9533"><code>f409d02</code></a>
Bump eslint from 10.2.0 to 10.3.0</li>
<li><a
href="https://github.com/js-cookie/js-cookie/commit/a686883c03a754c04546cfc1653911a70a640b40"><code>a686883</code></a>
Bump protobufjs in the npm_and_yarn group across 1 directory</li>
<li><a
href="https://github.com/js-cookie/js-cookie/commit/c6112d2d4f2881a12aaf89d9e2996ef6870eb6d0"><code>c6112d2</code></a>
Bump <code>@​protobufjs/utf8</code> in the npm_and_yarn group across 1
directory</li>
<li>Additional commits viewable in <a
href="https://github.com/js-cookie/js-cookie/compare/v3.0.5...v3.0.7">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for js-cookie since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=js-cookie&package-manager=npm_and_yarn&previous-version=3.0.5&new-version=3.0.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Weiko <[email protected]>
2026-05-28 21:25:35 +02:00
Félix MalfaitandGitHub 8a74ea8829 fix(contact-creation): enrich missing names on auto-created contacts (#21018)
## Summary

Three related fixes to the auto-creation of People records from calendar
events and email messages, all centred on the data-quality problem of
contacts being created with missing or malformed names.

### 1. Enrich names on existing contacts (commit 1)

Previously: when an email or calendar import matched an existing Person
by email, the existing record was left untouched — even if the new
source carried a better name.

This is the root cause of contacts like `"Félix"` (no last name)
sticking around forever: the `To:`/`Cc:` headers of outbound emails
rarely include a display name, and Google Calendar only returns
`displayName` for attendees already in the organizer's address book. So
the first sighting often creates a Person as `{firstName: "felix",
lastName: ""}`, and a later inbound `From: "Félix Malfait"
<[email protected]>` — which would have produced the right name — gets
silently dropped because the Person already exists.

The new `computePeopleToEnrichNames` bucket and
`CreatePersonService.enrichPeopleNames` method fill in missing
`firstName`/`lastName` fields from the new parsed name, with
conservative rules:
- Only enrich when the existing Person's `createdBy.source` is
`CALENDAR` or `EMAIL` — `MANUAL`, `IMPORT`, `API`, `WORKFLOW`, etc. are
never touched.
- Only fill empty fields. Non-empty `firstName`/`lastName` are never
overwritten.
- Soft-deleted contacts continue to be handled by the existing restore
path.

### 2. Handle multi-comma "Last, First, Suffix" display names (commit 2)

The comma-inverted swap in the parser previously required *exactly* one
comma. Names like `"Smith, Jane, Jr."`, `"O'Brien, Mary, MD"` or `"Doe,
John, Patrick"` fell through to the space-split fallback, which stored
the comma in `firstName` (e.g. `"Smith,"`) and produced garbled records
(the avatar shows a single "B" and the name reads `"Barbey, Julien"`
because the entire string lives in `firstName`).

The regex now splits on the first comma and treats the remainder as the
first name, collapsing any further commas to spaces. Single-comma
behaviour is unchanged.

### 3. Perf: skip the parser when an existing record is already
populated (commit 3)

`computePeopleToEnrichNames` runs on every cron-driven email/calendar
import batch. The first version called the display-name parser for every
matched existing person, even when both `firstName` and `lastName` were
already set — i.e. the steady-state case after the initial enrichment
pass.

Reordered so the cheap "both fields populated" check short-circuits
before any parsing happens. Same behaviour, fewer parser calls on the
hot path.

## Test plan

- [x] 8 new unit tests for the enrichment bucket: empty `lastName`
enrichment, both `EMAIL` and `CALENDAR` sources, non-overwrite of
non-empty fields, skip on `MANUAL`/`IMPORT`, skip when the new source
also has no last name, skip for soft-deleted, fill `firstName` while
preserving `lastName`, handle null `name` field
- [x] 5 new parser tests for multi-comma forms: `"Last, First, Suffix"`,
credential suffixes (`MD`), three-token forms, whitespace around inner
commas, `:GROUP` tag interaction
- [x] 1 new parser test on the single-comma path covering multi-word
first names (`"Smith, Mary Jane"`)
- [x] All 15 existing parser tests still pass
- [x] All 116 tests in `contact-creation-manager` pass
- [x] `npx nx typecheck twenty-server`
- [x] `npx oxlint --type-aware` + `npx oxfmt --check` on changed files
- [ ] Manual: trigger a fresh contact creation from an outbound email
with no display name, then a subsequent inbound email from the same
address with a full display name, and confirm the Person's last name
gets populated
2026-05-28 20:49:56 +02:00
Félix MalfaitandGitHub f4ead89956 refactor(twenty-orm): migrate 23 grandfathered entities to WorkspaceScopedRepository (#20987)
## Summary

Follow-up to #20953. Migrates 23 of the 30 entities that were left in
`WORKSPACE_SCOPED_EXEMPTIONS` last time, so the lint rule's
workspaceId-enforcement default now covers most of the core/metadata
schema.

### Migrated (23 entities, 88 files, 22 commits)

| Family | Entities |
|---|---|
| Trivial caches | `NavigationMenuItem`, `Skill`, `DataSource`,
`Webhook`, `CommandMenuItem`, `IndexMetadata` |
| Views | `View`, `ViewField`, `ViewFieldGroup`, `ViewFilter`,
`ViewFilterGroup`, `ViewGroup`, `ViewSort` |
| Layouts | `PageLayout`, `PageLayoutTab`, `PageLayoutWidget` |
| Roles & permissions | `Role`, `RoleTarget`, `PermissionFlag`,
`ObjectPermission`, `FieldPermission`, `RowLevelPermissionPredicate`,
`RowLevelPermissionPredicateGroup` |

For each entity: swap `@InjectRepository(X)` →
`@InjectWorkspaceScopedRepository(X)` (and the field type →
`WorkspaceScopedRepository<X>`); rewrite every call site to pass
`workspaceId` as the first arg (stripped from `where`/criteria — the
wrapper throws if you include it now); register
`provideWorkspaceScopedRepository(X)` in every owning NestJS module;
update affected spec providers to
`getWorkspaceScopedRepositoryToken(X)`.

### Rule update

- `ApplicationRegistrationVariableEntity` was misclassified — moved to
`STRUCTURAL_EXEMPTIONS` (no `workspaceId` column; it's keyed on
`applicationRegistrationId` at the instance level).
- 22 of the 23 migrated entities removed from
`WORKSPACE_SCOPED_EXEMPTIONS` entirely (zero remaining raw
`@InjectRepository` sites).
- `RoleTargetEntity` also removed; one call site in
`user-workspace.service.ts` keeps a raw injection with an
`eslint-disable` + reason because `softRemove(...)` is not on the
wrapper API yet (the migration would require threading `workspaceId`
through `deleteUserWorkspace`'s three callers).

### Still exempted (7 entities, follow-up PRs)

| Entity | Why deferred |
|---|---|
| `ApplicationEntity` | ~50 sites with several cross-workspace lookups
by id (auth, OAuth, file-storage, cleanup) |
| `CalendarChannelEntity` / `MessageChannelEntity` | Use
`.increment(...)` (not on wrapper) and
`repository.manager.transaction(...)` — wrapper needs to grow
`.increment` + the transaction sites need `withManager` or dual-inject |
| `FieldMetadataEntity` / `ObjectMetadataEntity` | The metadata services
`extends TypeOrmQueryService<X>` and `super(rawRepo)` — requires
dual-inject or reworking the inheritance |
| `KeyValuePairEntity` | Allows `workspaceId: IsNull()` for
instance-level config; wrapper rejects null |
| `UpgradeMigrationEntity` | Same — instance-level + cross-workspace
ledger |

## Test plan

- [x] `npx nx typecheck twenty-server` — clean
- [x] `npx nx lint twenty-server` — clean (0/0)
- [x] All 10 affected unit specs pass (115 tests) — api-key, agent-role,
permissions, workspace-roles-permissions-cache, view-filter-group,
workflow-version-step-operations, two-factor-authentication (service +
resolver), user-workspace, file
- [ ] Server integration tests in CI
2026-05-28 20:46:21 +02:00
Félix MalfaitandGitHub 865ca697ca Fix AI permission gating: use Ask AI for chat UI, AI Settings for admin endpoints (#21030)
## Summary

Closes #20662.

Two AI permission flags exist:
- **`AI`** (label "Ask AI") — user-facing: chat with AI agents, use AI
features
- **`AI_SETTINGS`** (label "AI") — admin: create and configure AI agents

After auditing every use of these flags I found:

### Frontend — chat UI gated by the admin permission (user-facing bug
from the issue)
A user granted only `Ask AI` could not see chat tabs, the "new chat"
button (desktop & mobile), or the chat content pane; thread
initialization was also skipped, leaving the chat in a half-initialized
state and producing intermittent `THREAD_NOT_FOUND` errors. Switched
these to `AI`:
- `MainNavigationDrawerTabsRow.tsx`
- `MainNavigationDrawer.tsx`
- `MobileNavigationBar.tsx`
- `AgentChatThreadInitializationEffect.tsx`

### Backend — admin-only resolvers gated by the user permission
(privilege escalation)
Two resolvers had a class-level guard of `AI`, letting any user with the
user-facing flag reach admin endpoints (skill CRUD, eval runs). Switched
the class-level guards to `AI_SETTINGS`:
- `SkillResolver` — create/update/delete/activate/deactivate skills
- `AgentTurnResolver` — read turns, run/grade evaluations

### Left as-is (already correct)
- `AgentResolver` — class-level `AI` for reads (workflow editors and
admin pages both need them), mutation-level `AI_SETTINGS` overrides for
writes
- `AgentChatResolver` & `AgentChatSubscriptionResolver` — already `AI`
- `AiGenerateTextController` — already `AI`
- Workspace AI config fields in `workspace.service.ts` — already
`AI_SETTINGS`

## Test plan

- [ ] As a user with `Ask AI` only (no `AI_SETTINGS`): chat tabs, "new
chat" button, and chat history pane are visible on desktop + mobile;
sending a message works; no `THREAD_NOT_FOUND` errors
- [ ] As a user with `AI_SETTINGS` but no `Ask AI`: chat UI is hidden
- [ ] As a user with `Ask AI` only: calling `skills` / `createSkill` /
`agentTurns` / `runEvaluationInput` via GraphQL returns permission
denied
- [ ] As an admin (`AI_SETTINGS`): skill settings and agent eval pages
still work
2026-05-28 20:21:58 +02:00
WeikoandGitHub 0cc463c7de Merge branch 'main' into c--introduce-permission-context-into-logic-functions 2026-05-28 17:03:32 +02:00
Weiko e863318f1c review 2 2026-05-27 20:14:43 +02:00
Weiko 9842038f0f review 2026-05-27 20:06:25 +02:00
Weiko bf02a9b5d4 add test coverage for the new helper 2026-05-27 19:42:13 +02:00
Weiko b83214cb94 use cache 2026-05-27 19:35:45 +02:00
Weiko ddf657669f use auth context guard + move build auth context logic from middleware to shared util 2026-05-27 19:19:17 +02:00
Weiko ebb5758810 Introduce permission context into logic functions 2026-05-27 19:04:49 +02:00
261 changed files with 4373 additions and 1953 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

+1 -1
View File
@@ -28,7 +28,7 @@
"resolutions": {
"graphql": "16.8.1",
"type-fest": "4.10.1",
"typescript": "5.9.2",
"typescript": "5.9.3",
"nodemailer": "8.0.4",
"graphql-redis-subscriptions/ioredis": "^5.6.0",
"@lingui/core": "5.1.2",
+1 -1
View File
@@ -50,7 +50,7 @@
"jest": "29.7.0",
"jest-environment-node": "^29.4.1",
"twenty-shared": "workspace:*",
"typescript": "^5.9.2",
"typescript": "^5.9.3",
"vite": "^7.0.0",
"vite-plugin-dts": "^4.5.4",
"vite-tsconfig-paths": "^4.2.1"
@@ -0,0 +1,67 @@
import { defineLogicFunction } from 'twenty-sdk/define';
import {
FieldPermissionLevel,
getPermissionContext,
ObjectPermissionLevel,
type RoutePayload,
} from 'twenty-sdk/logic-function';
type PermissionContextDemoBody = {
objectUniversalIdentifier?: string;
fieldUniversalIdentifier?: string;
permissionFlagUniversalIdentifier?: string;
};
export const main = async (
params: RoutePayload<PermissionContextDemoBody>,
): Promise<object> => {
const {
objectUniversalIdentifier,
fieldUniversalIdentifier,
permissionFlagUniversalIdentifier,
} = params.body || {};
const permissions = getPermissionContext();
return {
canReadAllObjectRecords: permissions.canReadAllObjectRecords,
canUpdateAllObjectRecords: permissions.canUpdateAllObjectRecords,
canAccessAllTools: permissions.canAccessAllTools,
canUpdateAllSettings: permissions.canUpdateAllSettings,
canReadObject: objectUniversalIdentifier
? permissions.hasObjectPermission(
objectUniversalIdentifier,
ObjectPermissionLevel.READ,
)
: null,
canUpdateObject: objectUniversalIdentifier
? permissions.hasObjectPermission(
objectUniversalIdentifier,
ObjectPermissionLevel.UPDATE,
)
: null,
canReadField:
objectUniversalIdentifier && fieldUniversalIdentifier
? permissions.hasFieldPermission(
objectUniversalIdentifier,
fieldUniversalIdentifier,
FieldPermissionLevel.READ,
)
: null,
hasPermissionFlag: permissionFlagUniversalIdentifier
? permissions.hasPermissionFlag(permissionFlagUniversalIdentifier)
: null,
};
};
export default defineLogicFunction({
universalIdentifier: '0ef6d1e4-1a89-4b30-9c43-703fec036c89',
name: 'permission-context-demo',
timeoutSeconds: 10,
handler: main,
httpRouteTriggerSettings: {
path: '/webhook/permission-context-demo',
httpMethod: 'POST',
isAuthRequired: true,
},
});
+1 -1
View File
@@ -49,7 +49,7 @@
"@typescript/native-preview": "^7.0.0-dev.20260116.1",
"tsc-alias": "^1.8.16",
"twenty-shared": "workspace:*",
"typescript": "^5.9.2",
"typescript": "^5.9.3",
"vite": "^7.0.0",
"vite-plugin-dts": "^4.5.4",
"vite-tsconfig-paths": "^4.2.1",
@@ -3537,6 +3537,11 @@ msgstr "Kode-redigeerder"
msgid "Code Execution"
msgstr "Uitvoering van kode"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Voer 'n werkstroom uit en keer hier terug om die uitvoerings te sien"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "محرر الشفرات"
msgid "Code Execution"
msgstr "تنفيذ التعليمات البرمجية"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "\\\\"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Editor de codi"
msgid "Code Execution"
msgstr "Execució de codi"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Executa un workflow i torna aquí per veure les seves execucions"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Editor kódu"
msgid "Code Execution"
msgstr "Spuštění kódu"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Spusťte pracovní postup a vraťte se sem pro zobrazení jeho provedení"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Kodeeditor"
msgid "Code Execution"
msgstr "Kørsel af kode"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Kør en arbejdsproces og vend tilbage hertil for at se dens eksekveringer"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Code-Editor"
msgid "Code Execution"
msgstr "Codeausführung"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Führen Sie einen Workflow aus und kehren Sie zurück, um die Ausführungen anzuzeigen"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Επεξεργαστής Κώδικα"
msgid "Code Execution"
msgstr "Εκτέλεση κώδικα"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Εκτελέστε ένα workflow και επιστρέψτε εδώ για να δείτε τις εκτελέσεις του"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
+10
View File
@@ -3532,6 +3532,11 @@ msgstr "Code Editor"
msgid "Code Execution"
msgstr "Code Execution"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr "Code Interpreter"
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13411,6 +13416,11 @@ msgstr "Run"
msgid "Run a workflow and return here to view its executions"
msgstr "Run a workflow and return here to view its executions"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr "Run code to analyze files and data"
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Editor de Código"
msgid "Code Execution"
msgstr "Ejecución de código"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Ejecuta un workflow y vuelve aquí para ver sus ejecuciones"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Koodieditori"
msgid "Code Execution"
msgstr "Koodin suoritus"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Suorita työnkulku ja palaa tänne nähdäksesi sen suoritukset"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Éditeur de code"
msgid "Code Execution"
msgstr "Exécution de code"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Exécutez un workflow et revenez ici pour voir ses exécutions"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -3537,6 +3537,11 @@ msgstr "עורך קוד"
msgid "Code Execution"
msgstr "ביצוע קוד"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "\\"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Kódszerkesztő"
msgid "Code Execution"
msgstr "Kódvégrehajtás"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Futtasson egy munkafolyamatot, és térjen vissza ide a végrehajtások megtekintéséhez"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Editor di Codice"
msgid "Code Execution"
msgstr "Esecuzione del codice"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Esegui un workflow e torna qui per visualizzare le esecuzioni"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "コードエディタ"
msgid "Code Execution"
msgstr "コード実行"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "ワークフローを実行し、その実行内容を見るためにここに戻る"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "코드 편집기"
msgid "Code Execution"
msgstr "코드 실행"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "워크플로를 실행하고 여기로 돌아와 실행 결과를 확인하세요"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Codebewerkingsprogramma"
msgid "Code Execution"
msgstr "Code-uitvoering"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Voer een workflow uit en keer hier terug om de uitvoeringen te bekijken"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Koderedigerer"
msgid "Code Execution"
msgstr "Kodekjøring"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Kjør en arbeidsflyt og kom tilbake her for å se på kjøringene"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Edytor kodu"
msgid "Code Execution"
msgstr "Wykonywanie kodu"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Uruchom workflow i wróć tutaj, aby zobaczyć jego wykonania"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3532,6 +3532,11 @@ msgstr ""
msgid "Code Execution"
msgstr ""
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13388,6 +13393,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr ""
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Editor de Código"
msgid "Code Execution"
msgstr "Execução de código"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Execute um workflow e retorne aqui para visualizar suas execuções"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Editor de Código"
msgid "Code Execution"
msgstr "Execução de Código"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Execute um workflow e volte aqui para ver as suas execuções"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Editor de Cod"
msgid "Code Execution"
msgstr "Executare cod"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Rulează un workflow și întoarce-te aici pentru a vedea execuțiile acestuia"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
Binary file not shown.
@@ -3537,6 +3537,11 @@ msgstr "Код Едитор"
msgid "Code Execution"
msgstr "Извршавање кода"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Покрените радни процес и вратите се овде да погледате његове извршавања"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Kodredigerare"
msgid "Code Execution"
msgstr "Kodkörning"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13395,6 +13400,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Kör en arbetsflöde och återvänd hit för att se dess körningar"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Kod Düzenleyici"
msgid "Code Execution"
msgstr "Kod Yürütme"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Bir iş akışı çalıştırın ve yürütmelerini görmek için buraya geri dönün"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Редактор коду"
msgid "Code Execution"
msgstr "Виконання коду"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Запустіть workflow і поверніться сюди, щоб переглянути його виконання"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "Trình chỉnh sửa mã"
msgid "Code Execution"
msgstr "Thực thi mã"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "Chạy một quy trình làm việc và trở lại đây để xem các thực thi của nó"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "代码编辑器"
msgid "Code Execution"
msgstr "代码执行"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "运行工作流并返回此处查看其执行情况"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -3537,6 +3537,11 @@ msgstr "代碼編輯器"
msgid "Code Execution"
msgstr "程式碼執行"
#. js-lingui-id: T9qjpu
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Code Interpreter"
msgstr ""
#. js-lingui-id: bKJYIa
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx
msgid "Code your function"
@@ -13393,6 +13398,11 @@ msgstr ""
msgid "Run a workflow and return here to view its executions"
msgstr "運行工作流程並返回此處查看其執行情況"
#. js-lingui-id: rRyKdg
#: src/modules/settings/roles/role-permissions/permission-flags/hooks/useActionRolePermissionFlagConfig.ts
msgid "Run code to analyze files and data"
msgstr ""
#. js-lingui-id: UPaWvi
#: src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx
msgid "Run Function"
@@ -31,9 +31,7 @@ import {
export const AgentChatThreadInitializationEffect = () => {
const client = useApolloClient();
const { replaceDraft, applyChanges } = useUpdateMetadataStoreDraft();
const hasAiSettingsPermission = useHasPermissionFlag(
PermissionFlagType.AI_SETTINGS,
);
const hasAiPermission = useHasPermissionFlag(PermissionFlagType.AI);
const currentAiChatThread = useAtomStateValue(currentAiChatThreadState);
const setCurrentAiChatThread = useSetAtomState(currentAiChatThreadState);
@@ -58,7 +56,7 @@ export const AgentChatThreadInitializationEffect = () => {
useAtomState(hasInitializedAgentChatThreadsState);
useEffect(() => {
if (storeEntry.status !== 'empty' || !hasAiSettingsPermission) {
if (storeEntry.status !== 'empty' || !hasAiPermission) {
return;
}
@@ -75,19 +73,13 @@ export const AgentChatThreadInitializationEffect = () => {
replaceDraft('agentChatThreads', result.data.chatThreads);
applyChanges();
});
}, [
storeEntry.status,
hasAiSettingsPermission,
client,
replaceDraft,
applyChanges,
]);
}, [storeEntry.status, hasAiPermission, client, replaceDraft, applyChanges]);
useEffect(() => {
setAgentChatThreadsLoading(
storeEntry.status === 'empty' && hasAiSettingsPermission,
storeEntry.status === 'empty' && hasAiPermission,
);
}, [storeEntry.status, hasAiSettingsPermission, setAgentChatThreadsLoading]);
}, [storeEntry.status, hasAiPermission, setAgentChatThreadsLoading]);
useEffect(() => {
if (
@@ -97,7 +89,7 @@ export const AgentChatThreadInitializationEffect = () => {
return;
}
if (storeEntry.status === 'empty' && hasAiSettingsPermission) {
if (storeEntry.status === 'empty' && hasAiPermission) {
return;
}
@@ -152,7 +144,7 @@ export const AgentChatThreadInitializationEffect = () => {
}, [
agentChatVisibleThreads,
currentAiChatThread,
hasAiSettingsPermission,
hasAiPermission,
hasInitializedAgentChatThreads,
setHasInitializedAgentChatThreads,
storeEntry.status,
@@ -69,7 +69,10 @@ export const SettingsAgentModelCapabilities = ({
return null;
}
if (!nativeCapabilities.webSearch && !nativeCapabilities.twitterSearch) {
const showNativeWebSearch = nativeCapabilities.webSearch;
const showNativeTwitterSearch = nativeCapabilities.twitterSearch;
if (!showNativeWebSearch && !showNativeTwitterSearch) {
return null;
}
@@ -91,7 +94,7 @@ export const SettingsAgentModelCapabilities = ({
};
const capabilities = [
...(nativeCapabilities.webSearch
...(showNativeWebSearch
? [
{
key: 'webSearch' as const,
@@ -101,7 +104,7 @@ export const SettingsAgentModelCapabilities = ({
},
]
: []),
...(nativeCapabilities.twitterSearch
...(showNativeTwitterSearch
? [
{
key: 'twitterSearch' as const,
@@ -224,6 +224,23 @@ export const PageChangeEffect = () => {
}
case isMatchingLocation(location, AppPath.RecordShowPage): {
const isNewRecord = location.state?.isNewRecord === true;
const isSidePanelOpen = store.get(isSidePanelOpenedState.atom);
if (!isSidePanelOpen) {
resetFocusStackToFocusItem({
focusStackItem: {
focusId: PageFocusId.RecordShowPage,
componentInstance: {
componentType: FocusComponentType.PAGE,
componentInstanceId: PageFocusId.RecordShowPage,
},
globalHotkeysConfig: {
enableGlobalHotkeysWithModifiers: true,
enableGlobalHotkeysConflictingWithKeyboard: true,
},
},
});
}
if (
isNewRecord &&
@@ -234,26 +251,6 @@ export const PageChangeEffect = () => {
fieldName: location.state.labelIdentifierFieldName,
});
}
const isSidePanelOpen = store.get(isSidePanelOpenedState.atom);
if (isSidePanelOpen) {
return;
}
resetFocusStackToFocusItem({
focusStackItem: {
focusId: PageFocusId.RecordShowPage,
componentInstance: {
componentType: FocusComponentType.PAGE,
componentInstanceId: PageFocusId.RecordShowPage,
},
globalHotkeysConfig: {
enableGlobalHotkeysWithModifiers: true,
enableGlobalHotkeysConflictingWithKeyboard: true,
},
},
});
break;
}
case isMatchingLocation(location, AppPath.SignInUp): {
@@ -16,12 +16,10 @@ export const MainNavigationDrawer = ({ className }: { className?: string }) => {
navigationDrawerActiveTabState,
);
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
const hasAiSettingsPermission = useHasPermissionFlag(
PermissionFlagType.AI_SETTINGS,
);
const hasAiPermission = useHasPermissionFlag(PermissionFlagType.AI);
const showAiChatContent =
hasAiSettingsPermission &&
hasAiPermission &&
navigationDrawerActiveTab === NAVIGATION_DRAWER_TABS.AI_CHAT_HISTORY;
return (
@@ -144,13 +144,11 @@ export const MainNavigationDrawerTabsRow = () => {
const setIsNavigationDrawerExpanded = useSetAtomState(
isNavigationDrawerExpandedState,
);
const hasAiSettingsPermission = useHasPermissionFlag(
PermissionFlagType.AI_SETTINGS,
);
const hasAiPermission = useHasPermissionFlag(PermissionFlagType.AI);
const isExpanded = isNavigationDrawerExpanded || isMobile;
if (!hasAiSettingsPermission) {
if (!hasAiPermission) {
return null;
}
@@ -41,9 +41,7 @@ export const MobileNavigationBar = () => {
const { switchToNewChat } = useSwitchToNewAiChat();
const { alphaSortedActiveNonSystemObjectMetadataItems } =
useFilteredObjectMetadataItems();
const hasAiSettingsPermission = useHasPermissionFlag(
PermissionFlagType.AI_SETTINGS,
);
const hasAiPermission = useHasPermissionFlag(PermissionFlagType.AI);
const setContextStoreCurrentObjectMetadataItemId = useSetAtomComponentState(
contextStoreCurrentObjectMetadataItemIdComponentState,
@@ -100,7 +98,7 @@ export const MobileNavigationBar = () => {
openRecordsSearchPage();
},
},
...(hasAiSettingsPermission
...(hasAiPermission
? [
{
name: 'newAiChat' as const,
@@ -4,7 +4,7 @@ import { useArrayField } from '@/object-record/record-field/ui/meta-types/hooks/
import { ArrayFieldMenuItem } from '@/object-record/record-field/ui/meta-types/input/components/ArrayFieldMenuItem';
import { MultiItemFieldInput } from '@/object-record/record-field/ui/meta-types/input/components/MultiItemFieldInput';
import { MULTI_ITEM_FIELD_INPUT_DROPDOWN_ID_PREFIX } from '@/object-record/record-field/ui/meta-types/input/constants/MultiItemFieldInputDropdownClickOutsideId';
import { arraySchema } from '@/object-record/record-field/ui/types/guards/isFieldArrayValue';
import { arrayFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/arrayFieldValueSchema';
import { useContext, useMemo } from 'react';
import { MULTI_ITEM_FIELD_DEFAULT_MAX_VALUES } from 'twenty-shared/constants';
import { isDefined } from 'twenty-shared/utils';
@@ -22,7 +22,7 @@ export const ArrayFieldInput = () => {
[draftValue],
);
const parseStringArrayToArrayValue = (arrayItems: string[]) => {
const parseResponse = arraySchema.safeParse(arrayItems);
const parseResponse = arrayFieldValueSchema.safeParse(arrayItems);
if (parseResponse.success) {
return parseResponse.data;
@@ -4,7 +4,7 @@ import { EmailsFieldMenuItem } from '@/object-record/record-field/ui/meta-types/
import { MULTI_ITEM_FIELD_INPUT_DROPDOWN_ID_PREFIX } from '@/object-record/record-field/ui/meta-types/input/constants/MultiItemFieldInputDropdownClickOutsideId';
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/ui/states/recordFieldInputIsFieldInErrorComponentState';
import { type FieldEmailsValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { emailsSchema } from '@/object-record/record-field/ui/types/guards/isFieldEmailsValue';
import { emailsFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/emailsFieldValueSchema';
import { emailSchema } from '@/object-record/record-field/ui/validation-schemas/emailSchema';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { useLingui } from '@lingui/react/macro';
@@ -41,7 +41,7 @@ export const EmailsFieldInput = () => {
additionalEmails: nextAdditionalEmails,
};
const parseResponse = emailsSchema.safeParse(nextValue);
const parseResponse = emailsFieldValueSchema.safeParse(nextValue);
if (parseResponse.success) {
return parseResponse.data;
@@ -9,7 +9,7 @@ import { MULTI_ITEM_FIELD_INPUT_DROPDOWN_ID_PREFIX } from '@/object-record/recor
import { uploadMultipleFiles } from '@/object-record/record-field/ui/meta-types/utils/uploadMultipleFiles';
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/ui/states/recordFieldInputIsFieldInErrorComponentState';
import { type FieldFilesValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { filesSchema } from '@/object-record/record-field/ui/types/guards/isFieldFilesValue';
import { filesFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/filesFieldValueSchema';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { filePreviewState } from '@/ui/field/display/states/filePreviewState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
@@ -39,7 +39,7 @@ export const FilesFieldInput = () => {
const parseFilesArrayToFilesValue = useCallback(
(filesArray: FieldFilesValue[]) => {
const parseResponse = filesSchema.safeParse(filesArray);
const parseResponse = filesFieldValueSchema.safeParse(filesArray);
if (parseResponse.success) {
return parseResponse.data;
}
@@ -5,7 +5,7 @@ import { MULTI_ITEM_FIELD_INPUT_DROPDOWN_ID_PREFIX } from '@/object-record/recor
import { getFieldLinkDefinedLinks } from '@/object-record/record-field/ui/meta-types/input/utils/getFieldLinkDefinedLinks';
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/ui/states/recordFieldInputIsFieldInErrorComponentState';
import { type FieldLinksValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { linksSchema } from '@/object-record/record-field/ui/types/guards/isFieldLinksValue';
import { linksFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/linksFieldValueSchema';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { useContext, useMemo } from 'react';
import { MULTI_ITEM_FIELD_DEFAULT_MAX_VALUES } from 'twenty-shared/constants';
@@ -38,7 +38,7 @@ export const LinksFieldInput = () => {
primaryLinkLabel: nextPrimaryLink?.label ?? null,
secondaryLinks: nextSecondaryLinks,
};
const parseResponse = linksSchema.safeParse(nextValue);
const parseResponse = linksFieldValueSchema.safeParse(nextValue);
if (parseResponse.success) {
return parseResponse.data;
}
@@ -18,7 +18,7 @@ import {
type FieldPhonesValue,
type PhoneRecord,
} from '@/object-record/record-field/ui/types/FieldMetadata';
import { phonesSchema } from '@/object-record/record-field/ui/types/guards/isFieldPhonesValue';
import { phonesFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/phonesFieldValueSchema';
import { PhoneCountryPickerDropdownButton } from '@/ui/input/components/internal/phone/components/PhoneCountryPickerDropdownButton';
import { useContext } from 'react';
import { MULTI_ITEM_FIELD_DEFAULT_MAX_VALUES } from 'twenty-shared/constants';
@@ -103,7 +103,7 @@ export const PhonesFieldInput = () => {
primaryPhoneCallingCode: nextPrimaryPhone?.callingCode ?? '',
additionalPhones: nextAdditionalPhones,
};
const parseResponse = phonesSchema.safeParse(nextValue);
const parseResponse = phonesFieldValueSchema.safeParse(nextValue);
if (parseResponse.success) {
return parseResponse.data;
}
@@ -49,7 +49,7 @@ export type FieldCurrencyDraftValue = {
};
export type FieldFullNameDraftValue = { firstName: string; lastName: string };
export type FieldAddressDraftValue = {
addressStreet1: string;
addressStreet1: string | null;
addressStreet2: string | null;
addressCity: string | null;
addressState: string | null;
@@ -256,7 +256,7 @@ export type FormFieldCurrencyValue = {
};
export type FieldFullNameValue = { firstName: string; lastName: string };
export type FieldAddressValue = {
addressStreet1: string;
addressStreet1: string | null;
addressStreet2: string | null;
addressCity: string | null;
addressState: string | null;
@@ -1,7 +1,4 @@
import {
isFieldAddressValue,
addressSchema,
} from '@/object-record/record-field/ui/types/guards/isFieldAddressValue';
import { isFieldAddressValue } from '@/object-record/record-field/ui/types/guards/isFieldAddressValue';
describe('isFieldAddressValue', () => {
it('should return true for valid address values', () => {
@@ -34,6 +31,21 @@ describe('isFieldAddressValue', () => {
).toBe(true);
});
it('should return true when addressStreet1 is null but other subfields are filled', () => {
expect(
isFieldAddressValue({
addressStreet1: null,
addressStreet2: null,
addressCity: 'Mountain View',
addressState: null,
addressPostcode: null,
addressCountry: null,
addressLat: null,
addressLng: null,
}),
).toBe(true);
});
it('should return false for incomplete address', () => {
expect(isFieldAddressValue({ addressStreet1: '123 Main St' })).toBe(false);
});
@@ -43,20 +55,3 @@ describe('isFieldAddressValue', () => {
expect(isFieldAddressValue('address')).toBe(false);
});
});
describe('addressSchema', () => {
it('should parse a valid address', () => {
const result = addressSchema.safeParse({
addressStreet1: '123 Main St',
addressStreet2: null,
addressCity: 'Paris',
addressState: null,
addressPostcode: '75001',
addressCountry: 'France',
addressLat: null,
addressLng: null,
});
expect(result.success).toBe(true);
});
});
@@ -1,28 +1,7 @@
import { z } from 'zod';
import { ALLOWED_ADDRESS_SUBFIELDS } from 'twenty-shared/types';
import { type FieldAddressValue } from '@/object-record/record-field/ui/types/FieldMetadata';
export const addressSchema = z.object({
addressStreet1: z.string(),
addressStreet2: z.string().nullable(),
addressCity: z.string().nullable(),
addressState: z.string().nullable(),
addressPostcode: z.string().nullable(),
addressCountry: z.string().nullable(),
addressLat: z.number().nullable(),
addressLng: z.number().nullable(),
});
import { addressFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/addressFieldValueSchema';
export const isFieldAddressValue = (
fieldValue: unknown,
): fieldValue is FieldAddressValue =>
addressSchema.safeParse(fieldValue).success;
export const addressSettingsSchema = z.object({
subFields: z
.array(z.enum(ALLOWED_ADDRESS_SUBFIELDS))
.min(1)
.optional()
.nullable(),
});
addressFieldValueSchema.safeParse(fieldValue).success;
@@ -1,8 +1,7 @@
import { type FieldArrayValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { z } from 'zod';
export const arraySchema = z.union([z.null(), z.array(z.string())]);
import { arrayFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/arrayFieldValueSchema';
export const isFieldArrayValue = (
fieldValue: unknown,
): fieldValue is FieldArrayValue => arraySchema.safeParse(fieldValue).success;
): fieldValue is FieldArrayValue =>
arrayFieldValueSchema.safeParse(fieldValue).success;
@@ -1,14 +1,7 @@
import { z } from 'zod';
import { CurrencyCode } from 'twenty-shared/constants';
import { type FieldCurrencyValue } from '@/object-record/record-field/ui/types/FieldMetadata';
const currencySchema = z.object({
currencyCode: z.union([z.enum(CurrencyCode), z.literal('')]).nullable(),
amountMicros: z.number().nullable(),
});
import { currencyFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/currencyFieldValueSchema';
export const isFieldCurrencyValue = (
fieldValue: unknown,
): fieldValue is FieldCurrencyValue =>
currencySchema.safeParse(fieldValue).success;
currencyFieldValueSchema.safeParse(fieldValue).success;
@@ -1,12 +1,7 @@
import { z } from 'zod';
import { type FieldEmailsValue } from '@/object-record/record-field/ui/types/FieldMetadata';
export const emailsSchema = z.object({
primaryEmail: z.string(),
additionalEmails: z.array(z.string()).nullable(),
}) satisfies z.ZodType<FieldEmailsValue>;
import { emailsFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/emailsFieldValueSchema';
export const isFieldEmailsValue = (
fieldValue: unknown,
): fieldValue is FieldEmailsValue => emailsSchema.safeParse(fieldValue).success;
): fieldValue is FieldEmailsValue =>
emailsFieldValueSchema.safeParse(fieldValue).success;
@@ -1,26 +1,7 @@
import { type FieldFilesValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { z } from 'zod';
import { filesFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/filesFieldValueSchema';
const fileSchema = z.object({
fileId: z.string(),
label: z.string(),
extension: z.string().optional(),
url: z.string().optional(),
fileCategory: z
.enum([
'ARCHIVE',
'AUDIO',
'IMAGE',
'PRESENTATION',
'SPREADSHEET',
'TEXT_DOCUMENT',
'VIDEO',
'OTHER',
] as const)
.optional(),
});
export const filesSchema = z.array(fileSchema);
export const isFieldFilesValue = (
fieldValue: unknown,
): fieldValue is FieldFilesValue[] => filesSchema.safeParse(fieldValue).success;
): fieldValue is FieldFilesValue[] =>
filesFieldValueSchema.safeParse(fieldValue).success;
@@ -1,13 +1,7 @@
import { z } from 'zod';
import { type FieldFullNameValue } from '@/object-record/record-field/ui/types/FieldMetadata';
const fullnameSchema = z.object({
firstName: z.string(),
lastName: z.string(),
});
import { fullNameFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/fullNameFieldValueSchema';
export const isFieldFullNameValue = (
fieldValue: unknown,
): fieldValue is FieldFullNameValue =>
fullnameSchema.safeParse(fieldValue).success;
fullNameFieldValueSchema.safeParse(fieldValue).success;
@@ -1,19 +1,7 @@
import { z } from 'zod';
import { type FieldLinksValue } from '@/object-record/record-field/ui/types/FieldMetadata';
export const linksSchema = z.object({
primaryLinkLabel: z.string().nullable(),
primaryLinkUrl: z.string().nullable(),
secondaryLinks: z
.array(
z.object({
label: z.string().nullable(),
url: z.string().nullable(),
}),
)
.nullable(),
}) satisfies z.ZodType<FieldLinksValue>;
import { linksFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/linksFieldValueSchema';
export const isFieldLinksValue = (
fieldValue: unknown,
): fieldValue is FieldLinksValue => linksSchema.safeParse(fieldValue).success;
): fieldValue is FieldLinksValue =>
linksFieldValueSchema.safeParse(fieldValue).success;
@@ -1,22 +1,7 @@
import { z } from 'zod';
import { type FieldPhonesValue } from '@/object-record/record-field/ui/types/FieldMetadata';
export const phonesSchema = z.object({
primaryPhoneNumber: z.string(),
primaryPhoneCountryCode: z.string(),
primaryPhoneCallingCode: z.string(),
additionalPhones: z
.array(
z.object({
number: z.string(),
callingCode: z.string(),
countryCode: z.string(),
}),
)
.nullable(),
}) satisfies z.ZodType<FieldPhonesValue>;
import { phonesFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/phonesFieldValueSchema';
export const isFieldPhonesValue = (
fieldValue: unknown,
): fieldValue is FieldPhonesValue => phonesSchema.safeParse(fieldValue).success;
): fieldValue is FieldPhonesValue =>
phonesFieldValueSchema.safeParse(fieldValue).success;
@@ -1,27 +1,7 @@
import { z } from 'zod';
import {
type FieldJsonValue,
type Json,
} from '@/object-record/record-field/ui/types/FieldMetadata';
// See https://zod.dev/?id=json-type
const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
const jsonSchema: z.ZodType<Json> = z.lazy(() =>
z.union([
literalSchema,
z.array(jsonSchema),
z.record(z.string(), jsonSchema),
]),
);
export const jsonWithoutLiteralsSchema: z.ZodType<FieldJsonValue> = z.union([
z.null(), // Exclude literal values other than null
z.array(jsonSchema),
z.record(z.string(), jsonSchema),
]);
import { type FieldJsonValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { rawJsonFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/rawJsonFieldValueSchema';
export const isFieldRawJsonValue = (
fieldValue: unknown,
): fieldValue is FieldJsonValue =>
jsonWithoutLiteralsSchema.safeParse(fieldValue).success;
rawJsonFieldValueSchema.safeParse(fieldValue).success;
@@ -1,12 +1,7 @@
import { type FieldRichTextValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { z } from 'zod';
export const richTextSchema: z.ZodType<FieldRichTextValue> = z.object({
blocknote: z.string().nullable(),
markdown: z.string().nullable(),
});
import { richTextFieldValueSchema } from '@/object-record/record-field/ui/validation-schemas/richTextFieldValueSchema';
export const isFieldRichTextValue = (
fieldValue: unknown,
): fieldValue is FieldRichTextValue =>
richTextSchema.safeParse(fieldValue).success;
richTextFieldValueSchema.safeParse(fieldValue).success;
@@ -0,0 +1,10 @@
import { ALLOWED_ADDRESS_SUBFIELDS } from 'twenty-shared/types';
import { z } from 'zod';
export const addressFieldSettingsSchema = z.object({
subFields: z
.array(z.enum(ALLOWED_ADDRESS_SUBFIELDS))
.min(1)
.optional()
.nullable(),
});
@@ -0,0 +1,12 @@
import { z } from 'zod';
export const addressFieldValueSchema = z.object({
addressStreet1: z.string().nullable(),
addressStreet2: z.string().nullable(),
addressCity: z.string().nullable(),
addressState: z.string().nullable(),
addressPostcode: z.string().nullable(),
addressCountry: z.string().nullable(),
addressLat: z.number().nullable(),
addressLng: z.number().nullable(),
});
@@ -0,0 +1,3 @@
import { z } from 'zod';
export const arrayFieldValueSchema = z.union([z.null(), z.array(z.string())]);
@@ -0,0 +1,7 @@
import { CurrencyCode } from 'twenty-shared/constants';
import { z } from 'zod';
export const currencyFieldValueSchema = z.object({
currencyCode: z.union([z.enum(CurrencyCode), z.literal('')]).nullable(),
amountMicros: z.number().nullable(),
});
@@ -0,0 +1,8 @@
import { z } from 'zod';
import { type FieldEmailsValue } from '@/object-record/record-field/ui/types/FieldMetadata';
export const emailsFieldValueSchema = z.object({
primaryEmail: z.string(),
additionalEmails: z.array(z.string()).nullable(),
}) satisfies z.ZodType<FieldEmailsValue>;
@@ -0,0 +1,22 @@
import { z } from 'zod';
const fileSchema = z.object({
fileId: z.string(),
label: z.string(),
extension: z.string().optional(),
url: z.string().optional(),
fileCategory: z
.enum([
'ARCHIVE',
'AUDIO',
'IMAGE',
'PRESENTATION',
'SPREADSHEET',
'TEXT_DOCUMENT',
'VIDEO',
'OTHER',
] as const)
.optional(),
});
export const filesFieldValueSchema = z.array(fileSchema);
@@ -0,0 +1,6 @@
import { z } from 'zod';
export const fullNameFieldValueSchema = z.object({
firstName: z.string(),
lastName: z.string(),
});
@@ -0,0 +1,16 @@
import { z } from 'zod';
import { type FieldLinksValue } from '@/object-record/record-field/ui/types/FieldMetadata';
export const linksFieldValueSchema = z.object({
primaryLinkLabel: z.string().nullable(),
primaryLinkUrl: z.string().nullable(),
secondaryLinks: z
.array(
z.object({
label: z.string().nullable(),
url: z.string().nullable(),
}),
)
.nullable(),
}) satisfies z.ZodType<FieldLinksValue>;
@@ -0,0 +1,18 @@
import { z } from 'zod';
import { type FieldPhonesValue } from '@/object-record/record-field/ui/types/FieldMetadata';
export const phonesFieldValueSchema = z.object({
primaryPhoneNumber: z.string(),
primaryPhoneCountryCode: z.string(),
primaryPhoneCallingCode: z.string().optional(),
additionalPhones: z
.array(
z.object({
number: z.string(),
callingCode: z.string(),
countryCode: z.string(),
}),
)
.nullable(),
}) satisfies z.ZodType<FieldPhonesValue>;

Some files were not shown because too many files have changed in this diff Show More