feat(core): Add skill Assistant availability column (no-changelog)

This commit is contained in:
Arvin Ansari 2026-10-19 16:39:14 +02:00
parent d74f3f2cdd
commit fc048fb7b6
No known key found for this signature in database
19 changed files with 112 additions and 3 deletions

View File

@ -135,7 +135,7 @@ Auto-generated from the PostgreSQL migrations in @n8n/db. Do not edit by hand.
| [public.settings](public.settings.md) | 3 | | BASE TABLE |
| [public.shared_credentials](public.shared_credentials.md) | 5 | | BASE TABLE |
| [public.shared_workflow](public.shared_workflow.md) | 5 | | BASE TABLE |
| [public.skill](public.skill.md) | 7 | | BASE TABLE |
| [public.skill](public.skill.md) | 8 | | BASE TABLE |
| [public.skill_file](public.skill_file.md) | 5 | | BASE TABLE |
| [public.skill_version](public.skill_version.md) | 11 | | BASE TABLE |
| [public.tag_entity](public.tag_entity.md) | 4 | | BASE TABLE |
@ -1713,6 +1713,7 @@ erDiagram
}
"public.skill" {
varchar_36_ agentId FK
boolean availableToAssistant
timestamp_3__with_time_zone createdAt
uuid createdById FK
varchar_36_ id

View File

@ -62,6 +62,7 @@ erDiagram
}
"public.skill" {
varchar_36_ agentId FK
boolean availableToAssistant
timestamp_3__with_time_zone createdAt
uuid createdById FK
varchar_36_ id

View File

@ -58,6 +58,7 @@ erDiagram
}
"public.skill" {
varchar_36_ agentId FK
boolean availableToAssistant
timestamp_3__with_time_zone createdAt
uuid createdById FK
varchar_36_ id

View File

@ -334,6 +334,7 @@ erDiagram
}
"public.skill" {
varchar_36_ agentId FK
boolean availableToAssistant
timestamp_3__with_time_zone createdAt
uuid createdById FK
varchar_36_ id

View File

@ -259,6 +259,7 @@ erDiagram
}
"public.skill" {
varchar_36_ agentId FK
boolean availableToAssistant
timestamp_3__with_time_zone createdAt
uuid createdById FK
varchar_36_ id

View File

@ -5,6 +5,7 @@
| Name | Type | Default | Nullable | Children | Parents | Comment |
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
| agentId | varchar(36) | | true | | [public.agents](public.agents.md) | Set when only this agent can use the skill. projectId is then NULL: the agent holds the project |
| availableToAssistant | boolean | false | false | | | |
| createdAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
| createdById | uuid | | true | | [public.user](public.user.md) | Author. NULL after the author is deleted |
| id | varchar(36) | | false | [public.agent_skill_dependency](public.agent_skill_dependency.md) [public.agent_version_skill_dependency](public.agent_version_skill_dependency.md) [public.skill_version](public.skill_version.md) | | skill_\<nanoid\>, the same format agents use |
@ -21,6 +22,7 @@
| FK_4de4501583cd1fcd6f5ea85f547 | FOREIGN KEY | FOREIGN KEY ("createdById") REFERENCES "user"(id) ON DELETE SET NULL |
| FK_7d4fb6af107e507d9a86293cf9d | FOREIGN KEY | FOREIGN KEY ("agentId") REFERENCES agents(id) ON DELETE CASCADE |
| PK_a0d33334424e64fb78dc3ce7196 | PRIMARY KEY | PRIMARY KEY (id) |
| skill_availableToAssistant_not_null | n | NOT NULL "availableToAssistant" |
| skill_createdAt_not_null | n | NOT NULL "createdAt" |
| skill_id_not_null | n | NOT NULL id |
| skill_source_not_null | n | NOT NULL source |
@ -48,6 +50,7 @@ erDiagram
"public.skill" {
varchar_36_ agentId FK
boolean availableToAssistant
timestamp_3__with_time_zone createdAt
uuid createdById FK
varchar_36_ id

View File

@ -104,6 +104,7 @@ erDiagram
}
"public.skill" {
varchar_36_ agentId FK
boolean availableToAssistant
timestamp_3__with_time_zone createdAt
uuid createdById FK
varchar_36_ id

View File

@ -427,6 +427,7 @@ erDiagram
}
"public.skill" {
varchar_36_ agentId FK
boolean availableToAssistant
timestamp_3__with_time_zone createdAt
uuid createdById FK
varchar_36_ id

View File

@ -135,7 +135,7 @@ Auto-generated from the SQLite migrations in @n8n/db. Do not edit by hand.
| [settings](settings.md) | 3 | | table |
| [shared_credentials](shared_credentials.md) | 5 | | table |
| [shared_workflow](shared_workflow.md) | 5 | | table |
| [skill](skill.md) | 7 | | table |
| [skill](skill.md) | 8 | | table |
| [skill_file](skill_file.md) | 5 | | table |
| [skill_version](skill_version.md) | 11 | | table |
| [tag_entity](tag_entity.md) | 4 | | table |
@ -1699,6 +1699,7 @@ erDiagram
}
"skill" {
varchar_36_ agentId FK
boolean availableToAssistant
datetime_3_ createdAt
varchar createdById FK
varchar_36_ id PK

View File

@ -72,6 +72,7 @@ erDiagram
}
"skill" {
varchar_36_ agentId FK
boolean availableToAssistant
datetime_3_ createdAt
varchar createdById FK
varchar_36_ id PK

View File

@ -67,6 +67,7 @@ erDiagram
}
"skill" {
varchar_36_ agentId FK
boolean availableToAssistant
datetime_3_ createdAt
varchar createdById FK
varchar_36_ id PK

View File

@ -336,6 +336,7 @@ erDiagram
}
"skill" {
varchar_36_ agentId FK
boolean availableToAssistant
datetime_3_ createdAt
varchar createdById FK
varchar_36_ id PK

View File

@ -265,6 +265,7 @@ erDiagram
}
"skill" {
varchar_36_ agentId FK
boolean availableToAssistant
datetime_3_ createdAt
varchar createdById FK
varchar_36_ id PK

View File

@ -6,7 +6,7 @@
<summary><strong>Table Definition</strong></summary>
```sql
CREATE TABLE "skill" ("id" varchar(36) PRIMARY KEY NOT NULL, "projectId" varchar(36), "agentId" varchar(36), "source" varchar(16) NOT NULL, "createdById" varchar, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), CONSTRAINT "CHK_skill_single_target" CHECK ("agentId" IS NULL OR "projectId" IS NULL), CONSTRAINT "FK_402ed37bff0d3134f6e782455ed" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE, CONSTRAINT "FK_7d4fb6af107e507d9a86293cf9d" FOREIGN KEY ("agentId") REFERENCES "agents" ("id") ON DELETE CASCADE, CONSTRAINT "FK_4de4501583cd1fcd6f5ea85f547" FOREIGN KEY ("createdById") REFERENCES "user" ("id") ON DELETE SET NULL)
CREATE TABLE "skill" ("id" varchar(36) PRIMARY KEY NOT NULL, "projectId" varchar(36), "agentId" varchar(36), "source" varchar(16) NOT NULL, "createdById" varchar, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "availableToAssistant" boolean NOT NULL DEFAULT false, CONSTRAINT "CHK_skill_single_target" CHECK ("agentId" IS NULL OR "projectId" IS NULL), CONSTRAINT "FK_402ed37bff0d3134f6e782455ed" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE, CONSTRAINT "FK_7d4fb6af107e507d9a86293cf9d" FOREIGN KEY ("agentId") REFERENCES "agents" ("id") ON DELETE CASCADE, CONSTRAINT "FK_4de4501583cd1fcd6f5ea85f547" FOREIGN KEY ("createdById") REFERENCES "user" ("id") ON DELETE SET NULL)
```
</details>
@ -16,6 +16,7 @@ CREATE TABLE "skill" ("id" varchar(36) PRIMARY KEY NOT NULL, "projectId" varchar
| Name | Type | Default | Nullable | Children | Parents | Comment |
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
| agentId | varchar(36) | | true | | [agents](agents.md) | |
| availableToAssistant | boolean | false | false | | | |
| createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
| createdById | varchar | | true | | [user](user.md) | |
| id | varchar(36) | | false | [agent_skill_dependency](agent_skill_dependency.md) [agent_version_skill_dependency](agent_version_skill_dependency.md) [skill_version](skill_version.md) | | |
@ -56,6 +57,7 @@ erDiagram
"skill" {
varchar_36_ agentId FK
boolean availableToAssistant
datetime_3_ createdAt
varchar createdById FK
varchar_36_ id PK

View File

@ -107,6 +107,7 @@ erDiagram
}
"skill" {
varchar_36_ agentId FK
boolean availableToAssistant
datetime_3_ createdAt
varchar createdById FK
varchar_36_ id PK

View File

@ -433,6 +433,7 @@ erDiagram
}
"skill" {
varchar_36_ agentId FK
boolean availableToAssistant
datetime_3_ createdAt
varchar createdById FK
varchar_36_ id PK

View File

@ -0,0 +1,16 @@
import type { MigrationContext, ReversibleMigration } from '../migration-types';
export class AddSkillAssistantAvailability1792418677997 implements ReversibleMigration {
async up({ runQuery, escape }: MigrationContext) {
// ALTER preserves the skill versions and references on SQLite.
await runQuery(
`ALTER TABLE ${escape.tableName('skill')} ADD COLUMN ${escape.columnName('availableToAssistant')} boolean NOT NULL DEFAULT false`,
);
}
async down({ runQuery, escape }: MigrationContext) {
await runQuery(
`ALTER TABLE ${escape.tableName('skill')} DROP COLUMN ${escape.columnName('availableToAssistant')}`,
);
}
}

View File

@ -23,6 +23,9 @@ export class Skill extends WithTimestamps {
@Column({ type: 'varchar', length: 36, nullable: true })
agentId: string | null;
@Column({ type: 'boolean', default: false })
availableToAssistant: boolean;
@Column({ type: 'varchar', length: 16 })
source: SkillSource;

View File

@ -0,0 +1,71 @@
import {
createTestMigrationContext,
initDbUpToMigration,
runSingleMigration,
type TestMigrationContext,
} from '@n8n/backend-test-utils';
import { DbConnection } from '@n8n/db';
import { Container } from '@n8n/di';
import { DataSource } from '@n8n/typeorm';
const migration = 'AddSkillAssistantAvailability1792418677997';
describe('Skill Assistant availability migration', () => {
let context: TestMigrationContext;
beforeAll(async () => {
await Container.get(DbConnection).init();
context = createTestMigrationContext(Container.get(DataSource));
await context.queryRunner.clearDatabase();
await context.queryRunner.release();
await initDbUpToMigration(migration);
context = createTestMigrationContext(Container.get(DataSource));
});
afterAll(async () => {
await context?.queryRunner.release();
await Container.get(DbConnection).close();
});
it('defaults existing and new skills to false and preserves versions through up/down/up', async () => {
const table = context.escape.tableName('skill');
const versions = context.escape.tableName('skill_version');
const col = context.escape.columnName;
await context.runQuery(
`INSERT INTO ${table} (${col('id')}, ${col('source')}) VALUES ('existing', 'ui')`,
);
await context.runQuery(
`INSERT INTO ${versions} (${col('id')}, ${col('skillId')}, ${col('version')}, ${col('name')}, ${col('description')}, ${col('instructions')}, ${col('contentHash')}) VALUES ('0199e890-7471-7000-8000-000000000001', 'existing', 1, 'Tone', 'Writing style', 'Be brief.', '${'a'.repeat(64)}')`,
);
await context.queryRunner.release();
await runSingleMigration(migration);
context = createTestMigrationContext(Container.get(DataSource));
await context.runQuery(
`INSERT INTO ${table} (${col('id')}, ${col('source')}) VALUES ('new', 'agent')`,
);
const readFlags = async () =>
await context.runQuery<Array<{ availableToAssistant: boolean | number }>>(
`SELECT ${col('availableToAssistant')} FROM ${table}`,
);
expect((await readFlags()).map((row) => Boolean(row.availableToAssistant))).toEqual([
false,
false,
]);
await context.runQuery(
`UPDATE ${table} SET ${col('availableToAssistant')} = true WHERE ${col('id')} = 'existing'`,
);
expect((await readFlags()).filter((row) => Boolean(row.availableToAssistant))).toHaveLength(1);
await context.queryRunner.release();
await Container.get(DataSource).undoLastMigration();
await runSingleMigration(migration);
context = createTestMigrationContext(Container.get(DataSource));
expect((await readFlags()).map((row) => Boolean(row.availableToAssistant))).toEqual([
false,
false,
]);
expect(await context.runQuery(`SELECT ${col('instructions')} FROM ${versions}`)).toEqual([
{ instructions: 'Be brief.' },
]);
});
});