Commit Graph

1405 Commits

Author SHA1 Message Date
Félix Malfait 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
2025-11-25 12:10:14 +01:00
github-actions[bot] a47b7dfed0 i18n - translations (#16041)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-25 10:36:15 +01:00
Weiko 04562b11fb Migrate metadata cache (#16030)
## Context
Deprecating legacy ObjectMetadata from cache in favor of flat entities.
Introducing utils to build byName/byNameSingular/byNamePlural in
isolated cases

## Next
- I had to introduce a util to build from flat to legacy
objectMetadataMaps, we should instead use flat maps directly when needed
(datasource, schema generation, etc)
- Deprecate metadata version in the cache
- Use the new cache strategy for flat entities with permissions and
feature flags and inject in the global datasource context
2025-11-24 19:50:47 +01:00
Etienne 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
2025-11-24 18:57:47 +01:00
Charles Bochet d526b07078 Add ability to discard Information Banner (#16019)
Fixes https://github.com/twentyhq/twenty/issues/14028

<img width="1917" height="965" alt="image"
src="https://github.com/user-attachments/assets/eeb5eab7-2c50-48be-8218-af6052b30777"
/>
2025-11-24 17:34:10 +01:00
Paul Rastoin a735e3dfef Dirty fix twenty cli ci build order issue (#16024)
# Introduction
- fix twenty-apps hello world deps lockfile 
- improve error response format in application resolver
- fixed tests by adding applicationId back to serverless function
service v2

## New log format
We should have a tmp logs folder where we write the errors so the user
can see the whole of them such as what's done in yarn logs
```ts
    console.log
      ✓ Client generated successfully!

      at GenerateService.generateClient (src/services/generate.service.ts:58:13)

    console.log
      Generated files at: /Users/paulrastoin/ws/twenty/packages/twenty-apps/hello-world/generated

      at GenerateService.generateClient (src/services/generate.service.ts:59:13)

    console.error
       Serverless functions Sync failed: {
        message: 'Multiple validation errors occurred while creating serverless function',
        extensions: {
          code: 'METADATA_VALIDATION_FAILED',
          errors: {
            fieldMetadata: [],
            objectMetadata: [],
            view: [],
            viewField: [],
            viewGroup: [],
            index: [],
            serverlessFunction: [Array],
            cronTrigger: [],
            databaseEventTrigger: [],
            routeTrigger: [],
            viewFilter: []
          },
          summary: {
            invalidViewFilter: 0,
            invalidObjectMetadata: 0,
            invalidView: 0,
            invalidViewField: 0,
            invalidIndex: 0,
            invalidServerlessFunction: 0,
            invalidDatabaseEventTrigger: 0,
            invalidCronTrigger: 0,
            invalidRouteTrigger: 0,
            invalidFieldMetadata: 0,
            invalidViewGroup: 0,
            totalErrors: 0
          },
          message: 'Validation failed for 0 object(s) and 0 field(s)',
          userFriendlyMessage: 'Validation failed for 0 object(s) and 0 field(s)'
        }
      }

      63 |         JSON.stringify(serverlessSyncResult.error, null, 2),
      64 |       );
    > 65 |       console.error(
         |               ^
      66 |         chalk.red(' Serverless functions Sync failed:'),
      67 |         serverlessSyncResult.error,
      68 |       );

      at AppSyncCommand.synchronize (src/commands/app-sync.command.ts:65:15)
      at async AppSyncCommand.execute (src/commands/app-sync.command.ts:21:14)
      at async Object.<anonymous> (src/__tests__/e2e/applications-install-delete-reinstall.e2e-spec.ts:28:22)

```
2025-11-24 15:49:58 +00:00
Paul Rastoin 8299488f21 Fix front data model edition + non nullable workspaceCustom application migration (#16016)
# Introduction
Two things:
- Enforcing non nullable workspace custom application Id for any
workspace
- Fixing front non editable data models following
https://github.com/twentyhq/twenty/pull/15911 that associate any custom
entities to an applicationId. The front was putting everything as
readonly when under an app ( we will have to handle the twenty standard
application in the future too )

## Fallback
### Migration
The non nullable migration will fail when released, that's why it's
being swallowed and re-run in an upgrade command post workspace custom
application creation for those that miss one. Allowing the migration to
pass in the end
The typeorm migration still need to exists for any new workspaces

### GetCurrentUser
In order to dynamically display isReadOnly in data model settings we're
fetching the workspaceCustomApplicationId through the `getCurrentUser`
If not fallback this endpoint would throw until we're handling existing
workspaces that do not have a custom workspace application
The fallback should be removed post release
2025-11-24 13:39:04 +00:00
github-actions[bot] 85b17a5059 i18n - translations (#16017)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-24 12:01:00 +01:00
github-actions[bot] 607dc283d2 i18n - translations (#16014)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-24 11:21:30 +01:00
github-actions[bot] 0e16b939c5 i18n - translations (#16010)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-23 23:20:27 +01:00
Paul Rastoin f9ab09c404 Metadata api create entity in workspace custom app (#15911)
# Introduction
Cleaner and fewer scope version of
https://github.com/twentyhq/twenty/pull/15745 ( removed sync-metadata
hack through, too ambitious migration and upgrade )

Please note that this PR won't have any interaction with the existing
sync-metadata
Which mean that the sync metadata does not update the standard entities
applicationId and universalIdentifier, and it won't we will deprecate it
on favor of a workspace migration aka twenty-standard app installation

## API Metadata
Any operation going through the api metadata nows automatically scope
the related entity to the workspace custom application instance. (
optionally passing an applicationId to allow current hacky implem of app
sync service )

We need to either ignore the tests or remove the cli status check from
the blocking status badges for a PR to be merged

## New workspace
Already handled in previous
https://github.com/twentyhq/twenty/pull/15625, when a workspace is
created it gets created a twenty standard and custom workspace instance

All his views and permissions will be prefilled to the its twenty
standard app instance with a specific universalIdentifier

## New universalIdentifier
At the contrary as before with standardIds, universalIdentifier are
unique for a given workspace
This means that createdAt field of both object company and opportunity
will have a unique universalIdentifier whereas they share the same
standardId

## FlatApplication
Introduced the flatApplication and cache. Will migrate existing
`MetadataName` to be `SyncableMetadataName` in a following PR

## What's next
Next we will describe a twenty standard app configuration as json that
will be used to generate a workspace migration that will be run instead
of the sync metadata, in a nutshell we aim to deprecated the sync
metadata
So we can standardize any entity to have a non nullable applicationId
and universalIdentifier

## Upgrade command
Introduced an upgrade command that will create a custom workspace
instance for any workspace that do not have one in order to align with
the new behavior when creating a new workspace
2025-11-23 22:35:17 +01:00
neo773 44c0dcde5b Message channel change 3 fix (#15986) 2025-11-23 20:56:02 +01:00
github-actions[bot] e209793e2d i18n - translations (#16001)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-22 13:56:45 +01:00
Charles Bochet 30e504628c Fix ORM event mixing records in batch updates (#15985)
While investigating the issue a customer was facing, I discovered that
before records and after records could be in different order, making the
orm event and timeline activity engine mix records
2025-11-22 13:16:04 +01:00
martmull 445b76fa26 Add uninstall button to application setting (#15988)
As title

<img width="878" height="668" alt="image"
src="https://github.com/user-attachments/assets/b0c9ae1e-036f-4bdd-9bd2-a2a37c2e3b99"
/>
2025-11-21 14:45:07 +00:00
Weiko 0a2d42e79f Implement workspace cache storage (#15962)
## Context
Implementing a single service managing all the cache scoped to a
workspace, this will be dynamically injected as a WorkspaceContext in
the app during a request lifetime (ingested by the future global
datasource for example).

Usage:

```typescript
this.globalWorkspaceOrmManager.executeInWorkspaceContext(
        authContext,
        async () => {
           // Everything here will have access to a workspaceContext, containing all the cache data + a ready to use datasource with workspace scoped metadata, permissions, feature flags, etc...
           // Internally will call loadWorkspaceContext
        }
```
Note: executeInWorkspaceContext will probably be owned by a higher level
service later and not only the ORM.

```typescript
  private async loadWorkspaceContext(
    authContext: WorkspaceAuthContext,
  ): Promise<WorkspaceContext> {
    const workspaceId = authContext.workspace.id;

    const cache =
      await this.workspaceContextCacheService.get<WorkspaceContextData>(
        workspaceId,
        [
          'objectMetadataMaps',
          'metadataVersion',
          'featureFlagsMap',
          'permissionsPerRoleId',
        ],
      );

    return {
      authContext,
      objectMetadataMaps: cache.objectMetadataMaps,
      metadataVersion: cache.metadataVersion,
      featureFlagsMap: cache.featureFlagsMap,
      permissionsPerRoleId: cache.permissionsPerRoleId,
    };
  }
  ```
  
  The cache retrieval strategy is as followed:
```
- Check if there is an ongoing promise fetching data from the cache =>
return the promise.
- Check in the local cache entry if lastCheckedAt has expired. If not,
return as it is without querying redis.
- Check in redis the cache entry hash and compare with local cache entry
hash, if they are the same return the local cache entry data
- Check in redis the cache entry data, if it's there return it and store
it into the local cache entry data and update local cache entry hash. If
it's not there recompute the data by querying the DB and update both
redis and local cache
```
2025-11-21 12:44:24 +01:00
Abdullah. 991bb6dbbf fix: crash in HeaderParser in dicer. (#15963)
Resolves [Dependabot Alert
74](https://github.com/twentyhq/twenty/security/dependabot/74).

Upgraded `graphql-upload` from `13.0.0` to `16.0.2`. Type exports
changed. API remains the same.

Tested the following upload flows:
- profile picture
- workspace logo
- rich text editor (image, video, file)
- record profile picture
- file associated to record.

They all work as intended, nothing breaks.
2025-11-21 11:09:47 +01:00
github-actions[bot] c737042209 i18n - translations (#15967)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-20 19:22:24 +01:00
Félix Malfait 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
2025-11-20 18:32:44 +01:00
martmull 3190ca5b9e 1858 extensibility create relation metadata decorator in thwenty sdkapplications (#15907)
as title

First PR 
I will update the twenty-cli in another PR
2025-11-20 10:22:42 +01:00
Félix Malfait bf638c3a4e Fix file controller route parameter extraction (#15924)
## Problem
When accessing file URLs like `/files/attachment/{token}/{filename}`,
the server was returning 500 errors with no logs. The issue was that the
route pattern `*path/:filename` was not properly extracting parameters,
causing `request.params[0]` to be undefined.

## Solution
- Changed route from `@Get('*path/:filename')` to
`@Get(':folder/:token/:filename')` for explicit parameter extraction
- Simplified `extractFileInfoFromRequest` to use named route parameters
directly instead of complex path parsing
- Removed unnecessary logging that was added during debugging
- Added test to verify route parameters are correctly extracted and
passed to FileService

## Testing
- Added unit test that verifies folder, token, and filename parameters
are correctly passed to the service
- Test will catch any future regressions where route parameters are not
properly extracted
2025-11-19 14:12:34 +01:00
github-actions[bot] 6fa75e1360 i18n - translations (#15908)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-18 18:01:56 +01:00
github-actions[bot] e02c24bd3a i18n - docs translations (#15904)
Created by Github action

---------

Co-authored-by: Abdul Rahman <ar5438376@gmail.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-11-18 17:21:48 +01:00
neo773 cbb5bce500 expose imap username field (#15866)
Had an edge with a customer where they had a custom username.
2025-11-18 17:12:02 +01:00
martmull f131a86cc5 Update lock parameters (#15901)
as title
2025-11-18 15:40:42 +00:00
martmull ee648197df Set TTL >> max await + increase max await from 1s to 2s (#15892)
- set TTL >> max await
- increase max await from 1s to 2s
2025-11-18 16:25:04 +01:00
nitin 8434610f18 seperate v1 and v2 seeds, make groupMode to add default in case its not provided through api but primary groupBy is present (#15815)
Context - 
- refactoring v1 and v2 seeds to test v2 dashboard flag both on and off
Right now, the db reset command fails if the feature flag is off

- whenever there is a groupBy field -- we should add a default groupMode
- we already do that on the front -- but implementing the same on server
so that api users get the same response

This is also the reason why the second seed on dashboards lags a lot --
because on that particular dashboard's widget -- we set groupBy but no
groupMode (in seeding util) -- and the effect of limiting the bars to
fifty runs depending on the groupMode
2025-11-18 11:29:12 +00:00
Etienne 76cc07a5f7 Revert x2 - Common - Field validation (#15491) (#15879)
Revert https://github.com/twentyhq/twenty/pull/15821

Should not have been reverted. Conflicting with this
[PR](https://github.com/twentyhq/twenty/pull/15742),
[reverted](https://github.com/twentyhq/twenty/pull/15875)

Co-authored-by: Weiko <corentin@twenty.com>
2025-11-18 12:04:00 +01:00
martmull d0d90735cb Revert "refactor: Simplify CRUD services to leverage Common API (#157… (#15875)
This reverts commit 11e07f90d2.
2025-11-18 09:44:15 +01:00
Paul Rastoin a39efeb1ab [BREAKING_CHANGE/GRAPHQL/OBJECT_METADATA_CREATE_ONE] Remove object/fields/view-fields v1 implementation (#15823)
# Introduction
Remove the v2 feature flag for view-field field-metadata and
object-metadata metadata entities

## Some details
- Disabled nestjs-query for object metadata creation and explicitly
calling it
- removed all v1 integration tests files

## Remarks
Not remove v2 referencing in both filenaming right now will handle that
globally later

## Breaking change
Due to object metadata resolver createOne standardization had to rename
the input from `CreateObjectInput` to `CreateOneObjectInput`
2025-11-17 10:55:19 +01:00
Paul Rastoin 48031a7ce2 Runner v2 on cascade delete_field/object + refactor workspace deletion side effect (#15830)
# Introduction
While removing the v1 https://github.com/twentyhq/twenty/pull/15823 I've
encountered the method `objectMetadataService.deleteObjectsMetadata`
that I didn't wanted to migrate as it is and if it's not challenging its
existence legitimacy.

## Motivations
In a nutshell on a workspace deletion object metadata and field metadata
cascading deletion is correclty handled
But that's not the case for all of a workspaces entities ( roles,
workspaceMigrations ) I suspect that we did not defined the foreignKey
explicitly through `typeorm`

## Battle testing v2
I still decided to give a try to a complex operation in the v2 such as a
workspace all object metadata deletion.
Spoiler it failed due to object being interdependent between them and
not being topologically sorted ( morph relation can introduce circular
dep anw ).
Note: Even after removing the delete field on delete object aggregator
we end up with an equivalent circular dep error which an object and its
field metadata identifier connection.

## Elegant `DEFERRED` and `DEFERRABLE` foreign keys
The most safe, low level solution would be to make all field relations
deferrable and start the runner transaction as deferred. But this
requires a quite invasive migration of existing FK

## On cascade solution
I've opted for the quick fix, on object or field deletion spread
cacasde.
It's pretty safe as the builder priorly validates the deletion and and
its related entities integrity
Only for both field and object deletion action types in v2 runner

## Integration coverage
Added a test that will scan a new workspace database core schema tables
and expect now result after workspace deletion through its only user
deletion
2025-11-16 22:03:50 +01:00
Félix Malfait 5dfb66917c Upgrade NestJS from 10.x to 11.x (#15836)
## Overview
This PR upgrades all NestJS dependencies from version 10.x to 11.x,
following the [official migration
guide](https://docs.nestjs.com/migration-guide). This builds on top of
the v9 to v10 upgrade completed in PR #15835.

## Changes

### Dependencies Updated
**Core packages (10.x → 11.x):**
- `@nestjs/common`: 10.4.16 → 11.0.8
- `@nestjs/core`: 10.4.16 → 11.0.8
- `@nestjs/platform-express`: 10.4.16 → 11.0.8
- `@nestjs/config`: 3.2.3 → 3.3.0
- `@nestjs/passport`: 10.0.3 → 11.0.0
- `@nestjs/axios`: 3.0.2 → 3.1.2
- `@nestjs/schedule`: ^3.0.0 → ^4.1.1
- `@nestjs/serve-static`: 4.0.2 → 5.0.1
- `@nestjs/cache-manager`: ^2.2.1 → ^2.3.0
- `@nestjs/jwt`: 10.2.0 → 11.0.0
- `@nestjs/typeorm`: 10.0.2 → 11.0.0
- `@nestjs/terminus`: 11.0.0 (already on v11)
- `@nestjs/event-emitter`: 2.1.0 (compatible)

**DevDependencies:**
- `@nestjs/testing`: ^10.4.16 → ^11.0.8
- `@nestjs/schematics`: ^10.1.0 → ^11.0.2
- `@nestjs/cli`: 10.3.0 → 11.0.0

### Code Changes
**Fixed: TwentyConfigModule conditional imports**
- Updated `TwentyConfigModule.forRoot()` to use spread operator for
conditional imports
- Fixes TypeScript error with NestJS 11's stricter DynamicModule type
checking

**Cleanup: Removed unused package**
- Removed `@revertdotdev/revert-react` (not being used anywhere in the
codebase)

## Breaking Changes Addressed

### 1.  Reflector Type Inference
- **Impact**: None - codebase only uses `reflector.get()` method
- **Analysis**: Does not use `getAllAndMerge()` or `getAllAndOverride()`
(the methods with breaking changes)
- **Files reviewed**: feature-flag.guard.ts,
message-queue-metadata.accessor.ts,
workspace-query-hook-metadata.accessor.ts

### 2.  Lifecycle Hooks Execution Order
- **Change**: Termination hooks (`OnModuleDestroy`,
`BeforeApplicationShutdown`, `OnApplicationShutdown`) now execute in
REVERSE order
- **Analysis**: Reviewed all lifecycle hook implementations
  - Redis client cleanup
  - Database connection cleanup (GlobalWorkspaceDataSource)
  - BullMQ queue/worker cleanup
  - Cache storage cleanup
- **Result**: Dependency order is safe - services using connections
clean up before the connections themselves

### 3.  Middleware Registration Order
- **Change**: Global middleware now executes first regardless of import
order
- **Analysis**: Middleware is not registered as global, so execution
order remains consistent
- **Files reviewed**: app.module.ts, middleware.module.ts

## Testing

All tests passing and build successful:

**Unit Tests (283+ tests):**
-  Health module: 38 tests passed
-  Auth module: 115 tests passed (passport v11 integration)
-  REST API: 90 tests passed (middleware and express platform)
-  Feature flags: 17 tests passed (Reflector usage)
-  Workspace: 23 tests passed

**Build & Quality:**
-  Type checking: Passed
-  Linting: Passed
-  Build: 3,683 files compiled successfully

## Verification

Tested critical NestJS functionality:
-  Authentication & Security (JWT, OAuth, guards)
-  HTTP Platform (Express integration, REST endpoints)
-  Dependency Injection (Services, factories, providers)
-  Cache Management (Redis with @nestjs/cache-manager)
-  GraphQL (Query runners, resolvers)
-  Configuration (Environment config)
-  Scheduling (Cron jobs with @nestjs/schedule v4)
-  Lifecycle Hooks (Module initialization and cleanup)
-  Reflector (Metadata reflection in guards)

## Related PRs
- #15835 - Upgrade NestJS from 9.x to 10.x (completed)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Upgrades NestJS to v11 and updates routing patterns, auth strategies,
GraphQL schema options, and build/dist paths (scripts, Docker, Nx,
migrations, assets), plus enables Devtools in development.
> 
> - **Backend (NestJS 11 upgrade)**:
> - Bump `@nestjs/*` packages (core, platform-express, jwt, passport,
typeorm, serve-static, schedule, cli/testing/schematics) to v11.
> - Update REST/route-trigger/file controllers to new wildcard syntax
(`*path`).
> - Refactor OAuth (Google/Microsoft) and SAML strategies (abstract base
+ explicit `validate`); minor typings.
>   - Enable `DevtoolsModule` in development.
> - **GraphQL**:
> - Add `buildSchemaOptions.orphanedTypes` for client-config types; keep
Yoga/Sentry setup.
> - **Build/Runtime & Config**:
> - Standardize dist layout (remove `src` in paths): update scripts,
Docker `CMD`, Nx `project.json`, render scripts, TypeORM migration
paths, asset resolution.
> - Adjust `nest-cli.json` (watchOptions, asset globs, migrations
outDir, monorepo/root).
> - Improve config module imports (spread conditional); tsconfig
excludes `node_modules`.
>   - Minor Nx default: `start` target caching disabled.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
1139fd85a97d0c72314d416d07464cc3c9942783. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-11-16 18:20:06 +01:00
Félix Malfait a2a7ff8b4b Upgrade NestJS from 9.x to 10.x (#15835)
## Overview
This PR upgrades all NestJS dependencies from version 9.x to 10.x,
following the [official migration
guide](https://docs.nestjs.com/v10/migration-guide). This is the first
step before upgrading to NestJS 11.x in a future PR.

## Changes

### Dependencies Updated
- `@nestjs/common`: 9.4.3 → 10.4.16
- `@nestjs/core`: 9.4.3 → 10.4.16
- `@nestjs/passport`: 9.0.3 → 10.0.3
- `@nestjs/platform-express`: 9.4.3 → 10.4.16
- `@nestjs/config`: 2.3.4 → 3.2.3
- `@nestjs/event-emitter`: 2.0.4 → 2.1.0
- `@nestjs/testing`: ^9.0.0 → ^10.4.16
- `@nestjs/schematics`: ^9.0.0 → ^10.1.0

### Code Changes
- Fixed `CacheModuleOptions` import from `@nestjs/common` to
`@nestjs/cache-manager` in cache-storage.module-factory.ts

## Breaking Changes Addressed
 **CacheModule Migration**: Already using `@nestjs/cache-manager`
package
 **TypeScript Version**: Using 5.9.2 (requires 4.8+)  
 **Node.js Version**: Using 24.5.0 (requires 16+)  
 **Deprecated APIs**: All v9 deprecations removed in v10 - standard
patterns in use

## Testing
All tests passing and build successful:
-  Health module: 38 tests passed
-  Auth module: 115 tests passed (critical - uses @nestjs/passport)
-  Admin panel: 30 tests passed
-  REST API: 90 tests passed (uses @nestjs/platform-express)
-  Feature flags: 17 tests passed
-  Workspace: 23 tests passed
-  GraphQL query runner: 17 tests passed
-  **Total: 330+ tests passing**
-  Build: 3,683 files compiled successfully
-  Type checking: Passed
-  Linting: Passed

## Verification
Tested critical NestJS functionality:
- Authentication & Security (JWT, OAuth, guards)
- HTTP Platform (Express integration, REST endpoints)
- Dependency Injection (Services, factories, providers)
- Cache Management (Redis with @nestjs/cache-manager)
- GraphQL (Query runners, resolvers)
- Configuration (Environment config)
- Event Emitters (Event-driven architecture)

## Next Steps
This upgrade positions the codebase for the next upgrade to NestJS 11.x,
which will be handled in a separate PR.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Upgrades NestJS to v10, fixes cache module import, and narrows CI
GraphQL generation diff to generated directories.
> 
> - **Backend**
> - **Dependencies**: Upgrade `@nestjs/common`, `@nestjs/core`,
`@nestjs/platform-express`, `@nestjs/passport`, `@nestjs/config`,
`@nestjs/event-emitter`, `@nestjs/testing`, and `@nestjs/schematics` to
v10-compatible versions in `packages/twenty-server/package.json`.
> - **Code**: Update `CacheModuleOptions` import to
`@nestjs/cache-manager` in
`src/engine/core-modules/cache-storage/cache-storage.module-factory.ts`.
> - **CI**
> - **GraphQL**: Restrict schema change detection to
`packages/twenty-front/src/generated` and
`packages/twenty-front/src/generated-metadata` in
`.github/workflows/ci-server.yaml`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f621230fcf06ea86585b58ea9b61d6a5064230db. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-11-15 23:06:13 +01:00
martmull f13c36234f Fix workspace-member-id missing from database events (#15819)
As title
2025-11-14 14:03:49 +00:00
martmull 867422627c Revert "Common - Field validation (#15491)" (#15821)
This reverts commit 261cba40ef.
2025-11-14 13:47:22 +01:00
Weiko 998365457e Improve infer deletion from entities (#15807)
## Context
inferDeletionFromEntities only accepts a set of keys for each entities
that needs deletion. This could be error-prone if tmr we want to add a
new side effect and forget to add the entity when in practice you want
to delete all entities that are in the fromToAllFlatEntityMaps (this is
the case for applications for example)
2025-11-13 18:18:27 +01:00
github-actions[bot] 9bc5486d0d i18n - translations (#15803)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-13 17:12:39 +01:00
github-actions[bot] 8cadd00d34 i18n - translations (#15799)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-13 16:34:00 +01:00
Félix Malfait cd5d0f9eac Introduce agent hints to reduce context bloat (#15763)
Introducing a new pattern that should reduce token consumption by 90%
for the most common use-cases
2025-11-13 16:15:01 +01:00
Etienne 261cba40ef Common - Field validation (#15491)
Closes : https://github.com/twentyhq/core-team-issues/issues/1622

To do in other PR : 
- Add migration command for non nullable text, raw_json & array fields
- Add null transformation

---------

Co-authored-by: Weiko <corentin@twenty.com>
2025-11-13 15:45:35 +01:00
Félix Malfait f6ed1e8295 Merge data navigator and manipulator agents (#15761)
Merge data manipulator and navigator agents
2025-11-13 15:25:52 +01:00
Abdullah. 0cab2b49fc (breaking change) Allow users with a single workspace to update their email. (#15736)
- Users with a single workspace are allowed to update their email across
`core.user` and `workspace_xyz.workspaceMember`.
- The latter happens asynchronously (built it like this for non-blocking
with multiple workspaces), but since we restrict the email update
functionality to a single user, we can also update the email in
workspaceMember synchronously - I left asynchronous there to receive
feedback on whether we should move to synchronous or not.
- Merged main and resolved conflicts to ensure we use the
`SettingsPermissionGuard` and the updated `workspace.service.ts` code.

One edge-case that I was trying to communicate on Discord: 

Say that an admin is a member of multiple workspaces. Therefore, they
can allow roles with PROFILE_INFORMATION permission to update their
email.

<p align="center">
<img width="553" height="115" alt="image"
src="https://github.com/user-attachments/assets/80382b1f-a9e3-4dac-b606-c2defeb2c330"
/>
</p>

However, since the admin is part of multiple workspaces, he/she cannot
even update own email - the field stays disabled, leading to some
confusion.

<p align="center">
<img width="545" height="255" alt="image"
src="https://github.com/user-attachments/assets/5e6d27db-c9a8-4d5e-9ab6-65c77beae5b4"
/>
</p>

However, the workspace can have another member with admin role or some
other role that has PROFILE_INFORMATION permission flag. That user will
be and should be allowed to update email, so we cannot hide `email` from
dropdown options.

<p align="center">
<img width="585" height="283" alt="image"
src="https://github.com/user-attachments/assets/a670d3ac-cf48-4865-a425-b909093d8420"
/>
</p>

The behavior is fine imo, just a little confusing for members with more
than one workspace.

I have also tested the flow by signing up to YC workspace with my org
google account (twenty.com), then changing email to my personal address.
- After changing, I need to login using Google with my personal account
to access YC workspace again.
- If I login using Google with org google account (twenty.com), a new
user account is created.

This behavior is consistent with Notion and Linear. 

Finally, as for the verification of email, the user is asked to verify
email while they're logged in, but just in case they logout without
verifying, the next login would force them to verify their email in the
email/password flow.

However, for Social/SSO, they must verify before they logout or else
they'd have to contact support for assistance. I have not looked into
how to show verification screen while logging in via Social/SSO yet, but
if that's something critical for completeness here, I shall revisit it.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-11-13 15:00:02 +01:00
neo773 3ef9be6622 message channel change #3 (#15757)
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-11-13 14:12:20 +01:00
Félix Malfait 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
2025-11-13 13:34:46 +01:00
nitin fddac24e7f connect line chart to backend (#15657)
https://github.com/user-attachments/assets/b797c5a8-32a8-4d3e-9fb1-7541d107a19f

optimizations:
before: 


https://github.com/user-attachments/assets/bf45a3f1-5f89-40c1-9ce4-c2d912b77d63


after:


https://github.com/user-attachments/assets/61bfd603-7b63-4695-8ae8-68fe7d3bfea0
2025-11-13 11:35:08 +00:00
Guillim 0389fcf00d removing feature flag IS_MORPH_RELATION_ENABLED (#15783)
releasing the morph feature for all workspaces
2025-11-13 12:03:25 +01:00
Raphaël Bosi 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.
2025-11-13 12:01:11 +01:00
Lucas Bordeau a453d53656 Fix Snackbar crashing the whole app (#15774)
This PR fixes a crash of the app when a Snackbar tries to render an
object.

<img width="3002" height="754" alt="image"
src="https://github.com/user-attachments/assets/988f97eb-5c8a-44f8-ac8e-e2953b872bac"
/>

What happened : the backend sent a MessageDescriptor in
`extensions.userFriendlyMessage` while it should have sent a translated
string.

But it is a problem that the Snackbar can end up rendering objects and
crashing the whole app because it is a the highest level in the tree.

So this PR hardens many points to avoid future bugs : 
- Sanitize what is rendered by the Snackbar to avoid any crash
- Translate any MessageDescriptor object that could end up in the
frontend
- Fix the places in the backend where a MessageDescriptor wasn't
translated and sent directly to the frontend in
`use-graphql-error-handler.hook.ts`

Fixes https://github.com/twentyhq/twenty/issues/15685
Fixes https://github.com/twentyhq/core-team-issues/issues/1869
2025-11-13 12:01:06 +01:00
Harshit Singh 065923d724 feat: Add Workflow duplicate step (#15622)
## Description

- This PR address issue
https://github.com/twentyhq/core-team-issues/issues/1800
- Added workflow duplicate option in workflow sidepanel
- Introduced DuplicateWorkflow.ts mutuation
- Updated graphql generated metadata with new duplicate type




## Visual Appearanc

<img width="1792" height="1031" alt="Screenshot 2025-11-07 at 4 46
22 PM"
src="https://github.com/user-attachments/assets/d75f99df-ae18-4b41-a5f4-21c50010cdbc"
/>


https://github.com/user-attachments/assets/3dbf73cf-f4dd-484c-94a3-29577cfbac25

---------

Co-authored-by: Devessier <baptiste@devessier.fr>
2025-11-12 10:53:25 +01:00
Félix Malfait 11e07f90d2 refactor: Simplify CRUD services to leverage Common API (#15742)
## Summary

This PR refactors all Record CRUD services to use the Common API
(CommonQueryRunners) instead of directly accessing TwentyORM, achieving
**77% code reduction** while maintaining full functionality.

## Changes

### Code Reduction: -547 net lines (77%)
- **Before**: 901 lines across 5 services
- **After**: 354 lines (services + utility)
- **Deleted**: 547 lines of redundant code

### Services Simplified

| Service | Before | After | Reduction |
|---------|--------|-------|-----------|
| CreateRecordService | 142 | 59 | -83 lines |
| UpdateRecordService | 177 | 51 | -126 lines |
| DeleteRecordService | 137 | 51 | -86 lines |
| FindRecordsService | 233 | 68 | -165 lines |
| UpsertRecordService | 212 | 129 | -83 lines |

### Architecture Change

**Before:**
```
CRUD Services → TwentyORM
- Manual query building
- Manual permission checking
- Manual transformations
- Duplicated logic
```

**After:**
```
CRUD Services → CommonQueryRunners → TwentyORM
- Common API handles queries
- Common API handles permissions
- Common API handles transformations
- Single source of truth
```

### Module Dependencies Simplified

**Removed:**
- TwentyORMModule
- RecordPositionModule
- RecordTransformerModule
- WorkflowCommonModule

**Added:**
- CoreCommonApiModule
- WorkspaceMetadataCacheModule

**From 4 heavy dependencies → 2 clean dependencies**

## What Changed

### New Code
- `common-api-context-builder.util.ts` - Single shared utility (68
lines)

### Refactored Services
All services now follow a simple pattern:
1. Build Common API context
2. Call appropriate CommonQueryRunner
3. Return formatted result

Each service is now 50-120 lines instead of 140-230 lines.

### Type Fixes
- Fixed `FindRecordsParams.orderBy` type (was
`Partial<ObjectRecordOrderBy>`, now `ObjectRecordOrderBy`)
- Fixed `FindRecordsInput.gqlOperationOrderBy` type (same fix)

## Benefits

###  Code Quality
- 77% less code to maintain
- No code duplication
- Simpler, clearer logic
- Proper TypeScript types (no hacks)

###  Common API Integration
All services now get Common API benefits:
- Consistent permission checking
- Query hooks (before/after execution)
- Automatic input transformation
- Automatic position handling
- Result processing and enrichment
- Same behavior as REST/GraphQL

###  Safety Preserved
- createdBy actor metadata preserved for workflows
- All field validation maintained
- All transformations maintained
- All error handling maintained

###  No Breaking Changes
- Same external API for all services
- Workflows continue to work
- AI operations continue to work
- MCP operations continue to work

## Testing

-  TypeScript compiles
-  All files pass linting
-  No type casting hacks
-  Proper type safety throughout
- ⚠️ Integration tests recommended

## What Common API Handles For Us

1. **Record Position** - Automatic via `RecordPositionService`
2. **Input Transformation** - Automatic for NUMBER, RICH_TEXT, PHONES,
EMAILS, LINKS
3. **createdBy Actor** - Injected via `CreatedByCreateOnePreQueryHook` +
explicit workflow actor
4. **Field Validation** - Only processes valid fields
5. **Permissions** - Validates permissions before execution
6. **Query Hooks** - Before/after execution hooks work
7. **Error Handling** - Consistent exception handling

## Files Modified (12)

**Services (6):**
- create-record.service.ts
- update-record.service.ts
- delete-record.service.ts
- find-records.service.ts
- upsert-record.service.ts
- record-crud.module.ts

**Types (2):**
- find-records-params.type.ts
- record-crud-input.type.ts

**Workflows (1):**
- find-records.workflow-action.ts

**New Files (3):**
- common-api-context-builder.util.ts
- REFACTORING_COMPLETE.md
- REFACTORING_ANALYSIS.md

## Verification Checklist

- [x] TypeScript compiles
- [x] Linter passes
- [x] No type hacks (`as unknown as` removed)
- [x] createdBy preserved for workflows
- [x] Module dependencies simplified
- [x] Common API integration complete
- [ ] Integration tests pass (recommended)
- [ ] Workflow execution tested (recommended)

## Related Issues

This refactoring establishes the pattern for making CRUD operations
consistent across all presentation layers (REST, GraphQL,
Tools/Workflows/AI/MCP).
2025-11-11 11:07:36 +01:00