Compare commits

..
Author SHA1 Message Date
prastoin 80e675f430 chore 2026-03-10 13:45:11 +01:00
prastoin 29091ca369 refactor(sdk): core client genql runtime and twenty patch file tree 2026-03-10 11:33:05 +01:00
prastoin 5e71b7cb9c create new tests 2026-03-09 19:14:15 +01:00
prastoin a641fbe804 remove second build 2026-03-09 18:45:27 +01:00
prastoin f536c4d8b3 patch 2026-03-09 18:37:44 +01:00
FelipeandGitHub 5ab3eeb830 fix: throw clear error on invalid LOG_LEVELS (#18495)
Fixes #18356

## Summary

Setting `LOG_LEVELS=debug,info,error,warn` crashes with `TypeError:
logLevels.map is not a function` because `CastToLogLevelArray` silently
returns `undefined` for invalid levels.

Now it throws a clear error message listing the invalid levels and valid
options:

```
Invalid log level(s): info. Valid levels are: log, error, warn, debug, verbose
```

## Changes

- Throw descriptive `Error` when invalid log levels are provided instead
of returning `undefined`
- Updated tests to verify the error message

## Test plan

- [x] All 8 existing tests passing
- [x] `"toto"` → throws `Invalid log level(s): toto. Valid levels are:
log, error, warn, debug, verbose`
- [x] `"verbose,error,toto"` → throws listing only `toto` as invalid
- [x] Valid levels (`log,error,warn,debug,verbose`) continue working as
before
2026-03-09 15:41:15 +00:00
Paul RastoinandGitHub 75bb3a904d [SDK] Refactor clients (#18433)
# Intoduction

Closes https://github.com/twentyhq/core-team-issues/issues/2289

In this PR all the clients becomes available under `twenty-sdk/clients`,
this is a breaking change but generated was too vague and thats still
the now or never best timing to do so

## CoreClient
The core client is now shipped with a default stub empty class for both
the schema and the client
Allowing its import, will still raises typescript errors when consumed
as generated but not generated

## MetadataClient
The metadata client is workspace agnostic, it's now generated and
commited in the repo. added a ci that prevents any schema desync due to
twenty-server additions

Same behavior than for the twenty-front generated graphql schema
2026-03-09 15:32:13 +00:00
Thomas TrompetteandGitHub 82a1179e23 Fix empty record index page (#18500)
Before
<img width="1507" height="851" alt="Capture d’écran 2026-03-09 à 15 27
17"
src="https://github.com/user-attachments/assets/6f93686d-a3f9-4fb1-a02e-d6b1a8347120"
/>

After
<img width="1507" height="851" alt="Capture d’écran 2026-03-09 à 15 26
53"
src="https://github.com/user-attachments/assets/68a44260-0332-48c7-92bf-7bef466a7a05"
/>
2026-03-09 15:16:04 +00:00
Charles BochetandGitHub 662de17644 ci: replace 4-core runners with ubuntu-latest (#18503)
## Summary

- Replace all `ubuntu-latest-4-cores` (paid larger runners) with
`ubuntu-latest` across CI workflows
- The free `ubuntu-latest` runner for public repos already provides **4
vCPUs + 16 GB RAM** — identical specs to the paid 4-core larger runner
- Affects 4 workflow files: `ci-server.yaml`, `ci-front.yaml`,
`ci-sdk.yaml`, `ci-zapier.yaml` (8 job definitions total, including the
10-shard integration test matrix)
- The `ubuntu-latest-8-cores` runners are intentionally **kept** for
memory-heavy jobs (frontend build, storybook build, E2E tests) where the
extra capacity (8 vCPUs, 32 GB RAM) is needed
2026-03-09 16:21:03 +01:00
1993614637 i18n - docs translations (#18496)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-03-09 16:11:29 +01:00
a3aae5c857 i18n - translations (#18498)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-03-09 16:06:10 +01:00
martmullandGitHub 22a203680e Fix wrong type usage (#18499)
fix wrong type usage + add tests
2026-03-09 14:51:46 +00:00
36bcc71f3d refactor(command-menu-item): rename Actions to CommandMenuItem (#18489)
actions are being renamed to command menu item, they will be migrated to
server and will be served as headless front components

---------

Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
2026-03-09 14:03:12 +00:00
Paul RastoinandGitHub 06bdb5ad6a [SDK] Agent in manifest (#18431)
# Introduction
Adding agent in the manifest, required for twenty standard app
extraction out of twenty-server
2026-03-09 11:10:14 +00:00
Baptiste DevessierandGitHub a9696705c1 Make all widgets of record page layouts non-editable except Fields widgets (#18471)
## Demo


https://github.com/user-attachments/assets/d5746f81-beae-4c46-abfe-9723da9bcc1d
2026-03-09 09:06:07 +00:00
0acc08c333 i18n - translations (#18494)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-03-09 09:45:50 +01:00
c2a79fc0c2 i18n - translations (#18475)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-03-09 09:28:49 +01:00
Abdul RahmanandGitHub 5b28e59ca7 Navbar drag drop using dnd kit (#18288) 2026-03-08 20:25:50 +00:00
574 changed files with 46950 additions and 17533 deletions
+1 -1
View File
@@ -65,7 +65,7 @@ jobs:
key: ${{ env.STORYBOOK_BUILD_CACHE_KEY_FOR_SAVE_ACTION }}
front-sb-test:
timeout-minutes: 30
runs-on: ubuntu-latest-4-cores
runs-on: ubuntu-latest
needs: front-sb-build
strategy:
fail-fast: false
+1 -1
View File
@@ -51,7 +51,7 @@ jobs:
tasks: ${{ matrix.task }}
sdk-e2e-test:
timeout-minutes: 30
runs-on: ubuntu-latest-4-cores
runs-on: ubuntu-latest
needs: [changed-files-check, sdk-test]
if: needs.changed-files-check.outputs.any_changed == 'true'
services:
+26 -9
View File
@@ -25,6 +25,7 @@ jobs:
packages/twenty-server/**
packages/twenty-front/src/generated/**
packages/twenty-front/src/generated-metadata/**
packages/twenty-sdk/src/clients/generated/metadata/**
packages/twenty-emails/**
packages/twenty-shared/**
@@ -32,7 +33,7 @@ jobs:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 30
runs-on: ubuntu-latest-4-cores
runs-on: ubuntu-latest
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
@@ -60,7 +61,7 @@ jobs:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 30
runs-on: ubuntu-latest-4-cores
runs-on: ubuntu-latest
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
@@ -79,7 +80,7 @@ jobs:
server-validation:
needs: server-build
timeout-minutes: 30
runs-on: ubuntu-latest-4-cores
runs-on: ubuntu-latest
services:
postgres:
image: twentycrm/twenty-postgres-spilo
@@ -158,8 +159,10 @@ jobs:
exit 1
fi
- name: GraphQL / Check for Pending Generation
- name: Check for Pending Code Generation
run: |
HAS_ERRORS=false
npx nx run twenty-front:graphql:generate
npx nx run twenty-front:graphql:generate --configuration=metadata
@@ -171,15 +174,29 @@ jobs:
git diff -- packages/twenty-front/src/generated packages/twenty-front/src/generated-metadata
echo "==================================================="
echo ""
echo "Please run 'npx nx run twenty-front:graphql:generate' and 'npx nx run twenty-front:graphql:generate --configuration=metadata' and commit the changes."
echo ""
exit 1
HAS_ERRORS=true
fi
npx nx run twenty-sdk:generate-metadata-client
if ! git diff --quiet -- packages/twenty-sdk/src/clients/generated/metadata; then
echo "::error::SDK metadata client changes detected. Please run 'npx nx run twenty-sdk:generate-metadata-client' and commit the changes."
echo ""
echo "The following SDK metadata client changes were detected:"
echo "==================================================="
git diff -- packages/twenty-sdk/src/clients/generated/metadata
echo "==================================================="
echo ""
HAS_ERRORS=true
fi
if [ "$HAS_ERRORS" = true ]; then
exit 1
fi
server-test:
needs: server-build
timeout-minutes: 30
runs-on: ubuntu-latest-4-cores
runs-on: ubuntu-latest
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
@@ -201,7 +218,7 @@ jobs:
server-integration-test:
timeout-minutes: 30
runs-on: ubuntu-latest-4-cores
runs-on: ubuntu-latest
needs: server-build
strategy:
fail-fast: false
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 30
runs-on: ubuntu-latest-4-cores
runs-on: ubuntu-latest
services:
postgres:
image: twentycrm/twenty-postgres-spilo
+2 -2
View File
@@ -43,7 +43,7 @@ yarn twenty auth:login
yarn twenty entity:add
# Start dev mode: watches, builds, and syncs local changes to your workspace
# (also auto-generates typed API clients — CoreApiClient and MetadataApiClient — in node_modules/twenty-sdk/generated)
# (also auto-generates typed CoreApiClient MetadataApiClient ships pre-built with the SDK — both available via `twenty-sdk/clients`)
yarn twenty app:dev
# Watch your application's function logs
@@ -107,7 +107,7 @@ npx create-twenty-app@latest my-app -m
- Use `yarn twenty auth:login` to authenticate with your Twenty workspace.
- Explore the generated project and add your first entity with `yarn twenty entity:add` (logic functions, front components, objects, roles, views, navigation menu items, skills).
- Use `yarn twenty app:dev` while you iterate — it watches, builds, and syncs changes to your workspace in real time.
- Two typed API clients are autogenerated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated`: `CoreApiClient` (for workspace data via `/graphql`) and `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`).
- `CoreApiClient` (for workspace data via `/graphql`) is auto-generated by `yarn twenty app:dev`. `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`) ships pre-built with the SDK. Both are available via `import { CoreApiClient, MetadataApiClient } from 'twenty-sdk/clients'`.
## Publish your application
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-twenty-app",
"version": "0.6.4",
"version": "0.7.0-canary.0",
"description": "Command-line interface to create Twenty application",
"main": "dist/cli.cjs",
"bin": "dist/cli.cjs",
@@ -113,6 +113,7 @@ export class CreateAppCommand {
includeExampleView: false,
includeExampleNavigationMenuItem: false,
includeExampleSkill: false,
includeExampleAgent: false,
includeExampleIntegrationTest: false,
};
}
@@ -126,6 +127,7 @@ export class CreateAppCommand {
includeExampleNavigationMenuItem: true,
includeExampleSkill: true,
includeExampleIntegrationTest: true,
includeExampleAgent: true,
};
}
@@ -8,5 +8,6 @@ export type ExampleOptions = {
includeExampleView: boolean;
includeExampleNavigationMenuItem: boolean;
includeExampleSkill: boolean;
includeExampleAgent: boolean;
includeExampleIntegrationTest: boolean;
};
@@ -25,6 +25,7 @@ const ALL_EXAMPLES: ExampleOptions = {
includeExampleView: true,
includeExampleNavigationMenuItem: true,
includeExampleSkill: true,
includeExampleAgent: true,
includeExampleIntegrationTest: true,
};
@@ -41,6 +42,7 @@ const NO_EXAMPLES: ExampleOptions = {
includeExampleObject: false,
includeExampleField: false,
includeExampleSkill: false,
includeExampleAgent: false,
includeExampleLogicFunction: false,
includeExampleFrontComponent: false,
includeExampleView: false,
@@ -476,6 +478,7 @@ describe('copyBaseApplicationProject', () => {
includeExampleObject: false,
includeExampleField: false,
includeExampleSkill: false,
includeExampleAgent: false,
includeExampleLogicFunction: false,
includeExampleFrontComponent: true,
includeExampleView: false,
@@ -513,6 +516,7 @@ describe('copyBaseApplicationProject', () => {
exampleOptions: {
includeExampleObject: false,
includeExampleSkill: false,
includeExampleAgent: false,
includeExampleField: false,
includeExampleLogicFunction: true,
includeExampleFrontComponent: false,
@@ -47,18 +47,17 @@ describe('scaffoldIntegrationTest', () => {
const content = await fs.readFile(testPath, 'utf8');
expect(content).toContain(
"import { appGenerateClient, appUninstall } from 'twenty-sdk/cli'",
"import { appBuild, appUninstall } from 'twenty-sdk/cli'",
);
expect(content).toContain(
"import { MetadataApiClient } from 'twenty-sdk/generated'",
"import { MetadataApiClient } from 'twenty-sdk/clients'",
);
expect(content).toContain(
"import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config'",
);
expect(content).toContain('TWENTY_TEST_API_KEY');
expect(content).toContain('assertServerIsReachable');
expect(content).toContain('appGenerateClient');
expect(content).toContain('appBuild');
expect(content).toContain('appUninstall');
expect(content).toContain('new MetadataApiClient()');
expect(content).toContain('findManyApplications');
expect(content).toContain('APPLICATION_UNIVERSAL_IDENTIFIER');
});
@@ -84,7 +83,8 @@ describe('scaffoldIntegrationTest', () => {
expect(content).toContain('.twenty-sdk-test');
expect(content).toContain('config.json');
expect(content).toContain('process.env.TWENTY_API_URL');
expect(content).toContain('process.env.TWENTY_TEST_API_KEY');
expect(content).toContain('process.env.TWENTY_API_KEY');
expect(content).toContain('assertServerIsReachable');
});
});
@@ -101,7 +101,8 @@ describe('scaffoldIntegrationTest', () => {
const content = await fs.readFile(vitestConfigPath, 'utf8');
expect(content).toContain('TWENTY_TEST_API_KEY');
expect(content).toContain('TWENTY_API_KEY');
expect(content).not.toContain('TWENTY_TEST_API_KEY');
expect(content).toContain('TWENTY_API_URL');
expect(content).toContain('setup-test.ts');
expect(content).toContain('tsconfig.spec.json');
@@ -103,6 +103,14 @@ export const copyBaseApplicationProject = async ({
});
}
if (exampleOptions.includeExampleAgent) {
await createExampleAgent({
appDirectory: sourceFolderPath,
fileFolder: 'agents',
fileName: 'example-agent.ts',
});
}
if (exampleOptions.includeExampleIntegrationTest) {
await scaffoldIntegrationTest({
appDirectory,
@@ -519,6 +527,36 @@ export default defineSkill({
await fs.writeFile(join(appDirectory, fileFolder ?? '', fileName), content);
};
const createExampleAgent = async ({
appDirectory,
fileFolder,
fileName,
}: {
appDirectory: string;
fileFolder?: string;
fileName: string;
}) => {
const universalIdentifier = v4();
const content = `import { defineAgent } from 'twenty-sdk';
export const EXAMPLE_AGENT_UNIVERSAL_IDENTIFIER =
'${universalIdentifier}';
export default defineAgent({
universalIdentifier: EXAMPLE_AGENT_UNIVERSAL_IDENTIFIER,
name: 'example-agent',
label: 'Example Agent',
description: 'A sample AI agent for your application',
icon: 'IconRobot',
prompt: 'You are a helpful assistant. Help users with their questions and tasks.',
});
`;
await fs.ensureDir(join(appDirectory, fileFolder ?? ''));
await fs.writeFile(join(appDirectory, fileFolder ?? '', fileName), content);
};
const createApplicationConfig = async ({
displayName,
description,
@@ -45,7 +45,7 @@ export default defineConfig({
setupFiles: ['src/__tests__/setup-test.ts'],
env: {
TWENTY_API_URL: 'http://localhost:3000',
TWENTY_TEST_API_KEY:
TWENTY_API_KEY:
'${SEED_API_KEY}',
},
},
@@ -93,16 +93,36 @@ import * as os from 'os';
import * as path from 'path';
import { beforeAll } from 'vitest';
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:3000';
const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
beforeAll(() => {
const assertServerIsReachable = async () => {
let response: Response;
try {
response = await fetch(\`\${TWENTY_API_URL}/healthz\`);
} catch {
throw new Error(
\`Twenty server is not reachable at \${TWENTY_API_URL}. \` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(\`Server at \${TWENTY_API_URL} returned \${response.status}\`);
}
};
beforeAll(async () => {
await assertServerIsReachable();
fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
const configFile = {
profiles: {
default: {
apiUrl: process.env.TWENTY_API_URL,
apiKey: process.env.TWENTY_TEST_API_KEY,
apiKey: process.env.TWENTY_API_KEY,
},
},
};
@@ -128,44 +148,24 @@ const createIntegrationTest = async ({
fileName: string;
}) => {
const content = `import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config';
import { appGenerateClient, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-sdk/generated';
import { appBuild, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-sdk/clients';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
const APP_PATH = process.cwd();
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:3000';
const assertServerIsReachable = async () => {
let response: Response;
try {
response = await fetch(\`\${TWENTY_API_URL}/healthz\`);
} catch {
throw new Error(
\`Twenty server is not reachable at \${TWENTY_API_URL}. \` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(\`Server at \${TWENTY_API_URL} returned \${response.status}\`);
}
};
describe('App installation', () => {
let appInstalled = false;
beforeAll(async () => {
await assertServerIsReachable();
const generateResult = await appGenerateClient({
const buildResult = await appBuild({
appPath: APP_PATH,
onProgress: (message: string) => console.log(\`[generate-client] \${message}\`),
onProgress: (message: string) => console.log(\`[build] \${message}\`),
});
if (!generateResult.success) {
if (!buildResult.success) {
throw new Error(
\`Client generation failed: \${generateResult.error?.message ?? 'Unknown error'}\`,
\`Build failed: \${buildResult.error?.message ?? 'Unknown error'}\`,
);
}
@@ -187,20 +187,7 @@ describe('App installation', () => {
});
it('should find the installed app in the applications list', async () => {
const apiKey = process.env.TWENTY_TEST_API_KEY;
if (!apiKey) {
throw new Error(
'No API key found. Set TWENTY_TEST_API_KEY in your vitest config env.',
);
}
const metadataClient = new MetadataApiClient({
url: \`\${TWENTY_API_URL}/metadata\`,
headers: {
Authorization: \`Bearer \${apiKey}\`,
},
});
const metadataClient = new MetadataApiClient();
const result = await metadataClient.query({
findManyApplications: {
+10 -29
View File
@@ -1,38 +1,19 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["typescript", "import", "unicorn"],
"plugins": ["typescript"],
"categories": {
"correctness": "off"
},
"ignorePatterns": ["node_modules"],
"ignorePatterns": ["node_modules", "dist"],
"rules": {
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"no-console": ["warn", { "allow": ["group", "groupCollapsed", "groupEnd"] }],
"no-control-regex": "off",
"no-debugger": "error",
"no-duplicate-imports": "error",
"no-undef": "off",
"no-unused-vars": "off",
"no-redeclare": "off",
"import/no-duplicates": "error",
"typescript/no-redeclare": "error",
"typescript/ban-ts-comment": "error",
"typescript/consistent-type-imports": ["error", {
"prefer": "type-imports",
"fixStyle": "inline-type-imports"
}],
"typescript/explicit-function-return-type": "off",
"typescript/explicit-module-boundary-types": "off",
"typescript/no-empty-object-type": ["error", {
"allowInterfaces": "with-single-extends"
}],
"typescript/no-empty-function": "off",
"typescript/no-explicit-any": "off",
"typescript/no-unused-vars": ["warn", {
"vars": "all",
"varsIgnorePattern": "^_",
"args": "after-used",
"argsIgnorePattern": "^_"
}]
"typescript/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_"
}
],
"typescript/no-explicit-any": "off"
}
}
+1
View File
@@ -4,6 +4,7 @@
- Rich app example: https://github.com/twentyhq/twenty/tree/main/packages/twenty-sdk/src/cli/__tests__/apps/rich-app
## UUID requirement
- All generated UUIDs must be valid UUID v4.
## Common Pitfalls
@@ -1,6 +1,6 @@
{
"name": "@twentyhq/hello-world",
"version": "0.2.2",
"name": "hello-world",
"version": "0.1.0",
"license": "MIT",
"engines": {
"node": "^24.5.0",
@@ -20,7 +20,7 @@
"@types/react": "^18.2.0",
"oxlint": "^0.16.0",
"react": "^18.2.0",
"twenty-sdk": "0.6.3",
"twenty-sdk": "0.6.4",
"typescript": "^5.9.3",
"vite-tsconfig-paths": "^4.2.1",
"vitest": "^3.1.1"
@@ -1,43 +1,22 @@
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config';
import { appGenerateClient, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-sdk/generated';
import { appBuild, appUninstall } from 'twenty-sdk/cli';
import { MetadataApiClient } from 'twenty-sdk/clients';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
const APP_PATH = process.cwd();
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:3000';
const assertServerIsReachable = async () => {
let response: Response;
try {
response = await fetch(`${TWENTY_API_URL}/healthz`);
} catch {
throw new Error(
`Twenty server is not reachable at ${TWENTY_API_URL}. ` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(`Server at ${TWENTY_API_URL} returned ${response.status}`);
}
};
describe('App installation', () => {
let appInstalled = false;
beforeAll(async () => {
await assertServerIsReachable();
const generateResult = await appGenerateClient({
const buildResult = await appBuild({
appPath: APP_PATH,
onProgress: (message: string) =>
console.log(`[generate-client] ${message}`),
onProgress: (message: string) => console.log(`[build] ${message}`),
});
if (!generateResult.success) {
if (!buildResult.success) {
throw new Error(
`Client generation failed: ${generateResult.error?.message ?? 'Unknown error'}`,
`Build failed: ${buildResult.error?.message ?? 'Unknown error'}`,
);
}
@@ -59,20 +38,7 @@ describe('App installation', () => {
});
it('should find the installed app in the applications list', async () => {
const apiKey = process.env.TWENTY_TEST_API_KEY;
if (!apiKey) {
throw new Error(
'No API key found. Set TWENTY_TEST_API_KEY in your vitest config env.',
);
}
const metadataClient = new MetadataApiClient({
url: `${TWENTY_API_URL}/metadata`,
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
const metadataClient = new MetadataApiClient();
const result = await metadataClient.query({
findManyApplications: {
@@ -3,16 +3,36 @@ import * as os from 'os';
import * as path from 'path';
import { beforeAll } from 'vitest';
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:3000';
const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
beforeAll(() => {
const assertServerIsReachable = async () => {
let response: Response;
try {
response = await fetch(`${TWENTY_API_URL}/healthz`);
} catch {
throw new Error(
`Twenty server is not reachable at ${TWENTY_API_URL}. ` +
'Make sure the server is running before executing integration tests.',
);
}
if (!response.ok) {
throw new Error(`Server at ${TWENTY_API_URL} returned ${response.status}`);
}
};
beforeAll(async () => {
await assertServerIsReachable();
fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
const configFile = {
profiles: {
default: {
apiUrl: process.env.TWENTY_API_URL,
apiKey: process.env.TWENTY_TEST_API_KEY,
apiKey: process.env.TWENTY_API_KEY,
},
},
};
@@ -2,7 +2,7 @@ import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
export const APPLICATION_UNIVERSAL_IDENTIFIER =
'1badae7c-8a42-4dea-b4b8-3c56e77c2f9a';
'6563e091-9f5b-4026-a3ea-7e3b3d09e218';
export default defineApplication({
universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
@@ -3,7 +3,7 @@ import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object'
export default defineField({
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
universalIdentifier: '2c503a0d-36c9-49ec-b82f-4fafe0eb6f47',
universalIdentifier: '770d32c2-cf12-4ab2-b66d-73f92dc239b5',
type: FieldType.NUMBER,
name: 'priority',
label: 'Priority',
@@ -10,7 +10,7 @@ export const HelloWorld = () => {
};
export default defineFrontComponent({
universalIdentifier: '26c17445-fbfb-4b34-99d6-f461e734ca97',
universalIdentifier: 'd371f098-5b2c-42f0-898d-94459f1ee337',
name: 'hello-world-front-component',
description: 'A sample front component',
component: HelloWorld,
@@ -5,7 +5,7 @@ const handler = async (): Promise<{ message: string }> => {
};
export default defineLogicFunction({
universalIdentifier: '4f0b7137-1399-4e50-ac00-3c3bb2555c38',
universalIdentifier: '2baa26eb-9aaf-4856-a4f4-30d6fd6480ee',
name: 'hello-world-logic-function',
description: 'A simple logic function',
timeoutSeconds: 5,
@@ -5,7 +5,7 @@ const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
};
export default definePostInstallLogicFunction({
universalIdentifier: 'c1410017-8536-42aa-a188-4bfc5a1c3dae',
universalIdentifier: '7a3f4684-51db-494d-833b-a747a3b90507',
name: 'post-install',
description: 'Runs after installation to set up the application.',
timeoutSeconds: 300,
@@ -5,7 +5,7 @@ const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
};
export default definePreInstallLogicFunction({
universalIdentifier: '68d005d4-1110-4fa0-8227-71e06d6b9f30',
universalIdentifier: '1272ffdb-8e2f-492c-ab37-66c2b97e9c23',
name: 'pre-install',
description: 'Runs before installation to prepare the application.',
timeoutSeconds: 300,
@@ -1,14 +1,11 @@
import { defineNavigationMenuItem } from 'twenty-sdk';
import { EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER } from 'src/views/example-view';
export default defineNavigationMenuItem({
universalIdentifier: '574a895f-1511-4b38-9d28-d6b8436738ff',
universalIdentifier: '10f90627-e9c2-44b7-9742-bed77e3d1b17',
name: 'example-navigation-menu-item',
icon: 'IconList',
color: 'blue',
position: 0,
// Link to a view:
// viewUniversalIdentifier: '...',
// Or link to an object:
// targetObjectUniversalIdentifier: '...',
// Or link to an external URL:
// link: 'https://example.com',
viewUniversalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER,
});
@@ -1,10 +1,10 @@
import { defineObject, FieldType } from 'twenty-sdk';
export const EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER =
'b75cfe84-18ce-47da-812a-53e25ee094af';
'dfd43356-39b3-4b55-b4a7-279bec689928';
export const NAME_FIELD_UNIVERSAL_IDENTIFIER =
'6ab9c690-06ce-455e-a2c9-8067a9747f96';
'd2d7f6cd-33f6-456f-bf00-17adeca926ba';
export default defineObject({
universalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
@@ -1,7 +1,7 @@
import { defineRole } from 'twenty-sdk';
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
'f14afc30-f2fa-4f70-9b12-903c5f852225';
'9238bc7b-d38f-4a1c-9d19-31ab7bc67a2f';
export default defineRole({
universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
@@ -1,7 +1,7 @@
import { defineSkill } from 'twenty-sdk';
export const EXAMPLE_SKILL_UNIVERSAL_IDENTIFIER =
'4f00dd76-c07b-4d55-a43a-7f17e7f6440a';
'd0940029-9d3c-40be-903a-52d65393028f';
export default defineSkill({
universalIdentifier: EXAMPLE_SKILL_UNIVERSAL_IDENTIFIER,
@@ -1,10 +1,22 @@
import { defineView } from 'twenty-sdk';
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
import { defineView, ViewKey } from 'twenty-sdk';
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, NAME_FIELD_UNIVERSAL_IDENTIFIER } from 'src/objects/example-object';
export const EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER = 'e004df40-29f3-47ba-b39d-d3a5c444367a';
export default defineView({
universalIdentifier: 'e574b32c-c058-492a-8a5c-780b844a8735',
name: 'example-view',
universalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER,
name: 'All example items',
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
icon: 'IconList',
key: ViewKey.INDEX,
position: 0,
fields: [
{
universalIdentifier: '496c40c2-5766-419c-93bf-20fdad3f34bb',
fieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
position: 0,
isVisible: true,
size: 200,
},
],
});
@@ -15,7 +15,7 @@ export default defineConfig({
setupFiles: ['src/__tests__/setup-test.ts'],
env: {
TWENTY_API_URL: 'http://localhost:3000',
TWENTY_TEST_API_KEY:
TWENTY_API_KEY:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ1c2VySWQiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtNDYzZi00MzViLTgyOGMtMTA3ZTAwN2EyNzExIiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWU3Yy00M2Q5LWE1ZGItNjg1YjUwNjlkODE2IiwidHlwZSI6IkFDQ0VTUyIsImF1dGhQcm92aWRlciI6InBhc3N3b3JkIiwiaWF0IjoxNzUxMjgxNzA0LCJleHAiOjIwNjY4NTc3MDR9.HMGqCsVlOAPVUBhKSGlD1X86VoHKt4LIUtET3CGIdik',
},
},
File diff suppressed because it is too large Load Diff
@@ -14,7 +14,7 @@ Apps let you build and manage Twenty customizations **as code**. Instead of conf
**What you can do today:**
- Define custom objects and fields as code (managed data model)
- Build logic functions with custom triggers
- Define skills for AI agents
- Define skills and agents for AI
- Deploy the same app across multiple workspaces
## Prerequisites
@@ -38,7 +38,7 @@ yarn twenty app:dev
The scaffolder supports two modes for controlling which example files are included:
```bash filename="Terminal"
# Default (exhaustive): all examples (object, field, logic function, front component, view, navigation menu item, skill)
# Default (exhaustive): all examples (object, field, logic function, front component, view, navigation menu item, skill, agent)
npx create-twenty-app@latest my-app
# Minimal: only core files (application-config.ts and default-role.ts)
@@ -114,8 +114,10 @@ my-twenty-app/
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
── skills/
└── example-skill.ts # Example AI agent skill definition
└── agents/
└── example-agent.ts # Example AI agent definition
```
With `--minimal`, only the core files are created (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts`, and `logic-functions/post-install.ts`).
@@ -147,6 +149,7 @@ The SDK detects entities by parsing your TypeScript files for **`export default
| `defineView()` | Saved view definitions |
| `defineNavigationMenuItem()` | Navigation menu item definitions |
| `defineSkill()` | AI agent skill definitions |
| `defineAgent()` | AI agent definitions |
<Note>
**File naming is flexible.** Entity detection is AST-based — the SDK scans your source files for the `export default define<Entity>({...})` pattern. You can organize your files and folders however you like. Grouping by entity type (e.g., `logic-functions/`, `roles/`) is just a convention for code organization, not a requirement.
@@ -166,7 +169,7 @@ export default defineObject({
Later commands will add more files and folders:
- `yarn twenty app:dev` will auto-generate two typed API clients in `node_modules/twenty-sdk/generated`: `CoreApiClient` (for workspace data via `/graphql`) and `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`).
- `yarn twenty app:dev` will auto-generate two typed API clients in `node_modules/twenty-sdk/clients`: `CoreApiClient` (for workspace data via `/graphql`) and `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`).
- `yarn twenty entity:add` will add entity definition files under `src/` for your custom objects, functions, front components, roles, skills, and more.
## Authentication
@@ -223,6 +226,7 @@ The SDK provides helper functions for defining your app entities. As described i
| `defineView()` | Define saved views for objects |
| `defineNavigationMenuItem()` | Define sidebar navigation links |
| `defineSkill()` | Define AI agent skills |
| `defineAgent()` | Define AI agents with system prompts |
These functions validate your configuration at build time and provide IDE autocompletion and type safety.
@@ -427,7 +431,7 @@ Each function file uses `defineLogicFunction()` to export a configuration with a
// src/app/createPostCard.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
import { CoreApiClient, type Person } from 'twenty-sdk/generated';
import { CoreApiClient, type Person } from 'twenty-sdk/clients';
const handler = async (params: RoutePayload) => {
const client = new CoreApiClient();
@@ -665,7 +669,7 @@ To mark a logic function as a tool, set `isTool: true` and provide a `toolInputS
```typescript
// src/logic-functions/enrich-company.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk';
import { CoreApiClient } from 'twenty-sdk/generated';
import { CoreApiClient } from 'twenty-sdk/clients';
const handler = async (params: { companyName: string; domain?: string }) => {
const client = new CoreApiClient();
@@ -790,15 +794,50 @@ You can create new skills in two ways:
- **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new skill.
- **Manual**: Create a new file and use `defineSkill()`, following the same pattern.
### Agents
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```typescript
// src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'sales-assistant',
label: 'Sales Assistant',
description: 'An AI agent that helps with sales tasks',
icon: 'IconRobot',
prompt: `You are a sales assistant. Help users with:
1. Researching prospects and companies
2. Drafting personalized outreach messages
3. Tracking follow-ups and next steps
4. Analyzing deal pipeline and suggesting actions`,
});
```
Key points:
- `name` is a unique identifier string for the agent (kebab-case recommended).
- `label` is the human-readable display name shown in the UI.
- `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
- `icon` (optional) sets the icon displayed in the UI.
- `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
- **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
- **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Generated typed clients
Two typed clients are auto-generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated` based on your workspace schema:
Two typed clients are auto-generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/clients` based on your workspace schema:
- **`CoreApiClient`** — queries the `/graphql` endpoint for workspace data
- **`MetadataApiClient`** — queries the `/metadata` endpoint for workspace configuration and file uploads
```typescript
import { CoreApiClient, MetadataApiClient } from 'twenty-sdk/generated';
import { CoreApiClient } from 'twenty-sdk/clients';
import { MetadataApiClient } from 'twenty-sdk/clients';
const client = new CoreApiClient();
const { me } = await client.query({ me: { id: true, displayName: true } });
@@ -807,7 +846,7 @@ const metadataClient = new MetadataApiClient();
const { currentWorkspace } = await metadataClient.query({ currentWorkspace: { id: true } });
```
Both clients are re-generated automatically by `yarn twenty app:dev` whenever your objects or fields change.
`CoreApiClient` is re-generated automatically by `yarn twenty app:dev` whenever your objects or fields change. `MetadataApiClient` ships pre-built with the SDK.
#### Runtime credentials in logic functions
@@ -823,10 +862,10 @@ Notes:
#### Uploading files
The generated `MetadataApiClient` includes an `uploadFile` method for attaching files to file-type fields on your workspace objects. Because standard GraphQL clients do not support multipart file uploads natively, the client provides this dedicated method that implements the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec) under the hood.
The `MetadataApiClient` includes an `uploadFile` method for attaching files to file-type fields on your workspace objects. Because standard GraphQL clients do not support multipart file uploads natively, the client provides this dedicated method that implements the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec) under the hood.
```typescript
import { MetadataApiClient } from 'twenty-sdk/generated';
import { MetadataApiClient } from 'twenty-sdk/clients';
import * as fs from 'fs';
const metadataClient = new MetadataApiClient();
@@ -15,7 +15,7 @@ description: أنشئ وأدِر تخصيصات Twenty على هيئة كود.
* عرِّف كائنات وحقولًا مخصصة على شكل كود (نموذج بيانات مُدار)
* أنشئ وظائف منطقية مع مشغلات مخصصة
* حدد المهارات لوكلاء الذكاء الاصطناعي
* تعريف المهارات والوكلاء للذكاء الاصطناعي
* انشر التطبيق نفسه عبر مساحات عمل متعددة
## المتطلبات الأساسية
@@ -39,7 +39,7 @@ yarn twenty app:dev
يدعم المُنشئ وضعين للتحكم في ملفات الأمثلة التي سيتم تضمينها:
```bash filename="Terminal"
# الافتراضي (شامل): جميع الأمثلة (كائن، حقل، دالة منطقية، مكوّن الواجهة الأمامية، عرض، عنصر قائمة التنقل، مهارة)
# الافتراضي (شامل): جميع الأمثلة (كائن، حقل، دالة منطقية، مكوّن الواجهة الأمامية، عرض، عنصر قائمة التنقل، مهارة، وكيل)
npx create-twenty-app@latest my-app
# الأدنى: الملفات الأساسية فقط (application-config.ts و default-role.ts)
@@ -115,8 +115,10 @@ my-twenty-app/
│ └── example-view.ts # تعريف عرض محفوظ — مثال
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # رابط تنقّل في الشريط الجانبي — مثال
── skills/
└── example-skill.ts # تعريف مهارة لوكيل الذكاء الاصطناعي — مثال},{
── skills/
└── example-skill.ts # تعريف مهارة لوكيل الذكاء الاصطناعي — مثال
└── agents/
└── example-agent.ts # تعريف وكيل الذكاء الاصطناعي — مثال
```
مع `--minimal`، سيتم إنشاء الملفات الأساسية فقط (`application-config.ts`، `roles/default-role.ts`، `logic-functions/pre-install.ts`، و`logic-functions/post-install.ts`).
@@ -148,6 +150,7 @@ my-twenty-app/
| `defineView()` | تعريفات العروض المحفوظة |
| `defineNavigationMenuItem()` | تعريفات عناصر قائمة التنقل |
| `defineSkill()` | تعريفات مهارات وكيل الذكاء الاصطناعي |
| `defineAgent()` | تعريفات وكلاء الذكاء الاصطناعي |
<Note>
**تسمية الملفات مرنة.** يعتمد اكتشاف الكيانات على بنية الشجرة المجردة (AST) — إذ يقوم SDK بفحص ملفات المصدر لديك بحثًا عن النمط `export default define<Entity>({...})`. يمكنك تنظيم ملفاتك ومجلداتك كيفما تشاء. التجميع حسب نوع الكيان (مثلًا، `logic-functions/` و`roles/`) هو مجرد عرف لتنظيم الشيفرة، وليس مطلبًا إلزاميًا.
@@ -225,6 +228,7 @@ yarn twenty auth:status
| `defineView()` | تعريف العروض المحفوظة للكائنات |
| `defineNavigationMenuItem()` | تعريف روابط التنقل في الشريط الجانبي |
| `defineSkill()` | عرّف مهارات وكيل الذكاء الاصطناعي |
| `defineAgent()` | Define AI agents with system prompts |
تتحقق هذه الدوال من تكوينك وقت البناء وتوفّر إكمالًا تلقائيًا في بيئة التطوير وأمان الأنواع.
@@ -802,6 +806,41 @@ export default defineSkill({
* **مُنشأ بالقالب**: شغِّل `yarn twenty entity:add` واختر خيار إضافة مهارة جديدة.
* **يدوي**: أنشئ ملفًا جديدًا واستخدم `defineSkill()` مع اتباع النمط نفسه.
### Agents
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```typescript
// src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'sales-assistant',
label: 'Sales Assistant',
description: 'An AI agent that helps with sales tasks',
icon: 'IconRobot',
prompt: `You are a sales assistant. Help users with:
1. Researching prospects and companies
2. Drafting personalized outreach messages
3. Tracking follow-ups and next steps
4. Analyzing deal pipeline and suggesting actions`,
});
```
النقاط الرئيسية:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` هو اسم العرض المقروء للبشر الظاهر في واجهة المستخدم.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (اختياري) يحدّد الأيقونة المعروضة في واجهة المستخدم.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### عملاء مُولَّدون مضبوطو الأنواع
يتم توليد عميلين مضبوطي الأنواع تلقائيًا بواسطة `yarn twenty app:dev` وتخزينهما في `node_modules/twenty-sdk/generated` استنادًا إلى مخطط مساحة العمل لديك:
@@ -15,7 +15,7 @@ Aplikace vám umožňují vytvářet a spravovat přizpůsobení Twenty **jako k
* Definujte vlastní objekty a pole jako kód (spravovaný datový model)
* Vytvářejte logické funkce s vlastními spouštěči
* Definujte dovednosti agentů AI
* Definujte dovednosti a agenty AI!
* Nasazujte stejnou aplikaci do více pracovních prostorů
## Předpoklady
@@ -39,7 +39,7 @@ yarn twenty app:dev
Nástroj pro generování kostry podporuje dva režimy pro řízení toho, které ukázkové soubory jsou zahrnuty:
```bash filename="Terminal"
# Výchozí (úplný): všechny příklady (objekt, pole, logická funkce, front-endová komponenta, zobrazení, položka navigační nabídky, dovednost)
# Výchozí (úplný): všechny příklady (objekt, pole, logická funkce, front-endová komponenta, zobrazení, položka navigační nabídky, dovednost, agent)
npx create-twenty-app@latest my-app
# Minimální: pouze základní soubory (application-config.ts a default-role.ts)
@@ -115,8 +115,10 @@ my-twenty-app/
│ └── example-view.ts # Ukázková definice uloženého zobrazení
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Ukázkový odkaz postranní navigace
── skills/
└── example-skill.ts # Ukázková definice dovednosti agenta AI
── skills/
└── example-skill.ts # Ukázková definice dovednosti agenta AI
└── agents/
└── example-agent.ts # Ukázková definice agenta AI
```
S volbou `--minimal` se vytvoří pouze základní soubory (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` a `logic-functions/post-install.ts`).
@@ -148,6 +150,7 @@ SDK detekuje entity analýzou vašich souborů TypeScript a hledá volání **`e
| `defineView()` | Definice uložených zobrazení |
| `defineNavigationMenuItem()` | Definice položek navigační nabídky |
| `defineSkill()` | Definice dovedností agenta AI |
| `defineAgent()` | Definice agentů AI |
<Note>
**Pojmenování souborů je flexibilní.** Detekce entit je založená na AST — SDK prochází vaše zdrojové soubory a hledá vzor `export default define<Entity>({...})`. Soubory a složky můžete organizovat, jak chcete. Seskupování podle typu entity (např. `logic-functions/`, `roles/`) je pouze konvence pro organizaci kódu, nikoli požadavek.
@@ -225,6 +228,7 @@ SDK poskytuje pomocné funkce pro definování entit vaší aplikace. Jak je pop
| `defineView()` | Definujte uložená zobrazení pro objekty |
| `defineNavigationMenuItem()` | Definujte odkazy postranní navigace |
| `defineSkill()` | Definuje dovednosti agenta AI |
| `defineAgent()` | Define AI agents with system prompts |
Tyto funkce validují vaši konfiguraci v době sestavení a poskytují automatické doplňování v IDE a typovou bezpečnost.
@@ -802,6 +806,41 @@ Nové dovednosti můžete vytvářet dvěma způsoby:
* **Vygenerované**: Spusťte `yarn twenty entity:add` a zvolte možnost přidat novou dovednost.
* **Ruční**: Vytvořte nový soubor a použijte `defineSkill()` podle stejného vzoru.
### Agenti
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```typescript
// src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'sales-assistant',
label: 'Sales Assistant',
description: 'An AI agent that helps with sales tasks',
icon: 'IconRobot',
prompt: `You are a sales assistant. Help users with:
1. Researching prospects and companies
2. Drafting personalized outreach messages
3. Tracking follow-ups and next steps
4. Analyzing deal pipeline and suggesting actions`,
});
```
Hlavní body:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` je uživatelsky čitelný název zobrazovaný v UI.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (volitelné) nastavuje ikonu zobrazovanou v UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Generované typované klienty
Dva typované klienty jsou automaticky generovány pomocí `yarn twenty app:dev` a ukládají se do `node_modules/twenty-sdk/generated` podle schématu vašeho pracovního prostoru:
@@ -15,7 +15,7 @@ Mit Apps können Sie Twenty-Anpassungen **als Code** erstellen und verwalten. An
* Benutzerdefinierte Objekte und Felder als Code definieren (verwaltetes Datenmodell)
* Logikfunktionen mit benutzerdefinierten Triggern erstellen
* Fähigkeiten für KI-Agenten definieren
* Skills und Agenten für KI definieren
* Dieselbe App in mehreren Workspaces bereitstellen
## Voraussetzungen
@@ -39,7 +39,7 @@ yarn twenty app:dev
Das Scaffolding-Tool unterstützt zwei Modi, um zu steuern, welche Beispieldateien enthalten sind:
```bash filename="Terminal"
# Standard (umfassend): alle Beispiele (Objekt, Feld, Logikfunktion, Frontend-Komponente, View, Navigationsmenüeintrag, Skill)
# Standard (umfassend): alle Beispiele (Objekt, Feld, Logikfunktion, Frontend-Komponente, View, Navigationsmenüeintrag, Skill, Agent)
npx create-twenty-app@latest my-app
# Minimal: nur Kerndateien (application-config.ts und default-role.ts)
@@ -96,27 +96,29 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Public assets folder (images, fonts, etc.)
public/ # Ordner für öffentliche Assets (Bilder, Schriftarten usw.)
src/
├── application-config.ts # Required - main application configuration
├── application-config.ts # Erforderlich - Hauptkonfiguration der Anwendung
├── roles/
│ └── default-role.ts # Default role for logic functions
│ └── default-role.ts # Standardrolle für Logikfunktionen
├── objects/
│ └── example-object.ts # Example custom object definition
│ └── example-object.ts # Beispiel für eine benutzerdefinierte Objektdefinition
├── fields/
│ └── example-field.ts # Example standalone field definition
│ └── example-field.ts # Beispiel für eine eigenständige Felddefinition
├── logic-functions/
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
│ ├── hello-world.ts # Beispiel für eine Logikfunktion
│ ├── pre-install.ts # Pre-Installations-Logikfunktion
│ └── post-install.ts # Post-Installations-Logikfunktion
├── front-components/
│ └── hello-world.tsx # Example front component
│ └── hello-world.tsx # Beispiel für eine Frontend-Komponente
├── views/
│ └── example-view.ts # Example saved view definition
│ └── example-view.ts # Beispiel für eine gespeicherte View-Definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
│ └── example-navigation-menu-item.ts # Beispiel für einen Navigationslink in der Seitenleiste
── skills/
└── example-skill.ts # Beispiel für eine Skill-Definition eines KI-Agenten
└── agents/
└── example-agent.ts # Beispiel für eine KI-Agenten-Definition
```
Mit `--minimal` werden nur die Kerndateien erstellt (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` und `logic-functions/post-install.ts`).
@@ -148,6 +150,7 @@ Das SDK erkennt Entitäten, indem es Ihre TypeScript-Dateien nach Aufrufen von *
| `defineView()` | Gespeicherte View-Definitionen |
| `defineNavigationMenuItem()` | Definitionen von Navigationsmenüeinträgen |
| `defineSkill()` | Skill-Definitionen für KI-Agenten |
| `defineAgent()` | KI-Agenten-Definitionen |
<Note>
**Dateibenennung ist flexibel.** Die Entitätserkennung ist AST-basiert — das SDK durchsucht Ihre Quelldateien nach dem Muster `export default define<Entity>({...})`. Sie können Ihre Dateien und Ordner nach Belieben organisieren. Die Gruppierung nach Entitätstyp (z. B. `logic-functions/`, `roles/`) ist lediglich eine Konvention zur Codeorganisation, keine Voraussetzung.
@@ -225,6 +228,7 @@ Das SDK stellt Hilfsfunktionen bereit, um die Entitäten Ihrer App zu definieren
| `defineView()` | Gespeicherte Views für Objekte definieren |
| `defineNavigationMenuItem()` | Seitenleisten-Navigationslinks definieren |
| `defineSkill()` | Definieren Sie Skills für KI-Agenten |
| `defineAgent()` | Define AI agents with system prompts |
Diese Funktionen validieren Ihre Konfiguration zur Build-Zeit und bieten IDE-Autovervollständigung sowie Typsicherheit.
@@ -802,6 +806,41 @@ Sie können neue Skills auf zwei Arten erstellen:
* **Generiert**: Führen Sie `yarn twenty entity:add` aus und wählen Sie die Option zum Hinzufügen eines neuen Skills.
* **Manuell**: Erstellen Sie eine neue Datei und verwenden Sie `defineSkill()` nach demselben Muster.
### Agenten
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```typescript
// src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'sales-assistant',
label: 'Sales Assistant',
description: 'An AI agent that helps with sales tasks',
icon: 'IconRobot',
prompt: `You are a sales assistant. Help users with:
1. Researching prospects and companies
2. Drafting personalized outreach messages
3. Tracking follow-ups and next steps
4. Analyzing deal pipeline and suggesting actions`,
});
```
Hauptpunkte:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` ist der menschenlesbare Anzeigename, der in der UI angezeigt wird.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (optional) legt das in der UI angezeigte Symbol fest.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Generierte typisierte Clients
Zwei typisierte Clients werden von `yarn twenty app:dev` automatisch generiert und basierend auf Ihrem Arbeitsbereichs-Schema in `node_modules/twenty-sdk/generated` gespeichert:
@@ -15,7 +15,7 @@ Le app ti consentono di creare e gestire le personalizzazioni di Twenty **come c
* Definisci oggetti e campi personalizzati come codice (modello dati gestito)
* Crea funzioni logiche con trigger personalizzati
* Definire le abilità per gli agenti IA
* Definire skill e agenti per l'IA
* Distribuisci la stessa app su più spazi di lavoro
## Prerequisiti
@@ -39,7 +39,7 @@ yarn twenty app:dev
Lo strumento di scaffolding supporta due modalità per controllare quali file di esempio vengono inclusi:
```bash filename="Terminal"
# Predefinita (esaustiva): tutti gli esempi (oggetto, campo, funzione logica, componente front-end, vista, voce del menu di navigazione, skill)
# Predefinita (esaustiva): tutti gli esempi (oggetto, campo, funzione logica, componente front-end, vista, voce del menu di navigazione, skill, agente)
npx create-twenty-app@latest my-app
# Minimale: solo i file principali (application-config.ts e default-role.ts)
@@ -96,27 +96,29 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Public assets folder (images, fonts, etc.)
public/ # Cartella delle risorse pubbliche (immagini, font, ecc.)
src/
├── application-config.ts # Required - main application configuration
├── application-config.ts # Obbligatorio - configurazione principale dell'applicazione
├── roles/
│ └── default-role.ts # Default role for logic functions
│ └── default-role.ts # Ruolo predefinito per le funzioni logiche
├── objects/
│ └── example-object.ts # Example custom object definition
│ └── example-object.ts # Definizione di oggetto personalizzato di esempio
├── fields/
│ └── example-field.ts # Example standalone field definition
│ └── example-field.ts # Definizione di campo autonomo di esempio
├── logic-functions/
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
│ ├── hello-world.ts # Funzione logica di esempio
│ ├── pre-install.ts # Funzione logica di pre-installazione
│ └── post-install.ts # Funzione logica di post-installazione
├── front-components/
│ └── hello-world.tsx # Example front component
│ └── hello-world.tsx # Componente front-end di esempio
├── views/
│ └── example-view.ts # Example saved view definition
│ └── example-view.ts # Definizione di vista salvata di esempio
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
│ └── example-navigation-menu-item.ts # Link di navigazione della barra laterale di esempio
── skills/
└── example-skill.ts # Definizione di skill per agente IA di esempio
└── agents/
└── example-agent.ts # Definizione di agente IA di esempio
```
Con `--minimal`, vengono creati solo i file principali (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` e `logic-functions/post-install.ts`).
@@ -148,6 +150,7 @@ L'SDK rileva le entità analizzando i tuoi file TypeScript alla ricerca di chiam
| `defineView()` | Definizioni di viste salvate |
| `defineNavigationMenuItem()` | Definizioni delle voci del menu di navigazione |
| `defineSkill()` | AI agent skill definitions |
| `defineAgent()` | Definizioni di agenti IA |
<Note>
**La denominazione dei file è flessibile.** Il rilevamento delle entità è basato sull'AST — l'SDK esegue la scansione dei file sorgente alla ricerca del pattern `export default define<Entity>({...})`. Puoi organizzare file e cartelle come preferisci. Raggruppare per tipo di entità (ad es., `logic-functions/`, `roles/`) è solo una convenzione per l'organizzazione del codice, non un requisito.
@@ -225,6 +228,7 @@ L'SDK fornisce funzioni helper per definire le entità della tua app. Come descr
| `defineView()` | Definisce viste salvate per gli oggetti |
| `defineNavigationMenuItem()` | Definisce i link di navigazione della barra laterale |
| `defineSkill()` | Define AI agent skills |
| `defineAgent()` | Define AI agents with system prompts |
Queste funzioni convalidano la configurazione in fase di build e offrono il completamento automatico nell'IDE e la sicurezza dei tipi.
@@ -802,6 +806,41 @@ You can create new skills in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new skill.
* **Manual**: Create a new file and use `defineSkill()`, following the same pattern.
### Agenti
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```typescript
// src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'sales-assistant',
label: 'Sales Assistant',
description: 'An AI agent that helps with sales tasks',
icon: 'IconRobot',
prompt: `You are a sales assistant. Help users with:
1. Researching prospects and companies
2. Drafting personalized outreach messages
3. Tracking follow-ups and next steps
4. Analyzing deal pipeline and suggesting actions`,
});
```
Punti chiave:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` is the human-readable display name shown in the UI.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (optional) sets the icon displayed in the UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Client tipizzati generati
Due client tipizzati sono generati automaticamente da `yarn twenty app:dev` e salvati in `node_modules/twenty-sdk/generated` in base allo schema della tua area di lavoro:
@@ -15,7 +15,7 @@ Os aplicativos permitem criar e gerenciar personalizações do Twenty **como có
* Defina objetos e campos personalizados como código (modelo de dados gerenciado)
* Crie funções de lógica com gatilhos personalizados
* Definir habilidades para agentes de IA
* Defina habilidades e agentes de IA
* Implemente o mesmo aplicativo em vários espaços de trabalho
## Pré-requisitos
@@ -39,7 +39,7 @@ yarn twenty app:dev
O gerador de estrutura oferece suporte a dois modos para controlar quais arquivos de exemplo são incluídos:
```bash filename="Terminal"
# Padrão (exhaustivo): todos os exemplos (objeto, campo, função de lógica, componente de front-end, visualização, item do menu de navegação, habilidade)
# Padrão (exhaustivo): todos os exemplos (objeto, campo, função de lógica, componente de front-end, visualização, item do menu de navegação, habilidade, agente)
npx create-twenty-app@latest my-app
# Mínimo: apenas arquivos principais (application-config.ts e default-role.ts)
@@ -96,27 +96,29 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Pasta de recursos públicos (imagens, fontes, etc.)
public/ # Public assets folder (images, fonts, etc.)
src/
├── application-config.ts # Obrigatório - configuração principal da aplicação
├── application-config.ts # Required - main application configuration
├── roles/
│ └── default-role.ts # Papel padrão para funções de lógica
│ └── default-role.ts # Default role for logic functions
├── objects/
│ └── example-object.ts # Exemplo de definição de objeto personalizado
│ └── example-object.ts # Example custom object definition
├── fields/
│ └── example-field.ts # Exemplo de definição de campo independente
│ └── example-field.ts # Example standalone field definition
├── logic-functions/
│ ├── hello-world.ts # Exemplo de função de lógica
│ ├── pre-install.ts # Função de lógica de pré-instalação
│ └── post-install.ts # Função de lógica de pós-instalação
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
├── front-components/
│ └── hello-world.tsx # Exemplo de componente de front-end
│ └── hello-world.tsx # Example front component
├── views/
│ └── example-view.ts # Exemplo de definição de visualização salva
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Exemplo de link de navegação da barra lateral
── skills/
└── example-skill.ts # Exemplo de definição de habilidade de agente de IA
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
└── agents/
└── example-agent.ts # Example AI agent definition
```
With `--minimal`, only the core files are created (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts`, and `logic-functions/post-install.ts`).
@@ -148,6 +150,7 @@ O SDK detecta entidades analisando seus arquivos TypeScript em busca de chamadas
| `defineView()` | Definições de visualizações salvas |
| `defineNavigationMenuItem()` | Definições de itens do menu de navegação |
| `defineSkill()` | Definições de habilidades de agente de IA |
| `defineAgent()` | Definições de agentes de IA |
<Note>
**A nomeação de arquivos é flexível.** A detecção de entidades é baseada em AST — o SDK varre seus arquivos fonte em busca do padrão `export default define<Entity>({...})`. Você pode organizar seus arquivos e pastas como quiser. Agrupar por tipo de entidade (por exemplo, `logic-functions/`, `roles/`) é apenas uma convenção para organização do código, não um requisito.
@@ -225,6 +228,7 @@ O SDK fornece funções utilitárias para definir as entidades do seu app. Confo
| `defineView()` | Define visualizações salvas para objetos |
| `defineNavigationMenuItem()` | Define links de navegação da barra lateral |
| `defineSkill()` | Define habilidades de agente de IA |
| `defineAgent()` | Define AI agents with system prompts |
Essas funções validam sua configuração em tempo de compilação e oferecem autocompletar na IDE e segurança de tipos.
@@ -803,6 +807,41 @@ Você pode criar novas habilidades de duas formas:
* **Gerado automaticamente**: Execute `yarn twenty entity:add` e escolha a opção para adicionar uma nova habilidade.
* **Manual**: Crie um novo arquivo e use `defineSkill()`, seguindo o mesmo padrão.
### Agentes
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```typescript
// src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'sales-assistant',
label: 'Sales Assistant',
description: 'An AI agent that helps with sales tasks',
icon: 'IconRobot',
prompt: `You are a sales assistant. Help users with:
1. Researching prospects and companies
2. Drafting personalized outreach messages
3. Tracking follow-ups and next steps
4. Analyzing deal pipeline and suggesting actions`,
});
```
Pontos-chave:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` é o nome de exibição legível por humanos mostrado na UI.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (opcional) define o ícone exibido na UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Clientes tipados gerados
Dois clientes tipados são gerados automaticamente pelo `yarn twenty app:dev` e armazenados em `node_modules/twenty-sdk/generated` com base no esquema do seu espaço de trabalho:
@@ -15,7 +15,7 @@ Aplicațiile vă permit să construiți și să gestionați personalizările Twe
* Definiți obiecte și câmpuri personalizate sub formă de cod (model de date gestionat)
* Creați funcții de logică cu declanșatoare personalizate
* Definiți abilități pentru agenți de IA
* Definiți abilități și agenți pentru AI
* Implementați aceeași aplicație în mai multe spații de lucru
## Cerințe
@@ -39,7 +39,7 @@ yarn twenty app:dev
Generatorul de schelet acceptă două moduri pentru a controla ce fișiere de exemplu sunt incluse:
```bash filename="Terminal"
# Implicit (exhaustiv): toate exemplele (obiect, câmp, funcție logică, componentă de interfață, vizualizare, element de meniu de navigare, abilitate)
# Implicit (exhaustiv): toate exemplele (obiect, câmp, funcție logică, componentă de interfață, vizualizare, element de meniu de navigare, abilitate, agent)
npx create-twenty-app@latest my-app
# Minimal: doar fișierele de bază (application-config.ts și default-role.ts)
@@ -96,27 +96,29 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Public assets folder (images, fonts, etc.)
public/ # Director pentru resurse publice (imagini, fonturi etc.)
src/
├── application-config.ts # Required - main application configuration
├── application-config.ts # Obligatoriu - configurația principală a aplicației
├── roles/
│ └── default-role.ts # Default role for logic functions
│ └── default-role.ts # Rol implicit pentru funcțiile logice
├── objects/
│ └── example-object.ts # Example custom object definition
│ └── example-object.ts # Exemplu de definiție a unui obiect personalizat
├── fields/
│ └── example-field.ts # Example standalone field definition
│ └── example-field.ts # Exemplu de definiție de câmp independent
├── logic-functions/
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
│ ├── hello-world.ts # Exemplu de funcție logică
│ ├── pre-install.ts # Funcție logică de pre-instalare
│ └── post-install.ts # Funcție logică post-instalare
├── front-components/
│ └── hello-world.tsx # Example front component
│ └── hello-world.tsx # Exemplu de componentă de interfață
├── views/
│ └── example-view.ts # Example saved view definition
│ └── example-view.ts # Exemplu de definiție a unei vizualizări salvate
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
│ └── example-navigation-menu-item.ts # Exemplu de link de navigare în bara laterală
── skills/
└── example-skill.ts # Exemplu de definiție a unei abilități a agentului AI
└── agents/
└── example-agent.ts # Exemplu de definiție a unui agent AI
```
Cu `--minimal`, sunt create doar fișierele de bază (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` și `logic-functions/post-install.ts`).
@@ -148,6 +150,7 @@ SDK-ul detectează entitățile analizând fișierele TypeScript pentru apeluri
| `defineView()` | Definiții pentru vizualizări salvate |
| `defineNavigationMenuItem()` | Definiții pentru elemente de meniu de navigare |
| `defineSkill()` | Definiții ale abilităților agentului AI |
| `defineAgent()` | Definiții ale agenților AI |
<Note>
**Denumirea fișierelor este flexibilă.** Detectarea entităților se bazează pe AST — SDK-ul scanează fișierele sursă pentru tiparul `export default define<Entity>({...})`. Puteți organiza fișierele și folderele cum doriți. Gruparea după tipul de entitate (de exemplu, `logic-functions/`, `roles/`) este doar o convenție pentru organizarea codului, nu o cerință.
@@ -225,6 +228,7 @@ SDK-ul oferă funcții ajutătoare pentru definirea entităților aplicației. D
| `defineView()` | Definește vizualizări salvate pentru obiecte |
| `defineNavigationMenuItem()` | Definește linkuri de navigare în bara laterală |
| `defineSkill()` | Definiți abilități pentru agentul AI |
| `defineAgent()` | Define AI agents with system prompts |
Aceste funcții validează configurația în timpul build-ului și oferă completare automată în IDE și siguranța tipurilor.
@@ -802,6 +806,41 @@ Puteți crea abilități noi în două moduri:
* **Generat**: Rulați `yarn twenty entity:add` și alegeți opțiunea de a adăuga o abilitate nouă.
* **Manual**: Creați un fișier nou și folosiți `defineSkill()`, urmând același model.
### Agenți
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```typescript
// src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'sales-assistant',
label: 'Sales Assistant',
description: 'An AI agent that helps with sales tasks',
icon: 'IconRobot',
prompt: `You are a sales assistant. Help users with:
1. Researching prospects and companies
2. Drafting personalized outreach messages
3. Tracking follow-ups and next steps
4. Analyzing deal pipeline and suggesting actions`,
});
```
Puncte cheie:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` este numele lizibil afișat în interfața cu utilizatorul (UI).
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (opțional) setează pictograma afișată în UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Generated typed clients
Two typed clients are auto-generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated` based on your workspace schema:
@@ -15,7 +15,7 @@ description: Создавайте и управляйте настройками
* Определяйте пользовательские объекты и поля в виде кода (управляемая модель данных)
* Создавайте логические функции с пользовательскими триггерами
* Определите навыки для ИИ-агентов
* Определите навыки и агентов для ИИ
* Развёртывайте одно и то же приложение в нескольких рабочих пространствах
## Требования
@@ -39,7 +39,7 @@ yarn twenty app:dev
Генератор каркаса поддерживает два режима для управления тем, какие файлы-примеры включаются:
```bash filename="Terminal"
# По умолчанию (полный набор): все примеры (объект, поле, логическая функция, фронтенд-компонент, представление, пункт меню навигации, навык)
# По умолчанию (полный набор): все примеры (объект, поле, логическая функция, фронтенд-компонент, представление, пункт меню навигации, навык, агент)
npx create-twenty-app@latest my-app
# Минимальный: только основные файлы (application-config.ts и default-role.ts)
@@ -96,27 +96,29 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Public assets folder (images, fonts, etc.)
public/ # Папка публичных ресурсов (изображения, шрифты и т. д.)
src/
├── application-config.ts # Required - main application configuration
├── application-config.ts # Обязательный — основная конфигурация приложения
├── roles/
│ └── default-role.ts # Default role for logic functions
│ └── default-role.ts # Роль по умолчанию для логических функций
├── objects/
│ └── example-object.ts # Example custom object definition
│ └── example-object.ts # Пример определения пользовательского объекта
├── fields/
│ └── example-field.ts # Example standalone field definition
│ └── example-field.ts # Пример определения отдельного поля
├── logic-functions/
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
│ ├── hello-world.ts # Пример логической функции
│ ├── pre-install.ts # Предустановочная логическая функция
│ └── post-install.ts # Послеустановочная логическая функция
├── front-components/
│ └── hello-world.tsx # Example front component
│ └── hello-world.tsx # Пример фронтенд-компонента
├── views/
│ └── example-view.ts # Example saved view definition
│ └── example-view.ts # Пример определения сохранённого представления
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
│ └── example-navigation-menu-item.ts # Пример ссылки боковой панели навигации
── skills/
└── example-skill.ts # Пример определения навыка агента ИИ
└── agents/
└── example-agent.ts # Пример определения агента ИИ
```
С `--minimal` создаются только основные файлы (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` и `logic-functions/post-install.ts`).
@@ -148,6 +150,7 @@ SDK обнаруживает сущности, разбирая ваши фай
| `defineView()` | Определения сохранённых представлений |
| `defineNavigationMenuItem()` | Определения пунктов меню навигации |
| `defineSkill()` | Определения навыков агента ИИ |
| `defineAgent()` | Определения агентов ИИ |
<Note>
**Имена файлов заданы гибко.** Обнаружение сущностей основано на AST — SDK сканирует ваши исходные файлы в поисках шаблона `export default define<Entity>({...})`. Вы можете организовывать файлы и папки как угодно. Группировка по типу сущности (например, `logic-functions/`, `roles/`) — это лишь соглашение для организации кода, а не требование.
@@ -225,6 +228,7 @@ SDK предоставляет вспомогательные функции д
| `defineView()` | Определяйте сохранённые представления для объектов |
| `defineNavigationMenuItem()` | Определяйте ссылки боковой панели навигации |
| `defineSkill()` | Определение навыков агента ИИ |
| `defineAgent()` | Define AI agents with system prompts |
Эти функции проверяют вашу конфигурацию на этапе сборки и обеспечивают автодополнение в IDE и безопасность типов.
@@ -802,6 +806,41 @@ export default defineSkill({
* **Сгенерировано**: Запустите `yarn twenty entity:add` и выберите опцию добавления нового навыка.
* **Вручную**: Создайте новый файл и используйте `defineSkill()`, следуя тому же шаблону.
### Агенты
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```typescript
// src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'sales-assistant',
label: 'Sales Assistant',
description: 'An AI agent that helps with sales tasks',
icon: 'IconRobot',
prompt: `You are a sales assistant. Help users with:
1. Researching prospects and companies
2. Drafting personalized outreach messages
3. Tracking follow-ups and next steps
4. Analyzing deal pipeline and suggesting actions`,
});
```
Основные моменты:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` — читаемое человеком отображаемое имя, показываемое в UI.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (необязательно) задаёт значок, отображаемый в UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Сгенерированные типизированные клиенты
Два типизированных клиента автоматически генерируются с помощью `yarn twenty app:dev` и сохраняются в `node_modules/twenty-sdk/generated` на основе схемы вашего рабочего пространства:
@@ -15,7 +15,7 @@ Uygulamalar, Twenty özelleştirmelerini **kod olarak** oluşturup yönetmenizi
* Özel nesneleri ve alanları kod olarak tanımlayın (yönetilen veri modeli)
* Özel tetikleyicilerle mantık fonksiyonları oluşturun
* Yapay zekâ ajanları için becerileri tanımlayın
* Define skills and agents for AI
* Aynı uygulamayı birden çok çalışma alanına dağıtın
## Ön Gereksinimler
@@ -39,10 +39,10 @@ yarn twenty app:dev
İskelet oluşturucu, hangi örnek dosyaların dahil edileceğini kontrol etmek için iki modu destekler:
```bash filename="Terminal"
# Varsayılan (kapsamlı): tüm örnekler (nesne, alan, mantık fonksiyonu, ön bileşen, görünüm, gezinme menüsü öğesi, yetenek)
# Default (exhaustive): all examples (object, field, logic function, front component, view, navigation menu item, skill, agent)
npx create-twenty-app@latest my-app
# Minimal: yalnızca çekirdek dosyalar (application-config.ts ve default-role.ts)
# Minimal: only core files (application-config.ts and default-role.ts)
npx create-twenty-app@latest my-app --minimal
```
@@ -96,27 +96,29 @@ my-twenty-app/
.oxlintrc.json
tsconfig.json
README.md
public/ # Genel varlıklar klasörü (görseller, yazı tipleri vb.)
public/ # Public assets folder (images, fonts, etc.)
src/
├── application-config.ts # Gerekli - ana uygulama yapılandırması
├── application-config.ts # Required - main application configuration
├── roles/
│ └── default-role.ts # Mantık fonksiyonları için varsayılan rol
│ └── default-role.ts # Default role for logic functions
├── objects/
│ └── example-object.ts # Örnek özel nesne tanımı
│ └── example-object.ts # Example custom object definition
├── fields/
│ └── example-field.ts # Örnek bağımsız alan tanımı
│ └── example-field.ts # Example standalone field definition
├── logic-functions/
│ ├── hello-world.ts # Örnek mantık fonksiyonu
│ ├── pre-install.ts # Kurulum öncesi mantık fonksiyonu
│ └── post-install.ts # Kurulum sonrası mantık fonksiyonu
│ ├── hello-world.ts # Example logic function
│ ├── pre-install.ts # Pre-install logic function
│ └── post-install.ts # Post-install logic function
├── front-components/
│ └── hello-world.tsx # Örnek ön bileşen
│ └── hello-world.tsx # Example front component
├── views/
│ └── example-view.ts # Örnek kaydedilmiş görünüm tanımı
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Örnek kenar çubuğu gezinme bağlantısı
── skills/
└── example-skill.ts # Örnek yapay zekâ ajanı yetenek tanımı
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
└── agents/
└── example-agent.ts # Example AI agent definition
```
`--minimal` ile yalnızca çekirdek dosyalar oluşturulur (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` ve `logic-functions/post-install.ts`).
@@ -148,6 +150,7 @@ SDK, TypeScript dosyalarınızı **`export default define<Entity>({...})`** ça
| `defineView()` | Kaydedilmiş görünüm tanımları |
| `defineNavigationMenuItem()` | Gezinme menüsü öğesi tanımları |
| `defineSkill()` | Yapay zekâ ajanı yetenek tanımları |
| `defineAgent()` | AI agent definitions |
<Note>
**Dosya adlandırma esnektir.** Varlık algılama AST tabanlıdır — SDK, kaynak dosyalarınızı `export default define<Entity>({...})` desenini bulmak için tarar. Dosyalarınızı ve klasörlerinizi dilediğiniz gibi düzenleyebilirsiniz. Varlık türüne göre gruplama (örn. `logic-functions/`, `roles/`) bir gereklilik değil, yalnızca kod organizasyonu için bir gelenektir.
@@ -225,6 +228,7 @@ SDK, uygulama varlıklarınızı tanımlamak için yardımcı fonksiyonlar sağl
| `defineView()` | Nesneler için kaydedilmiş görünümler tanımlayın |
| `defineNavigationMenuItem()` | Kenar çubuğu gezinme bağlantılarını tanımlayın |
| `defineSkill()` | Yapay zekâ ajanı yeteneklerini tanımlayın |
| `defineAgent()` | Define AI agents with system prompts |
Bu fonksiyonlar, derleme zamanında yapılandırmanızı doğrular ve IDE otomatik tamamlama ile tür güvenliği sağlar.
@@ -802,6 +806,41 @@ Yeni yetenekleri iki şekilde oluşturabilirsiniz:
* **Şablondan**: `yarn twenty entity:add` komutunu çalıştırın ve yeni bir yetenek ekleme seçeneğini seçin.
* **Manuel**: Yeni bir dosya oluşturun ve aynı deseni izleyerek `defineSkill()` kullanın.
### Temsilciler
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```typescript
// src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'sales-assistant',
label: 'Sales Assistant',
description: 'An AI agent that helps with sales tasks',
icon: 'IconRobot',
prompt: `You are a sales assistant. Help users with:
1. Researching prospects and companies
2. Drafting personalized outreach messages
3. Tracking follow-ups and next steps
4. Analyzing deal pipeline and suggesting actions`,
});
```
Önemli noktalar:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label`, UI'de gösterilen, insan tarafından okunabilir addır.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (isteğe bağlı), UI'de gösterilen simgeyi ayarlar.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### Oluşturulan tiplendirilmiş istemciler
Çalışma alanı şemanıza göre `yarn twenty app:dev` tarafından iki tiplendirilmiş istemci otomatik olarak oluşturulur ve `node_modules/twenty-sdk/generated` içine kaydedilir:
@@ -15,7 +15,7 @@ description: 以代码的形式构建并管理 Twenty 自定义项。
* 以代码定义自定义对象和字段(受管理的数据模型)
* 构建带有自定义触发器的逻辑函数
* 为 AI 智能体定义技能
* Define skills and agents for AI
* 将同一个应用部署到多个工作空间
## 先决条件
@@ -39,10 +39,10 @@ yarn twenty app:dev
脚手架工具支持两种模式,用于控制包含哪些示例文件:
```bash filename="Terminal"
# 默认(完整):所有示例(对象、字段、逻辑函数、前端组件、视图、导航菜单项、技能)
# Default (exhaustive): all examples (object, field, logic function, front component, view, navigation menu item, skill, agent)
npx create-twenty-app@latest my-app
# 最小化:仅核心文件(application-config.ts default-role.ts
# Minimal: only core files (application-config.ts and default-role.ts)
npx create-twenty-app@latest my-app --minimal
```
@@ -115,8 +115,10 @@ my-twenty-app/
│ └── example-view.ts # Example saved view definition
├── navigation-menu-items/
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
── skills/
└── example-skill.ts # Example AI agent skill definition
── skills/
└── example-skill.ts # Example AI agent skill definition
└── agents/
└── example-agent.ts # Example AI agent definition
```
使用 `--minimal` 时,只会创建核心文件(`application-config.ts`、`roles/default-role.ts`、`logic-functions/pre-install.ts` 和 `logic-functions/post-install.ts`)。
@@ -148,6 +150,7 @@ my-twenty-app/
| `defineView()` | Saved view definitions |
| `defineNavigationMenuItem()` | Navigation menu item definitions |
| `defineSkill()` | AI agent skill definitions |
| `defineAgent()` | AI agent definitions |
<Note>
**文件命名是灵活的。** 实体检测基于 AST — SDK 会扫描你的源文件以查找 `export default define<Entity>({...})` 模式。 你可以按照自己的喜好组织文件和文件夹。 按实体类型分组(例如 `logic-functions/`、`roles/`)只是代码组织的一种约定,并非必需。
@@ -212,19 +215,20 @@ twenty-sdk 提供你在应用中使用的类型化构件和辅助函数。 以
该 SDK 提供辅助函数用于定义你的应用实体。 如 [实体检测](#entity-detection) 中所述,你必须使用 `export default define<Entity>({...})` 才能让你的实体被检测到:
| 函数 | 目的 |
| ---------------------------------- | ------------------------------- |
| `defineApplication()` | 配置应用元数据(必需,每个应用一个) |
| `defineObject()` | 定义带字段的自定义对象 |
| `defineLogicFunction()` | 定义带处理程序的逻辑函数 |
| `definePreInstallLogicFunction()` | 定义一个安装前逻辑函数(每个应用一个) |
| `definePostInstallLogicFunction()` | 定义一个安装后逻辑函数(每个应用一个) |
| `defineFrontComponent()` | 为自定义 UI 定义前端组件 |
| `defineRole()` | 配置角色权限和对象访问 |
| `defineField()` | 为现有对象扩展额外字段 |
| `defineView()` | Define saved views for objects |
| `defineNavigationMenuItem()` | Define sidebar navigation links |
| `defineSkill()` | Define AI agent skills |
| 函数 | 目的 |
| ---------------------------------- | ------------------------------------ |
| `defineApplication()` | 配置应用元数据(必需,每个应用一个) |
| `defineObject()` | 定义带字段的自定义对象 |
| `defineLogicFunction()` | 定义带处理程序的逻辑函数 |
| `definePreInstallLogicFunction()` | 定义一个安装前逻辑函数(每个应用一个) |
| `definePostInstallLogicFunction()` | 定义一个安装后逻辑函数(每个应用一个) |
| `defineFrontComponent()` | 为自定义 UI 定义前端组件 |
| `defineRole()` | 配置角色权限和对象访问 |
| `defineField()` | 为现有对象扩展额外字段 |
| `defineView()` | Define saved views for objects |
| `defineNavigationMenuItem()` | Define sidebar navigation links |
| `defineSkill()` | Define AI agent skills |
| `defineAgent()` | Define AI agents with system prompts |
这些函数会在构建时校验你的配置,并提供 IDE 自动补全和类型安全。
@@ -802,6 +806,41 @@ You can create new skills in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new skill.
* **Manual**: Create a new file and use `defineSkill()`, following the same pattern.
### 代理
Agents define AI agents with system prompts that can operate within your workspace. Use `defineAgent()` to define agents with built-in validation:
```typescript
// src/agents/example-agent.ts
import { defineAgent } from 'twenty-sdk';
export default defineAgent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'sales-assistant',
label: 'Sales Assistant',
description: 'An AI agent that helps with sales tasks',
icon: 'IconRobot',
prompt: `You are a sales assistant. Help users with:
1. Researching prospects and companies
2. Drafting personalized outreach messages
3. Tracking follow-ups and next steps
4. Analyzing deal pipeline and suggesting actions`,
});
```
关键点:
* `name` is a unique identifier string for the agent (kebab-case recommended).
* `label` is the human-readable display name shown in the UI.
* `prompt` contains the system prompt — this is the instruction text that defines the agent's behavior.
* `icon` (optional) sets the icon displayed in the UI.
* `description` (optional) provides additional context about the agent's purpose.
You can create new agents in two ways:
* **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new agent.
* **Manual**: Create a new file and use `defineAgent()`, following the same pattern.
### 生成的类型化客户端
两个类型化客户端由 `yarn twenty app:dev` 自动生成(基于你的工作区架构),并存放在 `node_modules/twenty-sdk/generated`
+2 -2
View File
@@ -61,8 +61,8 @@ const jestConfig = {
extensionsToTreatAsEsm: ['.ts', '.tsx'],
coverageThreshold: {
global: {
statements: 49.5,
lines: 48,
statements: 49.3,
lines: 47.9,
functions: 39.5,
},
},
+1
View File
@@ -38,6 +38,7 @@
"@calcom/embed-react": "^1.5.3",
"@cyntler/react-doc-viewer": "^1.17.0",
"@dagrejs/dagre": "^1.1.8",
"@dnd-kit/react": "^0.3.2",
"@floating-ui/react": "^0.24.3",
"@graphiql/plugin-explorer": "^1.0.2",
"@graphiql/react": "^0.23.0",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

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