3be3c4e965a4f0b7ddff210bfb6bdba94e6a3957
216 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 |
||
|
|
b33b38cb02 |
Follow-up high fixes on date refactor (#15553)
This PR fixes important bugs on date filter handling following-up date refactor. Fixes : https://github.com/twentyhq/core-team-issues/issues/1814 |
||
|
|
1739ee0595 |
Add date granularity and timezone and first day of the week to graphs (#15543)
- Allow users to choose the date granularity of the x axis and the group by of the y axis on a bar chart - Display those options conditionally - Store timezones in graphs: each graph has its own timezone, defaults to the user timezone. There will be a picker in the v2 to choose the timezone. For now the timezone is not used by the backend, but it will be used in filters and in group by queries. - Store first day of the week https://github.com/user-attachments/assets/66a5d156-dd93-4ebe-8c8f-d172f93e25be |
||
|
|
afeb505eed |
[Breaking Change] Implement reliable date picker utils to handle all timezone combinations (#15377)
This PR implements the necessary tools to have `react-datepicker` calendar and our date picker components work reliably no matter the timezone difference between the user execution environment and the user application timezone. Fixes https://github.com/twentyhq/core-team-issues/issues/1781 This PR won't cover everything needed to have Twenty handle timezone properly, here is the follow-up issue : https://github.com/twentyhq/core-team-issues/issues/1807 # Features in this PR This PR brings a lot of features that have to be merged together. - DATE field type is now handled as string only, because it shouldn't involve timezone nor the JS Date object at all, since it is a day like a birthday date, and not an absolute point in time. - DATE_TIME field wasn't properly handled when the user settings timezone was different from the system one - A timezone abbreviation suffix has been added to most DATE_TIME display component, only when the timezone is different from the system one in the settings. - A lot of bugs, small features and improvements have been made here : https://github.com/twentyhq/core-team-issues/issues/1781 # Handling of timezones ## Essential concepts This topic is so complex and easy to misunderstand that it is necessary to define the precise terms and concepts first. It resembles character encoding and should be treated with the same care. - Wall-clock time : the time expressed in the timezone of a user, it is distinct from the absolute point in time it points to, much like a pointer being a different value than the value that it points to. - Absolute time : a point in time, regardless of the timezone, it is an objective point in time, of course it has to be expressed in a given timezone, because we have to talk about when it is located in time between humans, but it is in fact distinct from any wall clock time, it exists in itself without any clock running on earth. However, by convention the low-level way to store an absolute point in time is in UTC, which is a timezone, because there is no way to store an absolute point in time without a referential, much like a point in space cannot be stored without a referential. - DST : Daylight Save Time, makes the timezone shift in a specific period every year in a given timezone, to make better use of longer days for various reasons, not all timezones have DST. DST can be 1 hour or 30 min, 45 min, which makes computation difficult. - UTC : It is NOT an “absolute timezone”, it is the wall-clock time at 0° longitude without DST, which is an arbitrary and shared human convention. UTC is often used as the standard reference wall-clock time for talking about absolute point in time without having to do timezone and DST arithmetic. PostgreSQL stores everything in UTC by convention, but outputs everything in the server’s SESSION TIMEZONE. ## How should an absolute point in time be stored ? Since an absolute point in time is essentially distinct from its timezone it could be stored in an absolute way, but in practice it is impossible to store an absolute point in time without a referential. We have to say that a rocket launched at X given time, in UTC, EST, CET, etc. And of course, someone in China will say that it launched at 10:30, while in San Francisco it will have launched at 19:30, but it is THE SAME absolute point in time. Let’s take a related example in computer science with character encoding. If a text is stored without the associated encoding table, the correct meaning associated to the bits stored in memory can be lost forever. It can become impossible for a program to guess what encoding table should be used for a given text stored as bits, thus the glitches that appeared a lot back in the early days of internet and document processing. The same can happen with date time storing, if we don’t have the timezone associated with the absolute point in time, the information of when it absolutely happened is lost. It is NOT necessary to store an absolute point in time in UTC, it is more of a standard and practical wall-clock time to be associated with an absolute point in time. But an absolute point in time MUST be store with a timezone, with its time referential, otherwise the information of when it absolutely happened is lost. For example, it is easier to pass around a date as a string in UTC, like `2024-01-02T00:00:00Z` because it allows front-end and back-end code to “talk” in the same standard and DST-free wall-clock time, BUT it is not necessary. Because we have date libraries that operate on the standard ISO timezone tables, we can talk in different timezone and let the libraries handle the conversion internally. It is false to say that UTC is an absolute timezone or an absolute point in time, it is just the standard, conventional time referential, because one can perfectly store every absolute points in time in UTC+10 with a complex DST table and have the exactly correct absolute points in time, without any loss of information, without having any UTC+0 dates involved. Thus storing an absolute point in time without a timezone associated, for example with `timestamp` PostgreSQL data type, is equivalent to storing a wall-clock time and then throwing away voluntarily the information that allows to know when it absolutely happened, which is a voluntary data-loss if the code that stores and retrieves those wall-clock points in time don’t store the associated timezone somewhere. This is why we use `timestamptz` type in PostgreSQL, so that we make sure that the correct absolute point in time is stored at the exact time we send it to PostgreSQL server, no matter the front-end, back-end and SQL server's timezone differences. ## The JavaScript Date object The native JavaScript Date object is now officially considered legacy ([source](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)), the Date object stores an absolute point in time BUT it forces the storage to use its execution environment timezone, and one CANNOT modify this timezone, this is a legacy behavior. To obtain the desired result and store an absolute point in time with an arbitrary timezone there are several options : - The new Temporal API that is the successor of the legacy Date object. - Moment / Luxon / @date-fns/tz that expose objects that allow to use any timezone to store an absolute point in time. ## How PostgreSQL stores absolute point in times PostgreSQL stores absolute points in time internally in UTC ([source](https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-DATETIME-INPUT-TIME-STAMPS)), but the output date is expressed in the server’s session timezone ([source](https://www.postgresql.org/docs/current/sql-set.html)) which can be different from UTC. Example with the object companies in Twenty seed database, on a local instance, with a new “datetime” custom column : <img width="374" height="554" alt="image" src="https://github.com/user-attachments/assets/4394cb43-d97e-4479-801d-ca068f800e39" /> <img width="516" height="524" alt="image" src="https://github.com/user-attachments/assets/b652f36a-d2e2-47a4-8950-647ca688cbbd" /> ## Why can’t I just use the JavaScript native Date object with some manual logic ? Because the JavaScript Date object does not allow to change its internal timezone, the libraries that are based on it will behave on the execution environment timezone, thus leading to bugs that appear only on the computers of users in a timezone but not for other in another timezone. In our case the `react-datepicker` library forces to use the `Date` object, thus forcing the calendar to behave in the execution environment system timezone, which causes a lot of problems when we decide to display the Twenty application DATE_TIME values in another timezone than the user system one, the bugs that appear will be of the off-by-one date class, for example clicking on 23 will select 24, thus creating an unreliable feature for some system / application timezone combinations. A solution could be to manually compute the difference of minutes between the application user and the system timezones, but that’s not reliable because of DST which makes this computation unreliable when DST are applied at different period of the year for the two timezones. ## Why can’t I compute the timezone difference manually ? Because of DST, the work to compute the timezone difference reliably, not just for the usual happy path, is equivalent to developing the internal mechanism of a date timezone library, which is equivalent to use a library that handles timezones. ## Using `@date-fns/tz` to solve this problem We could have used `luxon` but it has a heavy bundle size, so instead we rely here on `@date-fns/tz` (~1kB) which gives us a `TZDate` object that allows to use any given timezone to store an absolute point-in-time. The solution here is to trick `react-datepicker` by shifting a Date object by the difference of timezone between the user application timezone and the system timezone. Let’s take a concerte example. System timezone : Midway, ⇒ UTC-11:00, has no DST. User application timezone : Auckland, NZ ⇒ UTC+13:00, has a DST. We’ll take the NZ daylight time, so that will make a timezone difference of 24 hours ! Let’s take an error-prone date : `2025-01-01T00:00:00` . This date is usually a good test-case because it can generate three classes of bugs : off-by-one day bugs, off-by-one month bugs and off-by-one year bugs, at the same time. Here is the absolute point in time we take expressed in the different wall-clock time points we manipulate Case | In system timezone ⇒ UTC-11 | In UTC | In user application timezone ⇒ UTC+13 -- | -- | -- | -- Original date | `2024-12-31T00:00:00-11:00` | `2024-12-31T11:00:00Z` | `2025-01-01T00:00:00+13:00` Date shifted for react-datepicker | `2025-01-01T00:00:00-11:00` | `2025-01-01T11:00:00Z` | `2025-01-02T00:00:00+13:00` We can see with this table that we have the number part of the date that is the same (`2025-01-01T00:00:00`) but with a different timezone to “trick” `react-datepicker` and have it display the correct day in its calendar. You can find the code in the hooks `useTurnPointInTimeIntoReactDatePickerShiftedDate` and `useTurnReactDatePickerShiftedDateBackIntoPointInTime` that contain the logic that produces the above table internally. ## Miscellaneous Removed FormDateFieldInput and FormDateTimeFieldInput stories as they do not behave the same depending of the execution environment and it would be easier to put them back after having refactored FormDateFieldInput and FormDateTimeFieldInput --------- Co-authored-by: Charles Bochet <charles@twenty.com> |
||
|
|
a82b74c1ff |
Make upsert action body common with create record instead of update record (#15442)
We do not want the fields to update multiselect for upsert record action. We want all available fields displayed by default. This makes upsert record action closer to create record than update record. This PR: - deletes WorkflowUpdateRecordBody that was common between update and upsert and put back content into update - creates WorkflowCreateRecordBody that is now common between create and upsert - simplifies shouldDisplayFormField Before - using fields to update as update record action <img width="546" height="823" alt="Capture d’écran 2025-10-30 à 10 00 24" src="https://github.com/user-attachments/assets/9206bc8b-75c2-40fa-a8de-e708b6b2cd05" /> After - displaying all fields as create record action <img width="546" height="823" alt="Capture d’écran 2025-10-30 à 10 00 04" src="https://github.com/user-attachments/assets/87141a47-946f-4604-be55-f4c21ff4a3d8" /> |
||
|
|
a6cc80eedd |
1751 extensibility twenty sdk v2 use twenty sdk to define a serverless function trigger (#15347)
This PR adds 2 columns handlerPath and handlerName in serverlessFunction
to locate the entrypoint of a serverless in a codebase
It adds the following decorators in twenty-sdk:
- ServerlessFunction
- DatabaseEventTrigger
- RouteTrigger
- CronTrigger
- ApplicationVariable
It still supports deprecated entity.manifest.jsonc
Overall code needs to be cleaned a little bit, but it should work
properly so you can try to test if the DEVX fits your needs
See updates in hello-world application
```typescript
import axios from 'axios';
import {
DatabaseEventTrigger,
ServerlessFunction,
RouteTrigger,
CronTrigger,
ApplicationVariable,
} from 'twenty-sdk';
@ApplicationVariable({
universalIdentifier: 'dedc53eb-9c12-4fe2-ba86-4a2add19d305',
key: 'TWENTY_API_KEY',
description: 'Twenty API Key',
isSecret: true,
})
@DatabaseEventTrigger({
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
eventName: 'person.created',
})
@RouteTrigger({
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
path: '/post-card/create',
httpMethod: 'GET',
isAuthRequired: false,
})
@CronTrigger({
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
pattern: '0 0 1 1 *', // Every year 1st of January
})
@ServerlessFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
})
class CreateNewPostCard {
main = async (params: { recipient: string }): Promise<string> => {
const { recipient } = params;
const options = {
method: 'POST',
url: 'http://localhost:3000/rest/postCards',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.TWENTY_API_KEY}`,
},
data: { name: recipient ?? 'Unknown' },
};
try {
const { data } = await axios.request(options);
console.log(`New post card to "${recipient}" created`);
return data;
} catch (error) {
console.error(error);
throw error;
}
};
}
export const createNewPostCardHandler = new CreateNewPostCard().main;
```
### [edit] V2
After the v1 proposal, I see that using a class method to define the
serverless function handler is pretty confusing. Lets leave
serverlessFunction configuration decorators on the class, but move the
handler like before. Here is the v2 hello-world serverless function:
```typescript
import axios from 'axios';
import {
DatabaseEventTrigger,
ServerlessFunction,
RouteTrigger,
CronTrigger,
ApplicationVariable,
} from 'twenty-sdk';
@ApplicationVariable({
universalIdentifier: 'dedc53eb-9c12-4fe2-ba86-4a2add19d305',
key: 'TWENTY_API_KEY',
description: 'Twenty API Key',
isSecret: true,
})
@DatabaseEventTrigger({
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
eventName: 'person.created',
})
@RouteTrigger({
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
path: '/post-card/create',
httpMethod: 'GET',
isAuthRequired: false,
})
@CronTrigger({
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
pattern: '0 0 1 1 *', // Every year 1st of January
})
@ServerlessFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
})
export class ServerlessFunctionDefinition {}
export const main = async (params: { recipient: string }): Promise<string> => {
const { recipient } = params;
const options = {
method: 'POST',
url: 'http://localhost:3000/rest/postCards',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.TWENTY_API_KEY}`,
},
data: { name: recipient ?? 'Unknown' },
};
try {
const { data } = await axios.request(options);
console.log(`New post card to "${recipient}" created`);
return data;
} catch (error) {
console.error(error);
throw error;
}
};
```
### [edit] V3
After the v2 proposal, we don't really like decorators on empty classes.
We decided to go with a Vercel approach with a config constant
```typescript
import axios from 'axios';
import { ServerlessFunctionConfig } from 'twenty-sdk';
export const main = async (params: { recipient: string }): Promise<string> => {
const { recipient } = params;
const options = {
method: 'POST',
url: 'http://localhost:3000/rest/postCards',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.TWENTY_API_KEY}`,
},
data: { name: recipient ?? 'Unknown' },
};
try {
const { data } = await axios.request(options);
console.log(`New post card to "${recipient}" created`);
return data;
} catch (error) {
console.error(error);
throw error;
}
};
export const config: ServerlessFunctionConfig = {
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
routeTriggers: [
{
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
path: '/post-card/create',
httpMethod: 'GET',
isAuthRequired: false,
}
],
cronTriggers: [
{
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
pattern: '0 0 1 1 *', // Every year 1st of January
}
],
databaseEventTriggers: [
{
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
eventName: 'person.created',
}
]
}
```
|
||
|
|
be3ceca0a3 |
Add listkit to tiptap extension in workflow node (#15363)
## Description - This PR address issue - https://github.com/twentyhq/core-team-issues/issues/1768 - Added listkit bundle from tiptap which includes BulletList, orderedList, ListItem and ListKeymap in one single import - This bundle also includes keyboards shortcuts - `Cmd + Shift + 7` and `Cmd + Shift + 8` for ordered and bullet list ## Visual Appearance https://github.com/user-attachments/assets/7eff1233-8503-4854-bad2-2521898bc568 ## Why this Approach - our current version of tiptap is 3.4.2 while the latest is 3.8.0 hence installing these versions manually would install the latest version of 3.8.0. The issue when downgrading to 3.4.2 was that Version 3.8.0 of `@tiptap/extension-list` requires `renderNestedMarkdownContent` from @tiptap/core but our `@tiptap/core` version 3.4.2 doesn't export this function. |
||
|
|
ab24cae2eb | Front references to views as records (#15425) | ||
|
|
da0e5ba342 |
Support primitive types in filters (#15402)
When using primitive types such as array, number and boolean, we display a text field in filters because fieldmetadataId is empty. We should instead support these as we would do for our own fields. Adding also a fix for https://github.com/twentyhq/twenty/issues/15282 --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> |
||
|
|
be08233fa4 |
Add sort section in find records action (#15340)
https://github.com/user-attachments/assets/020f3e97-316b-41db-b95d-7b938a7f82cf |
||
|
|
267af42412 |
Centralize v2 errors types in twenty-shared (#15358)
# Introduction Followup of https://github.com/twentyhq/twenty/pull/15331 ( Reducing size by concerns ) This PR centralizes v2 format error types in `twenty-shared` and consuming them in the existing v2 error format logic in `twenty-server` ## Next This https://github.com/twentyhq/twenty/pull/15360 handles the frontend v2 format error refactor ## Conclusion Related to https://github.com/twentyhq/core-team-issues/issues/1776 |
||
|
|
e613b15c5a |
fix: duplicate merge button bug (#15284)
Fixes - https://github.com/twentyhq/twenty/issues/15263 - Replaced `useLoadSelectedRecordsInContextStore` with `useLoadMergeRecords` in `useOpenMergeRecordsPageInCommandMenu` for improved functionality. - Updated `useMergePreview`, `useMergeRecordsActions`, and `useMergeRecordsSettings` to utilize `mergeRecordsState` instead of the deprecated context store hook. - Cleaned up imports and ensured consistency across merge-related hooks. https://github.com/user-attachments/assets/453539c9-7f2b-4e8c-bfa1-3ceebca07081 --------- Co-authored-by: Charles Bochet <charles@twenty.com> |
||
|
|
e37b8b61ba |
message channel change 2 (#15269)
Co-authored-by: Charles Bochet <charles@twenty.com> |
||
|
|
4effa5351c |
Allow to stop running workflow (#15270)
https://github.com/user-attachments/assets/599154e4-8743-471b-b05a-721b635bcf4e On stoppage: - if no running steps, mark pending as failed and end the workflow - if running steps, set as stopping and exit. Going to the next step, as the workflow is not running anymore, it will naturally stop |
||
|
|
4b846a42a2 |
feat: Attachement for Send Email workflow node (#15044)
## Description - This PR addresses the one issue out of https://github.com/twentyhq/core-team-issues/issues/1685 - Added backend support for workflow node to support attachement - updated send email schema, core utility - added workflowattachmentRow and workflowsendEmailAttachment file to handle file attachment in email workflow - updated Google and Microsoft to use MailComposer which unifies with SMTP provider and improves mail structure ## Visual Appearance https://github.com/user-attachments/assets/16478569-0a83-417e-a85e-70e41fe83343 --------- Co-authored-by: martmull <martmull@hotmail.fr> |
||
|
|
32558673c6 |
feat: Implement AI Router for Dynamic Agent Selection (#15227)
Adds intelligent routing system that automatically selects the best agent for user queries based on conversation context. ### Changes: - Added `routerModel` column to workspace table for configurable router LLM selection - Implemented `RouterService` with conversation history analysis and agent matching logic - Created router settings UI in AI Settings page with model dropdown - Removed agent-specific thread associations - threads are now agent-agnostic - Added real-time routing status notification in chat UI with shimmer effect - Removed automatic default assistant agent creation - Renamed GraphQL operations from agent-specific to generic (e.g., `agentChatThreads` → `chatThreads`) --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Félix Malfait <felix@twenty.com> |
||
|
|
45473218d3 |
Field deactivation side effect views calendar kanban viewFields (#15180)
# Introduction
Handling both:
- field deactivation side effect on view fields, view filters and views
- field deactivation side effect on view that targets it as
`kanbanAggregateFieldMetadataId`
- field deactivation side effect on view that targets it as
`calendarFieldMetadataId`
## Coverage
added coverage
```ts
PASS test/integration/metadata/suites/field-metadata/kanban-aggregate-field-deactivation-deletes-views.integration-spec.ts (13.132 s)
kanban-aggregate-field-deactivation-nullifies-kanban-properties
✓ should nullify kanban properties when field used as kanbanAggregateOperationFieldMetadataId is deactivated (3923 ms)
✓ should not modify views when field not used as kanbanAggregateOperationFieldMetadataId is deactivated (2958 ms)
✓ should nullify kanban properties on multiple views when they all use the same field as kanbanAggregateOperationFieldMetadataId (2542 ms)
✓ should nullify kanban properties when views have different aggregate operations on same field (3380 ms)
Test Suites: 1 passed, 1 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: 13.154 s
```
```ts
PASS test/integration/metadata/suites/field-metadata/view-group-field-deactivation-deletes-views.integration-spec.ts (12.639 s)
view-group-field-deactivation-deletes-views
✓ should delete view when field used in view group is deactivated (3469 ms)
✓ should not delete view when field not used in view group is deactivated (3109 ms)
✓ should delete multiple views when they all use the same field in view groups (2741 ms)
✓ should handle deactivation when view has multiple view groups with different fields (3008 ms)
Test Suites: 1 passed, 1 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: 12.664 s
```
```ts
PASS test/integration/metadata/suites/field-metadata/calendar-field-deactivation-deletes-views.integration-spec.ts (14.579 s)
calendar-field-deactivation-deletes-views
✓ should delete view when field used as calendarFieldMetadataId is deactivated (3388 ms)
✓ should not delete view when field not used as calendarFieldMetadataId is deactivated (2438 ms)
✓ should delete multiple views when they all use the same field as calendarFieldMetadataId (2635 ms)
✓ should handle deactivation when views have different calendar layouts on same field (3195 ms)
✓ should delete calendar view but not other view types when calendar field is deactivated (2682 ms)
Test Suites: 1 passed, 1 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 14.601 s, estimated 15 s
```
## View soft deletion
We decided to remove the soft deletion grain on all the views, in this
PR context we've only removed soft deleted validation requirement on any
view entities
## Conclusion
close https://github.com/twentyhq/core-team-issues/issues/1754
|
||
|
|
f7421c5fc0 |
Add queue management dashboard (#15202)
Adds a comprehensive queue management interface to the admin panel for viewing and managing background jobs. **Features:** - Queue detail pages showing paginated job lists (50 per page) - Filter jobs by state: completed, failed, active, waiting, delayed, paused - Checkbox selection with bulk actions (delete jobs, retry failed jobs) - Per-job dropdown menu for individual retry/delete - Expandable rows showing error messages, stack traces, and job data - Relative timestamps with hover tooltips - Display attempt counts on failed jobs - Dynamic retention policy info from backend **Changes:** - Backend: New AdminPanelQueueService with GraphQL endpoints for job listing, retry, and delete - Frontend: Queue detail page with QueueJobsTable component - Updated retention policy: completed jobs kept 4 hours, failed jobs kept 7 days (max 1000 each) - Added JobState enum for type safety <img width="634" height="696" alt="Screenshot_2025-10-20_at_11 45 25" src="https://github.com/user-attachments/assets/c67bcd27-26cf-47f5-9575-3cd5684d006b" /> <img width="484" height="680" alt="Screenshot_2025-10-20_at_11 45 14" src="https://github.com/user-attachments/assets/68725cc6-b3ec-4098-99ca-f9a717d6f8f1" /> <img width="490" height="643" alt="Screenshot_2025-10-20_at_11 45 05" src="https://github.com/user-attachments/assets/b68a5809-33ff-4452-b48b-741aff7f1dd6" /> <img width="685" height="662" alt="Screenshot 2025-10-20 at 13 15 01" src="https://github.com/user-attachments/assets/eeb5207b-de5c-4b18-bdde-392892053dab" /> |
||
|
|
27f50c4f4e |
feat: add-create-update-record in workflow (#14654)
## Description - this PR focuses on issue https://github.com/twentyhq/core-team-issues/issues/1476 - Added upsert action ## Visual Appearance <img width="1792" height="1041" alt="Screenshot 2025-10-03 at 12 57 58 PM" src="https://github.com/user-attachments/assets/57afb96c-d4b3-4a87-95f0-11ac4bd61dd8" /> <img width="1792" height="1031" alt="Screenshot 2025-10-03 at 12 57 48 PM" src="https://github.com/user-attachments/assets/9032d4c2-f0d2-46f1-8682-a7e5c280a303" /> --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr> |
||
|
|
4e5783eaf4 |
feat: workflow delay action (Pause - Wait/Sleep/Delay) (#14915)
## Description - This PR focuses on issue https://github.com/orgs/twentyhq/projects/1/views/33?pane=issue&itemId=93150683&issue=twentyhq%7Ccore-team-issues%7C20 - added Workflow delay as a Flow action - for V1 added Type 1: Resume at a specific date or time ## Visual Appearance <img width="1792" height="1038" alt="Screenshot 2025-10-09 at 5 46 18 PM" src="https://github.com/user-attachments/assets/e62980e9-59c7-4e5a-b8ec-1e848a462d3f" /> <img width="1792" height="1037" alt="Screenshot 2025-10-09 at 5 46 35 PM" src="https://github.com/user-attachments/assets/7c3f4e39-ab0a-40ed-97a8-4f0cdb86f295" /> --------- Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr> |
||
|
|
3bec43696f |
feat: multi role permission intersection (#15150)
Implements permission intersection (AND logic) to prevent permission escalation when agents act on behalf of users. ### Changes: - **Permission Intersection**: Operations requiring both user AND agent permissions - **RoleContext Type**: Unified type supporting single `roleId` or multiple `roleIds` for intersection - **CRUD Services**: Updated to accept `roleContext` for granular permission control - **Agent Integration**: Chat agents now use user + agent role intersection for all operations - **ORM Layer**: Enhanced `getRepository` to support multi-role permission checks ### Related: - Part 2 of ["Acting on behalf of user" concept PR](https://github.com/twentyhq/twenty/pull/15103) [Closes #1661](https://github.com/twentyhq/core-team-issues/issues/1661) --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com> |
||
|
|
59004306c9 |
Connect chart filters to backend (#15133)
This PR connects the chart filters settings page to the backend. Both for persisting the filters in the chart's configuration and also for querying with those filters. I made sure that the filters configuration is reset in the draft if we change the data source object. |
||
|
|
d3f3f991a5 |
Infer array current item schema (#15115)
This PR allows to infer the schema of the current item of an iterator step: - iterator step receive a variable - added an util that navigate to the array in schema - navigateOutputSchemaProperty - use the array value in schema to generate a new schema - used the existing getFunctionOutputSchema that I renamed and moved to twenty-shared Also cleaned a bit the existing schema for AI. Before https://github.com/user-attachments/assets/9767fc89-3524-4bfb-b1ab-8abe92084767 After https://github.com/user-attachments/assets/3650c1d2-14f2-44f9-b10c-e649fe04128d |
||
|
|
b16ab1b7c9 |
1518 extensibility front add an application section in settings (#15056)
Protected by IS_APPLICATION_ENABLED featureFlag Add `Application` section in settings <img width="301" height="137" alt="image" src="https://github.com/user-attachments/assets/ee53bdd2-36f6-45c6-8646-17b1e08abf00" /> A `settings/applications` route listing all installed applications <img width="661" height="428" alt="image" src="https://github.com/user-attachments/assets/69d534c4-4e9e-452a-a3d9-ded0223bb457" /> Introduce a new Tag for application managed items <img width="885" height="759" alt="image" src="https://github.com/user-attachments/assets/19767be5-61e5-4bd2-a51d-54ed9bfb1923" /> A `settings/applications/<application_id>` details setting page listing all objects, serverlessFunctions and agents created by the application: <img width="917" height="778" alt="image" src="https://github.com/user-attachments/assets/7fc056a6-1d73-4242-b2eb-6f8955d8597d" /> A `settings/applications/<application_id>/<serverless_function_id>` <img width="905" height="652" alt="image" src="https://github.com/user-attachments/assets/56ca0021-26bf-42cb-9abf-34879f16050a" /> Add trigger tab in serverless function details (readonly for now) <img width="899" height="724" alt="image" src="https://github.com/user-attachments/assets/5eeefa35-f2a4-4fd8-a640-7b5c5891f226" /> Set object, serverless and agent setting detail pages readonly for managed items <img width="1075" height="859" alt="image" src="https://github.com/user-attachments/assets/57c73d69-4980-47a2-b752-8dc5ab494530" /> <img width="648" height="582" alt="image" src="https://github.com/user-attachments/assets/5ad5f3f7-3bc3-4e40-870a-4981c6492524" /> <img width="982" height="692" alt="image" src="https://github.com/user-attachments/assets/7ad756c4-5d33-4a0a-9eb8-416c040362b9" /> <img width="1077" height="647" alt="image" src="https://github.com/user-attachments/assets/e086b9f5-4062-4d10-82a9-4023de3cad3f" /> |
||
|
|
f65783f900 |
[GroupBy] Allow sorting in bar chart (#15097)
Closes https://github.com/twentyhq/core-team-issues/issues/1628 From a technical perspective, we can add more ordering options, such as the ability to combine two sorts on the X axis, e.g. sort by Close date ASC and then by Sum ASC, which will sort groups that have the same close date between themselves depending on their sum ASC. @Bonapara could you provide design if you want this to be implemented (quite short on our hand i think - maybe in V2 though)? https://github.com/user-attachments/assets/6ef21fe1-9d8f-43c0-bfa2-f6fc6341cacf --------- Co-authored-by: ehconitin <nitinkoche03@gmail.com> |
||
|
|
6188c72f74 |
Simplify and enhance v2 type devxp (#15032)
# Introduction This PR introduces a huge type refactor that will leverage dynamic intra entity optimistic flat maps update in the future and also a more granular cache invalidation enhancing performances close https://github.com/twentyhq/core-team-issues/issues/1717 close https://github.com/twentyhq/core-team-issues/issues/1716 close https://github.com/twentyhq/core-team-issues/issues/1643 ## What's done ### Comparators centralization Comparator is now done through global configuration as const for each metadata names Thanks to Note: Definition of standard is evolving, standard is now scoped to an app. Meaning that a manifest should be able to update its own standards objects but on other app standards ones ? Each synchronizable entities will have a standardOverrides ? ## Typing refactor ### `AllFlatEntityTypesByMetadataName` **Single source of truth for the complete type ecosystem**, mapping each metadata name to its entity types, flat entities, and migration actions: ```typescript export type AllFlatEntityTypesByMetadataName = { fieldMetadata: { actions: { created: CreateFieldAction; updated: UpdateFieldAction; deleted: DeleteFieldAction; }; flatEntity: FlatFieldMetadata; entity: FieldMetadataEntity; }; objectMetadata: { /* ... */ }; // ... all 10 metadata types }; ``` ### `ALL_METADATA_NAME_MANY_TO_ONE_RELATIONS` **Explicitly declares database relationships** between entities with compile-time validation: ```typescript export const ALL_METADATA_NAME_MANY_TO_ONE_RELATIONS = { viewField: { view: 'viewId', fieldMetadata: 'fieldMetadataId', }, cronTrigger: { serverlessFunction: 'serverlessFunctionId', }, // ... all relations } as const satisfies MetadataNameAndRelations; ``` ### `ALL_FLAT_ENTITY_CONFIGURATION` **Centralizes comparison and serialization logic** for each metadata type: ```typescript export const ALL_FLAT_ENTITY_CONFIGURATION = { fieldMetadata: { propertiesToCompare: ['name', 'type', 'label', 'defaultValue', /* ... */], propertiesToStringify: ['options', 'settings', 'defaultValue'], }, objectMetadata: { propertiesToCompare: ['nameSingular', 'namePlural', 'isActive', /* ... */], propertiesToStringify: [], }, // ... all metadata types } as const satisfies AllFlatEntityConfiguration; ``` ## Combined Impact These three configurations work together to create a **strongly-typed, centrally-managed metadata system**: 1. **`AllFlatEntityTypesByMetadataName`** defines *what exists* 2. **`ALL_METADATA_NAME_MANY_TO_ONE_RELATIONS`** defines *how they relate* 3. **`ALL_FLAT_ENTITY_CONFIGURATION`** defines *how to compare and serialize them* **Result:** Builders and validators become thin wrappers around type-safe, configuration-driven logic instead of containing scattered, error-prone manual implementations. ## What's next ### StandardOverrides standardization Every metadata entity can be a standard one for a workspace if it's an installed app, which means it might not expose the whole entity api to be editable through an import dynamically The standard overrides logic should not be applied to Fields and Objects but to every entities At the moment we have a logic of `EDITABLE_PROPERTIES` through the api, and also `STANDARD_OVERRIDEDABLE_PROPERTIES` This should be configuration centered like `propertiesToCompare` and `propertiesToStringify`. Scoping this PR to two last for the moment. As update dispatch to standardOverrides could be considered as a side effect prefer waiting to start the side effect refactor ### Granular Optimistic deprecation With this new grain at runtime we will be able to add a flat entity and dispatch its addition to related flat maps, so we don't have to describe an optimistic method for each flat entity operations See `addFlatEntityToFlatEntityAndRelatedEntityMapsOrThrow` Note: Still in wip and included in this PR but about to create a new one to integrate these utils and remove existing methods ### ValidateBuildAndRun dynamic args typed defintion We should restrain the devxp to send expected flat maps entity as at least from to or dependency as we now have the grain both a type lvl and runtime to do so It should not be possible in the devxp to forgot adding the views to the v2 builder when passing the view field anymore ( that would lead to permanent validation error in view field integrity checks ) ## Conclusion Thanks for reading and reviewing ! Any suggestions are more than welcomed ! ( same as for questions too ! ) |
||
|
|
6f49fd1911 | Message channel change 1 (#14942) | ||
|
|
68c86871dd |
🦣🦣🦣 Table virtualization (#14743)
This big PR implements table virtualization with an offset paging, allowing a way more fluid UX. It is a v1 that should be improved in the future with partial data loading and optimization of the browser display performance of a row. But with this PR we have the solid enough technical foundation, both frontend and backend, to get to a smooth table UX. Fixes and improvements after first successful round of development (needed to have main clean) : - [x] Delete should refresh virtualized portion only and reset all table - [x] Fix add new : top and bottom - [x] Table empty shouldn’t show when first loading - [x] Fix d&d - [x] Fix sorts - [x] Fix drag when scrolling after a full virtual page (it throws an error) - [x] Si update mais qu’on a un sort ou filter, alors il faut trigger le refresh - [x] Reset scroll position between tables - [x] Reset scroll shadows between tables - [x] Setup d&n for virtual list : https://github.com/hello-pangea/dnd/blob/main/docs/patterns/virtual-lists.md - [x] Full table re-render when entering edit mode - [x] Clean code and prepare for merge Fixes https://github.com/twentyhq/core-team-issues/issues/1613 that contains other bugs to be fixed before merge --------- Co-authored-by: Charles Bochet <charles@twenty.com> |
||
|
|
651ab184a7 |
[GQL_VIEW_FILTER_API_BREAKING_CHANGE][WHEN_RELEASED_REQUIRES_CACHE_FLUSH] ViewFilter migration to workspace migration v2 (#15010)
# Introduction Migrating `viewFilter` to v2 in order to migrate later the field update side effect on view to v2 too ## What's done - Created flat-view-filter - flat view filter runner - flat view filter builder - create view filter service v2 and input transpilers - refactor the existing view filter resolver to fix standard ( BREAKING_CHANGE on graphql api update especially ) REST stays the same - refactored the front to consume the mutations autogenerated ## New generic tools ### Compare two flat entity Introducing a new util to compare two flat entity, it's strictly typed and will be added to the generic builder in a following PR This will ease flat entity addition as won't required to create a specific abstraction for comparison Generic builder will expect specific constant: properties to compare and properties to stringify ### Transform flat entity for comparison Forked and refactor the initial existing method for flat entity business scope and type safety ## Coverage Migrated existing integration tests to fit new contract API This PR does not add strong coverage on validation exceptions Deadlines are too short close https://github.com/twentyhq/core-team-issues/issues/1666 |
||
|
|
cbfd73cbd8 |
Enable filters in iterators (#15017)
Filters should not cut the whole workflow. These should only stop the branch. This PR: - adds a new skipped status - when a filter stops, it still goes to the next step - the next step will execute if there is at least a successful step - if only skipped step, it will be skipped as well It allows to use filters in iterators. https://github.com/user-attachments/assets/1cfca052-55c0-4ce5-9eb8-63736618d082 |
||
|
|
875dc1a6b8 |
Connect the bar chart to the group by resolver (#14885)
Closes https://github.com/twentyhq/core-team-issues/issues/1535 Video QA: https://github.com/user-attachments/assets/153fa3f1-08d9-4952-9456-635c602e1f57 https://github.com/user-attachments/assets/d3111afb-4c84-4f57-8a56-5cf666748c86 There are still some formatting and design issues (the labels which are on top of each other and the values which should be formatted) that will be fixed in a future PR --------- Co-authored-by: ehconitin <nitinkoche03@gmail.com> |
||
|
|
ebc6bbabaf |
[Fix] Command to migrate operand values for workflows (#14849)
In [this PR](https://github.com/twentyhq/twenty/pull/14785) we got rid of what we now call ViewFilterOperandDeprecated, a camelCase version of ViewFilterOperand, which we thought we only used in the FE. We did not notice that this enum was used to persist filters used in workflows, reflected in workflowVersion and workflowRun. As a result workflow runs were broken. [In this mitigation PR](https://github.com/twentyhq/twenty/pull/14837) (and [this one](https://github.com/twentyhq/twenty/pull/14841)) we updated the code handle both enum values from ViewFilterOperandDeprecated and ViewFilterOperand, but we still want to get rid of ViewFilterOperandDeprecated. the command in this PR replaces the occurences of enum values of ViewFilterOperandDeprecated. When this has been merged, deployed and run on the workspaces, we will be able to remove ViewFilterOperandDeprecated altogether; that will have to be done in 1.10 though not before. |
||
|
|
1d09cb949a |
Feat/multivalue limit (#14961)
## Summary 1. This change introduces the ability to limit how many values a multi-value field can contain (for example: maximum number of emails, phone numbers, links, or array items). 2. It centralizes the limit as a shared constant and type, validates settings on the server, updates front-end types and components to consume the setting, and adds a small settings UI so workspace admins can change the limit per field. 3. Default behavior is preserved: if no max is configured, the existing default (10) is used. ## **Fixes Issue:** [#14740 ](https://github.com/twentyhq/twenty/issues/14740) ## Manual Test Screenshot <img width="383" height="451" alt="Screenshot 2025-10-08 002413" src="https://github.com/user-attachments/assets/a7704af6-10ef-4d10-b8c9-a9eeca03bfd9" /> ### Values in fields https://github.com/user-attachments/assets/7f59c4f7-3aca-4f83-8c04-3d44988316e3 Let me know if any changes are needed --------- Co-authored-by: Félix Malfait <felix@twenty.com> Co-authored-by: Félix Malfait <felix.malfait@gmail.com> |
||
|
|
7419674cac |
Fix circular dependency at shared package building (#14978)
Fixing <img width="708" height="271" alt="Capture d’écran 2025-10-08 à 12 27 12" src="https://github.com/user-attachments/assets/2f459e2f-146b-4452-8517-59f58b8a33a4" /> The circular-chunk warning came from computeRecordGqlOperationFilter.ts importing from the barrel @/utils, which reexports turnRecordFilterIntoRecordGqlOperationFilter and friends, creating a re-export cycle across chunks. |