38785cd4e92905ca4c194710639b2bda16bcde24
249
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4bdd866a20 |
Fix/close filter by enter (#16643)
Fixes #16636 Added useCloseDropdown() hook and set onEnter prop to onEnter={closeDropDown()} using dropdownID EDIT from @charlesBochet after refactoring: - ObjectDropdownFilters are used in 3 places: Main Filter menu, EditableChip, AdvancedFilters - deprecate vectorSearch in view filter area, we are not using them, we are doing a anyField filter now. While refactoring the points below, I did not want to maintain vectorSearch as it was not used anymore - stop confusing the dropdownId (which is an id to interact with a specific dropdown) and componentInstanceIds (which is used to scope component states) for EditableFilter case - I haven't fixed the confusion for MainFilter case - It was already handled for AdvancedFilter case --------- Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> |
||
|
|
a560e8ac83 |
feat: 🎸 added higher resoulution options in the dateTime Filter (#16548)
Title: "feat: Add second, minute & hour resolution options to relative date Filter action" --- ## Summary This PR enables support for smaller time units — **Seconds, Minutes, and Hours** — in the *Relative Date* filter used in workflows, rather than being limited to days only. --- ## What Changed This PR extends the relative date filter to include support for the following units: ✔️ `SECOND` ✔️ `MINUTE` ✔️ `HOUR` ✔️ (Existing: `DAY`, `WEEK`, `MONTH`, etc.) Changes include: - Adding `SECOND`, `MINUTE`, and `HOUR` options to the internal relative date unit enum/constant. - Updating utility functions and parsers to correctly interpret and evaluate these new units. - Enhancing existing tests and adding new tests to cover second, minute, and hour relative filters. --- ## Testing New and updated tests include: - Unit tests for serialization of relative filter values including seconds, minutes, and hours. - Workflow filter evaluation tests that verify minute/hour resolution behaves correctly. Tests are included in the changeset. --- ## Backward Compatibility This change is fully backward compatible: - All existing relative date filters using days or larger units behave exactly as before. - Adding finer units does not alter existing stored data or workflow definitions. --- ## Issue Reference Fixes: **twentyhq/twenty#15525** <img width="1909" height="896" alt="image" src="https://github.com/user-attachments/assets/328d03dc-ca0b-4c3f-84e5-58961c178398" /> --------- Co-authored-by: Guillim <guillim@users.noreply.github.com> Co-authored-by: guillim <guigloo@msn.com> |
||
|
|
1f1a1ea138 |
fix(workflows): align variable regex with validation and prevent ReDoS (#16607)
**What this fixes:** - Addresses a CodeQL security finding: the regex used to find variables in workflow strings could be slow on malicious inputs (ReDoS). - Two alerts: [Code Scanning 181](https://github.com/twentyhq/twenty/security/code-scanning/181) and [Code Scanning 182](https://github.com/twentyhq/twenty/security/code-scanning/182) **Context:** - Our workflow system lets users insert variables like `{{user.name}}` or `{{trigger.properties.after.name}}` into strings and JSON (HTTP request bodies, record field values, etc.). - The `variable-resolver.ts` module scans these strings and replaces variables with actual values. - Our validation (`isValidVariable`) already enforces that variables contain no `{` or `}` inside them (only simple property paths like `user.name`). **The change:** - Updated the regex from `/\{\{(.*?)\}\}/g` to `/\{\{([^{}]+)\}\}/g` to match our validation pattern. - This removes the ReDoS risk and aligns the resolver with the validation contract. **Why this is safe:** - All supported workflow usage (simple variable paths) continues to work. - Both `match` and `replace` behave the same for valid variables. - Only unsupported patterns with nested braces (e.g., `{{foo {bar}}}`) would stop matching, which isn't part of our supported syntax anyway. |
||
|
|
1cbbd04761 |
Function trigger updates 2 (#16608)
- Improves route trigger job performances - expose function params types ## Before <img width="938" height="271" alt="image" src="https://github.com/user-attachments/assets/5752ba64-f31d-44ed-974d-536e63458f2c" /> ## After <img width="1000" height="559" alt="image" src="https://github.com/user-attachments/assets/b1f4927a-5f43-49f0-a606-244c72356772" /> |
||
|
|
e289f3056e |
1895 extensibility v1 application tokens 3 (#16504)
- moves applicationRoleId to application entity - add new `APPLICATION` FieldActorSource and `APPLICATION` JwtTokenTypeEnum value - create a new token with applicationId when executing a function - when applicationId is in token, check for application.defaultRole permissions -use twenty-shared types in `twenty-sdk/application` - create a new import from generate called "Twenty" that you can use directly without having to set TWENTY_API_KEY AND TWENTY_API_URL (keep metadata or core parameter only) - provide to serverless unique one time BEARER TOKEN to run it Result <img width="977" height="566" alt="image" src="https://github.com/user-attachments/assets/e78428a0-5b13-4975-aa13-58ee3b32450c" /> <img width="910" height="596" alt="image" src="https://github.com/user-attachments/assets/6ec72bf5-7655-4093-a45e-ad269595a324" /> <img width="741" height="568" alt="image" src="https://github.com/user-attachments/assets/7683944c-fd79-4417-8fb2-8e4815cc112f" /> |
||
|
|
95e0793f81 |
Compute output schema on frontend (#16530)
Fixes https://github.com/twentyhq/core-team-issues/issues/1382 Current issue : all step output schemas are computed and stored on backend side. Which means that, when the database schema is updated - like a field creation - steps needs to be deleted an recreated. Which is invisible to users. Solution : schema generation is moved on frontend side 1. Coming on the page the first time, the schema is populated for all steps except a few ones that are handled differently (Code, Webhook, http node, Agent) 2. A separated state allow to determine if a step needs a recomputation. 3. The user only needs a refresh to see the whole schema re-computed Follow-up: - check if remaining backend steps could be moved to runtime computation. But Code will still require storage. - Clean backend service that is not used anymore |
||
|
|
2e104c8e76 |
feat(ai): add code interpreter for AI data analysis (#16559)
## Summary
- Add code interpreter tool that enables AI to execute Python code for
data analysis, CSV processing, and chart generation
- Support for both local (development) and E2B (sandboxed production)
execution drivers
- Real-time streaming of stdout/stderr and generated files
- Frontend components for displaying code execution results with
expandable sections
## Code Quality Improvements
- Extract `getMimeType` to shared utility to reduce code duplication
between drivers
- Fix security issue: escape single quotes/backslashes in E2B driver env
variable injection
- Add `buildExecutionState` helper to reduce duplicated state object
construction
- Add `DEFAULT_CODE_INTERPRETER_TIMEOUT_MS` constant for consistency
- Fix lingui linting warning and TypeScript theme errors in frontend
## Test Plan
- [ ] Test code interpreter with local driver in development
- [ ] Test code interpreter with E2B driver in production environment
- [ ] Verify streaming output displays correctly in chat UI
- [ ] Verify generated files (charts, CSVs) are uploaded and
downloadable
- [ ] Test file upload flow (CSV, Excel) triggers code interpreter
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Updates generated i18n catalogs for Polish and pseudo-English, adding
strings for code execution/output (code interpreter) and various UI
messages, with minor text adjustments.
>
> - **Localization**:
> - **Generated catalogs**: Refresh `locales/generated/pl-PL.ts` and
`locales/generated/pseudo-en.ts`.
> - Add strings for code execution/output (e.g., code, copy code/output,
running/waiting states, download files, generated files, Python code
execution).
> - Include new UI texts (errors, prompts, menus) and minor text
corrections.
> - No changes to `pt-BR`; other files unchanged functionally.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
|
||
|
|
042972d7b2 |
fix(workflow): line break not supported by Send Email Nodes (#16561)
Closes #16557 Tiptap Editor (which the Send Email Node uses) , creates a content json with type 'hardBreak' for line breaks. The was no rederer defined for this `hardBreak` node type, so the `renderNode` function was ignoring that node (returning null). **Fix :** Added a renderer for `hardBreak` node type. |
||
|
|
1119e3d77e |
fix(twenty-shared): preserve special characters in URLs (#16312)
# Fix: URL Encoding Bug & Code Refactor
## Issue
**Bug**: URLs with encoded characters (e.g., `%20` for spaces) were
being double-encoded or incorrectly processed in
[lowercaseUrlOriginAndRemoveTrailingSlash](cci:1://file:///Users/apple/WebstormProjects/twenty/packages/twenty-shared/src/utils/url/lowercaseUrlOriginAndRemoveTrailingSlash.ts:4:0-16:2),
causing URL mismatches and potential data integrity issues.
**Build Error**: `TS2307: Cannot find module
'src/modules/messaging/message-import-manager/drivers/imap/utils/safe-decode-uri-component.util'`
## Root Cause Analysis
### 1. Missing URL Decoding
The
[lowercaseUrlOriginAndRemoveTrailingSlash](cci:1://file:///Users/apple/WebstormProjects/twenty/packages/twenty-shared/src/utils/url/lowercaseUrlOriginAndRemoveTrailingSlash.ts:4:0-16:2)
function was processing URLs without properly decoding URI components.
When URLs contained encoded characters like `%20`, `%2F`, etc., they
weren't being normalized correctly.
### 2. Invalid Cross-Package Import
The fix attempted to import
[safeDecodeURIComponent](cci:1://file:///Users/apple/WebstormProjects/twenty/packages/twenty-shared/src/utils/url/safeDecodeURIComponent.ts:0:0-6:2)
from `twenty-server`:
```typescript
import { safeDecodeURIComponent } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/safe-decode-uri-component.util';
```
This failed because:
- The file resides in `twenty-shared`, a separate package
- TypeScript cannot resolve internal paths from another package
- The monorepo uses package exports (`twenty-shared/utils`) for
cross-package imports, not direct file paths
## Solution
### 1. Bug Fix: Added Safe URI Decoding
Updated
[lowercaseUrlOriginAndRemoveTrailingSlash.ts](cci:7://file:///Users/apple/WebstormProjects/twenty/packages/twenty-shared/src/utils/url/lowercaseUrlOriginAndRemoveTrailingSlash.ts:0:0-0:0)
to properly decode URL components:
```typescript
export const lowercaseUrlOriginAndRemoveTrailingSlash = (rawUrl: string) => {
const url = getURLSafely(rawUrl);
if (!isDefined(url)) {
return rawUrl;
}
const lowercaseOrigin = url.origin.toLowerCase();
const path =
safeDecodeURIComponent(url.pathname) +
safeDecodeURIComponent(url.search) +
url.hash;
return (lowercaseOrigin + path).replace(/\/$/, '');
};
```
The
[safeDecodeURIComponent](cci:1://file:///Users/apple/WebstormProjects/twenty/packages/twenty-shared/src/utils/url/safeDecodeURIComponent.ts:0:0-6:2)
wrapper handles malformed URI sequences gracefully by returning the
original string if decoding fails, preventing runtime crashes.
### 2. Refactor: Consolidated Shared Utility
**Before**: Duplicate utility existed in `twenty-server`
```
twenty-server/src/modules/messaging/.../utils/safe-decode-uri-component.util.ts
```
**After**: Single source of truth in `twenty-shared`
```
twenty-shared/src/utils/url/safeDecodeURIComponent.ts
```
This follows the established pattern in the codebase where shared
utilities live in `twenty-shared` and are imported via subpath exports:
```typescript
// In twenty-server
import { safeDecodeURIComponent } from 'twenty-shared/utils';
// In twenty-shared (local import)
import { safeDecodeURIComponent } from './safeDecodeURIComponent';
```
## Files Changed
| File | Action | Description |
|------|--------|-------------|
|
[twenty-shared/src/utils/url/safeDecodeURIComponent.ts](cci:7://file:///Users/apple/WebstormProjects/twenty/packages/twenty-shared/src/utils/url/safeDecodeURIComponent.ts:0:0-0:0)
| **Created** | New shared utility (moved from twenty-server) |
|
[twenty-shared/src/utils/url/index.ts](cci:7://file:///Users/apple/WebstormProjects/twenty/packages/twenty-shared/src/utils/url/index.ts:0:0-0:0)
| **Modified** | Added export for
[safeDecodeURIComponent](cci:1://file:///Users/apple/WebstormProjects/twenty/packages/twenty-shared/src/utils/url/safeDecodeURIComponent.ts:0:0-6:2)
|
|
[twenty-shared/src/utils/url/lowercaseUrlOriginAndRemoveTrailingSlash.ts](cci:7://file:///Users/apple/WebstormProjects/twenty/packages/twenty-shared/src/utils/url/lowercaseUrlOriginAndRemoveTrailingSlash.ts:0:0-0:0)
| **Modified** | Fixed import path, now uses local relative import |
| `twenty-server/.../imap-message-text-extractor.service.ts` |
**Modified** | Updated import to use `twenty-shared/utils` |
| `twenty-server/.../safe-decode-uri-component.util.ts` | **Deleted** |
Removed duplicate utility |
## The Utility
```typescript
// safeDecodeURIComponent.ts
export const safeDecodeURIComponent = (text: string): string => {
try {
return decodeURIComponent(text);
} catch {
return text;
}
};
```
This wrapper is necessary because `decodeURIComponent()` throws a
`URIError` on malformed sequences (e.g., `%E0%A4%A`). The safe version
returns the original string instead of crashing.
## Testing
- **342 tests passed** in `twenty-shared`
- `lowercaseUrlOriginAndRemoveTrailingSlash.test.ts` validates URL
normalization behavior
- No regressions in existing functionality
## Impact
- **Bug Fixed**: URLs with encoded characters are now properly
normalized
- **Code Quality**: Eliminated code duplication between packages
- **Maintainability**: Single source of truth for URI decoding utility
- **Build**: Resolved TS2307 compilation error
---------
Co-authored-by: Joker <apple@Apples-MacBook-Pro.local>
|
||
|
|
8dbcd506ed |
feat: add feature to customize onClick behaviour for phone, email and links data type (#16265)
Fixes Issue: #15797 This PR adds a configurable click behavior setting for Phone, Email, and Links field types, allowing users to customize what happens when clicking on these fields. A new option (**Click Behaviour**) to configure the default behaviour for onClick of data is added in the settings page (Settings → Data Model → Object → Field Edit) for Phone, Email and Links data types. Users can now choose between two actions: - **Copy to clipboard**: Copies the value to clipboard with a success toast message - **Open as link**: Opens the value as a link (tel:, mailto:, or http/https) The default behaviour is persisted for all these three types(phone- Copy to clipboard, email & links: open link) to maintain backward compatibility. **Screenshots :** <img width="2084" height="1736" alt="image" src="https://github.com/user-attachments/assets/eb5d129c-e3e0-4334-b426-eb38d8a4840c" /> <img width="1474" height="1428" alt="image" src="https://github.com/user-attachments/assets/487f4e44-6151-4254-bc5c-5398be5fc087" /> <img width="1594" height="1398" alt="image" src="https://github.com/user-attachments/assets/abdbc213-7223-4d57-809e-4d55c90cda80" /> --------- Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> |
||
|
|
4f91b48470 |
feat(ai): add context usage display to AI chat (BREAKING: deploy server first) (#16518)
## Summary - Add a context usage indicator to the AI chat interface inspired by Vercel's AI SDK Context component - Display token consumption, context window utilization percentage, and estimated cost in credits - Show a circular progress ring with percentage, revealing detailed breakdown on hover ## Changes ### Backend - Stream usage metadata (tokens, model config) via `messageMetadata` callback in `agent-chat-streaming.service.ts` - Return model config from `chat-execution.service.ts` - Add usage and model types to `ExtendedUIMessage` metadata ### Frontend - New `ContextUsageProgressRing` component - circular SVG progress indicator - New `AIChatContextUsageButton` component with hover card showing: - Progress bar with used/total tokens - Input/output token counts with credit costs - Total credits consumed - Track cumulative usage in Recoil state (`agentChatUsageState`) - Reset usage when creating new chat thread - Integrate button into `AIChatTab` ## Test plan - [ ] Open AI chat and send a message - [ ] Verify the context usage button appears with percentage - [ ] Hover over the button to see detailed breakdown - [ ] Verify credits are calculated correctly - [ ] Create a new chat thread and verify usage resets to 0 |
||
|
|
ec8773437e |
Improved table flash on reload (#16419)
This PR fixes https://github.com/twentyhq/core-team-issues/issues/1732 There is still some room for improvement but the main goal is reached : removing the flash effect each time the table virtualization has to recompute due to an update after initial loading. # QA ## Create https://github.com/user-attachments/assets/1b4fc307-42ce-4ba6-b557-68ac7fcad40f ## Update with sort https://github.com/user-attachments/assets/e0700f44-8926-4395-8ab8-b32773f21de8 ## Update with filter https://github.com/user-attachments/assets/d325f85a-1a7b-4366-aac3-250331be7575 ## Soft delete https://github.com/user-attachments/assets/2c980183-c637-4aa7-a0ca-244e61396b20 ## Restore and destroy https://github.com/user-attachments/assets/01af9ec7-b442-4686-a1d7-ea1fc543fe62 ## Drag & drop https://github.com/user-attachments/assets/bba76bdb-d4ec-433d-b44e-37fdb9952b06 --------- Co-authored-by: Charles Bochet <charles@twenty.com> |
||
|
|
5af2d37253 |
[DASHBOARDS] Dashboard duplication (#16291)
## Description - Created a Dashboard duplication action - Created a new duplication custom resolver - Created the service using the v2 of the API - Created the integration tests following the v2 methodology ## Video QA https://github.com/user-attachments/assets/e409951a-5946-4da0-91a0-1f7d2ecadb08 |
||
|
|
f48adb5f07 |
Migrate page layout services (#16443)
Last step of the page layout migration: - Migrate services - Write integration tests |
||
|
|
4996f3dd28 |
Finalize twenty standard app as workspace migration object and fields (#16353)
# Introduction Related to https://github.com/twentyhq/core-team-issues/issues/1995 In this PR we're fixing the remaining object/fields validation errors resulting from standard objects and fields now passing a validation that wasn't when using the sync metadata ## Key Changes - **Field naming**: Renamed `iCalUID` to `iCalUid` for consistent camelCase convention across calendar events - **Enum standardization**: Uppercased enum values for message channels (email→EMAIL), message participants (from→FROM, to→TO, cc→CC, bcc→BCC), and message direction (incoming→INCOMING, outgoing→OUTGOING) - **Label simplification**: Removed example values from workspace member number format labels for cleaner UI - **Migration infrastructure**: Added `isSystemBuild` flag throughout field metadata service pipeline to allow system-level updates of standard fields that bypass normal restrictions ## Migrating the existing data We've created an upgrade command that will identify using the existing object and field standard id field that needs to be updated, even though the sync metadata still in usage could have fix them ( and the goal is to deprecate it by the end of the sprint ) We will call the updateOneField for each of them, we're passing by the field service in order to battle test what are going to be the temporary way to handle standard migrations when we will start deprecating the sync metadata but haven't still refactored the v2 workspace migration to be workspace agnostic ## Twenty eng migration Tested the whole migration + upgrade on twenty eng Here are generated workspace migration Records are handled natively gracefully too ### ICalUid ```json { "status": "success", "workspaceMigration": { "relatedFlatEntityMapsKeys": [ "flatFieldMetadataMaps", "flatIndexMaps", "flatViewFilterMaps", "flatViewGroupMaps", "flatViewMaps", "flatViewFieldMaps", "flatObjectMetadataMaps" ], "actions": [ { "type": "update_field", "fieldMetadataId": "", "objectMetadataId": "", "updates": [ { "from": "iCalUID", "to": "iCalUid", "property": "name" } ] } ], "workspaceId": "" } } ``` ### Incoming Outgoing None as already caps in database somehow ```json { "status": "success", "workspaceMigration": { "relatedFlatEntityMapsKeys": [ "flatFieldMetadataMaps", "flatIndexMaps", "flatViewFilterMaps", "flatViewGroupMaps", "flatViewMaps", "flatViewFieldMaps", "flatObjectMetadataMaps" ], "actions": [], "workspaceId": "" } } ``` ### EMAIL ```json { "status": "success", "workspaceMigration": { "relatedFlatEntityMapsKeys": [ "flatFieldMetadataMaps", "flatIndexMaps", "flatViewFilterMaps", "flatViewGroupMaps", "flatViewMaps", "flatViewFieldMaps", "flatObjectMetadataMaps" ], "actions": [ { "type": "update_field", "fieldMetadataId": "", "objectMetadataId": "", "updates": [ { "from": "'email'", "to": "'EMAIL'", "property": "defaultValue" }, { "from": [ { "color": "green", "id": "", "label": "Email", "position": 0, "value": "email" }, { "color": "blue", "id": "", "label": "SMS", "position": 1, "value": "sms" } ], "to": [ { "color": "green", "id": "", "label": "Email", "position": 0, "value": "EMAIL" }, { "color": "blue", "id": "", "label": "SMS", "position": 1, "value": "SMS" } ], "property": "options" } ] } ], "workspaceId": "e" } } ``` ### MessageParticipantRole ```json { "status": "success", "workspaceMigration": { "relatedFlatEntityMapsKeys": [ "flatFieldMetadataMaps", "flatIndexMaps", "flatViewFilterMaps", "flatViewGroupMaps", "flatViewMaps", "flatViewFieldMaps", "flatObjectMetadataMaps" ], "actions": [ { "type": "update_field", "fieldMetadataId": "", "objectMetadataId": "", "updates": [ { "from": "'from'", "to": "'FROM'", "property": "defaultValue" }, { "from": [ { "color": "green", "id": "", "label": "From", "position": 0, "value": "from" }, { "color": "blue", "id": "", "label": "To", "position": 1, "value": "to" }, { "color": "orange", "id": "", "label": "Cc", "position": 2, "value": "cc" }, { "color": "red", "id": "", "label": "Bcc", "position": 3, "value": "bcc" } ], "to": [ { "color": "green", "id": "", "label": "From", "position": 0, "value": "FROM" }, { "color": "blue", "id": "", "label": "To", "position": 1, "value": "TO" }, { "color": "orange", "id": "", "label": "Cc", "position": 2, "value": "CC" }, { "color": "red", "id": "", "label": "Bcc", "position": 3, "value": "BCC" } ], "property": "options" } ] } ], "workspaceId": "" } } ``` ### Workspace member number format labels ```json { "status": "success", "workspaceMigration": { "relatedFlatEntityMapsKeys": [ "flatFieldMetadataMaps", "flatIndexMaps", "flatViewFilterMaps", "flatViewGroupMaps", "flatViewMaps", "flatViewFieldMaps", "flatObjectMetadataMaps" ], "actions": [ { "type": "update_field", "fieldMetadataId": "", "objectMetadataId": "", "updates": [ { "from": [ { "color": "turquoise", "id": "", "label": "System", "position": 0, "value": "SYSTEM" }, { "color": "blue", "id": "", "label": "Commas and dot (1,234.56)", "position": 1, "value": "COMMAS_AND_DOT" }, { "color": "green", "id": "", "label": "Spaces and comma (1 234,56)", "position": 2, "value": "SPACES_AND_COMMA" }, { "color": "orange", "id": "", "label": "Dots and comma (1.234,56)", "position": 3, "value": "DOTS_AND_COMMA" }, { "color": "purple", "id": "", "label": "Apostrophe and dot (1'234.56)", "position": 4, "value": "APOSTROPHE_AND_DOT" } ], "to": [ { "color": "turquoise", "id": "", "label": "System", "position": 0, "value": "SYSTEM" }, { "color": "blue", "id": "", "label": "Commas and dot", "position": 1, "value": "COMMAS_AND_DOT" }, { "color": "green", "id": "", "label": "Spaces and comma", "position": 2, "value": "SPACES_AND_COMMA" }, { "color": "orange", "id": "", "label": "Dots and comma", "position": 3, "value": "DOTS_AND_COMMA" }, { "color": "purple", "id": "", "label": "Apostrophe and dot", "position": 4, "value": "APOSTROPHE_AND_DOT" } ], "property": "options" } ] } ], "workspaceId": "" } } ``` |
||
|
|
77e592502c |
fix: improve CRON schedule validation and display (#16360)
Fixes multiple issues with CRON schedule input validation and execution time display. ### Issues Fixed 1. **UTC label placement** - Added "UTC" suffix to specific times (e.g., "at 09:30 UTC") but not to interval descriptions (e.g., "every hour") 2. **Upcoming execution time calculation** - Fixed incorrect execution times for malformed CRON expressions by implementing auto-correction ### Changes - Created `normalizeCronExpression` utility to standardize cron expressions before parsing - Updated `formatTime` to support optional UTC suffix - Enhanced `getHoursDescription` to append UTC to specific times - Added comprehensive test coverage (102 tests passing) ### Before - `"1 /3 * * *"` showed daily executions at same time (incorrect) - `"9 * * *"` showed same time repeated 3 times (incorrect) - No UTC labels on schedule descriptions (confusing) ### After - All malformed expressions auto-corrected and show correct execution times - UTC labels clearly indicate timezone for specific times - User-friendly error messages for truly invalid patterns Closes #15870 |
||
|
|
ac89b5aff6 |
Improve object record changed performances (#16398)
Using deepEqual, average over 3 calls: - version trigger 1.53ms - version steps 38.3ms - run state 372.8 ms Using stringified hash, average over 3 calls: - version trigger 0.07ms - version steps 0.74 ms - run state 0.521ms Short fastDeepEqual - version trigger 0.028ms - version steps 0.197ms - run state 0.214ms Lib fastDeepEqual - version trigger 0.038ms - version steps 0.187ms - run state 0.230ms |
||
|
|
5fb7e76005 | Migrate page layout to v2 (#16364) | ||
|
|
7f1e69740a |
1895 extensibility v1 application tokens (#16365)
First PR to implement application tokens - add new application role in twenty-server - move duplicated constants and types to twenty-shared - will add role configuration utils into twenty-sdk in another PR |
||
|
|
077be7644c | Migrate page layout widget to v2 of the API (#16323) | ||
|
|
28cdb02fbb |
Twenty standard application Objects and fields as allFlatEntityMaps ID non-agnostic (#16298)
# Introduction Related to https://github.com/twentyhq/core-team-issues/issues/1995 This PR introduces the basis of the `twentyStandard` application as code on demand, it's highly tied to `ids` where it will becomes workspace agnostic following the builder and runner `universalIdentifier` refactor later. The goal here to allow computing the `allFlatEntityMaps` `to` of the `twentyStandard` application on a empty workspace ( workspace creation ). Allowing installing the twenty standard app through a workspace migration instead of passing by the sync metadata Nothing done will be run in production for the moment if it's not the small validation refactor we've introduced Please note that everything introduced here will be replaced at some point by a twenty app instance when the twenty sdk is mature enough to handle of the edge cases we need here ## How we've proceeded We've been iterating over every workspace entity both objects and their fields, and transpiled them to flatEntity. Being sure we migrate the defaultValue, settings and so on accordingly. We've also compute all the ids in prior of the whole entities computation so we don't face any hoisting issue. ## Current state At the moment only handling all of the 29 standard objects and their fields Settings a unique universalIdentifier for all of them Will come views, agent role targets and so on later ## `workspace:compute-twenty-standard-migration` command This command allow generating a workspace migration that will result in installing the twenty standard app in an empty workspace It's temporary and aims to allow debugging for the moment we might not keep it in the future as it is right now It contains debug writeFileSync which is expected no worries greptile ## `LabelFieldMetadataIdentifierId` Small refactor allowing defining the label identifier field metadata id of a uuid field metadata type for system object, as some of our standard object don't have a name field and don't aim to Also please note that we might remove this build options later in the sake of the currently installed universal identifier application that we could compare with the deterministic twenty standard one ## `runFlatFieldMetadataValidators` Deprecated this pattern which was redundant and not v2 friendly pattern ## Current errors that will address in upcoming PR Current standard objects and fields metadata does not pass the validation that we have in place, as historically the sync metadata would directly consume the repositories and would just ignore the validation. This is about to change. Will handle the below errors in dedicated PRs as they will required upgrade commands in order to migrate the data, or will handle that from the sync metadata instead still to be determined but nothing critical here - camel case field metadata name - options label invalid format ```json { "status": "fail", "report": { "fieldMetadata": [ { "status": "fail", "errors": [ { "code": "INVALID_FIELD_INPUT", "message": "Name should be in camelCase", "userFriendlyMessage": { "id": "P+jdmX", "message": "Name should be in camelCase" }, "value": "iCalUID" } ], "flatEntityMinimalInformation": { "id": "68dd83cd-92c8-4233-bb28-47939bab6124", "name": "iCalUID", "objectMetadataId": "11c16ab6-9176-439e-a2db-a12c5a58a524" }, "type": "create_field" }, { "status": "fail", "errors": [ { "code": "INVALID_FIELD_INPUT", "message": "Value must be in UPPER_CASE and follow snake_case \"email\"", "userFriendlyMessage": { "id": "UBPzFQ", "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"", "values": { "sanitizedValue": "email" } }, "value": "email" }, { "code": "INVALID_FIELD_INPUT", "message": "Value must be in UPPER_CASE and follow snake_case \"sms\"", "userFriendlyMessage": { "id": "UBPzFQ", "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"", "values": { "sanitizedValue": "sms" } }, "value": "sms" } ], "flatEntityMinimalInformation": { "id": "e3caaf2a-e07d-4146-8dfc-9eef904e82c9", "name": "type", "objectMetadataId": "4b777de5-4c7b-4af4-9b92-655c0f87512b" }, "type": "create_field" }, { "status": "fail", "errors": [ { "code": "INVALID_FIELD_INPUT", "message": "Value must be in UPPER_CASE and follow snake_case \"incoming\"", "userFriendlyMessage": { "id": "UBPzFQ", "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"", "values": { "sanitizedValue": "incoming" } }, "value": "incoming" }, { "code": "INVALID_FIELD_INPUT", "message": "Value must be in UPPER_CASE and follow snake_case \"outgoing\"", "userFriendlyMessage": { "id": "UBPzFQ", "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"", "values": { "sanitizedValue": "outgoing" } }, "value": "outgoing" } ], "flatEntityMinimalInformation": { "id": "d96233a4-93be-45ea-9548-3b50f3c700cf", "name": "direction", "objectMetadataId": "480a648a-d2e5-482a-992f-ef053e1b4bb0" }, "type": "create_field" }, { "status": "fail", "errors": [ { "code": "INVALID_FIELD_INPUT", "message": "Value must be in UPPER_CASE and follow snake_case \"from\"", "userFriendlyMessage": { "id": "UBPzFQ", "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"", "values": { "sanitizedValue": "from" } }, "value": "from" }, { "code": "INVALID_FIELD_INPUT", "message": "Value must be in UPPER_CASE and follow snake_case \"to\"", "userFriendlyMessage": { "id": "UBPzFQ", "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"", "values": { "sanitizedValue": "to" } }, "value": "to" }, { "code": "INVALID_FIELD_INPUT", "message": "Value must be in UPPER_CASE and follow snake_case \"cc\"", "userFriendlyMessage": { "id": "UBPzFQ", "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"", "values": { "sanitizedValue": "cc" } }, "value": "cc" }, { "code": "INVALID_FIELD_INPUT", "message": "Value must be in UPPER_CASE and follow snake_case \"bcc\"", "userFriendlyMessage": { "id": "UBPzFQ", "message": "Value must be in UPPER_CASE and follow snake_case \"{sanitizedValue}\"", "values": { "sanitizedValue": "bcc" } }, "value": "bcc" } ], "flatEntityMinimalInformation": { "id": "961c598e-67c3-452d-8bb2-b92c0bc64404", "name": "role", "objectMetadataId": "8af8a13c-ff97-4cd3-b70d-52a7dc2924b4" }, "type": "create_field" }, { "status": "fail", "errors": [ { "code": "INVALID_FIELD_INPUT", "message": "Label must not contain a comma", "userFriendlyMessage": { "id": "k731jp", "message": "Label must not contain a comma" }, "value": "Commas and dot (1,234.56)" }, { "code": "INVALID_FIELD_INPUT", "message": "Label must not contain a comma", "userFriendlyMessage": { "id": "k731jp", "message": "Label must not contain a comma" }, "value": "Spaces and comma (1 234,56)" }, { "code": "INVALID_FIELD_INPUT", "message": "Label must not contain a comma", "userFriendlyMessage": { "id": "k731jp", "message": "Label must not contain a comma" }, "value": "Dots and comma (1.234,56)" } ], "flatEntityMinimalInformation": { "id": "7fa20caf-2597-42e3-84e5-15a91b125b9b", "name": "numberFormat", "objectMetadataId": "a6974302-9e72-461c-aa09-9390f4ff16fc" }, "type": "create_field" } ], "objectMetadata": [], "view": [], "viewField": [], "viewGroup": [], "index": [], "serverlessFunction": [], "cronTrigger": [], "databaseEventTrigger": [], "routeTrigger": [], "viewFilter": [], "role": [], "roleTarget": [], "agent": [] } } ``` |
||
|
|
b2d785de4b |
Page layout tab v2 (#16319)
# Introduction Migrating `pageLayoutTab` to the v2 engine - Types and constants - Builder and validate - Runner Introduced a new `StrictSyncableEntity` that enforces that `universalIdentifier` and `applicationId` are defined As these entities are brand new we could enforce this rule already This still requires a migration command to associate the existing entities to custom workspace application instance and define universalIdentifier Handled retro-comp of the migration through a migration as upgrade command fallback --------- Co-authored-by: bosiraphael <raphael.bosi@gmail.com> |
||
|
|
d1befa7e35 |
Query complexity validation (#16274)
Validations : - relations count (in common api) - oneToMany relation nested count (in common) - requested fields count (in gql) - root resolver count (in gql) - root resolver duplicates (in gql) - specific complexity for metadata / nesting count (in gql) |
||
|
|
f0f648181e |
add is not operand on numeric fields (#16299)
closes https://github.com/twentyhq/twenty/issues/16162 |
||
|
|
59672e3e34 |
Migrate agent v2 (#16214)
# Introduction Closes https://github.com/twentyhq/core-team-issues/issues/1980 In this PR we migrate the agent from v1 to v2. ## New FlatRoleTargetByAgentIdMaps Derivated the `flatRoleTargetMaps` to be building a `flatRoleTargetByAgentIdMaps` to ease retrieving a roleId to associate to an agent ## Coverage Added strong coverage on both failing and successful CRU agents operations --------- Co-authored-by: Weiko <corentin@twenty.com> |
||
|
|
ee08060798 |
Improve deactivated objects & fields behaviors. (#16090)
Closes [1918](https://github.com/twentyhq/core-team-issues/issues/1918).
- For the first point in the issue, we just show the deactivated entries
along with the deactivated text.
---
- For the second point, we show a banner and control the
enabled/disabled state of save button depending on whether we're
allowing the user to create table with the typed name.
- For example, we do not want to allow the user to create a table with
reserved name, so we disable the save button without showing a banner.
- Similarly, we do not want the user to create a table with a name that
already exists in the database. In this case, we show a banner and we
also disable the save button.
- Finally, we do not want to allow the user to create a table where
singular and plural name are the same. Therefore, we disable the save
button for names like `works`.
---
- For the third point, if we add the delete button, it logically means
that we allow the user to delete a custom object/field even it has not
been deactivated yet, so did that.
- Upon deleting the object/field, if we wait for the metadata to refetch
before we navigate, this is what we see because the path does not exist
any longer after deletion and we're waiting for refetch on the path
until we navigate away.
https://github.com/user-attachments/assets/dbe0569c-db88-4285-851f-22551b1ca81e
- To avoid this page from appearing, I replaced awaiting refetch to not
awaiting refetch and redirecting while the refetch happens in the
background.
- Therefore, when we delete something, there is a slight delay for when
it is actually cleared out from the list, but the Not Found view does
not appear on the screen.
https://github.com/user-attachments/assets/47f49579-ce51-4d6a-b857-72046247bb4b
- I tried optimistically removing the object/field from the metadata,
but it leads to some issues (crashes the app) and I have not been able
to find a solution for it yet.
- Therefore, instead of getting stuck at perfection and blocking myself,
I stopped getting into the issue further and created this PR by ensuring
that the desired functionality works.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Display deactivated objects/fields by default, add delete actions with
confirmation, and unify metadata name computation (auto-suffix reserved
keywords) across front/back with conflict checks in object creation.
>
> - **Frontend (Settings/Data Model)**:
> - **Visibility/UX**: Show `Deactivated` labels for objects/fields;
filters default to include inactive (`showDeactivated`/`showInactive`
true); replace field action dropdown with chevron link.
> - **Delete flows**: Add delete buttons for custom objects/fields with
confirmation modals and background refetch to avoid Not Found flashes.
> - **Creation/Edit validation**: Add name conflict detection banner in
`SettingsDataModelObjectAboutForm` and disable Save on conflicts;
simplify `metadataLabelSchema` to use computed name; form fields
validate on change and sync API names.
> - **Shared (twenty-shared/metadata)**:
> - Add `computeMetadataNameFromLabel` util (slugify+camelCase) and
`RESERVED_METADATA_NAME_KEYWORDS`; auto-append `Custom` to reserved
names; export constants/utilities.
> - **Backend**:
> - Migrate to shared `computeMetadataNameFromLabel`; update validators
to use shared reserved keywords with new messages; allow deletion of
active custom fields/objects (keep standard guards); adjust
services/decorators accordingly.
> - **Tests/Stories**:
> - Update unit/integration snapshots for new reserved-name messages and
behaviors; add missing i18n/router decorators in stories.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
|
||
|
|
ea3c5d2d45 |
Migrate role and role target to v2 (#16009)
# Introduction close https://github.com/twentyhq/core-team-issues/issues/1930 close https://github.com/twentyhq/core-team-issues/issues/1929 Migrating role and roleTarget entities to the v2 core engine, allowing v2 caching leverage and allow migrating agent to v2 that needs role target in prior After agent we should be able to pass twenty standard app totally though workspace migration ## Role target assignation Please note that role target have 3 creation entrypoints: - Agent - User workspace - ApiKey Refactored all 3 of them to pass through a new role-target.service.ts that consumes the v2 under the hood. --------- Co-authored-by: Weiko <corentin@twenty.com> |
||
|
|
04b01170ed |
Introduce a workspace member page. (#16031)
- Refactored workspace member details into a focused Infos-only page.
- Aligned the flow with SettingsProfile, including controlled name
inputs, debounced saves, and stable instance IDs.
- Added a dedicated member-picture upload flow. Introduced the
MemberPictureUploader, connected to the
uploadWorkspaceMemberProfilePicture mutation.
- Backend now includes a workspace-member resolver/module for
profile-picture uploads. The endpoint is permission-guarded, streams
files through FileUploadService, and returns the signed file without
modifying the member entity.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Adds a workspace member detail page with picture/name management,
integrates a new avatar upload mutation, and updates list routing;
replaces the old profile picture uploader across profile and onboarding.
>
> - **Frontend**
> - **Settings Members**:
> - Add `pages/settings/members/SettingsWorkspaceMember` with
`MemberInfosTab`, `MemberNameFields`, and `MemberEmailField` for
viewing/editing member info.
> - Update routes in `SettingsRoutes` and add
`SettingsPath.WorkspaceMemberPage`.
> - Update `SettingsWorkspaceMembers` to navigate to member detail on
row click and simplify row actions (remove dropdown menu).
> - **Avatar Upload**:
> - Introduce `WorkspaceMemberPictureUploader` using
`uploadWorkspaceMemberProfilePicture` mutation.
> - Replace `ProfilePictureUploader` in `SettingsProfile` and
`onboarding/CreateProfile`.
> - **GraphQL (client)**:
> - Add `uploadWorkspaceMemberProfilePicture` mutation types/hooks in
`generated(-metadata)/graphql.ts`.
> - **Backend**
> - Add `UserWorkspaceResolver` with
`uploadWorkspaceMemberProfilePicture` mutation guarded by
`WorkspaceAuthGuard` and `SettingsPermissionGuard` (WORKSPACE_MEMBERS),
using `FileUploadService`.
> - Register resolver and `PermissionsModule` in `UserWorkspaceModule`.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
|
||
|
|
eaac569812 |
Fix variable usage in Search Record workflow action (#16147)
Closes https://github.com/twentyhq/twenty/issues/16141 --------- Co-authored-by: prastoin <paul@twenty.com> |
||
|
|
1a45576990 |
Morph-add-new-object-destination (#16027)
Add new object target to an existing morph relation (backend only) Fixes https://github.com/twentyhq/core-team-issues/issues/1898 --------- Co-authored-by: prastoin <paul@twenty.com> |
||
|
|
9c9a01d55a |
[groupBy][Requires cache flush] Add WEEK date granularity (#16099)
Closes https://github.com/twentyhq/core-team-issues/issues/1921 https://github.com/user-attachments/assets/bf400ec1-ce25-4d9e-b875-774168452514 |
||
|
|
4f20fd35c5 |
feat: Add Agent Evaluation System and Refactor AI Modules (#16111)
## Summary This PR introduces a comprehensive agent evaluation system and refactors the AI module structure for better organization. ## Key Changes ### 🎯 Agent Evaluation System - Added **Agent Turn Evaluation** entities, DTOs, and database schema - New GraphQL mutations: `evaluateAgentTurn` and `runEvaluationInput` - Added `evaluationInputs` field to Agent entity for storing test inputs - New `AgentTurnGraderService` for automatic turn evaluation - Added evaluation UI with new **Evals** and **Logs** tabs in agent detail pages ### 🏗️ Entity & Module Refactoring - Renamed `AgentChatMessage` → `AgentMessage` for clarity - Consolidated chat entities: `AgentMessage`, `AgentTurn`, and `AgentChatThread` - Reorganized AI modules under `ai/` subdirectory structure - Updated imports across codebase to reflect new module paths ### 🤖 New Agents & Roles - Added **Dashboard Builder Agent** for dashboard creation and management - Added **Dashboard Manager Role** with appropriate permissions - Updated role permissions to be more granular (users vs agents vs API keys) ### 🔐 Permission System Updates - Added `HTTP_REQUEST_TOOL` permission flag - Updated Workflow Manager role permissions (restricted tool access) - Enhanced permission flag types to differentiate between user/agent/API key contexts - Added `isRelevantForAgents`, `isRelevantForApiKeys`, `isRelevantForUsers` to permission flags ### 📨 Message Role Enhancement - Added `system` role to `AgentMessageRole` enum (alongside user/assistant) - Updated message handling to support system prompts ### 🎨 UI/UX Improvements - New tabs in agent detail: **Evals** and **Logs** - Added turn detail page: `/ai/agents/:agentId/turns/:turnId` - Fixed text overflow in `SettingsListItemCardContent` - Updated role applicability labels ("Assignable to Workspace Members") ### 🛠️ Technical Improvements - Fixed Zod schema validation for UUID and Date fields (use string validators) - Updated `ToolRegistryService` to properly register HTTP tool with permission flag - Enhanced error handling in agent execution services - Updated database migrations for new entity schema ## Database Migrations - `1764210000000-add-system-role-to-agent-message.ts` - `1764220000000-add-evaluation-inputs-to-agent.ts` - `1764200000000-add-agent-turn-evaluation.ts` - `1764100000000-refactor-agent-chat-entities.ts` ## Testing - [ ] Agent evaluation flow tested - [ ] Dashboard Builder agent tested - [ ] Permission system validated - [ ] UI tabs and navigation tested - [ ] Database migrations run successfully ## Breaking Changes ⚠️ **Entity Rename**: `AgentChatMessage` renamed to `AgentMessage` - GraphQL queries need updating ## Related Issues <!-- Link any related issues here --> ## Screenshots <!-- Add screenshots if applicable --> |
||
|
|
dc2c2c413c |
[Workflows] Fix filtering on relative date (#16087)
https://github.com/user-attachments/assets/4079d59e-0550-401f-a29b-b235edd8ed48 Closes https://github.com/twentyhq/twenty/issues/16054 |
||
|
|
e7ebf51e50 |
Replace agent handoff system with planning-based router (#16003)
## Overview This PR replaces the dynamic agent handoff system with a more predictable planning-based router that decides upfront how to handle multi-agent coordination. ## Major Changes ### 🔄 Architecture Shift: Handoffs → Planning **Removed:** - `AgentHandoffEntity` and handoff tracking system - `AgentHandoffService` and `AgentHandoffExecutorService` - Dynamic agent-to-agent transfers during execution - Handoff tool generation and description templates **Added:** - `AiRouterService` with two strategies: `simple` (single agent) and `planned` (multi-agent) - `AgentPlanExecutorService` for executing multi-step plans - Plan validation (cycle detection, dependency resolution) - `UnifiedRouterResult` type with discriminated union ### 🤖 New Standard Agents Added two new specialized agents: - **Researcher Agent**: Web search, fact-finding, competitive intelligence - **Code Agent**: TypeScript function generation for serverless workflows ### 🏗️ Router Refactoring (Latest) Split router responsibilities into focused services: - `AiRouterStrategyDeciderService`: Decides simple vs planned strategy - `AiRouterPlanGeneratorService`: Generates and validates execution plans - `AiRouterService`: Coordinates between services (reduced from 426→275 lines) ### ⚙️ Configuration Improvements - Added `outputStrategy` to agent definitions (`direct` vs `synthesize`) - Removed hardcoded special cases for workflow-builder - Added `plannerModel` field to workspace entity - Increased `MAX_STEPS` from 10 to 25 for complex workflows ### 📝 Agent Prompt Refinements Significantly simplified prompts for better clarity: - Workflow Builder: 51→36 lines - Helper: 49→28 lines - Data Manipulator: Enhanced with sorting guidance ### 🔍 Enhanced Debugging - Plan reasoning and step count in data message parts - Router debug info with token usage tracking - Better logging throughout execution pipeline ## Benefits 1. **Simpler Mental Model**: Router decides upfront vs dynamic transfers 2. **Better Predictability**: Users see the plan before execution 3. **Cleaner Architecture**: SRP with focused services 4. **Configuration Over Code**: Agent behavior via config, not hardcoded logic 5. **Plan Validation**: Catches invalid dependencies and cycles ## Migration Notes - Database migration removes `agentHandoff` table - Adds `plannerModel` column to workspace table - No API breaking changes (agent endpoints unchanged) ## Testing - Integration tests updated to remove handoff dependencies - Agent tool test utilities simplified - Plan validation covered by new logic ## Next Steps (Future PRs) - Parallel execution of independent plan steps - Dynamic re-planning based on results - Plan caching for common routing patterns - Error recovery strategies in plan executor |
||
|
|
46d1ea6505 |
[groupBy] groupBy relation fields (#15951)
Example query Here person has - a N - 1 relationship with company - a N - 1 morph relationship with pet or company <img width="862" height="374" alt="image" src="https://github.com/user-attachments/assets/59bc9b82-c943-43de-ad82-d3393b76904b" /> <img width="415" height="629" alt="image" src="https://github.com/user-attachments/assets/9a3176bc-99cd-4983-8611-68ca3a2cf527" /> truncated response <img width="299" height="447" alt="image" src="https://github.com/user-attachments/assets/45af0322-9e66-4eae-8353-6c0dda487bbe" /> We don't allow grouping by relations of relations. Left to do - rest api - tests on permissions |
||
|
|
208c0857ee |
common api - null equivalence (#15926)
closes https://github.com/twentyhq/core-team-issues/issues/1629 To do before requesting review : - filter update Migration to come in an other PR Strat : 1/ Null transformation - [x] Transform NULL equivalent value to NULL in field validation in common api - pre-query - with feature flag - [ ] Same logic in ORM (Not done, complex to handle feature flag here) - [x] Transform NULL value to equivalent in data formatting in ORM - post-query 2/ Migration (in other PR) for fieldMetadata not nullable with default defaultValue (empty string, ...) - [ ] Remove NOT NULL db constraint - [ ] Update record value to NULL - [ ] Update field metadata : isNullable:true - [ ] Update uniqueIndex whereClause (also for standard uniqueIndex) - [ ] Activate feature flag 3/ Update metadata creation - [x] No more default default value - [x] Update standard field nullability - [x] Remove index default whereClause for standard field 4/ Update filter - [x] When filtering on NULL or empty string, be sure all records are returned (the one with NULL + the one with "") 5/ Test - [ ] Strat. to do |
||
|
|
061cc897af |
Improve record group aggregate query performance (#15828)
This PR is a first step for improving the performance on boards and table with groups. It is related to : https://github.com/twentyhq/core-team-issues/issues/1870 Here we implement only a groupBy query for aggregate values in the group section. This also allows to improve the DX of aggregate computing and group by query creation and parsing. ## Demo Main : https://github.com/user-attachments/assets/5d2a8077-5322-4928-a551-f03583bcfb87 This PR : https://github.com/user-attachments/assets/d0e82b28-72c3-40f0-b5cb-045f1a736ffb ## Aggregate update bug fix This PR also solves a bug with aggregate update that was already present on main. The bug is linked to core views not being updated properly during a modification of the aggregate operation on a view. We should probably improve the view lifecycle and state management because it is a bit too complex right now. Main : https://github.com/user-attachments/assets/10dbfb8b-dfa0-4f21-8698-d222871a43e7 This PR : https://github.com/user-attachments/assets/bac41890-5191-4e4c-b82b-19b1039e9ab5 ## Miscellaneous - Fixed optimistic rendering of group by queries, when adding a new record, the aggregate recomputes well. ## TODO - We might want to improve the optimistic for group by queries that don't have records nor more than one dimension. |
||
|
|
a281f2a773 |
feat: add configurable response format for AI agents (text/JSON) (#15953)
## Summary
This PR adds configurable response format support for AI agents,
allowing them to return either plain text or structured JSON data based
on a defined schema.
## Key Features
### 1. Agent Response Format Configuration
- Added `AgentResponseFormat` type supporting:
- `text`: Returns plain text responses (default)
- `json`: Returns structured JSON based on defined schema
- New `AgentResponseSchema` type moved to `twenty-shared/ai` for sharing
between frontend/backend
### 2. Settings UI
- New `SettingsAgentResponseFormat` component for configuring response
format
- Visual schema builder for defining JSON output structure
- Real-time validation and preview
- Integrated into agent settings tab
### 3. Workflow Integration
- AI Agent workflow action automatically uses agent's configured
response format
- Output schema dynamically generated from agent's response format
- Workflow variable picker shows structured fields for JSON responses
- Backward compatible with existing text-only agents
### 4. Backend Implementation
- Added `convertAgentSchemaToZod` utility to validate JSON responses
- Agent executor service handles both text and JSON generation
- Automatic agent creation/cloning when adding AI agent steps to
workflows
- Unique agent naming with conflict resolution
### 5. Database Migration
- Migration `1763622159656-update-agent-response-format.ts`
- Sets default `responseFormat` to `{"type":"text"}` for existing agents
- Updated all standard agents with proper response format
## Changes by Module
### Frontend (`twenty-front`)
- 🆕 `AgentResponseFormat` type
- 🆕 `SettingsAgentResponseFormat` component
- ✏️ Updated `WorkflowEditActionAiAgent` to support response format
configuration
- 🗑️ Removed deprecated `useAiAgentOutputSchema` hook and
`AiAgentOutputSchema` type
### Backend (`twenty-server`)
- 🆕 `AgentResponseFormat` type in agent entity
- 🆕 `convertAgentSchemaToZod` utility for schema validation
- ✏️ Updated `AiAgentExecutorService` to handle both text and JSON
generation
- ✏️ Updated `WorkflowSchemaWorkspaceService` to generate output schema
from agent config
- ✏️ Enhanced `WorkflowVersionStepOperationsWorkspaceService` with agent
creation/cloning
- 🆕 Agent naming constants for conflict resolution
### Shared (`twenty-shared`)
- 🆕 `AgentResponseSchema` type
- 🆕 `ModelConfiguration` type moved to shared package
- Updated exports in `ai/index.ts`
## Code Quality
- Removed useless comments following code style guidelines
- All linter checks passed
- Type-safe implementation with proper TypeScript types
## Testing
- ✅ Database migration tested
- ✅ Agent creation/cloning in workflows verified
- ✅ Response format switching (text ↔ JSON) validated
- ✅ Backward compatibility with existing agents confirmed
## Migration Notes
- Existing agents will have `responseFormat: {type: 'text'}` set
automatically
- No breaking changes - all existing functionality preserved
- Agents can be updated to use JSON format through settings UI
|
||
|
|
6607fe0504 |
Fix wrong empty string formatting (#15949)
As title, solves this kind of issues https://twentyfortwenty.twenty.com/object/workflowRun/d6aca50e-68ba-4715-8835-ea22bd44fb88 Solves null currencyDisplay when null currencyCode and null amountMicros ### Before <img width="147" height="81" alt="image" src="https://github.com/user-attachments/assets/d250d65e-b984-43f4-ad67-1397a684cf6a" /> ### After <img width="128" height="74" alt="image" src="https://github.com/user-attachments/assets/4f9c9d80-f8bb-495a-9be0-ecfb984ded81" /> |
||
|
|
3190ca5b9e |
1858 extensibility create relation metadata decorator in thwenty sdkapplications (#15907)
as title First PR I will update the twenty-cli in another PR |
||
|
|
5bb4abc23d |
Add optional limit variable to groupBy queries (#15885)
Closes https://github.com/twentyhq/core-team-issues/issues/1600. Two remarks - This `limit` variable does not reduce postgre's work at it still needs to scan the whole table. It did not seem possible to me to optimize this as we cannot foresee which dimensions will be used by the user, and an optimization could only result from an index on the dimension(s) (e.g.: group companies by addressCity limit 50 can be optimized if we have an index on companies.addressCity + we had a default orderBy on adressCity). But this will still optimize the FE which at the moment receives all groups and truncates the result. - I have not done the work on the FE as the addition of limit is a breaking change, and will break until the workspaces' schema is rebuilt, so we need to flush the cache. I think this could be acceptable as the feature is in the lab but I preferred not doing it yet as it would have no impact since in the BE I added a default limit to 50 groups, and I expect more FE work will be done to allow the user to choose their own limit |
||
|
|
222feb90b4 |
fix: update glob version to 11.1.0 (#15884)
Some Glob CLI related alerts were generated last night. I believe they're safe to dismiss since I do not expect us to use the Glob CLI in production environment, and those alerts do not impact the API, but still updating the dependency version just in case. Not sure if this would resolve all/any of the alerts since glob is a dependency for many other dependencies, so a good number of dependency variants are pulled in. The alert that confirms it's just a CLI related vulnerability: [Dependabot Alert 307](https://github.com/twentyhq/twenty/security/dependabot/307) |
||
|
|
f86c5e78b1 |
fix(workflow): UUID filter in "Search Records" action (#15817)
## Summary - add a regression test that reproduces the workflow "Search Records → ID is <UUID>" failure (issue #15067 / #15746) - adjust `turnRecordFilterIntoRecordGqlOperationFilter` so UUID filters fall back to the literal value when no `recordIdsForUuid` context is provided, always emitting an `in` clause ## Testing - npx nx test twenty-shared -- --testPathPattern=computeRecordGqlOperationFilter.test.ts --coverage=false - Manual: workflow "Search Records" with filter "ID is <UUID>" now returns the correct record Fixes #15067 Fixes #15746 --------- Co-authored-by: remi <remi@labox-apps.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> |
||
|
|
0fdc7ba834 |
Twenty-shared tests parses decorator (#15765)
## Introduction Since we've moved some class validator instances from twenty-server to twenty-shared tests are red because they do not know how to parse decorators declarations We've fixed this by explicitly installing `class-validator` in `twenty-shared` and configuring jest swc accordingly ## Twenty-server class validator patch I don't even know if that's something we need anymore Seems to be a patch either fixing or introducing credit card and phone number validation Would prefer discussing the need or not to either before merging this as it could introduce regression at runtime: - Centralize the patch to be consumed in both `twenty-server` and `twenty-shared` - Remove the patch We should also document every patch motivations we do as it's quite though to iterate over a such huge one |
||
|
|
dd0601ab6f |
Fix phone not empty filter (#15790)
This PR fixes the filter on not empty phones. We can end up with phone numbers that only have a country code, but it makes no sense to consider this special edge case "not empty". If we have no phone number, then it's empty, so this PR applies this particular use case for NOT EMPTY filter on phone fields. Fixes https://github.com/twentyhq/twenty/issues/15638 |
||
|
|
f9c61833ec |
Add debug info in AI chat (#15758)
## 🐛 Critical Bug Fix ### Cost Calculation Error (1000x undercharge) - **Fixed**: Cost conversion utility was calculating credits at 1/1000th of actual value - **Before**: `cents * 10` ❌ - **After**: `(cents / 100) * DOLLAR_TO_CREDIT_MULTIPLIER` ✅ - **Impact**: Users were being undercharged by 1000x - Example: 0.75 cents should = 7,500 credits - Bug calculated it as 7.5 credits --- ## 🎯 Code Centralization & DRY ### Unified Cost Calculation - Centralized all cost conversions to use `convertCentsToBillingCredits` utility - Refactored 3 different implementations into 1 single source of truth - Files updated: - `ai-billing.service.ts` - `agent-streaming.service.ts` (2 usages) **Before** (multiple implementations): ```typescript // Wrong implementation const credits = cents * 10; // Verbose implementation const costInDollars = costInCents / 100; const creditsUsed = Math.round(costInDollars * DOLLAR_TO_CREDIT_MULTIPLIER); ``` **After** (unified): ```typescript const creditsUsed = Math.round(convertCentsToBillingCredits(costInCents)); ``` --- ## ✨ UI Component Refactoring ### RoutingDebugDisplay.tsx - **Reduced from 118 lines to 34 lines** (71% reduction) - Extracted `renderTimingRow` helper to eliminate 15 repetitive JSX blocks - Added `formatTokenBreakdown` helper for token display logic - Much easier to add new debug metrics **Before**: 15 nearly-identical blocks of repetitive JSX **After**: Clean, DRY implementation with reusable helpers --- ## 🧹 Code Quality Improvements ### Removed Debug Code - Removed `console.log` accidentally left in `RoutingStatusDisplay.tsx` ### Cleaned Up Comments (18+ removed) Removed redundant comments that stated the obvious: - ❌ "Calculate routing cost if we have token usage" - ❌ "Send the updated routing status with execution metrics to the client" - ❌ "Count tool calls in the response" - ❌ "AI SDK's LanguageModelUsage uses inputTokens/outputTokens" - And 14+ more... Kept meaningful comments: - ✅ "Timing is optional, ignore errors" (explains catch block) - ✅ Type definition grouping comments --- ## 📊 Statistics **Files Modified**: 10 - `convert-cents-to-billing-credits.util.ts` (fixed formula) - `ai-billing.service.ts` (use centralized utility) - `agent-streaming.service.ts` (use utility, remove comments) - `agent-execution.service.ts` (remove comments) - `ai-router.service.ts` (remove comments) - `RoutingStatusDisplay.tsx` (remove debug code) - `RoutingDebugDisplay.tsx` (major refactor) ⭐ - `isDebugModeState.ts` (new file) - `DataMessagePart.ts` (type extensions) - `useClientConfig.ts` (debug mode support) **Impact**: - Lines removed: ~130 (redundant code + comments) - Lines added: ~45 (helper functions) - **Net reduction**: ~85 lines - **Bug fixes**: 1 critical (1000x cost error) - **Centralizations**: 3 locations now using shared utility - **Major refactors**: 1 UI component (71% reduction) --- ## ✅ Verification - ✅ All linter checks pass - ✅ All tests pass (`ai-billing.service.spec.ts` verified) - ✅ No `any` types in affected code - ✅ No TODO/FIXME markers --- ## 🎯 Principles Applied 1. ✅ **Fix Root Causes, Not Symptoms** - Fixed utility function, then used it everywhere 2. ✅ **DRY (Don't Repeat Yourself)** - Centralized cost calculation and UI rendering 3. ✅ **Single Source of Truth** - One place for cost conversion formula 4. ✅ **Code as Documentation** - Removed comments that repeated what code says 5. ✅ **Composability** - Created reusable helper functions 6. ✅ **Type Safety** - Maintained strict typing throughout |
||
|
|
cb759aa22e |
Fix concurrent page layout edition bug (#15740)
This PR fixes a bug which could happen if two people were editing a page layout at the same time. If one person deletes a widget or a tab and saves first, and the second person saves after but doesn't delete this widget or tab, an error is raised. This is because, in the backend, a diff is computed to know which tab or widget to create, update or delete. But the logic was maid without considering soft deletion. So, when the second person saves, the diff tries to create the widget or tab which had been soft deleted by the first use. Since they have the same id, a duplicate primary key error is raised. Now we always consider that the last user to save has the truth. So we restore the tab/widget and update it. |
||
|
|
9880f192a5 | Move composite types to twenty-shared (#15741) | ||
|
|
137aba049d |
Fix tsconfigpaths root (#15683)
# Introduction
We've facing facing intra package build error for a moment such as:
```ts
vite v7.1.12 building for production...
src/components/BaseEmail.tsx:20:19 - error TS2719: Type 'import("/Users/paulrastoin/ws/twenty/node_modules/@lingui/core/dist/index").I18n' is not assignable to type 'import("/Users/paulrastoin/ws/twenty/node_modules/@lingui/core/dist/index").I18n'. Two different types with this name exist, but they are unrelated.
Types have separate declarations of a private property '_locale'.
20 <I18nProvider i18n={i18nInstance}>
~~~~
node_modules/@lingui/react/dist/shared/react.b2b749a9.d.ts:42:5
42 i18n: I18n;
~~~~
The expected type comes from property 'i18n' which is declared here on type 'IntrinsicAttributes & Omit<I18nContext, "_"> & { children?: ReactNode; }'
```
and now since hacktoberfest merge getting even more such as:
```ts
➜ twenty git:(main) ✗ npx nx build twenty-emails
✔ 2/2 dependent project tasks succeeded [2 read from cache]
Hint: you can run the command with --verbose to see the full dependent project outputs
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
[tsconfig-paths] An error occurred while parsing "/Users/paulrastoin/ws/twenty/packages/twenty-apps/hacktoberfest-2025/linkedin-browser-extension/browser-extension/tsconfig.json". See below for details. To disable this message, set the `ignoreConfigErrors` option to true.
TSConfckParseError: failed to resolve "extends":"./.wxt/tsconfig.json" in /Users/paulrastoin/ws/twenty/packages/twenty-apps/hacktoberfest-2025/linkedin-browser-extension/browser-extension/tsconfig.json
at resolveExtends (file:///Users/paulrastoin/ws/twenty/node_modules/tsconfck/src/parse.js:261:8)
at parseExtends (file:///Users/paulrastoin/ws/twenty/node_modules/tsconfck/src/parse.js:196:24)
... 5 lines matching cause stack trace ...
at async createBuilder (file:///Users/paulrastoin/ws/twenty/node_modules/vite/dist/node/chunks/config.js:34104:19)
at async CAC.<anonymous> (file:///Users/paulrastoin/ws/twenty/node_modules/vite/dist/node/cli.js:629:10) {
code: 'EXTENDS_RESOLVE',
cause: Error: Cannot find module './.wxt/tsconfig.json'
Require stack:
- /Users/paulrastoin/ws/twenty/packages/twenty-apps/hacktoberfest-2025/linkedin-browser-extension/browser-extension/tsconfig.json
at Module._resolveFilename (node:internal/modules/cjs/loader:1410:15)
at require.resolve (node:internal/modules/helpers:163:19)
at resolveExtends (file:///Users/paulrastoin/ws/twenty/node_modules/tsconfck/src/parse.js:249:14)
at parseExtends (file:///Users/paulrastoin/ws/twenty/node_modules/tsconfck/src/parse.js:196:24)
at Module.parse (file:///Users/paulrastoin/ws/twenty/node_modules/tsconfck/src/parse.js:54:23)
at async Promise.all (index 21)
at async BasicMinimalPluginContext.configResolved (/Users/paulrastoin/ws/twenty/node_modules/vite-tsconfig-paths/dist/index.js:134:9)
at async Promise.all (index 0)
at async resolveConfig (file:///Users/paulrastoin/ws/twenty/node_modules/vite/dist/node/chunks/config.js:35892:2)
at async createBuilder (file:///Users/paulrastoin/ws/twenty/node_modules/vite/dist/node/chunks/config.js:34104:19) {
code: 'MODULE_NOT_FOUND',
requireStack: [
'/Users/paulrastoin/ws/twenty/packages/twenty-apps/hacktoberfest-2025/linkedin-browser-extension/browser-extension/tsconfig.json'
]
},
tsconfigFile: '/Users/paulrastoin/ws/twenty/packages/twenty-apps/hacktoberfest-2025/linkedin-browser-extension/browser-extension/tsconfig.json'
}
vite v7.1.12 building for production...
src/components/BaseEmail.tsx:20:19 - error TS2719: Type 'import("/Users/paulrastoin/ws/twenty/node_modules/@lingui/core/dist/index").I18n' is not assignable to type 'import("/Users/paulrastoin/ws/twenty/node_modules/@lingui/core/dist/index").I18n'. Two different types with this name exist, but they are unrelated.
Types have separate declarations of a private property '_locale'.
20 <I18nProvider i18n={i18nInstance}>
~~~~
node_modules/@lingui/react/dist/shared/react.b2b749a9.d.ts:42:5
42 i18n: I18n;
~~~~
The expected type comes from property 'i18n' which is declared here on type 'IntrinsicAttributes & Omit<I18nContext, "_"> & { children?: ReactNode; }'
✓ 492 modules transformed.
[vite:dts] Start generate declaration files...
computing gzip size (31)...[vite:dts] Declaration files built in 981ms.
dist/locales/generated/zh-CN.mjs 4.16 kB │ gzip: 2.02 kB
dist/locales/generated/zh-TW.mjs 4.21 kB │ gzip: 2.05 kB
dist/locales/generated/en.mjs 4.34 kB │ gzip: 1.28 kB
dist/locales/generated/fi-FI.mjs 4.45 kB │ gzip: 1.90 kB
dist/locales/generated/af-ZA.mjs 4.50 kB │ gzip: 1.88 kB
dist/locales/generated/no-NO.mjs 4.52 kB │ gzip: 1.85 kB
dist/locales/generated/da-DK.mjs 4.52 kB │ gzip: 1.84 kB
dist/locales/generated/pt-BR.mjs 4.54 kB │ gzip: 1.89 kB
dist/locales/generated/sv-SE.mjs 4.54 kB │ gzip: 1.88 kB
dist/locales/generated/nl-NL.mjs 4.55 kB │ gzip: 1.87 kB
dist/locales/generated/pt-PT.mjs 4.56 kB │ gzip: 1.88 kB
dist/locales/generated/it-IT.mjs 4.59 kB │ gzip: 1.87 kB
dist/locales/generated/pl-PL.mjs 4.66 kB │ gzip: 2.04 kB
dist/locales/generated/cs-CZ.mjs 4.67 kB │ gzip: 2.05 kB
dist/locales/generated/es-ES.mjs 4.70 kB │ gzip: 1.90 kB
dist/locales/generated/tr-TR.mjs 4.70 kB │ gzip: 2.02 kB
dist/locales/generated/de-DE.mjs 4.71 kB │ gzip: 1.97 kB
dist/locales/generated/ca-ES.mjs 4.73 kB │ gzip: 1.92 kB
dist/locales/generated/ko-KR.mjs 4.73 kB │ gzip: 2.14 kB
dist/locales/generated/ro-RO.mjs 4.73 kB │ gzip: 1.95 kB
dist/locales/generated/fr-FR.mjs 4.74 kB │ gzip: 1.92 kB
dist/locales/generated/hu-HU.mjs 4.82 kB │ gzip: 2.09 kB
dist/locales/generated/he-IL.mjs 4.88 kB │ gzip: 1.99 kB
dist/locales/generated/ja-JP.mjs 4.95 kB │ gzip: 2.19 kB
dist/locales/generated/vi-VN.mjs 5.19 kB │ gzip: 2.14 kB
dist/locales/generated/ar-SA.mjs 5.35 kB │ gzip: 2.21 kB
dist/locales/generated/pseudo-en.mjs 5.68 kB │ gzip: 2.27 kB
dist/locales/generated/sr-Cyrl.mjs 5.82 kB │ gzip: 2.32 kB
dist/locales/generated/uk-UA.mjs 6.11 kB │ gzip: 2.41 kB
dist/locales/generated/el-GR.mjs 6.47 kB │ gzip: 2.53 kB
dist/locales/generated/ru-RU.mjs 6.62 kB │ gzip: 2.55 kB
dist/index.mjs 822.22 kB │ gzip: 179.78 kB
dist/locales/generated/zh-CN.js 4.23 kB │ gzip: 2.08 kB
dist/locales/generated/zh-TW.js 4.28 kB │ gzip: 2.10 kB
dist/locales/generated/en.js 4.41 kB │ gzip: 1.33 kB
dist/locales/generated/fi-FI.js 4.52 kB │ gzip: 1.95 kB
dist/locales/generated/af-ZA.js 4.56 kB │ gzip: 1.93 kB
dist/locales/generated/no-NO.js 4.58 kB │ gzip: 1.90 kB
dist/locales/generated/da-DK.js 4.59 kB │ gzip: 1.89 kB
dist/locales/generated/pt-BR.js 4.60 kB │ gzip: 1.94 kB
dist/locales/generated/sv-SE.js 4.61 kB │ gzip: 1.93 kB
dist/locales/generated/nl-NL.js 4.62 kB │ gzip: 1.92 kB
dist/locales/generated/pt-PT.js 4.63 kB │ gzip: 1.93 kB
dist/locales/generated/it-IT.js 4.66 kB │ gzip: 1.92 kB
dist/locales/generated/pl-PL.js 4.73 kB │ gzip: 2.09 kB
dist/locales/generated/cs-CZ.js 4.74 kB │ gzip: 2.10 kB
dist/locales/generated/es-ES.js 4.77 kB │ gzip: 1.95 kB
dist/locales/generated/tr-TR.js 4.77 kB │ gzip: 2.07 kB
dist/locales/generated/de-DE.js 4.77 kB │ gzip: 2.02 kB
dist/locales/generated/ca-ES.js 4.80 kB │ gzip: 1.97 kB
dist/locales/generated/ko-KR.js 4.80 kB │ gzip: 2.20 kB
dist/locales/generated/ro-RO.js 4.80 kB │ gzip: 2.00 kB
dist/locales/generated/fr-FR.js 4.81 kB │ gzip: 1.97 kB
dist/locales/generated/hu-HU.js 4.89 kB │ gzip: 2.14 kB
dist/locales/generated/he-IL.js 4.95 kB │ gzip: 2.04 kB
dist/locales/generated/ja-JP.js 5.02 kB │ gzip: 2.24 kB
dist/locales/generated/vi-VN.js 5.26 kB │ gzip: 2.19 kB
dist/locales/generated/ar-SA.js 5.42 kB │ gzip: 2.26 kB
dist/locales/generated/pseudo-en.js 5.75 kB │ gzip: 2.32 kB
dist/locales/generated/sr-Cyrl.js 5.89 kB │ gzip: 2.37 kB
dist/locales/generated/uk-UA.js 6.18 kB │ gzip: 2.47 kB
dist/locales/generated/el-GR.js 6.54 kB │ gzip: 2.58 kB
dist/locales/generated/ru-RU.js 6.69 kB │ gzip: 2.60 kB
dist/index.js 606.48 kB │ gzip: 160.10 kB
✓ built in 2.45s
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
NX Successfully ran target build for project twenty-emails and 2 tasks it depends on (5s)
Nx read the output from the cache instead of running the command for 2 out of 3 tasks.
```
Fixing folder to search for tsconfig from
|
||
|
|
abde3c04ac |
1630 extensibility twenty cli ability to create edit and delete fields (#15501)
As title - adds decorators in twenty-sdk - update twenty-cli load-manifest to it gets @FieldMetadata infos + testing - update twenty-server so it CRUD fields properly, using universalIdentifier - Fix UI so we can update managed objects records - move FieldMetadata items from twenty-server to twenty-shared |