mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-18 23:09:03 +02:00
Merge updated verification into self-healing investigations
This commit is contained in:
commit
0fce39607e
|
|
@ -78,11 +78,24 @@ Creates GitHub PRs with titles that pass n8n's `check-pr-title` CI validation.
|
|||
body structure, then populate each section with actual content before
|
||||
creating the PR:
|
||||
- **Summary**: describe what the PR does
|
||||
- **Why**: explain why the change is needed (see Why below)
|
||||
- **How to test**: describe how to test the changes. Include an example workflow if appropriate
|
||||
- **Related tickets**: add the Linear ticket URL (`https://linear.app/n8n/issue/[TICKET-ID]`) and any GitHub issue links
|
||||
- **Checklist**: keep as-is from the template
|
||||
- Add a "🤖 PR Summary generated by AI" at the end of the body
|
||||
|
||||
Before you create the PR, proofread the body with these checks. Rewrite
|
||||
each sentence that fails a check:
|
||||
1. The Summary and the Why together are under the word budget. If not,
|
||||
first remove the details that the reviewer reads in the diff, then
|
||||
shorten the Why.
|
||||
2. Each sentence names who does what: a component, a process, or a person
|
||||
as the subject, with a plain technical verb.
|
||||
3. Each domain term has a plain definition where it first appears.
|
||||
4. Each sentence is true for the diff.
|
||||
|
||||
When a later commit changes a fact in the body, update the body.
|
||||
|
||||
```bash
|
||||
gh pr create --draft --title "<type>(<scope>): <summary>" --body "$(cat <<'EOF'
|
||||
<populated body based on pull_request_template.md>
|
||||
|
|
@ -94,12 +107,43 @@ Creates GitHub PRs with titles that pass n8n's `check-pr-title` CI validation.
|
|||
|
||||
Based on `.github/pull_request_template.md`:
|
||||
|
||||
Write the PR body in ASD-STE100 Simplified Technical English: use short
|
||||
sentences, the active voice, and one instruction for each sentence.
|
||||
Write for a reviewer who does not know this area of the code. The reviewer
|
||||
must understand the change before they open the diff. Use ASD-STE100
|
||||
Simplified Technical English: short sentences, active voice.
|
||||
1. Keep the Summary and the Why together under about 200 words. A small PR
|
||||
needs much less.
|
||||
2. Define each domain term in plain words where it first appears. Do not use
|
||||
a ticket ID or an internal name as an explanation.
|
||||
3. Leave out details that the reviewer reads in the diff: error cases, status
|
||||
codes, renamed fields, the order of calls, file lists, test names. Mention
|
||||
one only when it changes behaviour that a reviewer could miss. Also leave
|
||||
out what reviewers already know (framework behaviour).
|
||||
4. Use plain technical verbs, not metaphors ("passes the value", not
|
||||
"hands the value" or "lets the value through").
|
||||
|
||||
### Summary Section
|
||||
- Describe what the PR does
|
||||
- Include screenshots/videos for UI changes
|
||||
### Summary: what the PR does
|
||||
1. Start with one to three sentences of background for a reader who is new to
|
||||
the area. Then state what the PR does.
|
||||
2. Write **prose** about behaviour, not a list of code edits. Name a class or
|
||||
a function when it represents an essential/core concept and it helps
|
||||
the reader to navigate the change.
|
||||
3. If the PR changes no product code, say so in the first sentence
|
||||
(for example: "This PR only adds tests.").
|
||||
4. Do not justify the change here. The justification goes in the Why.
|
||||
5. Include screenshots or videos for UI changes.
|
||||
|
||||
### Why: the reasoning
|
||||
Always fill the `## Why` section. The Summary and the core of the
|
||||
Why are what the reviewer needs most. Keep the core short:
|
||||
1. Do not repeat the Summary.
|
||||
2. State the problem or the constraint that makes the pre-existing code unfit.
|
||||
3. If there are accepted gaps, or changes for existing code paths, state them
|
||||
in a separate short paragraph.
|
||||
|
||||
Add rejected alternatives only when a reviewer would otherwise ask about them,
|
||||
if any. Use one or two sentences for each alternative: state the concrete
|
||||
reason it fails. Write an alternative that was never built in the conditional.
|
||||
Together, the alternatives must be shorter than the core Why section.
|
||||
|
||||
### How to test Section
|
||||
- Explain how to test the changes
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ Run through this before requesting review. Each item is a real, recurring review
|
|||
- [ ] Identifiers go through **`escape.tableName(...)` / `escape.columnName(...)`**. Never hand-write `n8n_table` prefixes. — [Always escape identifiers](#always-escape-identifiers)
|
||||
- [ ] **Match column type to value semantics.** Native `uuid` for UUIDs, `timestampTimezone()` for timestamps, a numeric type for numbers, `bool` for booleans, `json` for structured data. Never `varchar` as a catch-all. — [Column types](#column-types)
|
||||
- [ ] **Pick the narrowest sane type within that category:** `int`/`smallint` not `bigint` when range allows; `text` not `varchar(255)` for unbounded strings; never `double` for version numbers. — [Column types](#column-types)
|
||||
- [ ] **A generated `id` key is an integer** (`int.primary.autoGenerate2`). A UUID key is a UUIDv7. For a UUID or string key, write the reason in the PR description. A join table uses its foreign-key columns as the key, not an `id` column. — [Primary Keys](#primary-keys)
|
||||
- [ ] **Default `notNull`**, relax only when justified. PK is implicitly NOT NULL. Migration's `notNull` matches the entity's nullability. — [NOT NULL and entity parity](#not-null-and-entity-parity)
|
||||
- [ ] **Enum-like columns** carry `.withEnumCheck([...])` AND `.comment('explains values')`. Opaque IDs / unix timestamps / JSON shapes also get `.comment()`. — [Constrain enum-like strings](#constrain-enum-like-strings), [Add comments on columns](#add-comments-on-columns)
|
||||
- [ ] **Every reference column has an explicit FK** with deliberate `onDelete`. Name FKs explicitly when SQLite recreate cycles risk duplicating them. Avoid polymorphic `(typeCol, idCol)` patterns. — [Foreign Key Constraints](#foreign-key-constraints), [General Design Guidance](#general-design-guidance)
|
||||
|
|
@ -439,11 +440,32 @@ Don't set the default of a `status` column to a terminal value — `"running"` m
|
|||
|
||||
### Primary Keys
|
||||
|
||||
Every table needs a primary key. Choose the type in this order:
|
||||
Every table needs a primary key. **Use an integer key by default.** Read the options below in order. Use the first option that fits. Do not skip an option only because you like a later one better.
|
||||
|
||||
1. **Integer** — `column('id').int.primary.autoGenerate2`. Preferred for new tables: compact, fast joins, no ordering surprises. `autoGenerate2` uses Postgres `IDENTITY` (preferred over the deprecated `serial`-based `autoGenerate`).
|
||||
2. **UUID** — `column('id').uuid.primary`. Use when IDs are generated client-side, exposed in URLs, or need to be unguessable. Generate UUIDs in application code via `randomUUID()` from `node:crypto`; do **not** chain `.autoGenerate2` on `.uuid` (the DSL throws — `DEFAULT uuid_generate_v4()` fails on managed Postgres like Supabase because it needs the `uuid-ossp` extension in `public`). Use `.uuid` instead of `.varchar(36)`.
|
||||
3. **String** — `column('id').varchar(36).primary` for IDs whose format isn't a UUID (e.g. nanoid-style IDs). Convention: nanoid length 16 for entity IDs.
|
||||
1. **Integer (default)** — `column('id').int.primary.autoGenerate2`. Use this for every new table. Use a different option only when one of its conditions is true. Integer keys are small and make joins fast. `autoGenerate2` uses Postgres `IDENTITY`. Do not use `autoGenerate`. It uses `serial`, which is deprecated.
|
||||
2. **UUID** — `column('id').uuid.primary`. Use this only when one of these conditions is true:
|
||||
- The application must know the ID before the insert. For example, the client makes the ID.
|
||||
- The ID is in a URL or a public API. Users must not see how many rows exist or guess other IDs.
|
||||
|
||||
**You must use UUIDv7.** Make the ID in application code with the `uuid` package: `import { v7 as uuidv7 } from 'uuid'`.
|
||||
|
||||
Why: a UUIDv7 starts with a timestamp. Thus, new rows go near the end of the index. A UUIDv4 is random, for example `randomUUID()` from `node:crypto`. New rows go to random positions in the index. This splits index pages and makes the index larger.
|
||||
|
||||
A UUIDv7 does not give an exact creation order. Two IDs made in the same millisecond can sort in the wrong order. Clock differences between hosts can also cause this. If you need the exact order, sort by a timestamp column.
|
||||
|
||||
Do **not** chain `.autoGenerate2` on `.uuid`. The DSL throws an error. The database default makes a UUIDv4. Also, `uuid_generate_v4()` needs the `uuid-ossp` extension. Some managed Postgres services, such as Supabase, do not have it. Use `.uuid`, not `.varchar(36)`.
|
||||
3. **String** — `column('id').varchar(36).primary`. Use this only when one of these conditions is true:
|
||||
- The entity already uses IDs in a format that is not a UUID. For example, the public API or the UI already shows nanoid IDs for this entity.
|
||||
- The UI shows the ID to users. A UUID is too long to read, copy, or type.
|
||||
|
||||
Entity IDs are usually nanoids with 16 characters.
|
||||
|
||||
**Give a reason for every key that is not an integer.** Write the reason in the PR description. Reviewers will ask for it. These reasons are not sufficient:
|
||||
- "Other tables use string IDs."
|
||||
- "This module already does it this way."
|
||||
- "We might need it later."
|
||||
|
||||
**These options apply to a generated `id` column only.** A join table that only links other tables does not need an `id` column. Use its foreign-key columns as a composite key (see below). A foreign-key column always has the same type as the key that it refers to.
|
||||
|
||||
**Keep ID-column types consistent across related tables.** Mixing `uuid` and `varchar(36)` for what is "the same kind of ID" creates JOIN footguns.
|
||||
|
||||
|
|
@ -696,7 +718,7 @@ If only Postgres needs the change, just put the file in `postgresdb/`; don't wri
|
|||
|
||||
### Postgres-version-aware UUID generation
|
||||
|
||||
`gen_random_uuid()` requires Postgres ≥ 13. n8n dropped Postgres 12 — prefer it over `uuid_generate_v4()` (which needs the `uuid-ossp` extension and breaks on managed services like Supabase). For UUID PKs, generate at the application level with `randomUUID()` — see [Primary Keys](#primary-keys).
|
||||
`gen_random_uuid()` requires Postgres ≥ 13. n8n dropped Postgres 12 — prefer it over `uuid_generate_v4()` (which needs the `uuid-ossp` extension and breaks on managed services like Supabase). For UUID PKs, make a UUIDv7 in application code — see [Primary Keys](#primary-keys).
|
||||
|
||||
### SQLite doesn't enforce `varchar(N)` length
|
||||
|
||||
|
|
|
|||
158
.agents/skills/env-var-docs/SKILL.md
Normal file
158
.agents/skills/env-var-docs/SKILL.md
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
---
|
||||
name: n8n:env-var-docs
|
||||
description: >-
|
||||
Writes and reviews the JSDoc on `@Env(...)` config properties. The docs team
|
||||
extracts these comments from source into the public environment variable
|
||||
reference, so they are user documentation, not code comments. Use when adding
|
||||
or changing an `@Env` property, a config class, or reviewing a PR that touches
|
||||
one.
|
||||
---
|
||||
|
||||
# Environment variable docs
|
||||
|
||||
The JSDoc above every `@Env('...')` property is exported almost verbatim into
|
||||
the [environment variables reference](https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/use-environment-variables)
|
||||
on docs.n8n.io. The generated table has four columns: variable name,
|
||||
description, data type, default. Your JSDoc is the description column. The
|
||||
reader is a self-hosting operator, not an n8n developer.
|
||||
|
||||
Follow the [n8n docs style guide](https://docs.n8n.io/contribute/contribution-guide-for-n8n-docs/style-guide-for-n8n-docs)
|
||||
for these comments. It overrides the ASD-STE100 rule in `AGENTS.md` for this
|
||||
one case. The rules below are the parts of that guide that matter for a
|
||||
one-paragraph description.
|
||||
|
||||
## Rules
|
||||
|
||||
- **Every `@Env` property has a JSDoc.** No exceptions. A missing comment
|
||||
ships as an empty description cell.
|
||||
- **Write for the operator.** Say what the setting does, when to change it,
|
||||
and what happens when it's unset or wrong. Don't explain why the code is
|
||||
written the way it is.
|
||||
- **No code references.** No file names, class names, function names,
|
||||
constants, license keys, or other internals the reader can't open. Values
|
||||
the operator types into the variable are fine and often needed, for example
|
||||
a credential type ID like `googleOAuth2Api` or a mode like `postgresdb`.
|
||||
The test: would the operator paste this into a config file, or would they
|
||||
need the source to know what it means? Details for developers go in a
|
||||
regular `//` comment on the implementation, not in the JSDoc.
|
||||
- **No attack scenarios.** Say what the setting protects, not how an
|
||||
attacker would exploit it. This is a public page.
|
||||
- **Don't repeat the default.** The default renders in its own column. Don't
|
||||
write "Defaults to true" or "Default: 1 hour". Do say what the unset state
|
||||
means when it's not obvious, for example "When unset, n8n uses the Redis
|
||||
host".
|
||||
- **State units and formats.** Say "in milliseconds", "in bytes", "a
|
||||
comma-separated list", "a JSON object". The data type column only says
|
||||
`Number` or `String`.
|
||||
- **Use allowed values when the set is small.** "One of `sqlite`,
|
||||
`postgresdb`." For a list, show one inline example in the sentence.
|
||||
- **No JSDoc tags.** No `@example`, `@default`, `@see`, `@deprecated`. The
|
||||
exporter copies the tag name into the docs. Put the example or the
|
||||
deprecation notice in a sentence instead.
|
||||
- **Product names:** "Gateway credits", not "n8n Connect" or "AI Gateway".
|
||||
"Publish a workflow", not "activate". `n8n` is always lowercase.
|
||||
- **Style:** present tense, active voice, second person. Use contractions.
|
||||
No em dashes, use a comma or a new sentence. No "e.g." or "i.e.", write
|
||||
"for example" and "that is". Spell out zero to nine, numerals from 10.
|
||||
Values the operator types stay numerals, for example "Set to 0".
|
||||
Sentence case. One idea per sentence. End every sentence with a period.
|
||||
- **Plain words:** "use" not "utilize", "lets you" not "allows you to", cut
|
||||
"powerful", "seamless", "simply", "it's important to note".
|
||||
- **Length:** one to three sentences. If the setting needs more, the extra
|
||||
belongs in a docs page the comment can't link to yet; keep the comment to
|
||||
what fits a table cell.
|
||||
|
||||
## Examples
|
||||
|
||||
Bad, as found in the generated docs:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Instance-level enablement for n8n Connect (AI Gateway). Licensed instances
|
||||
* are enabled by default; false opts out. Budget / free allowance still comes
|
||||
* from the license (`quota:aiGatewayBudget`).
|
||||
*/
|
||||
```
|
||||
|
||||
Wrong product name, a restated default, and an internal license key. Good:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Whether Gateway credits are available on this instance. Set to false to
|
||||
* turn them off on a licensed instance. The credit allowance comes from your
|
||||
* license.
|
||||
*/
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Time to live in milliseconds for Redis cache entries. Set to 0 to disable
|
||||
* expiry. Default: 1 hour.
|
||||
*/
|
||||
```
|
||||
|
||||
The default is already in the table. Good:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Time to live in milliseconds for Redis cache entries. Set to 0 to keep
|
||||
* entries until Redis evicts them.
|
||||
*/
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Comma-separated list of credential types for which overwrites are skipped
|
||||
* when the credential has been customized.
|
||||
* @example `N8N_SKIP_CREDENTIAL_OVERWRITE=zohoOAuth2Api,salesforceOAuth2Api`
|
||||
*/
|
||||
```
|
||||
|
||||
The `@example` tag lands in the description as literal text. Good:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Comma-separated list of credential types to skip when applying credential
|
||||
* overwrites, for example `zohoOAuth2Api,salesforceOAuth2Api`. n8n skips an
|
||||
* overwrite only when a user has already changed one of its fields.
|
||||
*/
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Bind OAuth credential callbacks to the browser that initiated the flow.
|
||||
* When enabled, a session-scoped `n8n-oauth-binding` cookie is set on flow
|
||||
* initiation and verified on callback. Defeats phishing attacks where an
|
||||
* attacker injects an OAuth authorize URL into a victim's browser to land
|
||||
* tokens on the attacker's credential.
|
||||
*/
|
||||
```
|
||||
|
||||
Attack narrative and an internal cookie name on a public page. Good:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Whether an OAuth credential connection must finish in the same browser
|
||||
* that started it. When enabled, n8n rejects the callback if it arrives
|
||||
* from a different browser.
|
||||
*/
|
||||
```
|
||||
|
||||
## Review checklist
|
||||
|
||||
When you review a PR that adds or changes an `@Env` property, check each
|
||||
comment against this list and quote the fix in the review:
|
||||
|
||||
1. JSDoc present.
|
||||
2. No code identifiers, file names, or internal keys.
|
||||
3. No default restated.
|
||||
4. Units, format, or allowed values stated.
|
||||
5. No JSDoc tags.
|
||||
6. No em dashes, Latin abbreviations, or filler words.
|
||||
7. Reads correctly as a sentence in a table cell with no surrounding context.
|
||||
1
.claude/plugins/n8n/skills/env-var-docs
Symbolic link
1
.claude/plugins/n8n/skills/env-var-docs
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../../.agents/skills/env-var-docs
|
||||
1
.github/CI-TELEMETRY.md
vendored
1
.github/CI-TELEMETRY.md
vendored
|
|
@ -85,6 +85,7 @@ return 'blacksmith';
|
|||
| Accessibility buckets | `packages/quality/testing/playwright/reporters/a11y-reporter.ts` | Per-bucket axe score, violated rules, violating elements |
|
||||
| Build stats | `.github/scripts/send-build-stats.mjs` | Per-package build duration, cache hit/miss, run total |
|
||||
| Docker stats | `.github/scripts/send-docker-stats.mjs` | Image size per platform, docker build duration |
|
||||
| Dive image efficiency | `packages/quality/efficiency/images/image-efficiency.spec.ts` | Layer content size, estimated waste, efficiency score, and layer count through the shared Playwright reporter |
|
||||
| Container stack | `packages/quality/environments/containers/telemetry.ts` | E2E stack startup times per service |
|
||||
|
||||
## Secrets
|
||||
|
|
|
|||
4
.github/WORKFLOWS.md
vendored
4
.github/WORKFLOWS.md
vendored
|
|
@ -411,6 +411,7 @@ Run-workflow dropdown, which only a user with write access can pick.
|
|||
|-----------------------------|---------------------------------------------------------|
|
||||
| `util-codespace-preview.yml`| Wake, re-serve or delete a PR preview instance by hand |
|
||||
| `util-data-tooling.yml` | SQLite/PostgreSQL export/import validation (manual) |
|
||||
| `test-image-efficiency.yml` | Measure n8n and runners image size and layer waste with Dive |
|
||||
| `util-probe-registry.yml` | Diagnose slow npm metadata fetches (temporary) |
|
||||
| `sec-sync-bundle-branches.yml` | Sync both bundle branches on demand, including before a bundle cut |
|
||||
|
||||
|
|
@ -586,7 +587,7 @@ Push to master
|
|||
| Daily 03:00 | `sec-sync-bundle-branches.yml` | Merge the base into `bundle/*` |
|
||||
| Daily 00:00 | `docker-build-push.yml` | Nightly Docker images |
|
||||
| Daily 00:00 | `test-db.yml` | Database compatibility |
|
||||
| Daily 00:00 | `test-e2e-performance-reusable.yml`| Performance E2E |
|
||||
| Daily 00:00 | `test-e2e-performance-reusable.yml`| Browser performance and idle memory baselines |
|
||||
| Daily 00:00 | `release-storybook.yml` | Storybook deploy |
|
||||
| Daily 00:00 | `release-chromatic.yml` | Visual regression |
|
||||
| Daily 00:00 | `util-check-docs-urls.yml` | Doc link validation |
|
||||
|
|
@ -599,6 +600,7 @@ Push to master
|
|||
| Daily 08:00 | `build-v3-nightly.yml` | Nightly v3 Docker images |
|
||||
| Monday 00:00 | `util-update-node-popularity.yml` | Node usage stats |
|
||||
| Monday 02:00 | `test-e2e-coverage-weekly.yml` | Weekly E2E coverage |
|
||||
| Wednesday 04:00 | `util-update-get-n8n-versions.yml` | get-n8n stable versions |
|
||||
| Saturday 22:00 | `test-evals-ai.yml` | AI workflow evals |
|
||||
| 1st of month 04:00 | `util-refresh-cubic-schema.yml` | Refresh vendored cubic schema |
|
||||
|
||||
|
|
|
|||
11
.github/pull_request_template.md
vendored
11
.github/pull_request_template.md
vendored
|
|
@ -1,8 +1,15 @@
|
|||
## Summary
|
||||
|
||||
<!--
|
||||
Describe what the PR does.
|
||||
Photos and videos are recommended.
|
||||
Start with 1 to 3 sentences of background for a new reader. Then describe what the PR does, in prose, not as a list of edits.
|
||||
Add photos or videos for UI changes.
|
||||
-->
|
||||
|
||||
## Why
|
||||
|
||||
<!--
|
||||
State the problem or the constraint that makes the current code unfit. Do not repeat the Summary.
|
||||
Add a rejected alternative only when a reviewer would ask about it, in one or two sentences.
|
||||
-->
|
||||
|
||||
## How to test
|
||||
|
|
|
|||
2
.github/test-metrics/e2e-impact-map.json
vendored
2
.github/test-metrics/e2e-impact-map.json
vendored
File diff suppressed because one or more lines are too long
8
.github/workflows/ci-pull-requests.yml
vendored
8
.github/workflows/ci-pull-requests.yml
vendored
|
|
@ -80,6 +80,7 @@ jobs:
|
|||
!**/*.test.ts
|
||||
!**/*.spec.ts
|
||||
!packages/quality/testing/playwright/**
|
||||
!packages/quality/efficiency/**
|
||||
!packages/frontend/@n8n/storybook/**
|
||||
!.devcontainer/**
|
||||
!scripts/agent-setup.mjs
|
||||
|
|
@ -146,10 +147,17 @@ jobs:
|
|||
cubic.yaml
|
||||
.agents/review-rules/**
|
||||
e2e-performance:
|
||||
packages/quality/efficiency/memory/idle/**
|
||||
packages/quality/efficiency/playwright.config.ts
|
||||
packages/quality/efficiency/package.json
|
||||
packages/quality/testing/playwright/package.json
|
||||
packages/quality/testing/playwright/quality-*.ts
|
||||
packages/quality/testing/playwright/fixtures/**
|
||||
packages/quality/testing/playwright/tests/performance/**
|
||||
packages/quality/testing/playwright/utils/performance-helper.ts
|
||||
packages/quality/environments/containers/**
|
||||
.github/workflows/test-e2e-performance-reusable.yml
|
||||
.github/workflows/test-e2e-reusable.yml
|
||||
# Whole directories, not the `engine-*` files alone. Engine v2
|
||||
# routing lives in ordinary control plane code: `workflow-runner.ts`
|
||||
# and the webhook and trigger paths decide whether a run reaches the
|
||||
|
|
|
|||
10
.github/workflows/prepare-docker-reusable.yml
vendored
10
.github/workflows/prepare-docker-reusable.yml
vendored
|
|
@ -9,6 +9,16 @@ name: 'Prepare n8n Docker (reusable)'
|
|||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
QA_METRICS_WEBHOOK_URL:
|
||||
description: 'QA metrics webhook URL.'
|
||||
required: false
|
||||
QA_METRICS_WEBHOOK_USER:
|
||||
description: 'QA metrics webhook username.'
|
||||
required: false
|
||||
QA_METRICS_WEBHOOK_PASSWORD:
|
||||
description: 'QA metrics webhook password.'
|
||||
required: false
|
||||
inputs:
|
||||
build-variant:
|
||||
description: 'standard, coverage, or pc'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
name: 'Test: E2E Performance'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
|
@ -24,6 +27,17 @@ jobs:
|
|||
artifact-prefix: performance
|
||||
secrets: inherit
|
||||
|
||||
idle-memory-baselines:
|
||||
needs: [prepare-docker]
|
||||
uses: ./.github/workflows/test-e2e-reusable.yml
|
||||
with:
|
||||
test-mode: docker-artifact
|
||||
test-command: pnpm --filter=@n8n/efficiency test:memory
|
||||
workers: '1'
|
||||
currents-project-id: 'O9BJaN'
|
||||
artifact-prefix: memory-performance
|
||||
secrets: inherit
|
||||
|
||||
canvas-perf-sentinels:
|
||||
name: Canvas Perf Sentinels
|
||||
# Sentinels are nightly only — wall-clock browser perf has 15-30% variance,
|
||||
|
|
|
|||
7
.github/workflows/test-e2e-reusable.yml
vendored
7
.github/workflows/test-e2e-reusable.yml
vendored
|
|
@ -195,9 +195,10 @@ jobs:
|
|||
with:
|
||||
name: ${{ inputs.artifact-prefix }}-shard-${{ matrix.shard }}
|
||||
path: |
|
||||
packages/quality/testing/playwright/test-results/
|
||||
packages/quality/testing/playwright/test-results.json
|
||||
packages/quality/testing/playwright/playwright-report/
|
||||
packages/quality/*/test-results*
|
||||
packages/quality/*/playwright-report/
|
||||
packages/quality/*/*/test-results*
|
||||
packages/quality/*/*/playwright-report/
|
||||
packages/quality/testing/playwright/coverage/lcov.info
|
||||
packages/quality/testing/playwright/coverage/by-spec/
|
||||
packages/quality/testing/playwright/coverage/index.html
|
||||
|
|
|
|||
71
.github/workflows/test-image-efficiency.yml
vendored
Normal file
71
.github/workflows/test-image-efficiency.yml
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
name: 'Test: Image Efficiency'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prepare-docker:
|
||||
uses: ./.github/workflows/prepare-docker-reusable.yml
|
||||
secrets:
|
||||
QA_METRICS_WEBHOOK_URL: ${{ secrets.QA_METRICS_WEBHOOK_URL }}
|
||||
QA_METRICS_WEBHOOK_USER: ${{ secrets.QA_METRICS_WEBHOOK_USER }}
|
||||
QA_METRICS_WEBHOOK_PASSWORD: ${{ secrets.QA_METRICS_WEBHOOK_PASSWORD }}
|
||||
|
||||
image-efficiency:
|
||||
name: Image Efficiency
|
||||
needs: prepare-docker
|
||||
runs-on: ${{ vars.RUNNER_PROVIDER == 'github' && 'ubuntu-latest' || 'blacksmith-2vcpu-ubuntu-2204' }}
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
CURRENTS_RECORD_KEY: ${{ secrets.CURRENTS_RECORD_KEY }}
|
||||
CURRENTS_PROJECT_ID: 'nHHLA5'
|
||||
CURRENTS_CI_BUILD_ID: ${{ github.repository }}-${{ github.run_id }}-${{ github.run_attempt }}-images
|
||||
QA_METRICS_WEBHOOK_URL: ${{ secrets.QA_METRICS_WEBHOOK_URL }}
|
||||
QA_METRICS_WEBHOOK_USER: ${{ secrets.QA_METRICS_WEBHOOK_USER }}
|
||||
QA_METRICS_WEBHOOK_PASSWORD: ${{ secrets.QA_METRICS_WEBHOOK_PASSWORD }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: useblacksmith/checkout@bcec731f1eb1367240608d1c889a75b96db6ec53 # v1.5.0
|
||||
|
||||
- name: Setup Environment
|
||||
uses: ./.github/actions/setup-nodejs
|
||||
with:
|
||||
build-command: pnpm turbo run build --filter=@n8n/utils...
|
||||
|
||||
- name: Load images from cache
|
||||
uses: ./.github/actions/load-n8n-docker
|
||||
|
||||
- name: Install Dive
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Keep this version in sync with efficiency/images/dive-results.ts.
|
||||
VERSION=0.13.1
|
||||
DIR=$(mktemp -d "$RUNNER_TEMP/dive.XXXXXX")
|
||||
ARCHIVE="$DIR/dive.tar.gz"
|
||||
curl --fail --location --retry 3 \
|
||||
"https://github.com/wagoodman/dive/releases/download/v${VERSION}/dive_${VERSION}_linux_amd64.tar.gz" \
|
||||
--output "$ARCHIVE"
|
||||
echo "0970549eb4a306f8825a84145a2534153badb4d7dcf3febd1967c706367c3d0e $ARCHIVE" | sha256sum --check
|
||||
tar -xzf "$ARCHIVE" -C "$DIR" dive
|
||||
echo "$DIR" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Measure image layer efficiency
|
||||
run: >-
|
||||
pnpm --filter=@n8n/efficiency exec playwright test
|
||||
--project=image-efficiency:n8n --project=image-efficiency:runners
|
||||
|
||||
- name: Upload image efficiency reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: image-efficiency-${{ github.run_attempt }}
|
||||
path: |
|
||||
packages/quality/efficiency/test-results/
|
||||
packages/quality/efficiency/test-results.json
|
||||
packages/quality/efficiency/playwright-report/
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
118
.github/workflows/util-update-get-n8n-versions.yml
vendored
Normal file
118
.github/workflows/util-update-get-n8n-versions.yml
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
name: 'Util: Update get-n8n Versions'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run every Wednesday at 04:00 UTC.
|
||||
- cron: '0 4 * * 3'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: update-get-n8n-versions
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
update-versions:
|
||||
name: Update get-n8n versions
|
||||
if: >-
|
||||
github.ref_name == github.event.repository.default_branch &&
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'n8n-io/n8n'))
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Generate GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||
with:
|
||||
app-id: ${{ secrets.N8N_ASSISTANT_APP_ID }}
|
||||
private-key: ${{ secrets.N8N_ASSISTANT_PRIVATE_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Resolve stable versions
|
||||
id: versions
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
run: |
|
||||
n8n_version="$(gh api repos/n8n-io/n8n/releases/latest --jq '.tag_name | sub("^n8n@"; "")')"
|
||||
|
||||
registry_token="$(curl -fsSL --retry 3 'https://ghcr.io/token?scope=repository:n8n-io/n8n-sandbox-service-api:pull' | jq -er '.token')"
|
||||
stable_digest="$(docker buildx imagetools inspect \
|
||||
'ghcr.io/n8n-io/n8n-sandbox-service-api:stable' \
|
||||
--format '{{json .Manifest}}' | jq -er '.digest')"
|
||||
sandbox_version=''
|
||||
while IFS= read -r version; do
|
||||
version_digest="$(docker buildx imagetools inspect \
|
||||
"ghcr.io/n8n-io/n8n-sandbox-service-api:${version}" \
|
||||
--format '{{json .Manifest}}' | jq -er '.digest')"
|
||||
if [ "$version_digest" = "$stable_digest" ]; then
|
||||
sandbox_version="$version"
|
||||
break
|
||||
fi
|
||||
done < <(curl -fsSL --retry 3 \
|
||||
-H "Authorization: Bearer $registry_token" \
|
||||
'https://ghcr.io/v2/n8n-io/n8n-sandbox-service-api/tags/list?n=1000' |
|
||||
jq -r '.tags[]' |
|
||||
grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' |
|
||||
sort -Vr)
|
||||
|
||||
printf '%s\n' "$n8n_version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'
|
||||
printf '%s\n' "$sandbox_version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'
|
||||
|
||||
for image in api runner-dind sandbox; do
|
||||
docker manifest inspect "ghcr.io/n8n-io/n8n-sandbox-service-${image}:${sandbox_version}" >/dev/null
|
||||
done
|
||||
|
||||
sed -i "s/^FALLBACK_N8N_VERSION=.*/FALLBACK_N8N_VERSION=\"${n8n_version}\"/" docker/get-n8n.sh
|
||||
sed -i "s/^SANDBOX_VERSION=.*/SANDBOX_VERSION=\"${sandbox_version}\"/" docker/get-n8n.sh
|
||||
|
||||
echo "n8n_version=$n8n_version" >> "$GITHUB_OUTPUT"
|
||||
echo "sandbox_version=$sandbox_version" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check for changes
|
||||
id: changes
|
||||
run: |
|
||||
if git diff --quiet -- docker/get-n8n.sh; then
|
||||
echo 'has-changes=false' >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo 'has-changes=true' >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Test installer upgrade
|
||||
if: steps.changes.outputs.has-changes == 'true'
|
||||
run: |
|
||||
shellcheck docker/get-n8n.sh docker/test-get-n8n.sh
|
||||
dash -n docker/get-n8n.sh
|
||||
dash -n docker/test-get-n8n.sh
|
||||
sh docker/test-get-n8n.sh --e2e
|
||||
|
||||
- name: Create pull request
|
||||
if: steps.changes.outputs.has-changes == 'true'
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
add-paths: docker/get-n8n.sh
|
||||
commit-message: 'chore: Update get-n8n fallback versions'
|
||||
labels: 'automation:scheduled-update'
|
||||
title: 'chore: Update get-n8n fallback versions'
|
||||
body: |
|
||||
This automated PR updates the get-n8n fallback release to ${{ steps.versions.outputs.n8n_version }}.
|
||||
It also updates the sandbox service release to ${{ steps.versions.outputs.sandbox_version }}.
|
||||
|
||||
The workflow verified that all sandbox images use the same release. It also upgraded a legacy installation to these versions and checked the running stack.
|
||||
|
||||
_Generated by the weekly get-n8n version update workflow._
|
||||
branch: update-get-n8n-versions
|
||||
base: master
|
||||
delete-branch: true
|
||||
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
||||
committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
||||
|
|
@ -14,6 +14,11 @@ frontend, and extensible node-based workflow engine.
|
|||
- Write all technical text (code comments, PR descriptions, issue and ticket
|
||||
descriptions, docs) in ASD-STE100 Simplified Technical English: use short
|
||||
sentences, the active voice, and one instruction for each sentence
|
||||
- **JSDoc on `@Env(...)` config properties is public documentation.** The
|
||||
docs team exports these comments into the environment variable reference on
|
||||
docs.n8n.io. Write them for a self-hosting operator and follow the
|
||||
`n8n:env-var-docs` skill, which applies the n8n docs style guide and
|
||||
overrides the ASD-STE100 rule above for these comments
|
||||
- **Secrets on the command line:** if a developer opted into anonymous dev
|
||||
metrics (`scripts/dev-metrics`), pnpm command arguments are recorded. Arguments
|
||||
of secret-carrying words (`config`, `login`, `publish`, `token`) — whether a
|
||||
|
|
|
|||
155
CHANGELOG.md
155
CHANGELOG.md
|
|
@ -1,3 +1,158 @@
|
|||
# [2.44.0](https://github.com/n8n-io/n8n/compare/n8n@2.43.0...n8n@2.44.0) (2026-10-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Align execution retry menu and clarify actions ([#40710](https://github.com/n8n-io/n8n/issues/40710)) ([84d2fd6](https://github.com/n8n-io/n8n/commit/84d2fd6f3ae68e973aa49f7e2b1dd5dbf06ad18c))
|
||||
* Allow per-process crash journal paths ([#37670](https://github.com/n8n-io/n8n/issues/37670)) ([447e939](https://github.com/n8n-io/n8n/commit/447e939b732b778662572098d81f014527e3ffe2))
|
||||
* **core:** Allow reverting a workflow version that uses credentials the user cannot use ([#40701](https://github.com/n8n-io/n8n/issues/40701)) ([033a9b5](https://github.com/n8n-io/n8n/commit/033a9b512c2d52d05b43d4dd2d0e10634dc36e2a))
|
||||
* **core:** Apply node type policies to agent node tools ([#39711](https://github.com/n8n-io/n8n/issues/39711)) ([e7d8533](https://github.com/n8n-io/n8n/commit/e7d8533880df0d36438e39a07f0594fcaaaaab55))
|
||||
* **core:** Apply the credential type policy to credential tests and probes ([#39927](https://github.com/n8n-io/n8n/issues/39927)) ([9191cf3](https://github.com/n8n-io/n8n/commit/9191cf3fb07ce6bbb7b2217592ce56372b52f6c2))
|
||||
* **core:** Block the assistant when AI credits hit zero ([#40659](https://github.com/n8n-io/n8n/issues/40659)) ([8b7065a](https://github.com/n8n-io/n8n/commit/8b7065a87684bb3a055d8bcfc5d34c9b70094080))
|
||||
* **core:** Check the credential save policy before package import creates stubs ([#40687](https://github.com/n8n-io/n8n/issues/40687)) ([a3391ba](https://github.com/n8n-io/n8n/commit/a3391ba93c4574405d06e79e2efc791b6ba8a21f))
|
||||
* **core:** Check the target project's policy on folder, credential and delete-transfer moves ([#40301](https://github.com/n8n-io/n8n/issues/40301)) ([77370af](https://github.com/n8n-io/n8n/commit/77370af99a273073faf7df57a1fe8f71d3833bbe))
|
||||
* **core:** Count jobs as running from the start of job processing ([#40050](https://github.com/n8n-io/n8n/issues/40050)) ([dc5c80f](https://github.com/n8n-io/n8n/commit/dc5c80f26af8d32e5887ccbf7634f3da1be5f771))
|
||||
* **core:** Expose 1Password fields for secret preview and autocomplete ([#40538](https://github.com/n8n-io/n8n/issues/40538)) ([bef5f3f](https://github.com/n8n-io/n8n/commit/bef5f3f97aca163ac289af61e10869c0fa7fff60))
|
||||
* **core:** Fix Kimi K3 and other OpenAI-compatible Assistant model providers on self-hosted Assistant ([#40439](https://github.com/n8n-io/n8n/issues/40439)) ([1ef7756](https://github.com/n8n-io/n8n/commit/1ef77564e3bef2e34c57d8a67df892ace3858a5d))
|
||||
* **core:** Handle each Chat Hub segment once in queue mode ([#40791](https://github.com/n8n-io/n8n/issues/40791)) ([9ff8d4f](https://github.com/n8n-io/n8n/commit/9ff8d4fc738ca0e54db863c574e5a6d93df9ce2e))
|
||||
* **core:** Improve node placement when n8n Assistant edits a workflow ([#40139](https://github.com/n8n-io/n8n/issues/40139)) ([d900743](https://github.com/n8n-io/n8n/commit/d900743bce0cf8274c42e1f4a579fc8b5cf74997))
|
||||
* **core:** Keep waiting executions unchanged when a resume request fails ([#40683](https://github.com/n8n-io/n8n/issues/40683)) ([bbd35e2](https://github.com/n8n-io/n8n/commit/bbd35e281b100a36aa209518c5ab027eabf85a1f))
|
||||
* **core:** Let the editor download binary data of an Engine v2 execution ([#40663](https://github.com/n8n-io/n8n/issues/40663)) ([c9cb3ee](https://github.com/n8n-io/n8n/commit/c9cb3ee3abd23fe146cd4a2daed49c4bddb80d97))
|
||||
* **core:** Let the streaming heartbeat stop on Chat Hub responses ([#40792](https://github.com/n8n-io/n8n/issues/40792)) ([ee8a608](https://github.com/n8n-io/n8n/commit/ee8a608a22ec7a23dc795ee0554edd34186e87d2))
|
||||
* **core:** Match regex case-insensitively when ignore case is enabled ([#33589](https://github.com/n8n-io/n8n/issues/33589)) ([2596f3a](https://github.com/n8n-io/n8n/commit/2596f3ac1c657aa4d2ddf40430b9e5940fb9c1b0))
|
||||
* **core:** Match the Brazilian real symbol in the in-isolate currency regex ([#40893](https://github.com/n8n-io/n8n/issues/40893)) ([d3deffb](https://github.com/n8n-io/n8n/commit/d3deffbfa59d358614458f8b1ae2d56d118a9ca5))
|
||||
* **core:** Pass `storedAt` into `WorkflowExecute` at every callsite ([#40495](https://github.com/n8n-io/n8n/issues/40495)) ([5ab2187](https://github.com/n8n-io/n8n/commit/5ab21874169d44eb01a19d6f0ba19bb7a254d952))
|
||||
* **core:** Remove credentials of deleted projects on source control pull ([#40692](https://github.com/n8n-io/n8n/issues/40692)) ([978f810](https://github.com/n8n-io/n8n/commit/978f810bbef9e284b564db4897be7085257064b1))
|
||||
* **core:** Report approval tools with no tool connected in workflow validation ([#40561](https://github.com/n8n-io/n8n/issues/40561)) ([bc0a9cb](https://github.com/n8n-io/n8n/commit/bc0a9cbc112aaed2853f14c32157082f41ba42b4))
|
||||
* **core:** Report MCP server connection failures in verify_mcp_server ([#40559](https://github.com/n8n-io/n8n/issues/40559)) ([fbbbadc](https://github.com/n8n-io/n8n/commit/fbbbadc292791112e30101edee229e9d5f4dbc02))
|
||||
* **core:** Require workflow create scope for single node runs in n8n Assistant ([#40908](https://github.com/n8n-io/n8n/issues/40908)) ([27fcb96](https://github.com/n8n-io/n8n/commit/27fcb968fb711a9957eed48fa78c5adfa78fc607))
|
||||
* **core:** Resume tool calls for deferred tools that a skill loaded ([#40126](https://github.com/n8n-io/n8n/issues/40126)) ([1c84af2](https://github.com/n8n-io/n8n/commit/1c84af2e96bda41cd84852af82e5cae5b823f0f8))
|
||||
* **core:** Return jobs dequeued during worker shutdown to the queue ([#40086](https://github.com/n8n-io/n8n/issues/40086)) ([084f3b3](https://github.com/n8n-io/n8n/commit/084f3b376a0d64594320853bae90f13537d3b8c9))
|
||||
* **core:** Return jobs fetched before a worker paused to the queue ([#40162](https://github.com/n8n-io/n8n/issues/40162)) ([f26747b](https://github.com/n8n-io/n8n/commit/f26747bfc665ce0c137b0b7645245b1a97d1d01b))
|
||||
* **core:** Save agent draft definitions atomically ([#40374](https://github.com/n8n-io/n8n/issues/40374)) ([9ee5d1a](https://github.com/n8n-io/n8n/commit/9ee5d1ad7a54cde445ac945fc56a7ba5a4ee4707))
|
||||
* **core:** Serve the migration report on every main in a multi-main setup ([#40180](https://github.com/n8n-io/n8n/issues/40180)) ([a6f1e79](https://github.com/n8n-io/n8n/commit/a6f1e79394adced5958219f812e8ca14a10b9962))
|
||||
* **core:** Show the policy block before node issues on execute and publish ([#40290](https://github.com/n8n-io/n8n/issues/40290)) ([0bbea43](https://github.com/n8n-io/n8n/commit/0bbea438e1906a0773cdbb46e273b4c01fea359b))
|
||||
* **core:** Stabilise background child approval wait ([#40834](https://github.com/n8n-io/n8n/issues/40834)) ([2ca4e55](https://github.com/n8n-io/n8n/commit/2ca4e55502e03985d2c9b1b0033b6f5151ccb8f4))
|
||||
* **core:** Start a new test session when call_agent gets an unknown session ID ([#40458](https://github.com/n8n-io/n8n/issues/40458)) ([e16566b](https://github.com/n8n-io/n8n/commit/e16566b31324991a369708b7eb1f46027f80445b))
|
||||
* **core:** Stop marking the Message an Agent node as hidden ([#40358](https://github.com/n8n-io/n8n/issues/40358)) ([3366d2b](https://github.com/n8n-io/n8n/commit/3366d2b9b3151a769116a66499668c3a36cf65a4))
|
||||
* **core:** Validate execution timeout against the configured maximum in Instance AI saves ([#40769](https://github.com/n8n-io/n8n/issues/40769)) ([2176819](https://github.com/n8n-io/n8n/commit/2176819865a03e9247dac0a61177572ac4f7711c))
|
||||
* **core:** Validate the size argument of the chunk() expression extension ([#40880](https://github.com/n8n-io/n8n/issues/40880)) ([3ff9c83](https://github.com/n8n-io/n8n/commit/3ff9c83bdaf873509e4470f97e26919e0653add8))
|
||||
* **core:** Wire Entra OAuth2 into agent Azure OpenAI runtime ([#40218](https://github.com/n8n-io/n8n/issues/40218)) ([96a8ff4](https://github.com/n8n-io/n8n/commit/96a8ff4d15baa2ab15debc8ea02da81847f5a504))
|
||||
* **Databricks Node:** Request float encoding for embeddings from the Unity AI Gateway ([#40684](https://github.com/n8n-io/n8n/issues/40684)) ([4c0902f](https://github.com/n8n-io/n8n/commit/4c0902f6bf4a0aa6634d5598e75fc677aa55f429))
|
||||
* **editor:** Add resend to retryable Agent preview errors ([#40592](https://github.com/n8n-io/n8n/issues/40592)) ([f7e52a8](https://github.com/n8n-io/n8n/commit/f7e52a851ed9416615167826daf706d90a19d53b))
|
||||
* **editor:** Align nodes panel to the embedded canvas in the n8n Assistant split view ([#40795](https://github.com/n8n-io/n8n/issues/40795)) ([37799ad](https://github.com/n8n-io/n8n/commit/37799ad2f21067ec7951c2656c9e938dc87443e1))
|
||||
* **editor:** Align tooltip trigger box with its content ([#40422](https://github.com/n8n-io/n8n/issues/40422)) ([a694531](https://github.com/n8n-io/n8n/commit/a694531a0367bc6b7991b194ec762e7ad51aaa0a))
|
||||
* **editor:** Keep Agent Builder settings inside a narrow column ([#40759](https://github.com/n8n-io/n8n/issues/40759)) ([e2b4fee](https://github.com/n8n-io/n8n/commit/e2b4fee0ec0f0f34245664c23869153089362236))
|
||||
* **editor:** Keep Assistant fixes in Agent builder and correct tooltips ([#40348](https://github.com/n8n-io/n8n/issues/40348)) ([a13705a](https://github.com/n8n-io/n8n/commit/a13705aa8fff5a170ba5b069946e205e9acc1985))
|
||||
* **editor:** Keep folder creation available globally after Community registration ([#40082](https://github.com/n8n-io/n8n/issues/40082)) ([e1025f3](https://github.com/n8n-io/n8n/commit/e1025f35e4203b49436aedb89dcb679e96169528))
|
||||
* **editor:** Keep same-named parameter values apart when their types differ ([#40170](https://github.com/n8n-io/n8n/issues/40170)) ([6013045](https://github.com/n8n-io/n8n/commit/601304543d2b87e6aa42211c3a8c6c2766c2c61d))
|
||||
* **editor:** Keep the prompt visible while the assistant starts a new thread ([#40796](https://github.com/n8n-io/n8n/issues/40796)) ([be402be](https://github.com/n8n-io/n8n/commit/be402be262974d46f79ba3464e20efc0ee734ac4))
|
||||
* **editor:** Keep the selected credential visible when the picker does not list it ([#40450](https://github.com/n8n-io/n8n/issues/40450)) ([66b382d](https://github.com/n8n-io/n8n/commit/66b382de52fb56db87987910929e932292837305))
|
||||
* **editor:** Keep the workflow setup step counter on one line ([#40771](https://github.com/n8n-io/n8n/issues/40771)) ([55eb2ce](https://github.com/n8n-io/n8n/commit/55eb2ce44291648adf6ca05b5b1605efea8d77f7))
|
||||
* **editor:** Keep typed characters in Markdown fields ([#40805](https://github.com/n8n-io/n8n/issues/40805)) ([16e29da](https://github.com/n8n-io/n8n/commit/16e29da52f3173ee5edc7b96697a4c227b4983d0))
|
||||
* **editor:** Load node types before authenticated pages render ([#40812](https://github.com/n8n-io/n8n/issues/40812)) ([8297b2e](https://github.com/n8n-io/n8n/commit/8297b2e6d263d63571926545bf8b5c490f5125bf))
|
||||
* **editor:** Place the restricted node popover per host, keep one open, and stack the admin dialog ([#40678](https://github.com/n8n-io/n8n/issues/40678)) ([b28dceb](https://github.com/n8n-io/n8n/commit/b28dceb462d8e51855d4e13a87e9c975638ef977))
|
||||
* **editor:** Polish n8n Assistant and Execute button UI details ([#40414](https://github.com/n8n-io/n8n/issues/40414)) ([76cab1e](https://github.com/n8n-io/n8n/commit/76cab1e26d97cbbf50790926f1bbb14cfb12d078))
|
||||
* **editor:** Preserve Live Data highlights on scroll in expression editor ([#33842](https://github.com/n8n-io/n8n/issues/33842)) ([0f02b50](https://github.com/n8n-io/n8n/commit/0f02b509be304523955e7700c7c9eeefe58d0aa9))
|
||||
* **editor:** Remove redundant description for create workflow option in agent config ([#40882](https://github.com/n8n-io/n8n/issues/40882)) ([b542f66](https://github.com/n8n-io/n8n/commit/b542f6611998c23b40b54d25d1e923841ee24160))
|
||||
* **editor:** Rename the "Executions fail" label to "Executions will fail" ([#40888](https://github.com/n8n-io/n8n/issues/40888)) ([2a51f38](https://github.com/n8n-io/n8n/commit/2a51f38d55f37f65382ebf3900a340fc62b15314))
|
||||
* **editor:** Respect the default voice for chat read aloud ([#40440](https://github.com/n8n-io/n8n/issues/40440)) ([022b239](https://github.com/n8n-io/n8n/commit/022b23945ba576024685bfe1702489f427a3c9fe))
|
||||
* **editor:** Shorten the share button label on the credential sharing tab ([#40637](https://github.com/n8n-io/n8n/issues/40637)) ([c133d6c](https://github.com/n8n-io/n8n/commit/c133d6c893a879c907d20bb71a10817d358db553))
|
||||
* **editor:** Show the Agent artifact while the builder creates it ([#40535](https://github.com/n8n-io/n8n/issues/40535)) ([1c63119](https://github.com/n8n-io/n8n/commit/1c631197563b8f65d343d060355050c4551f381d))
|
||||
* **editor:** Show the owner and credential type for credentials the user cannot use ([#40669](https://github.com/n8n-io/n8n/issues/40669)) ([63dc285](https://github.com/n8n-io/n8n/commit/63dc2854936f3e282ca422cd9b9babeab65a2375))
|
||||
* **editor:** Show the renamed agent name in the assistant artifact tab ([#40770](https://github.com/n8n-io/n8n/issues/40770)) ([4032797](https://github.com/n8n-io/n8n/commit/40327977903613fee5d033efd31c6e01416a2bf0))
|
||||
* **editor:** Show the unavailable credential at the top of the node picker ([#40693](https://github.com/n8n-io/n8n/issues/40693)) ([1933723](https://github.com/n8n-io/n8n/commit/19337237ec59bd03c89e300a23289d2bdfbf269e))
|
||||
* **editor:** Show why a node on another branch can't be referenced ([#40565](https://github.com/n8n-io/n8n/issues/40565)) ([a195096](https://github.com/n8n-io/n8n/commit/a19509673336fbbf6687b107ce0af5bcfec68393))
|
||||
* **editor:** Small UI fixes to budget guardrail setup components ([#40383](https://github.com/n8n-io/n8n/issues/40383)) ([958d23d](https://github.com/n8n-io/n8n/commit/958d23d81cd99d7b9fd63d2484b79382c2d20c23))
|
||||
* **editor:** Use pulsing status dot for the enabled n8n Assistant setting ([#40712](https://github.com/n8n-io/n8n/issues/40712)) ([3ba5229](https://github.com/n8n-io/n8n/commit/3ba522910b1cc3009c108a202634388fd838717f))
|
||||
* **Google Gemini Chat Model Node:** Point the AI builder to the model catalog for new Gemini models ([#40159](https://github.com/n8n-io/n8n/issues/40159)) ([c21ed4f](https://github.com/n8n-io/n8n/commit/c21ed4f13aee6a1551207a92122bea2859e1b8d3))
|
||||
* **Invoice Ninja Node:** Fix retrieving currencies for bank transactions ([#12997](https://github.com/n8n-io/n8n/issues/12997)) ([00849c4](https://github.com/n8n-io/n8n/commit/00849c4deabada87641c146773c0981e873a6328))
|
||||
* Keep execution details clear of preview canvas ([#40176](https://github.com/n8n-io/n8n/issues/40176)) ([af98327](https://github.com/n8n-io/n8n/commit/af98327a5e4f2332b78b4d460e6eb208f0782d28))
|
||||
* Load only the publish history that the editor shows ([#40183](https://github.com/n8n-io/n8n/issues/40183)) ([b28adf9](https://github.com/n8n-io/n8n/commit/b28adf97895bbdba2e0c777bdfb5cd11d04c0e56))
|
||||
* **Microsoft Outlook Trigger Node:** Report failed polls and limit the backlog per poll ([#40147](https://github.com/n8n-io/n8n/issues/40147)) ([6bc5c93](https://github.com/n8n-io/n8n/commit/6bc5c93513efbe4d236c5794cfd07e1ac4d916f6))
|
||||
* Multiple migration timestamps withing a single PR ([#40379](https://github.com/n8n-io/n8n/issues/40379)) ([7db7fa4](https://github.com/n8n-io/n8n/commit/7db7fa42c80a8a24fb5ca5ec7448ffb12431fad9))
|
||||
* **Odoo Node:** Normalize datetime fields to Odoo format before write ([#34186](https://github.com/n8n-io/n8n/issues/34186)) ([6b77613](https://github.com/n8n-io/n8n/commit/6b77613ee827560d9f34f551478a74daf4848dec))
|
||||
* Prevent node types corruption when multiple loaders run at the same time ([#40633](https://github.com/n8n-io/n8n/issues/40633)) ([fdc3948](https://github.com/n8n-io/n8n/commit/fdc394819cecf96379ed7ba475364912099a6296))
|
||||
* Remove loading flashes from data tables ([#39874](https://github.com/n8n-io/n8n/issues/39874)) ([2f4a386](https://github.com/n8n-io/n8n/commit/2f4a3868fbd74c07e91888eb35b1f3d4bcb2acb1))
|
||||
* Report resource-operation violations as errors ([#40830](https://github.com/n8n-io/n8n/issues/40830)) ([a180010](https://github.com/n8n-io/n8n/commit/a1800105ef8f7c229ffd044e1233d273e95b6cd2))
|
||||
* Return UTC ISO dates without changing other cells ([#40156](https://github.com/n8n-io/n8n/issues/40156)) ([8059b63](https://github.com/n8n-io/n8n/commit/8059b63da89ba89309cbc6eacce715110c9c5748))
|
||||
* **S3 Node:** Percent-encode the copy source path ([#40282](https://github.com/n8n-io/n8n/issues/40282)) ([81f34c2](https://github.com/n8n-io/n8n/commit/81f34c23744424d17ca99eaf03beb9861c21a1ca))
|
||||
* Separate agent response steps and show saved reasoning ([#40590](https://github.com/n8n-io/n8n/issues/40590)) ([1e6da4e](https://github.com/n8n-io/n8n/commit/1e6da4e615900d7665e6eeafe9ce6002d1510c31))
|
||||
* Update matching nodes when selecting imported credentials ([#39650](https://github.com/n8n-io/n8n/issues/39650)) ([4b5b68e](https://github.com/n8n-io/n8n/commit/4b5b68ee2929344288b93469efe732379e3904a1))
|
||||
* Use configured OAuth server rate limits ([#40542](https://github.com/n8n-io/n8n/issues/40542)) ([4c8b76b](https://github.com/n8n-io/n8n/commit/4c8b76b6f08d090bf6e7cf22f66d60ec3d0efdc6))
|
||||
* Use ID token claims when UserInfo is unavailable ([#40779](https://github.com/n8n-io/n8n/issues/40779)) ([b8b0387](https://github.com/n8n-io/n8n/commit/b8b0387a31b556e296529235f9c4a75b307bb8f1))
|
||||
* Validate and default public API pagination limits ([#40483](https://github.com/n8n-io/n8n/issues/40483)) ([f212d5b](https://github.com/n8n-io/n8n/commit/f212d5b900fe92c3012001b32f4cf17bd831921e))
|
||||
* Wait for MockServer HTTP status without following logs ([#40846](https://github.com/n8n-io/n8n/issues/40846)) ([4593838](https://github.com/n8n-io/n8n/commit/45938389b86b6bfe25a8a8f3d132b600ee807f58))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add Antigravity to the MCP client connection dialog ([#40879](https://github.com/n8n-io/n8n/issues/40879)) ([f0a38cf](https://github.com/n8n-io/n8n/commit/f0a38cfe5801ba8d62b97f6d4ffeb031f23aa231))
|
||||
* Add Google Vertex AI credentials ([#40665](https://github.com/n8n-io/n8n/issues/40665)) ([a7cba90](https://github.com/n8n-io/n8n/commit/a7cba90a854ea34198dd9c1fcb4f7eeae08c364d))
|
||||
* **ai-builder:** Reopen closed AI Assistant tabs when the agent changes the artifact ([#40081](https://github.com/n8n-io/n8n/issues/40081)) ([5d38667](https://github.com/n8n-io/n8n/commit/5d3866775c828e766ca16642d5ae2a340d4133ca))
|
||||
* **Azure AI Foundry Chat Model Node:** Speak the Anthropic Messages schema for Claude deployments ([#40306](https://github.com/n8n-io/n8n/issues/40306)) ([c134edf](https://github.com/n8n-io/n8n/commit/c134edf473276ff63387d28aea0d08c9ff676f7c))
|
||||
* **Azure OpenAI Chat Model Node:** Rename the Foundry nodes and credentials to Microsoft Foundry ([#40398](https://github.com/n8n-io/n8n/issues/40398)) ([e5c8a8b](https://github.com/n8n-io/n8n/commit/e5c8a8bb62d2ce468d1cda3965562eeaa69996f8))
|
||||
* Configure workflow tool inputs for Agents ([#40285](https://github.com/n8n-io/n8n/issues/40285)) ([4cb98ae](https://github.com/n8n-io/n8n/commit/4cb98aea9fbcede8ae414c3f817c7b0490d401f8))
|
||||
* **core:** Add credentialTransfer policy enforcement point ([#40300](https://github.com/n8n-io/n8n/issues/40300)) ([c2f6920](https://github.com/n8n-io/n8n/commit/c2f6920794e66e7108b0005233506f7694a11a7e))
|
||||
* **core:** Add execution identity to every OTel span ([#40649](https://github.com/n8n-io/n8n/issues/40649)) ([22ba4e7](https://github.com/n8n-io/n8n/commit/22ba4e7bc5c6fbe5243ff5c227c718ab7197c5e1))
|
||||
* **core:** Add overwrite schema conflict policy to data table package import ([#40345](https://github.com/n8n-io/n8n/issues/40345)) ([3af9817](https://github.com/n8n-io/n8n/commit/3af98170d0d98a201df1a45f2ecee17f5f00f298))
|
||||
* **core:** Add overwrite-non-destructive schema conflict policy to data table package import ([#40446](https://github.com/n8n-io/n8n/issues/40446)) ([dc33c29](https://github.com/n8n-io/n8n/commit/dc33c29d1af3f47a6179a76ff21ccc7bd1f75357))
|
||||
* **core:** Add Prometheus metrics for database connection recovery ([#39890](https://github.com/n8n-io/n8n/issues/39890)) ([d904e7a](https://github.com/n8n-io/n8n/commit/d904e7a2476794acdf3928faacd86ae9b3e45220))
|
||||
* **core:** Add the WhatsApp agent channel ([#38750](https://github.com/n8n-io/n8n/issues/38750)) ([b0d1a30](https://github.com/n8n-io/n8n/commit/b0d1a30f50fdf6b3720c2a5773e28efff5c5b0b5))
|
||||
* **core:** Add workflow suggestion review actions ([#39858](https://github.com/n8n-io/n8n/issues/39858)) ([eaf8c25](https://github.com/n8n-io/n8n/commit/eaf8c25d1c2b680fe55f58fc9f6d10647e6ae640))
|
||||
* **core:** Allow confirming destructive data table changes when continuing an apply ([#40491](https://github.com/n8n-io/n8n/issues/40491)) ([9946cfe](https://github.com/n8n-io/n8n/commit/9946cfe901b538ec5ca29e85cde3ec2044d33d00))
|
||||
* **core:** Apply the member credential rule to Owners and admins in team projects ([#40448](https://github.com/n8n-io/n8n/issues/40448)) ([3cff8dc](https://github.com/n8n-io/n8n/commit/3cff8dc4ecac5fd173d198f3e319ab473cdd7b22))
|
||||
* **core:** Block create/update of workflows with deprecated nodes ([#31360](https://github.com/n8n-io/n8n/issues/31360)) ([ea8417e](https://github.com/n8n-io/n8n/commit/ea8417e29ad6f8b981d9a04b17283d5d77f2b5bb))
|
||||
* **core:** Email the agent owner when a monthly budget alert is crossed ([#40544](https://github.com/n8n-io/n8n/issues/40544)) ([19e1f70](https://github.com/n8n-io/n8n/commit/19e1f70ea62ad151abe18b5a25721caa35d7bedd))
|
||||
* **core:** Emit opt-in start marker spans for workflow and node executions ([#40762](https://github.com/n8n-io/n8n/issues/40762)) ([c713545](https://github.com/n8n-io/n8n/commit/c7135451ebd552267aedf4898d845c53a97aab54))
|
||||
* **core:** Experiment to show assistant composer and starter templates in agent list ([#40396](https://github.com/n8n-io/n8n/issues/40396)) ([fe26ba8](https://github.com/n8n-io/n8n/commit/fe26ba8d3a63963d6c7868338e73fb77378e94a9))
|
||||
* **core:** Handle inbound media handling for whatsapp ([#39409](https://github.com/n8n-io/n8n/issues/39409)) ([50c2ca0](https://github.com/n8n-io/n8n/commit/50c2ca0a27189136d82339bdafd5d8c7513a9916))
|
||||
* **core:** Handle message quality rate limits for whatsapp integration ([#39205](https://github.com/n8n-io/n8n/issues/39205)) ([3c33856](https://github.com/n8n-io/n8n/commit/3c33856997b084a3a87038c0d72f80d9b91af1b7))
|
||||
* **core:** Hide credential-only nodes under a credential type policy ([#39652](https://github.com/n8n-io/n8n/issues/39652)) ([370b30f](https://github.com/n8n-io/n8n/commit/370b30f9195ba8d5ceaf75fb4cdfcf127105a3c6))
|
||||
* **core:** Pair data table columns by id on package import ([#40709](https://github.com/n8n-io/n8n/issues/40709)) ([27b43de](https://github.com/n8n-io/n8n/commit/27b43de85f987ddd783e7c7ccbde7977d09a6d03))
|
||||
* **core:** Persist self-healing results and handoffs ([#40115](https://github.com/n8n-io/n8n/issues/40115)) ([168a0ac](https://github.com/n8n-io/n8n/commit/168a0ac86056afd743bfde63863abbab4e8515f4))
|
||||
* **core:** Post a budget-stop message on external agent channels ([#40767](https://github.com/n8n-io/n8n/issues/40767)) ([5e01906](https://github.com/n8n-io/n8n/commit/5e01906a1e6c177c0ce02d18317ce2d7b6ee7e97))
|
||||
* **core:** Promote folder and project renames alongside selected workflows ([#40502](https://github.com/n8n-io/n8n/issues/40502)) ([a249e20](https://github.com/n8n-io/n8n/commit/a249e202905f30829fe3a7eacd2eba8dff6573ef))
|
||||
* **core:** Report crashed executions from startup log recovery ([#40390](https://github.com/n8n-io/n8n/issues/40390)) ([668e71d](https://github.com/n8n-io/n8n/commit/668e71d54a26da7c12d092a7375a7cb3349a8471))
|
||||
* **core:** Report destructive data table changes in the promotion apply preflight ([#40454](https://github.com/n8n-io/n8n/issues/40454)) ([5d208c8](https://github.com/n8n-io/n8n/commit/5d208c85ed275c22cc5597124b7a9cf590d18f11))
|
||||
* **core:** Report the deployment artifact in telemetry ([#40319](https://github.com/n8n-io/n8n/issues/40319)) ([344dea5](https://github.com/n8n-io/n8n/commit/344dea5c77fc5f61ce10171a2553faa9bef4e609))
|
||||
* **core:** Run the workflow history trim as a durable daily system task ([#40085](https://github.com/n8n-io/n8n/issues/40085)) ([38eea65](https://github.com/n8n-io/n8n/commit/38eea65993339ea3e8156cdbacef247749331351))
|
||||
* **core:** Search, create and reopen agents from the command bar ([#40504](https://github.com/n8n-io/n8n/issues/40504)) ([75a9cc7](https://github.com/n8n-io/n8n/commit/75a9cc7e1219d35b441b02c20446dd89db186ff7))
|
||||
* **core:** Serve Prometheus metrics while the database is unavailable ([#39908](https://github.com/n8n-io/n8n/issues/39908)) ([4e217ae](https://github.com/n8n-io/n8n/commit/4e217aeac6b029692fb8a692a3849ae8e5310b5a))
|
||||
* **core:** Serve the instance CSP on all non-script responses ([#40205](https://github.com/n8n-io/n8n/issues/40205)) ([cabf0bf](https://github.com/n8n-io/n8n/commit/cabf0bf3b466a725deada3b532347b7db530dcac))
|
||||
* **core:** Show every user the migration report, scoped to the workflows they can edit ([#40449](https://github.com/n8n-io/n8n/issues/40449)) ([f4f7dcf](https://github.com/n8n-io/n8n/commit/f4f7dcf13b6d93d46a2860e2c2577f9f6dda4c52))
|
||||
* **core:** Show the agent chat attach button on the left of the composer ([#40320](https://github.com/n8n-io/n8n/issues/40320)) ([209adf9](https://github.com/n8n-io/n8n/commit/209adf9a7ee9e71cfe3d5119316ca9fce4b3ac68))
|
||||
* **Databricks Node:** Guard the Vector Store insert against the wrong index ([#39699](https://github.com/n8n-io/n8n/issues/39699)) ([0932e95](https://github.com/n8n-io/n8n/commit/0932e9572e2650165b260fc591d1f02204c17aae))
|
||||
* **editor:** Add a state column to the migration rule detail ([#40388](https://github.com/n8n-io/n8n/issues/40388)) ([682d092](https://github.com/n8n-io/n8n/commit/682d0920ba5cb2ab5e3ac0671f993c8e529e8389))
|
||||
* **editor:** Add agent setup checklist ([#39919](https://github.com/n8n-io/n8n/issues/39919)) ([883ade4](https://github.com/n8n-io/n8n/commit/883ade4b9d2505f359057eb10c8d71ab153033a7))
|
||||
* **editor:** Add order to settings sidebar links ([#40505](https://github.com/n8n-io/n8n/issues/40505)) ([cef87cf](https://github.com/n8n-io/n8n/commit/cef87cfcab3f59a657c8c27309f4807bd2d4a93a))
|
||||
* **editor:** Add project selection to Vertex AI credentials ([#40667](https://github.com/n8n-io/n8n/issues/40667)) ([cdc23c6](https://github.com/n8n-io/n8n/commit/cdc23c666ffd6ed55f019aa24c99efdaf10fad14))
|
||||
* **editor:** Animate the n8n Assistant activity indicator ([#40497](https://github.com/n8n-io/n8n/issues/40497)) ([04e1e7f](https://github.com/n8n-io/n8n/commit/04e1e7fc4771ff1371ac41f1d74aef7ced26b175))
|
||||
* **editor:** Disable restricted model providers in the agent model selector ([#40658](https://github.com/n8n-io/n8n/issues/40658)) ([cde7a9d](https://github.com/n8n-io/n8n/commit/cde7a9dbd985ae9e83de2d39dccc508a41d9a676))
|
||||
* **editor:** Explain a restricted model provider with the restriction popover ([#40705](https://github.com/n8n-io/n8n/issues/40705)) ([75de193](https://github.com/n8n-io/n8n/commit/75de193d9090d160dd191667adb85dc423951045))
|
||||
* **editor:** Explain instance-role access in the credential sharing "Used in" row ([#40555](https://github.com/n8n-io/n8n/issues/40555)) ([48f05f9](https://github.com/n8n-io/n8n/commit/48f05f92fe9e7bd1c1d220a53a8c17055a17e70c))
|
||||
* **editor:** Rebuild the command bar with type tabs and paged search ([#40461](https://github.com/n8n-io/n8n/issues/40461)) ([e36093b](https://github.com/n8n-io/n8n/commit/e36093b11db0580add10306d27d090a27790f4fb))
|
||||
* **editor:** Rebuild the personal settings page with the design-system settings components ([#40298](https://github.com/n8n-io/n8n/issues/40298)) ([09436b7](https://github.com/n8n-io/n8n/commit/09436b78f9f6f320073f911ca11160ad9a66d94f))
|
||||
* **editor:** Recommend Claude Opus 5.5 and GPT-6 models in AI Assistant setup ([#40660](https://github.com/n8n-io/n8n/issues/40660)) ([1ad3c54](https://github.com/n8n-io/n8n/commit/1ad3c54803569dd83976c055785b51b62555bf44))
|
||||
* **editor:** Replace Windsurf with Devin in the MCP client dialog ([#40912](https://github.com/n8n-io/n8n/issues/40912)) ([e2c4054](https://github.com/n8n-io/n8n/commit/e2c4054cba14a2f1dc29215fc623f6ae42b7a238))
|
||||
* **editor:** Show the restriction callout and lock the agent tool configuration form ([#40657](https://github.com/n8n-io/n8n/issues/40657)) ([01da889](https://github.com/n8n-io/n8n/commit/01da88958d490e5006874cce2b05554347301b9b))
|
||||
* **editor:** Show who created each preference on the Preferences page ([#40868](https://github.com/n8n-io/n8n/issues/40868)) ([22ff2a0](https://github.com/n8n-io/n8n/commit/22ff2a06368b49fa25b0ad1c2cf74640c56a0284))
|
||||
* **editor:** Sort the credential sharing list by people, then projects ([#40478](https://github.com/n8n-io/n8n/issues/40478)) ([02af7e7](https://github.com/n8n-io/n8n/commit/02af7e7659cce85749c719bb6a1b910043357baf))
|
||||
* **Embeddings Azure OpenAI Node:** Add Entra ID authentication ([#39390](https://github.com/n8n-io/n8n/issues/39390)) ([830486a](https://github.com/n8n-io/n8n/commit/830486a851ceb3eeada1eb945e7cd11c05262901))
|
||||
* **Google Gemini Node:** Show Nano Banana 2.1 and 2 Lite in the image model list ([#40596](https://github.com/n8n-io/n8n/issues/40596)) ([97c54f6](https://github.com/n8n-io/n8n/commit/97c54f618c6210bbe1dcfadaac702d40e89a820b))
|
||||
* Make health check timeout configurable ([#39548](https://github.com/n8n-io/n8n/issues/39548)) ([df8605e](https://github.com/n8n-io/n8n/commit/df8605e8750443d66388060212306cd205a4f5d3))
|
||||
* **Microsoft SharePoint Trigger Node:** Add the Microsoft SharePoint Trigger node ([#40055](https://github.com/n8n-io/n8n/issues/40055)) ([7c126fa](https://github.com/n8n-io/n8n/commit/7c126fab32772241473e1f180c6806af4ce189f8))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **core:** Keep agent builder system prompt static for prompt caching ([#40364](https://github.com/n8n-io/n8n/issues/40364)) ([ee1db0b](https://github.com/n8n-io/n8n/commit/ee1db0bc12bebb572afd04e6e53efc30ffa01209))
|
||||
* **core:** Let the agent builder chain config writes without re-reading the config ([#40462](https://github.com/n8n-io/n8n/issues/40462)) ([6b4da93](https://github.com/n8n-io/n8n/commit/6b4da93dcbb909bcf86c03251e3c74033d0dd70f))
|
||||
* **core:** Let the agent builder edit skill instructions in place ([#40447](https://github.com/n8n-io/n8n/issues/40447)) ([1de45a1](https://github.com/n8n-io/n8n/commit/1de45a1a9b0b0db7036cb25e27bffc95a7df41e2))
|
||||
* **core:** Prepare Instance AI skill sandbox in the background ([#40363](https://github.com/n8n-io/n8n/issues/40363)) ([b36c2b5](https://github.com/n8n-io/n8n/commit/b36c2b506b92f391f859ec10067e57d96c8b09c8))
|
||||
* **core:** Send agent builder config writes as objects with changed fields only ([#40666](https://github.com/n8n-io/n8n/issues/40666)) ([2aa3fbf](https://github.com/n8n-io/n8n/commit/2aa3fbf18547cfb6719baabdcd8719fa6d0f53af))
|
||||
|
||||
|
||||
# [2.43.0](https://github.com/n8n-io/n8n/compare/n8n@2.42.0...n8n@2.43.0) (2026-10-06)
|
||||
|
||||
|
||||
|
|
|
|||
16
OWNERS
16
OWNERS
|
|
@ -115,7 +115,7 @@ packages/cli/src/binary-data/ @n8n-io/catalysts
|
|||
packages/cli/src/posthog/ @n8n-io/growth
|
||||
packages/cli/src/modules/data-table/ @n8n-io/knowledge
|
||||
|
||||
packages/cli/src/evaluation.ee/ @n8n-io/ai
|
||||
packages/cli/src/evaluation.ee/ @n8n-io/ai-trust
|
||||
packages/cli/src/chat/ @n8n-io/ai
|
||||
packages/cli/src/tool-generation/ @n8n-io/ai
|
||||
packages/cli/src/modules/workflow-builder/ @n8n-io/ai-assistant
|
||||
|
|
@ -166,7 +166,7 @@ packages/cli/src/services/ai-usage.service.ts @n8n-io/ai-assistant
|
|||
packages/cli/src/services/__tests__/ai-usage.service.test.ts @n8n-io/ai-assistant
|
||||
packages/cli/src/controllers/__tests__/ai.controller.test.ts @n8n-io/ai-assistant
|
||||
packages/cli/src/services/ai-workflow-builder.service.ts @n8n-io/ai-assistant
|
||||
packages/cli/src/services/annotation-tag.service.ee.ts @n8n-io/ai
|
||||
packages/cli/src/services/annotation-tag.service.ee.ts @n8n-io/context
|
||||
packages/cli/src/services/folder.service.ts @n8n-io/context
|
||||
packages/cli/src/services/tag.service.ts @n8n-io/context
|
||||
packages/cli/src/services/cta.service.ts @n8n-io/knowledge
|
||||
|
|
@ -282,3 +282,15 @@ packages/@n8n/db/src/entities/deployment-key.ts @n8n-io/iam required
|
|||
packages/@n8n/db/src/repositories/deployment-key.repository.ts @n8n-io/iam required
|
||||
packages/@n8n/eslint-config/src/configs/encryption-boundary.ts @n8n-io/iam required
|
||||
packages/@n8n/eslint-config/src/rules/no-encryption-guardrail-disable.ts @n8n-io/iam required
|
||||
|
||||
# AI Trust (evaluations)
|
||||
# Placed last so these rules override the broader package rules above.
|
||||
|
||||
packages/@n8n/instance-ai/evaluations/ @n8n-io/ai-trust
|
||||
packages/cli/src/modules/instance-ai/eval/ @n8n-io/ai-trust
|
||||
packages/quality/testing/playwright/tests/evals/instance-ai/ @n8n-io/ai-trust
|
||||
.agents/skills/create-instance-ai-eval/ @n8n-io/ai-trust
|
||||
.agents/skills/create-agent-builder-eval/ @n8n-io/ai-trust
|
||||
packages/cli/src/modules/agent-evals/ @n8n-io/ai-trust
|
||||
packages/@n8n/agents/src/evals/ @n8n-io/ai-trust
|
||||
packages/frontend/editor-ui/src/features/ai/evaluation.ee/ @n8n-io/ai-trust
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"repository": "n8n-io/cat-bot",
|
||||
"releaseTag": "harness-v0.3.0",
|
||||
"version": "0.3.0",
|
||||
"assetName": "n8n-opencode-harness-0.3.0.tgz"
|
||||
"releaseTag": "harness-v0.6.0",
|
||||
"version": "0.6.0",
|
||||
"assetName": "n8n-opencode-harness-0.6.0.tgz"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ set -eu
|
|||
SCRIPT_VERSION="1.4.0"
|
||||
# The version to install is derived from the latest stable GitHub release in
|
||||
# resolve_n8n_version(); this fallback only applies when that lookup fails.
|
||||
FALLBACK_N8N_VERSION="2.32.0"
|
||||
FALLBACK_N8N_VERSION="2.43.4"
|
||||
# Sandbox service release (API, runner and sandbox images ship together).
|
||||
# Pinned here, not resolved at run time: the stack definition must match it.
|
||||
# --upgrade moves existing installs to this pin.
|
||||
SANDBOX_VERSION="1.3.4"
|
||||
SANDBOX_VERSION="1.8.1"
|
||||
N8N_DIR="${N8N_DIR:-./n8n}"
|
||||
N8N_PORT=5678
|
||||
SOURCE_URL="https://github.com/n8n-io/n8n/blob/master/docker/get-n8n.sh"
|
||||
|
|
|
|||
58
docs/generated/postgres-schema/README.md
generated
58
docs/generated/postgres-schema/README.md
generated
|
|
@ -29,10 +29,12 @@ Auto-generated from the PostgreSQL migrations in @n8n/db. Do not edit by hand.
|
|||
| [public.agent_message_queue](public.agent_message_queue.md) | 10 | | BASE TABLE |
|
||||
| [public.agent_plan](public.agent_plan.md) | 8 | | BASE TABLE |
|
||||
| [public.agent_plan_history](public.agent_plan_history.md) | 6 | | BASE TABLE |
|
||||
| [public.agent_skill_dependency](public.agent_skill_dependency.md) | 4 | | BASE TABLE |
|
||||
| [public.agent_task_definition](public.agent_task_definition.md) | 8 | | BASE TABLE |
|
||||
| [public.agent_task_run_lock](public.agent_task_run_lock.md) | 6 | | BASE TABLE |
|
||||
| [public.agent_task_snapshot](public.agent_task_snapshot.md) | 9 | | BASE TABLE |
|
||||
| [public.agent_thread_grants](public.agent_thread_grants.md) | 4 | | BASE TABLE |
|
||||
| [public.agent_version_skill_dependency](public.agent_version_skill_dependency.md) | 4 | | BASE TABLE |
|
||||
| [public.agent_workflow_dependency](public.agent_workflow_dependency.md) | 3 | | BASE TABLE |
|
||||
| [public.agents](public.agents.md) | 14 | | BASE TABLE |
|
||||
| [public.agents_memory_entries](public.agents_memory_entries.md) | 13 | | BASE TABLE |
|
||||
|
|
@ -132,6 +134,9 @@ 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_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 |
|
||||
| [public.test_case_execution](public.test_case_execution.md) | 14 | | BASE TABLE |
|
||||
| [public.test_run](public.test_run.md) | 16 | | BASE TABLE |
|
||||
|
|
@ -224,10 +229,16 @@ erDiagram
|
|||
"public.agent_message_queue" }o--|| "public.agent_execution_threads" : "FOREIGN KEY (#quot;threadId#quot;) REFERENCES agent_execution_threads(id) ON DELETE CASCADE"
|
||||
"public.agent_plan" }o--|| "public.agent_execution_threads" : "FOREIGN KEY (#quot;threadId#quot;) REFERENCES agent_execution_threads(id) ON DELETE CASCADE"
|
||||
"public.agent_plan_history" }o--|| "public.agent_plan" : "FOREIGN KEY (#quot;planId#quot;) REFERENCES agent_plan(id) ON DELETE CASCADE"
|
||||
"public.agent_skill_dependency" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agent_skill_dependency" }o--|| "public.skill" : "FOREIGN KEY (#quot;skillId#quot;) REFERENCES skill(id) ON DELETE CASCADE"
|
||||
"public.agent_skill_dependency" }o--o| "public.skill_version" : "FOREIGN KEY (#quot;skillVersionId#quot;) REFERENCES skill_version(id)"
|
||||
"public.agent_task_definition" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agent_task_run_lock" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agent_task_snapshot" }o--|| "public.agent_history" : "FOREIGN KEY (#quot;versionId#quot;) REFERENCES agent_history(#quot;versionId#quot;) ON DELETE CASCADE"
|
||||
"public.agent_thread_grants" }o--|| "public.agent_execution_threads" : "FOREIGN KEY (#quot;threadId#quot;) REFERENCES agent_execution_threads(id) ON DELETE CASCADE"
|
||||
"public.agent_version_skill_dependency" }o--|| "public.agent_history" : "FOREIGN KEY (#quot;agentVersionId#quot;) REFERENCES agent_history(#quot;versionId#quot;) ON DELETE CASCADE"
|
||||
"public.agent_version_skill_dependency" }o--|| "public.skill" : "FOREIGN KEY (#quot;skillId#quot;) REFERENCES skill(id) ON DELETE CASCADE"
|
||||
"public.agent_version_skill_dependency" }o--|| "public.skill_version" : "FOREIGN KEY (#quot;skillVersionId#quot;) REFERENCES skill_version(id)"
|
||||
"public.agent_workflow_dependency" }o--|| "public.workflow_entity" : "FOREIGN KEY (#quot;workflowId#quot;) REFERENCES workflow_entity(id) ON DELETE CASCADE"
|
||||
"public.agent_workflow_dependency" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agents" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
|
|
@ -374,6 +385,12 @@ erDiagram
|
|||
"public.shared_credentials" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.shared_workflow" }o--|| "public.workflow_entity" : "FOREIGN KEY (#quot;workflowId#quot;) REFERENCES workflow_entity(id) ON DELETE CASCADE"
|
||||
"public.shared_workflow" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.skill" }o--o| "public.user" : "FOREIGN KEY (#quot;createdById#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL"
|
||||
"public.skill" }o--o| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.skill" }o--o| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.skill_file" }o--|| "public.skill_version" : "FOREIGN KEY (#quot;skillVersionId#quot;) REFERENCES skill_version(id) ON DELETE CASCADE"
|
||||
"public.skill_version" }o--o| "public.user" : "FOREIGN KEY (#quot;createdById#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL"
|
||||
"public.skill_version" }o--|| "public.skill" : "FOREIGN KEY (#quot;skillId#quot;) REFERENCES skill(id) ON DELETE CASCADE"
|
||||
"public.test_case_execution" }o--o| "public.execution_entity" : "FOREIGN KEY (#quot;executionId#quot;) REFERENCES execution_entity(id) ON DELETE SET NULL"
|
||||
"public.test_case_execution" }o--|| "public.test_run" : "FOREIGN KEY (#quot;testRunId#quot;) REFERENCES test_run(id) ON DELETE CASCADE"
|
||||
"public.test_run" }o--|| "public.workflow_entity" : "FOREIGN KEY (#quot;workflowId#quot;) REFERENCES workflow_entity(id) ON DELETE CASCADE"
|
||||
|
|
@ -684,6 +701,12 @@ erDiagram
|
|||
uuid planId FK
|
||||
integer revision
|
||||
}
|
||||
"public.agent_skill_dependency" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ skillId FK
|
||||
uuid skillVersionId FK
|
||||
}
|
||||
"public.agent_task_definition" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
|
|
@ -719,6 +742,12 @@ erDiagram
|
|||
varchar_128_ threadId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.agent_version_skill_dependency" {
|
||||
varchar_36_ agentVersionId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ skillId FK
|
||||
uuid skillVersionId FK
|
||||
}
|
||||
"public.agent_workflow_dependency" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
|
|
@ -1671,6 +1700,35 @@ erDiagram
|
|||
timestamp_3__with_time_zone updatedAt
|
||||
varchar_36_ workflowId FK
|
||||
}
|
||||
"public.skill" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_36_ id
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.skill_file" {
|
||||
text content
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_512_ path
|
||||
uuid skillVersionId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.skill_version" {
|
||||
varchar_64_ contentHash
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_1024_ description
|
||||
json frontmatter
|
||||
uuid id
|
||||
text instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
integer version
|
||||
}
|
||||
"public.tag_entity" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ id
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
| skills | json | | true | | | Frozen map of `skillId → AgentSkill` at publish time |
|
||||
| tools | json | | true | | | Frozen map of `toolId → { code, descriptor }` at publish time |
|
||||
| updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
| versionId | varchar(36) | | false | [public.agent_execution_threads](public.agent_execution_threads.md) [public.agent_task_snapshot](public.agent_task_snapshot.md) [public.agents](public.agents.md) | | |
|
||||
| versionId | varchar(36) | | false | [public.agent_execution_threads](public.agent_execution_threads.md) [public.agent_task_snapshot](public.agent_task_snapshot.md) [public.agent_version_skill_dependency](public.agent_version_skill_dependency.md) [public.agents](public.agents.md) | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
|
|
@ -43,6 +43,7 @@ erDiagram
|
|||
"public.agent_history" }o--o| "public.user" : "FOREIGN KEY (#quot;publishedById#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL"
|
||||
"public.agent_execution_threads" }o--o| "public.agent_history" : "FOREIGN KEY (#quot;taskVersionId#quot;) REFERENCES agent_history(#quot;versionId#quot;) ON DELETE SET NULL"
|
||||
"public.agent_task_snapshot" }o--|| "public.agent_history" : "FOREIGN KEY (#quot;versionId#quot;) REFERENCES agent_history(#quot;versionId#quot;) ON DELETE CASCADE"
|
||||
"public.agent_version_skill_dependency" }o--|| "public.agent_history" : "FOREIGN KEY (#quot;agentVersionId#quot;) REFERENCES agent_history(#quot;versionId#quot;) ON DELETE CASCADE"
|
||||
"public.agents" }o--o| "public.agent_history" : "FOREIGN KEY (#quot;activeVersionId#quot;) REFERENCES agent_history(#quot;versionId#quot;) ON DELETE SET NULL"
|
||||
|
||||
"public.agent_history" {
|
||||
|
|
@ -121,6 +122,12 @@ erDiagram
|
|||
timestamp_3__with_time_zone updatedAt
|
||||
varchar_36_ versionId FK
|
||||
}
|
||||
"public.agent_version_skill_dependency" {
|
||||
varchar_36_ agentVersionId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ skillId FK
|
||||
uuid skillVersionId FK
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
|
|
|||
88
docs/generated/postgres-schema/public.agent_skill_dependency.md
generated
Normal file
88
docs/generated/postgres-schema/public.agent_skill_dependency.md
generated
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# public.agent_skill_dependency
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| agentId | varchar(36) | | false | | [public.agents](public.agents.md) | |
|
||||
| createdAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
| skillId | varchar(36) | | false | | [public.skill](public.skill.md) | |
|
||||
| skillVersionId | uuid | | true | | [public.skill_version](public.skill_version.md) | Set when the agent draft pins a saved version. NULL = follows the latest saved version |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| FK_068f21aefba31ae621482f396dc | FOREIGN KEY | FOREIGN KEY ("skillId") REFERENCES skill(id) ON DELETE CASCADE |
|
||||
| FK_72325bb13511a51847c53fcb377 | FOREIGN KEY | FOREIGN KEY ("agentId") REFERENCES agents(id) ON DELETE CASCADE |
|
||||
| FK_7676571bd6f41d9e32f0d066fe9 | FOREIGN KEY | FOREIGN KEY ("skillVersionId") REFERENCES skill_version(id) |
|
||||
| PK_18a54f60491d3e2aa2200b70f4a | PRIMARY KEY | PRIMARY KEY ("agentId", "skillId") |
|
||||
| agent_skill_dependency_agentId_not_null | n | NOT NULL "agentId" |
|
||||
| agent_skill_dependency_createdAt_not_null | n | NOT NULL "createdAt" |
|
||||
| agent_skill_dependency_skillId_not_null | n | NOT NULL "skillId" |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| IDX_068f21aefba31ae621482f396d | CREATE INDEX "IDX_068f21aefba31ae621482f396d" ON public.agent_skill_dependency USING btree ("skillId") |
|
||||
| PK_18a54f60491d3e2aa2200b70f4a | CREATE UNIQUE INDEX "PK_18a54f60491d3e2aa2200b70f4a" ON public.agent_skill_dependency USING btree ("agentId", "skillId") |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"public.agent_skill_dependency" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agent_skill_dependency" }o--|| "public.skill" : "FOREIGN KEY (#quot;skillId#quot;) REFERENCES skill(id) ON DELETE CASCADE"
|
||||
"public.agent_skill_dependency" }o--o| "public.skill_version" : "FOREIGN KEY (#quot;skillVersionId#quot;) REFERENCES skill_version(id)"
|
||||
|
||||
"public.agent_skill_dependency" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ skillId FK
|
||||
uuid skillVersionId FK
|
||||
}
|
||||
"public.agents" {
|
||||
varchar_36_ activeVersionId FK
|
||||
boolean availableInMCP
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ id
|
||||
json integrations
|
||||
varchar_128_ name
|
||||
varchar_255_ projectId FK
|
||||
integer revision
|
||||
json schema
|
||||
timestamp_3__with_time_zone setupCompletedAt
|
||||
json skills
|
||||
json tools
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
varchar_36_ versionId
|
||||
}
|
||||
"public.skill" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_36_ id
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.skill_version" {
|
||||
varchar_64_ contentHash
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_1024_ description
|
||||
json frontmatter
|
||||
uuid id
|
||||
text instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
integer version
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
85
docs/generated/postgres-schema/public.agent_version_skill_dependency.md
generated
Normal file
85
docs/generated/postgres-schema/public.agent_version_skill_dependency.md
generated
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# public.agent_version_skill_dependency
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| agentVersionId | varchar(36) | | false | | [public.agent_history](public.agent_history.md) | |
|
||||
| createdAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
| skillId | varchar(36) | | false | | [public.skill](public.skill.md) | |
|
||||
| skillVersionId | uuid | | false | | [public.skill_version](public.skill_version.md) | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| FK_97472baf9196d914d9cd9de6201 | FOREIGN KEY | FOREIGN KEY ("agentVersionId") REFERENCES agent_history("versionId") ON DELETE CASCADE |
|
||||
| FK_c494292243a7e510acd0331c3d1 | FOREIGN KEY | FOREIGN KEY ("skillId") REFERENCES skill(id) ON DELETE CASCADE |
|
||||
| FK_df63badfd7d3f2aa622ece471c4 | FOREIGN KEY | FOREIGN KEY ("skillVersionId") REFERENCES skill_version(id) |
|
||||
| PK_df0a2577371b94f3c7008d81822 | PRIMARY KEY | PRIMARY KEY ("agentVersionId", "skillId") |
|
||||
| agent_version_skill_dependency_agentVersionId_not_null | n | NOT NULL "agentVersionId" |
|
||||
| agent_version_skill_dependency_createdAt_not_null | n | NOT NULL "createdAt" |
|
||||
| agent_version_skill_dependency_skillId_not_null | n | NOT NULL "skillId" |
|
||||
| agent_version_skill_dependency_skillVersionId_not_null | n | NOT NULL "skillVersionId" |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| IDX_c494292243a7e510acd0331c3d | CREATE INDEX "IDX_c494292243a7e510acd0331c3d" ON public.agent_version_skill_dependency USING btree ("skillId") |
|
||||
| IDX_df63badfd7d3f2aa622ece471c | CREATE INDEX "IDX_df63badfd7d3f2aa622ece471c" ON public.agent_version_skill_dependency USING btree ("skillVersionId") |
|
||||
| PK_df0a2577371b94f3c7008d81822 | CREATE UNIQUE INDEX "PK_df0a2577371b94f3c7008d81822" ON public.agent_version_skill_dependency USING btree ("agentVersionId", "skillId") |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"public.agent_version_skill_dependency" }o--|| "public.agent_history" : "FOREIGN KEY (#quot;agentVersionId#quot;) REFERENCES agent_history(#quot;versionId#quot;) ON DELETE CASCADE"
|
||||
"public.agent_version_skill_dependency" }o--|| "public.skill" : "FOREIGN KEY (#quot;skillId#quot;) REFERENCES skill(id) ON DELETE CASCADE"
|
||||
"public.agent_version_skill_dependency" }o--|| "public.skill_version" : "FOREIGN KEY (#quot;skillVersionId#quot;) REFERENCES skill_version(id)"
|
||||
|
||||
"public.agent_version_skill_dependency" {
|
||||
varchar_36_ agentVersionId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ skillId FK
|
||||
uuid skillVersionId FK
|
||||
}
|
||||
"public.agent_history" {
|
||||
varchar_36_ agentId FK
|
||||
varchar_255_ author
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid publishedById FK
|
||||
json schema
|
||||
json skills
|
||||
json tools
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
varchar_36_ versionId
|
||||
}
|
||||
"public.skill" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_36_ id
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.skill_version" {
|
||||
varchar_64_ contentHash
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_1024_ description
|
||||
json frontmatter
|
||||
uuid id
|
||||
text instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
integer version
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
19
docs/generated/postgres-schema/public.agents.md
generated
19
docs/generated/postgres-schema/public.agents.md
generated
|
|
@ -7,7 +7,7 @@
|
|||
| activeVersionId | varchar(36) | | true | | [public.agent_history](public.agent_history.md) | |
|
||||
| availableInMCP | boolean | false | false | | | Whether MCP clients granted agent scopes may operate on this agent |
|
||||
| createdAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
| id | varchar(36) | | false | [public.agent_background_job](public.agent_background_job.md) [public.agent_channel_status](public.agent_channel_status.md) [public.agent_chat_attachments](public.agent_chat_attachments.md) [public.agent_chat_subscriptions](public.agent_chat_subscriptions.md) [public.agent_checkpoints](public.agent_checkpoints.md) [public.agent_credential_dependency](public.agent_credential_dependency.md) [public.agent_eval_dataset](public.agent_eval_dataset.md) [public.agent_execution_threads](public.agent_execution_threads.md) [public.agent_files](public.agent_files.md) [public.agent_history](public.agent_history.md) [public.agent_task_definition](public.agent_task_definition.md) [public.agent_task_run_lock](public.agent_task_run_lock.md) [public.agent_workflow_dependency](public.agent_workflow_dependency.md) [public.agents_memory_entries](public.agents_memory_entries.md) [public.agents_memory_entry_candidates](public.agents_memory_entry_candidates.md) [public.agents_memory_entry_locks](public.agents_memory_entry_locks.md) [public.agents_memory_entry_sources](public.agents_memory_entry_sources.md) [public.agents_observation_cursors](public.agents_observation_cursors.md) [public.agents_observation_locks](public.agents_observation_locks.md) [public.agents_observations](public.agents_observations.md) | | |
|
||||
| id | varchar(36) | | false | [public.agent_background_job](public.agent_background_job.md) [public.agent_channel_status](public.agent_channel_status.md) [public.agent_chat_attachments](public.agent_chat_attachments.md) [public.agent_chat_subscriptions](public.agent_chat_subscriptions.md) [public.agent_checkpoints](public.agent_checkpoints.md) [public.agent_credential_dependency](public.agent_credential_dependency.md) [public.agent_eval_dataset](public.agent_eval_dataset.md) [public.agent_execution_threads](public.agent_execution_threads.md) [public.agent_files](public.agent_files.md) [public.agent_history](public.agent_history.md) [public.agent_skill_dependency](public.agent_skill_dependency.md) [public.agent_task_definition](public.agent_task_definition.md) [public.agent_task_run_lock](public.agent_task_run_lock.md) [public.agent_workflow_dependency](public.agent_workflow_dependency.md) [public.agents_memory_entries](public.agents_memory_entries.md) [public.agents_memory_entry_candidates](public.agents_memory_entry_candidates.md) [public.agents_memory_entry_locks](public.agents_memory_entry_locks.md) [public.agents_memory_entry_sources](public.agents_memory_entry_sources.md) [public.agents_observation_cursors](public.agents_observation_cursors.md) [public.agents_observation_locks](public.agents_observation_locks.md) [public.agents_observations](public.agents_observations.md) [public.skill](public.skill.md) | | |
|
||||
| integrations | json | '[]'::json | false | | | |
|
||||
| name | varchar(128) | | false | | | |
|
||||
| projectId | varchar(255) | | false | | [public.project](public.project.md) | |
|
||||
|
|
@ -61,6 +61,7 @@ erDiagram
|
|||
"public.agent_execution_threads" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agent_files" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agent_history" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agent_skill_dependency" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agent_task_definition" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agent_task_run_lock" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agent_workflow_dependency" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
|
|
@ -71,6 +72,7 @@ erDiagram
|
|||
"public.agents_observation_cursors" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agents_observation_locks" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agents_observations" }o--|| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.skill" }o--o| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.agents" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
|
||||
"public.agents" {
|
||||
|
|
@ -216,6 +218,12 @@ erDiagram
|
|||
varchar_2_ storedAt
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.agent_skill_dependency" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ skillId FK
|
||||
uuid skillVersionId FK
|
||||
}
|
||||
"public.agent_task_definition" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
|
|
@ -320,6 +328,15 @@ erDiagram
|
|||
integer tokenCount
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.skill" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_36_ id
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.project" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid creatorId FK
|
||||
|
|
|
|||
12
docs/generated/postgres-schema/public.project.md
generated
12
docs/generated/postgres-schema/public.project.md
generated
|
|
@ -9,7 +9,7 @@
|
|||
| customTelemetryTags | json | '[]'::json | false | | | |
|
||||
| description | varchar(512) | | true | | | |
|
||||
| icon | json | | true | | | |
|
||||
| id | varchar(36) | | false | [public.activity_event](public.activity_event.md) [public.agent_chat_attachments](public.agent_chat_attachments.md) [public.agent_execution_threads](public.agent_execution_threads.md) [public.agents](public.agents.md) [public.ai_preference](public.ai_preference.md) [public.data_table](public.data_table.md) [public.folder](public.folder.md) [public.insights_metadata](public.insights_metadata.md) [public.instance_ai_threads](public.instance_ai_threads.md) [public.project_pool_settings](public.project_pool_settings.md) [public.project_relation](public.project_relation.md) [public.project_secrets_provider_access](public.project_secrets_provider_access.md) [public.promotion_connection_project](public.promotion_connection_project.md) [public.role_mapping_rule_project](public.role_mapping_rule_project.md) [public.self_healing_investigation](public.self_healing_investigation.md) [public.self_healing_result](public.self_healing_result.md) [public.shared_credentials](public.shared_credentials.md) [public.shared_workflow](public.shared_workflow.md) [public.type_availability_policy_scope](public.type_availability_policy_scope.md) [public.variables](public.variables.md) [public.workflow_review_request](public.workflow_review_request.md) [public.workflow_suggestion](public.workflow_suggestion.md) | | |
|
||||
| id | varchar(36) | | false | [public.activity_event](public.activity_event.md) [public.agent_chat_attachments](public.agent_chat_attachments.md) [public.agent_execution_threads](public.agent_execution_threads.md) [public.agents](public.agents.md) [public.ai_preference](public.ai_preference.md) [public.data_table](public.data_table.md) [public.folder](public.folder.md) [public.insights_metadata](public.insights_metadata.md) [public.instance_ai_threads](public.instance_ai_threads.md) [public.project_pool_settings](public.project_pool_settings.md) [public.project_relation](public.project_relation.md) [public.project_secrets_provider_access](public.project_secrets_provider_access.md) [public.promotion_connection_project](public.promotion_connection_project.md) [public.role_mapping_rule_project](public.role_mapping_rule_project.md) [public.self_healing_investigation](public.self_healing_investigation.md) [public.self_healing_result](public.self_healing_result.md) [public.shared_credentials](public.shared_credentials.md) [public.shared_workflow](public.shared_workflow.md) [public.skill](public.skill.md) [public.type_availability_policy_scope](public.type_availability_policy_scope.md) [public.variables](public.variables.md) [public.workflow_review_request](public.workflow_review_request.md) [public.workflow_suggestion](public.workflow_suggestion.md) | | |
|
||||
| name | varchar(255) | | false | | | |
|
||||
| type | varchar(36) | | false | | | |
|
||||
| updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
|
|
@ -57,6 +57,7 @@ erDiagram
|
|||
"public.self_healing_result" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.shared_credentials" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.shared_workflow" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.skill" }o--o| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.type_availability_policy_scope" }o--o| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.variables" }o--o| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.workflow_review_request" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
|
|
@ -271,6 +272,15 @@ erDiagram
|
|||
timestamp_3__with_time_zone updatedAt
|
||||
varchar_36_ workflowId FK
|
||||
}
|
||||
"public.skill" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_36_ id
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.type_availability_policy_scope" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_16_ defaultAction
|
||||
|
|
|
|||
131
docs/generated/postgres-schema/public.skill.md
generated
Normal file
131
docs/generated/postgres-schema/public.skill.md
generated
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
# public.skill
|
||||
|
||||
## Columns
|
||||
|
||||
| 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 |
|
||||
| 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 |
|
||||
| projectId | varchar(36) | | true | | [public.project](public.project.md) | Set for a project skill, team or personal. NULL otherwise |
|
||||
| source | varchar(16) | | false | | | How the skill was created, set by the server: "ui" (the skills hub) or "agent" (the agent builder or the copy of agent skills) |
|
||||
| updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| CHK_skill_single_target | CHECK | CHECK ((("agentId" IS NULL) OR ("projectId" IS NULL))) |
|
||||
| FK_402ed37bff0d3134f6e782455ed | FOREIGN KEY | FOREIGN KEY ("projectId") REFERENCES project(id) ON DELETE CASCADE |
|
||||
| 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_createdAt_not_null | n | NOT NULL "createdAt" |
|
||||
| skill_id_not_null | n | NOT NULL id |
|
||||
| skill_source_not_null | n | NOT NULL source |
|
||||
| skill_updatedAt_not_null | n | NOT NULL "updatedAt" |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| IDX_402ed37bff0d3134f6e782455e | CREATE INDEX "IDX_402ed37bff0d3134f6e782455e" ON public.skill USING btree ("projectId") |
|
||||
| IDX_7d4fb6af107e507d9a86293cf9 | CREATE INDEX "IDX_7d4fb6af107e507d9a86293cf9" ON public.skill USING btree ("agentId") |
|
||||
| PK_a0d33334424e64fb78dc3ce7196 | CREATE UNIQUE INDEX "PK_a0d33334424e64fb78dc3ce7196" ON public.skill USING btree (id) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"public.skill" }o--o| "public.agents" : "FOREIGN KEY (#quot;agentId#quot;) REFERENCES agents(id) ON DELETE CASCADE"
|
||||
"public.skill" }o--o| "public.user" : "FOREIGN KEY (#quot;createdById#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL"
|
||||
"public.agent_skill_dependency" }o--|| "public.skill" : "FOREIGN KEY (#quot;skillId#quot;) REFERENCES skill(id) ON DELETE CASCADE"
|
||||
"public.agent_version_skill_dependency" }o--|| "public.skill" : "FOREIGN KEY (#quot;skillId#quot;) REFERENCES skill(id) ON DELETE CASCADE"
|
||||
"public.skill_version" }o--|| "public.skill" : "FOREIGN KEY (#quot;skillId#quot;) REFERENCES skill(id) ON DELETE CASCADE"
|
||||
"public.skill" }o--o| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
|
||||
"public.skill" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_36_ id
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.agents" {
|
||||
varchar_36_ activeVersionId FK
|
||||
boolean availableInMCP
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ id
|
||||
json integrations
|
||||
varchar_128_ name
|
||||
varchar_255_ projectId FK
|
||||
integer revision
|
||||
json schema
|
||||
timestamp_3__with_time_zone setupCompletedAt
|
||||
json skills
|
||||
json tools
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
varchar_36_ versionId
|
||||
}
|
||||
"public.user" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
boolean disabled
|
||||
varchar_255_ email
|
||||
varchar_32_ firstName
|
||||
uuid id
|
||||
date lastActiveAt
|
||||
varchar_32_ lastName
|
||||
boolean mfaEnabled
|
||||
text mfaRecoveryCodes
|
||||
text mfaSecret
|
||||
varchar_255_ password
|
||||
json personalizationAnswers
|
||||
varchar_128_ roleSlug FK
|
||||
json settings
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.agent_skill_dependency" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ skillId FK
|
||||
uuid skillVersionId FK
|
||||
}
|
||||
"public.agent_version_skill_dependency" {
|
||||
varchar_36_ agentVersionId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ skillId FK
|
||||
uuid skillVersionId FK
|
||||
}
|
||||
"public.skill_version" {
|
||||
varchar_64_ contentHash
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_1024_ description
|
||||
json frontmatter
|
||||
uuid id
|
||||
text instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
integer version
|
||||
}
|
||||
"public.project" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid creatorId FK
|
||||
json customTelemetryTags
|
||||
varchar_512_ description
|
||||
json icon
|
||||
varchar_36_ id
|
||||
varchar_255_ name
|
||||
varchar_36_ type
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
62
docs/generated/postgres-schema/public.skill_file.md
generated
Normal file
62
docs/generated/postgres-schema/public.skill_file.md
generated
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# public.skill_file
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| content | text | | false | | | |
|
||||
| createdAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
| path | varchar(512) | | false | | | Relative path, references/*.md in v1 |
|
||||
| skillVersionId | uuid | | false | | [public.skill_version](public.skill_version.md) | |
|
||||
| updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| FK_e9ab50b795ec8858702b7333d13 | FOREIGN KEY | FOREIGN KEY ("skillVersionId") REFERENCES skill_version(id) ON DELETE CASCADE |
|
||||
| PK_160721f9e4c22026c16138af2a1 | PRIMARY KEY | PRIMARY KEY ("skillVersionId", path) |
|
||||
| skill_file_content_not_null | n | NOT NULL content |
|
||||
| skill_file_createdAt_not_null | n | NOT NULL "createdAt" |
|
||||
| skill_file_path_not_null | n | NOT NULL path |
|
||||
| skill_file_skillVersionId_not_null | n | NOT NULL "skillVersionId" |
|
||||
| skill_file_updatedAt_not_null | n | NOT NULL "updatedAt" |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| PK_160721f9e4c22026c16138af2a1 | CREATE UNIQUE INDEX "PK_160721f9e4c22026c16138af2a1" ON public.skill_file USING btree ("skillVersionId", path) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"public.skill_file" }o--|| "public.skill_version" : "FOREIGN KEY (#quot;skillVersionId#quot;) REFERENCES skill_version(id) ON DELETE CASCADE"
|
||||
|
||||
"public.skill_file" {
|
||||
text content
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_512_ path
|
||||
uuid skillVersionId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.skill_version" {
|
||||
varchar_64_ contentHash
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_1024_ description
|
||||
json frontmatter
|
||||
uuid id
|
||||
text instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
integer version
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
118
docs/generated/postgres-schema/public.skill_version.md
generated
Normal file
118
docs/generated/postgres-schema/public.skill_version.md
generated
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# public.skill_version
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| contentHash | varchar(64) | | false | | | sha256 of name, description, instructions, frontmatter and files. Save creates no version when the content matches the latest one |
|
||||
| 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 |
|
||||
| description | varchar(1024) | | false | | | |
|
||||
| frontmatter | json | | true | | | SKILL.md frontmatter fields other than name and description, e.g. allowed-tools |
|
||||
| id | uuid | | false | [public.agent_skill_dependency](public.agent_skill_dependency.md) [public.agent_version_skill_dependency](public.agent_version_skill_dependency.md) [public.skill_file](public.skill_file.md) | | |
|
||||
| instructions | text | | false | | | |
|
||||
| name | varchar(128) | | false | | | Free-text skill name, as it was when this version was saved |
|
||||
| skillId | varchar(36) | | false | | [public.skill](public.skill.md) | |
|
||||
| updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
| version | integer | | false | | | 1..n per skill. A Save that changes the content adds one; none ever changes |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| CHK_skill_version_version | CHECK | CHECK ((version > 0)) |
|
||||
| FK_9565d0fbf32cd463f9b47c7adab | FOREIGN KEY | FOREIGN KEY ("createdById") REFERENCES "user"(id) ON DELETE SET NULL |
|
||||
| FK_f87064c0a29efd2d5195328d91a | FOREIGN KEY | FOREIGN KEY ("skillId") REFERENCES skill(id) ON DELETE CASCADE |
|
||||
| PK_05167d59ac7599128e22400172d | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
| UQ_93bb78a1eb4e0890fac6b69df95 | UNIQUE | UNIQUE ("skillId", version) |
|
||||
| skill_version_contentHash_not_null | n | NOT NULL "contentHash" |
|
||||
| skill_version_createdAt_not_null | n | NOT NULL "createdAt" |
|
||||
| skill_version_description_not_null | n | NOT NULL description |
|
||||
| skill_version_id_not_null | n | NOT NULL id |
|
||||
| skill_version_instructions_not_null | n | NOT NULL instructions |
|
||||
| skill_version_name_not_null | n | NOT NULL name |
|
||||
| skill_version_skillId_not_null | n | NOT NULL "skillId" |
|
||||
| skill_version_updatedAt_not_null | n | NOT NULL "updatedAt" |
|
||||
| skill_version_version_not_null | n | NOT NULL version |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| PK_05167d59ac7599128e22400172d | CREATE UNIQUE INDEX "PK_05167d59ac7599128e22400172d" ON public.skill_version USING btree (id) |
|
||||
| UQ_93bb78a1eb4e0890fac6b69df95 | CREATE UNIQUE INDEX "UQ_93bb78a1eb4e0890fac6b69df95" ON public.skill_version USING btree ("skillId", version) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"public.skill_version" }o--o| "public.user" : "FOREIGN KEY (#quot;createdById#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL"
|
||||
"public.agent_skill_dependency" }o--o| "public.skill_version" : "FOREIGN KEY (#quot;skillVersionId#quot;) REFERENCES skill_version(id)"
|
||||
"public.agent_version_skill_dependency" }o--|| "public.skill_version" : "FOREIGN KEY (#quot;skillVersionId#quot;) REFERENCES skill_version(id)"
|
||||
"public.skill_file" }o--|| "public.skill_version" : "FOREIGN KEY (#quot;skillVersionId#quot;) REFERENCES skill_version(id) ON DELETE CASCADE"
|
||||
"public.skill_version" }o--|| "public.skill" : "FOREIGN KEY (#quot;skillId#quot;) REFERENCES skill(id) ON DELETE CASCADE"
|
||||
|
||||
"public.skill_version" {
|
||||
varchar_64_ contentHash
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_1024_ description
|
||||
json frontmatter
|
||||
uuid id
|
||||
text instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
integer version
|
||||
}
|
||||
"public.user" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
boolean disabled
|
||||
varchar_255_ email
|
||||
varchar_32_ firstName
|
||||
uuid id
|
||||
date lastActiveAt
|
||||
varchar_32_ lastName
|
||||
boolean mfaEnabled
|
||||
text mfaRecoveryCodes
|
||||
text mfaSecret
|
||||
varchar_255_ password
|
||||
json personalizationAnswers
|
||||
varchar_128_ roleSlug FK
|
||||
json settings
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.agent_skill_dependency" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ skillId FK
|
||||
uuid skillVersionId FK
|
||||
}
|
||||
"public.agent_version_skill_dependency" {
|
||||
varchar_36_ agentVersionId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ skillId FK
|
||||
uuid skillVersionId FK
|
||||
}
|
||||
"public.skill_file" {
|
||||
text content
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_512_ path
|
||||
uuid skillVersionId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.skill" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_36_ id
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
26
docs/generated/postgres-schema/public.user.md
generated
26
docs/generated/postgres-schema/public.user.md
generated
|
|
@ -8,7 +8,7 @@
|
|||
| disabled | boolean | false | false | | | |
|
||||
| email | varchar(255) | | true | | | |
|
||||
| firstName | varchar(32) | | true | | | |
|
||||
| id | uuid | gen_random_uuid() | false | [public.activity_event](public.activity_event.md) [public.agent_eval_dataset](public.agent_eval_dataset.md) [public.agent_eval_rating](public.agent_eval_rating.md) [public.agent_eval_run](public.agent_eval_run.md) [public.agent_execution_threads](public.agent_execution_threads.md) [public.agent_history](public.agent_history.md) [public.ai_preference](public.ai_preference.md) [public.auth_identity](public.auth_identity.md) [public.chat_hub_agents](public.chat_hub_agents.md) [public.chat_hub_sessions](public.chat_hub_sessions.md) [public.chat_hub_tools](public.chat_hub_tools.md) [public.dynamic_credential_user_entry](public.dynamic_credential_user_entry.md) [public.evaluation_collection](public.evaluation_collection.md) [public.idempotency_key](public.idempotency_key.md) [public.instance_ai_mcp_registry_connections](public.instance_ai_mcp_registry_connections.md) [public.instance_ai_pending_confirmations](public.instance_ai_pending_confirmations.md) [public.instance_ai_thread_grants](public.instance_ai_thread_grants.md) [public.instance_ai_thread_tabs](public.instance_ai_thread_tabs.md) [public.migration_workflow_owner](public.migration_workflow_owner.md) [public.oauth_access_tokens](public.oauth_access_tokens.md) [public.oauth_authorization_codes](public.oauth_authorization_codes.md) [public.oauth_refresh_tokens](public.oauth_refresh_tokens.md) [public.oauth_user_consents](public.oauth_user_consents.md) [public.project](public.project.md) [public.project_relation](public.project_relation.md) [public.self_healing_investigation](public.self_healing_investigation.md) [public.self_healing_result](public.self_healing_result.md) [public.trusted_source_identity](public.trusted_source_identity.md) [public.user_api_keys](public.user_api_keys.md) [public.user_favorites](public.user_favorites.md) [public.workflow_builder_session](public.workflow_builder_session.md) [public.workflow_publish_history](public.workflow_publish_history.md) [public.workflow_review_activity](public.workflow_review_activity.md) [public.workflow_review_activity_comment](public.workflow_review_activity_comment.md) [public.workflow_review_request](public.workflow_review_request.md) [public.workflow_review_request_authors](public.workflow_review_request_authors.md) [public.workflow_review_request_reviewers](public.workflow_review_request_reviewers.md) [public.workflow_suggestion](public.workflow_suggestion.md) [public.workflow_suggestion_activity](public.workflow_suggestion_activity.md) | | |
|
||||
| id | uuid | gen_random_uuid() | false | [public.activity_event](public.activity_event.md) [public.agent_eval_dataset](public.agent_eval_dataset.md) [public.agent_eval_rating](public.agent_eval_rating.md) [public.agent_eval_run](public.agent_eval_run.md) [public.agent_execution_threads](public.agent_execution_threads.md) [public.agent_history](public.agent_history.md) [public.ai_preference](public.ai_preference.md) [public.auth_identity](public.auth_identity.md) [public.chat_hub_agents](public.chat_hub_agents.md) [public.chat_hub_sessions](public.chat_hub_sessions.md) [public.chat_hub_tools](public.chat_hub_tools.md) [public.dynamic_credential_user_entry](public.dynamic_credential_user_entry.md) [public.evaluation_collection](public.evaluation_collection.md) [public.idempotency_key](public.idempotency_key.md) [public.instance_ai_mcp_registry_connections](public.instance_ai_mcp_registry_connections.md) [public.instance_ai_pending_confirmations](public.instance_ai_pending_confirmations.md) [public.instance_ai_thread_grants](public.instance_ai_thread_grants.md) [public.instance_ai_thread_tabs](public.instance_ai_thread_tabs.md) [public.migration_workflow_owner](public.migration_workflow_owner.md) [public.oauth_access_tokens](public.oauth_access_tokens.md) [public.oauth_authorization_codes](public.oauth_authorization_codes.md) [public.oauth_refresh_tokens](public.oauth_refresh_tokens.md) [public.oauth_user_consents](public.oauth_user_consents.md) [public.project](public.project.md) [public.project_relation](public.project_relation.md) [public.self_healing_investigation](public.self_healing_investigation.md) [public.self_healing_result](public.self_healing_result.md) [public.skill](public.skill.md) [public.skill_version](public.skill_version.md) [public.trusted_source_identity](public.trusted_source_identity.md) [public.user_api_keys](public.user_api_keys.md) [public.user_favorites](public.user_favorites.md) [public.workflow_builder_session](public.workflow_builder_session.md) [public.workflow_publish_history](public.workflow_publish_history.md) [public.workflow_review_activity](public.workflow_review_activity.md) [public.workflow_review_activity_comment](public.workflow_review_activity_comment.md) [public.workflow_review_request](public.workflow_review_request.md) [public.workflow_review_request_authors](public.workflow_review_request_authors.md) [public.workflow_review_request_reviewers](public.workflow_review_request_reviewers.md) [public.workflow_suggestion](public.workflow_suggestion.md) [public.workflow_suggestion_activity](public.workflow_suggestion_activity.md) | | |
|
||||
| lastActiveAt | date | | true | | | |
|
||||
| lastName | varchar(32) | | true | | | |
|
||||
| mfaEnabled | boolean | false | false | | | |
|
||||
|
|
@ -77,6 +77,8 @@ erDiagram
|
|||
"public.self_healing_investigation" }o--|| "public.user" : "FOREIGN KEY (#quot;backgroundUserId#quot;) REFERENCES #quot;user#quot;(id) ON DELETE CASCADE"
|
||||
"public.self_healing_result" }o--|| "public.user" : "FOREIGN KEY (#quot;backgroundUserId#quot;) REFERENCES #quot;user#quot;(id) ON DELETE CASCADE"
|
||||
"public.self_healing_result" }o--o| "public.user" : "FOREIGN KEY (#quot;dismissedById#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL"
|
||||
"public.skill" }o--o| "public.user" : "FOREIGN KEY (#quot;createdById#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL"
|
||||
"public.skill_version" }o--o| "public.user" : "FOREIGN KEY (#quot;createdById#quot;) REFERENCES #quot;user#quot;(id) ON DELETE SET NULL"
|
||||
"public.trusted_source_identity" }o--|| "public.user" : "FOREIGN KEY (#quot;userId#quot;) REFERENCES #quot;user#quot;(id) ON DELETE CASCADE"
|
||||
"public.user_api_keys" }o--|| "public.user" : "FOREIGN KEY (#quot;userId#quot;) REFERENCES #quot;user#quot;(id) ON DELETE CASCADE"
|
||||
"public.user_favorites" }o--|| "public.user" : "FOREIGN KEY (#quot;userId#quot;) REFERENCES #quot;user#quot;(id) ON DELETE CASCADE"
|
||||
|
|
@ -414,6 +416,28 @@ erDiagram
|
|||
json usage
|
||||
varchar_36_ workflowId FK
|
||||
}
|
||||
"public.skill" {
|
||||
varchar_36_ agentId FK
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_36_ id
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.skill_version" {
|
||||
varchar_64_ contentHash
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid createdById FK
|
||||
varchar_1024_ description
|
||||
json frontmatter
|
||||
uuid id
|
||||
text instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
integer version
|
||||
}
|
||||
"public.trusted_source_identity" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
timestamp_3__with_time_zone lastSeenAt
|
||||
|
|
|
|||
58
docs/generated/sqlite-schema/README.md
generated
58
docs/generated/sqlite-schema/README.md
generated
|
|
@ -29,10 +29,12 @@ Auto-generated from the SQLite migrations in @n8n/db. Do not edit by hand.
|
|||
| [agent_message_queue](agent_message_queue.md) | 10 | | table |
|
||||
| [agent_plan](agent_plan.md) | 8 | | table |
|
||||
| [agent_plan_history](agent_plan_history.md) | 6 | | table |
|
||||
| [agent_skill_dependency](agent_skill_dependency.md) | 4 | | table |
|
||||
| [agent_task_definition](agent_task_definition.md) | 8 | | table |
|
||||
| [agent_task_run_lock](agent_task_run_lock.md) | 6 | | table |
|
||||
| [agent_task_snapshot](agent_task_snapshot.md) | 9 | | table |
|
||||
| [agent_thread_grants](agent_thread_grants.md) | 4 | | table |
|
||||
| [agent_version_skill_dependency](agent_version_skill_dependency.md) | 4 | | table |
|
||||
| [agent_workflow_dependency](agent_workflow_dependency.md) | 3 | | table |
|
||||
| [agents](agents.md) | 14 | | table |
|
||||
| [agents_memory_entries](agents_memory_entries.md) | 13 | | table |
|
||||
|
|
@ -132,6 +134,9 @@ 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_file](skill_file.md) | 5 | | table |
|
||||
| [skill_version](skill_version.md) | 11 | | table |
|
||||
| [tag_entity](tag_entity.md) | 4 | | table |
|
||||
| [test_case_execution](test_case_execution.md) | 16 | | table |
|
||||
| [test_run](test_run.md) | 16 | | table |
|
||||
|
|
@ -206,10 +211,16 @@ erDiagram
|
|||
"agent_message_queue" }o--o| "agent_execution" : "FOREIGN KEY (steeringExecutionId) REFERENCES agent_execution (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE"
|
||||
"agent_plan" }o--|| "agent_execution_threads" : "FOREIGN KEY (threadId) REFERENCES agent_execution_threads (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_plan_history" |o--|| "agent_plan" : "FOREIGN KEY (planId) REFERENCES agent_plan (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_skill_dependency" }o--o| "skill_version" : "FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE"
|
||||
"agent_skill_dependency" |o--|| "skill" : "FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_skill_dependency" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_task_definition" }o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_task_run_lock" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_task_snapshot" |o--|| "agent_history" : "FOREIGN KEY (versionId) REFERENCES agent_history (versionId) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_thread_grants" |o--|| "agent_execution_threads" : "FOREIGN KEY (threadId) REFERENCES agent_execution_threads (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_version_skill_dependency" }o--|| "skill_version" : "FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE"
|
||||
"agent_version_skill_dependency" |o--|| "skill" : "FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_version_skill_dependency" |o--|| "agent_history" : "FOREIGN KEY (agentVersionId) REFERENCES agent_history (versionId) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_workflow_dependency" |o--|| "workflow_entity" : "FOREIGN KEY (workflowId) REFERENCES workflow_entity (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_workflow_dependency" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agents" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
|
|
@ -356,6 +367,12 @@ erDiagram
|
|||
"shared_credentials" |o--|| "credentials_entity" : "FOREIGN KEY (credentialsId) REFERENCES credentials_entity (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"shared_workflow" |o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"shared_workflow" |o--|| "workflow_entity" : "FOREIGN KEY (workflowId) REFERENCES workflow_entity (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill" }o--o| "user" : "FOREIGN KEY (createdById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"skill" }o--o| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill" }o--o| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill_file" |o--|| "skill_version" : "FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill_version" }o--o| "user" : "FOREIGN KEY (createdById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"skill_version" }o--|| "skill" : "FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"test_case_execution" }o--|| "test_run" : "FOREIGN KEY (testRunId) REFERENCES test_run (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"test_case_execution" }o--o| "execution_entity" : "FOREIGN KEY (pastExecutionId) REFERENCES execution_entity (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"test_case_execution" }o--o| "execution_entity" : "FOREIGN KEY (executionId) REFERENCES execution_entity (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
|
|
@ -670,6 +687,12 @@ erDiagram
|
|||
varchar planId PK
|
||||
INTEGER revision PK
|
||||
}
|
||||
"agent_skill_dependency" {
|
||||
varchar_36_ agentId PK
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ skillId PK
|
||||
varchar skillVersionId FK
|
||||
}
|
||||
"agent_task_definition" {
|
||||
varchar_36_ agentId FK
|
||||
datetime_3_ createdAt
|
||||
|
|
@ -705,6 +728,12 @@ erDiagram
|
|||
varchar_128_ threadId PK
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"agent_version_skill_dependency" {
|
||||
varchar_36_ agentVersionId PK
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ skillId PK
|
||||
varchar skillVersionId FK
|
||||
}
|
||||
"agent_workflow_dependency" {
|
||||
varchar_36_ agentId PK
|
||||
datetime_3_ createdAt
|
||||
|
|
@ -1657,6 +1686,35 @@ erDiagram
|
|||
datetime_3_ updatedAt
|
||||
varchar_36_ workflowId PK
|
||||
}
|
||||
"skill" {
|
||||
varchar_36_ agentId FK
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_36_ id PK
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"skill_file" {
|
||||
TEXT content
|
||||
datetime_3_ createdAt
|
||||
varchar_512_ path PK
|
||||
varchar skillVersionId PK
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"skill_version" {
|
||||
varchar_64_ contentHash
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_1024_ description
|
||||
TEXT frontmatter
|
||||
varchar id PK
|
||||
TEXT instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
datetime_3_ updatedAt
|
||||
INTEGER version
|
||||
}
|
||||
"tag_entity" {
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ id PK
|
||||
|
|
|
|||
9
docs/generated/sqlite-schema/agent_history.md
generated
9
docs/generated/sqlite-schema/agent_history.md
generated
|
|
@ -23,7 +23,7 @@ CREATE TABLE "agent_history" ("versionId" varchar(36) PRIMARY KEY NOT NULL, "age
|
|||
| skills | TEXT | | true | | | |
|
||||
| tools | TEXT | | true | | | |
|
||||
| updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
| versionId | varchar(36) | | false | [agent_execution_threads](agent_execution_threads.md) [agent_task_snapshot](agent_task_snapshot.md) [agents](agents.md) | | |
|
||||
| versionId | varchar(36) | | false | [agent_execution_threads](agent_execution_threads.md) [agent_task_snapshot](agent_task_snapshot.md) [agent_version_skill_dependency](agent_version_skill_dependency.md) [agents](agents.md) | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
|
|
@ -50,6 +50,7 @@ erDiagram
|
|||
"agent_history" }o--o| "user" : "FOREIGN KEY (publishedById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"agent_execution_threads" }o--o| "agent_history" : "FOREIGN KEY (taskVersionId) REFERENCES agent_history (versionId) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"agent_task_snapshot" |o--|| "agent_history" : "FOREIGN KEY (versionId) REFERENCES agent_history (versionId) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_version_skill_dependency" |o--|| "agent_history" : "FOREIGN KEY (agentVersionId) REFERENCES agent_history (versionId) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agents" }o--o| "agent_history" : "FOREIGN KEY (activeVersionId) REFERENCES agent_history (versionId) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
|
||||
"agent_history" {
|
||||
|
|
@ -128,6 +129,12 @@ erDiagram
|
|||
datetime_3_ updatedAt
|
||||
varchar_36_ versionId PK
|
||||
}
|
||||
"agent_version_skill_dependency" {
|
||||
varchar_36_ agentVersionId PK
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ skillId PK
|
||||
varchar skillVersionId FK
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
|
|
|||
98
docs/generated/sqlite-schema/agent_skill_dependency.md
generated
Normal file
98
docs/generated/sqlite-schema/agent_skill_dependency.md
generated
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# agent_skill_dependency
|
||||
|
||||
## Description
|
||||
|
||||
<details>
|
||||
<summary><strong>Table Definition</strong></summary>
|
||||
|
||||
```sql
|
||||
CREATE TABLE "agent_skill_dependency" ("agentId" varchar(36) NOT NULL, "skillId" varchar(36) NOT NULL, "skillVersionId" varchar, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), CONSTRAINT "FK_72325bb13511a51847c53fcb377" FOREIGN KEY ("agentId") REFERENCES "agents" ("id") ON DELETE CASCADE, CONSTRAINT "FK_068f21aefba31ae621482f396dc" FOREIGN KEY ("skillId") REFERENCES "skill" ("id") ON DELETE CASCADE, CONSTRAINT "FK_7676571bd6f41d9e32f0d066fe9" FOREIGN KEY ("skillVersionId") REFERENCES "skill_version" ("id") ON DELETE NO ACTION, PRIMARY KEY ("agentId", "skillId"))
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| agentId | varchar(36) | | false | | [agents](agents.md) | |
|
||||
| createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
| skillId | varchar(36) | | false | | [skill](skill.md) | |
|
||||
| skillVersionId | varchar | | true | | [skill_version](skill_version.md) | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| - (Foreign key ID: 0) | FOREIGN KEY | FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE |
|
||||
| - (Foreign key ID: 1) | FOREIGN KEY | FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
|
||||
| - (Foreign key ID: 2) | FOREIGN KEY | FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
|
||||
| agentId | PRIMARY KEY | PRIMARY KEY (agentId) |
|
||||
| skillId | PRIMARY KEY | PRIMARY KEY (skillId) |
|
||||
| sqlite_autoindex_agent_skill_dependency_1 | PRIMARY KEY | PRIMARY KEY (agentId, skillId) |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| IDX_068f21aefba31ae621482f396d | CREATE INDEX "IDX_068f21aefba31ae621482f396d" ON "agent_skill_dependency" ("skillId") |
|
||||
| sqlite_autoindex_agent_skill_dependency_1 | PRIMARY KEY (agentId, skillId) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"agent_skill_dependency" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_skill_dependency" |o--|| "skill" : "FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_skill_dependency" }o--o| "skill_version" : "FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE"
|
||||
|
||||
"agent_skill_dependency" {
|
||||
varchar_36_ agentId PK
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ skillId PK
|
||||
varchar skillVersionId FK
|
||||
}
|
||||
"agents" {
|
||||
varchar_36_ activeVersionId FK
|
||||
boolean availableInMCP
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ id PK
|
||||
TEXT integrations
|
||||
varchar_128_ name
|
||||
varchar_255_ projectId FK
|
||||
INTEGER revision
|
||||
TEXT schema
|
||||
datetime_3_ setupCompletedAt
|
||||
TEXT skills
|
||||
TEXT tools
|
||||
datetime_3_ updatedAt
|
||||
varchar_36_ versionId
|
||||
}
|
||||
"skill" {
|
||||
varchar_36_ agentId FK
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_36_ id PK
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"skill_version" {
|
||||
varchar_64_ contentHash
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_1024_ description
|
||||
TEXT frontmatter
|
||||
varchar id PK
|
||||
TEXT instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
datetime_3_ updatedAt
|
||||
INTEGER version
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
94
docs/generated/sqlite-schema/agent_version_skill_dependency.md
generated
Normal file
94
docs/generated/sqlite-schema/agent_version_skill_dependency.md
generated
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# agent_version_skill_dependency
|
||||
|
||||
## Description
|
||||
|
||||
<details>
|
||||
<summary><strong>Table Definition</strong></summary>
|
||||
|
||||
```sql
|
||||
CREATE TABLE "agent_version_skill_dependency" ("agentVersionId" varchar(36) NOT NULL, "skillId" varchar(36) NOT NULL, "skillVersionId" varchar NOT NULL, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), CONSTRAINT "FK_97472baf9196d914d9cd9de6201" FOREIGN KEY ("agentVersionId") REFERENCES "agent_history" ("versionId") ON DELETE CASCADE, CONSTRAINT "FK_c494292243a7e510acd0331c3d1" FOREIGN KEY ("skillId") REFERENCES "skill" ("id") ON DELETE CASCADE, CONSTRAINT "FK_df63badfd7d3f2aa622ece471c4" FOREIGN KEY ("skillVersionId") REFERENCES "skill_version" ("id") ON DELETE NO ACTION, PRIMARY KEY ("agentVersionId", "skillId"))
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| agentVersionId | varchar(36) | | false | | [agent_history](agent_history.md) | |
|
||||
| createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
| skillId | varchar(36) | | false | | [skill](skill.md) | |
|
||||
| skillVersionId | varchar | | false | | [skill_version](skill_version.md) | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| - (Foreign key ID: 0) | FOREIGN KEY | FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE |
|
||||
| - (Foreign key ID: 1) | FOREIGN KEY | FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
|
||||
| - (Foreign key ID: 2) | FOREIGN KEY | FOREIGN KEY (agentVersionId) REFERENCES agent_history (versionId) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
|
||||
| agentVersionId | PRIMARY KEY | PRIMARY KEY (agentVersionId) |
|
||||
| skillId | PRIMARY KEY | PRIMARY KEY (skillId) |
|
||||
| sqlite_autoindex_agent_version_skill_dependency_1 | PRIMARY KEY | PRIMARY KEY (agentVersionId, skillId) |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| IDX_c494292243a7e510acd0331c3d | CREATE INDEX "IDX_c494292243a7e510acd0331c3d" ON "agent_version_skill_dependency" ("skillId") |
|
||||
| IDX_df63badfd7d3f2aa622ece471c | CREATE INDEX "IDX_df63badfd7d3f2aa622ece471c" ON "agent_version_skill_dependency" ("skillVersionId") |
|
||||
| sqlite_autoindex_agent_version_skill_dependency_1 | PRIMARY KEY (agentVersionId, skillId) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"agent_version_skill_dependency" |o--|| "agent_history" : "FOREIGN KEY (agentVersionId) REFERENCES agent_history (versionId) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_version_skill_dependency" |o--|| "skill" : "FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_version_skill_dependency" }o--|| "skill_version" : "FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE"
|
||||
|
||||
"agent_version_skill_dependency" {
|
||||
varchar_36_ agentVersionId PK
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ skillId PK
|
||||
varchar skillVersionId FK
|
||||
}
|
||||
"agent_history" {
|
||||
varchar_36_ agentId FK
|
||||
varchar_255_ author
|
||||
datetime_3_ createdAt
|
||||
varchar publishedById FK
|
||||
TEXT schema
|
||||
TEXT skills
|
||||
TEXT tools
|
||||
datetime_3_ updatedAt
|
||||
varchar_36_ versionId PK
|
||||
}
|
||||
"skill" {
|
||||
varchar_36_ agentId FK
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_36_ id PK
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"skill_version" {
|
||||
varchar_64_ contentHash
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_1024_ description
|
||||
TEXT frontmatter
|
||||
varchar id PK
|
||||
TEXT instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
datetime_3_ updatedAt
|
||||
INTEGER version
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
19
docs/generated/sqlite-schema/agents.md
generated
19
docs/generated/sqlite-schema/agents.md
generated
|
|
@ -18,7 +18,7 @@ CREATE TABLE "agents" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" varchar(128
|
|||
| activeVersionId | varchar(36) | | true | | [agent_history](agent_history.md) | |
|
||||
| availableInMCP | boolean | false | false | | | |
|
||||
| createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
| id | varchar(36) | | false | [agent_background_job](agent_background_job.md) [agent_channel_status](agent_channel_status.md) [agent_chat_attachments](agent_chat_attachments.md) [agent_chat_subscriptions](agent_chat_subscriptions.md) [agent_checkpoints](agent_checkpoints.md) [agent_credential_dependency](agent_credential_dependency.md) [agent_eval_dataset](agent_eval_dataset.md) [agent_execution_threads](agent_execution_threads.md) [agent_files](agent_files.md) [agent_history](agent_history.md) [agent_task_definition](agent_task_definition.md) [agent_task_run_lock](agent_task_run_lock.md) [agent_workflow_dependency](agent_workflow_dependency.md) [agents_memory_entries](agents_memory_entries.md) [agents_memory_entry_candidates](agents_memory_entry_candidates.md) [agents_memory_entry_locks](agents_memory_entry_locks.md) [agents_memory_entry_sources](agents_memory_entry_sources.md) [agents_observation_cursors](agents_observation_cursors.md) [agents_observation_locks](agents_observation_locks.md) [agents_observations](agents_observations.md) | | |
|
||||
| id | varchar(36) | | false | [agent_background_job](agent_background_job.md) [agent_channel_status](agent_channel_status.md) [agent_chat_attachments](agent_chat_attachments.md) [agent_chat_subscriptions](agent_chat_subscriptions.md) [agent_checkpoints](agent_checkpoints.md) [agent_credential_dependency](agent_credential_dependency.md) [agent_eval_dataset](agent_eval_dataset.md) [agent_execution_threads](agent_execution_threads.md) [agent_files](agent_files.md) [agent_history](agent_history.md) [agent_skill_dependency](agent_skill_dependency.md) [agent_task_definition](agent_task_definition.md) [agent_task_run_lock](agent_task_run_lock.md) [agent_workflow_dependency](agent_workflow_dependency.md) [agents_memory_entries](agents_memory_entries.md) [agents_memory_entry_candidates](agents_memory_entry_candidates.md) [agents_memory_entry_locks](agents_memory_entry_locks.md) [agents_memory_entry_sources](agents_memory_entry_sources.md) [agents_observation_cursors](agents_observation_cursors.md) [agents_observation_locks](agents_observation_locks.md) [agents_observations](agents_observations.md) [skill](skill.md) | | |
|
||||
| integrations | TEXT | '[]' | false | | | |
|
||||
| name | varchar(128) | | false | | | |
|
||||
| projectId | varchar(255) | | false | | [project](project.md) | |
|
||||
|
|
@ -63,6 +63,7 @@ erDiagram
|
|||
"agent_execution_threads" }o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_files" }o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_history" }o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_skill_dependency" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_task_definition" }o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_task_run_lock" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_workflow_dependency" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
|
|
@ -73,6 +74,7 @@ erDiagram
|
|||
"agents_observation_cursors" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agents_observation_locks" |o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agents_observations" }o--|| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill" }o--o| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agents" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
|
||||
"agents" {
|
||||
|
|
@ -218,6 +220,12 @@ erDiagram
|
|||
varchar_2_ storedAt
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"agent_skill_dependency" {
|
||||
varchar_36_ agentId PK
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ skillId PK
|
||||
varchar skillVersionId FK
|
||||
}
|
||||
"agent_task_definition" {
|
||||
varchar_36_ agentId FK
|
||||
datetime_3_ createdAt
|
||||
|
|
@ -322,6 +330,15 @@ erDiagram
|
|||
INTEGER tokenCount
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"skill" {
|
||||
varchar_36_ agentId FK
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_36_ id PK
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"project" {
|
||||
datetime_3_ createdAt
|
||||
varchar creatorId FK
|
||||
|
|
|
|||
12
docs/generated/sqlite-schema/project.md
generated
12
docs/generated/sqlite-schema/project.md
generated
|
|
@ -20,7 +20,7 @@ CREATE TABLE "project" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" varchar(25
|
|||
| customTelemetryTags | TEXT | '[]' | false | | | |
|
||||
| description | varchar(512) | | true | | | |
|
||||
| icon | TEXT | | true | | | |
|
||||
| id | varchar(36) | | false | [activity_event](activity_event.md) [agent_chat_attachments](agent_chat_attachments.md) [agent_execution_threads](agent_execution_threads.md) [agents](agents.md) [ai_preference](ai_preference.md) [data_table](data_table.md) [folder](folder.md) [insights_metadata](insights_metadata.md) [instance_ai_threads](instance_ai_threads.md) [project_pool_settings](project_pool_settings.md) [project_relation](project_relation.md) [project_secrets_provider_access](project_secrets_provider_access.md) [promotion_connection_project](promotion_connection_project.md) [role_mapping_rule_project](role_mapping_rule_project.md) [self_healing_investigation](self_healing_investigation.md) [self_healing_result](self_healing_result.md) [shared_credentials](shared_credentials.md) [shared_workflow](shared_workflow.md) [type_availability_policy_scope](type_availability_policy_scope.md) [variables](variables.md) [workflow_review_request](workflow_review_request.md) [workflow_suggestion](workflow_suggestion.md) | | |
|
||||
| id | varchar(36) | | false | [activity_event](activity_event.md) [agent_chat_attachments](agent_chat_attachments.md) [agent_execution_threads](agent_execution_threads.md) [agents](agents.md) [ai_preference](ai_preference.md) [data_table](data_table.md) [folder](folder.md) [insights_metadata](insights_metadata.md) [instance_ai_threads](instance_ai_threads.md) [project_pool_settings](project_pool_settings.md) [project_relation](project_relation.md) [project_secrets_provider_access](project_secrets_provider_access.md) [promotion_connection_project](promotion_connection_project.md) [role_mapping_rule_project](role_mapping_rule_project.md) [self_healing_investigation](self_healing_investigation.md) [self_healing_result](self_healing_result.md) [shared_credentials](shared_credentials.md) [shared_workflow](shared_workflow.md) [skill](skill.md) [type_availability_policy_scope](type_availability_policy_scope.md) [variables](variables.md) [workflow_review_request](workflow_review_request.md) [workflow_suggestion](workflow_suggestion.md) | | |
|
||||
| name | varchar(255) | | false | | | |
|
||||
| type | varchar(36) | | false | | | |
|
||||
| updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
|
|
@ -63,6 +63,7 @@ erDiagram
|
|||
"self_healing_result" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"shared_credentials" |o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"shared_workflow" |o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill" }o--o| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"type_availability_policy_scope" }o--o| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"variables" }o--o| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"workflow_review_request" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
|
|
@ -277,6 +278,15 @@ erDiagram
|
|||
datetime_3_ updatedAt
|
||||
varchar_36_ workflowId PK
|
||||
}
|
||||
"skill" {
|
||||
varchar_36_ agentId FK
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_36_ id PK
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"type_availability_policy_scope" {
|
||||
datetime_3_ createdAt
|
||||
varchar_16_ defaultAction
|
||||
|
|
|
|||
139
docs/generated/sqlite-schema/skill.md
generated
Normal file
139
docs/generated/sqlite-schema/skill.md
generated
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# skill
|
||||
|
||||
## Description
|
||||
|
||||
<details>
|
||||
<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)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| agentId | varchar(36) | | true | | [agents](agents.md) | |
|
||||
| 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) | | |
|
||||
| projectId | varchar(36) | | true | | [project](project.md) | |
|
||||
| source | varchar(16) | | false | | | |
|
||||
| updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| - | CHECK | CHECK ("agentId" IS NULL OR "projectId" IS NULL) |
|
||||
| - (Foreign key ID: 0) | FOREIGN KEY | FOREIGN KEY (createdById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE |
|
||||
| - (Foreign key ID: 1) | FOREIGN KEY | FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
|
||||
| - (Foreign key ID: 2) | FOREIGN KEY | FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
|
||||
| id | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
| sqlite_autoindex_skill_1 | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| IDX_402ed37bff0d3134f6e782455e | CREATE INDEX "IDX_402ed37bff0d3134f6e782455e" ON "skill" ("projectId") |
|
||||
| IDX_7d4fb6af107e507d9a86293cf9 | CREATE INDEX "IDX_7d4fb6af107e507d9a86293cf9" ON "skill" ("agentId") |
|
||||
| sqlite_autoindex_skill_1 | PRIMARY KEY (id) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"skill" }o--o| "agents" : "FOREIGN KEY (agentId) REFERENCES agents (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill" }o--o| "user" : "FOREIGN KEY (createdById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"agent_skill_dependency" |o--|| "skill" : "FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"agent_version_skill_dependency" |o--|| "skill" : "FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill_version" }o--|| "skill" : "FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill" }o--o| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
|
||||
"skill" {
|
||||
varchar_36_ agentId FK
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_36_ id PK
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"agents" {
|
||||
varchar_36_ activeVersionId FK
|
||||
boolean availableInMCP
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ id PK
|
||||
TEXT integrations
|
||||
varchar_128_ name
|
||||
varchar_255_ projectId FK
|
||||
INTEGER revision
|
||||
TEXT schema
|
||||
datetime_3_ setupCompletedAt
|
||||
TEXT skills
|
||||
TEXT tools
|
||||
datetime_3_ updatedAt
|
||||
varchar_36_ versionId
|
||||
}
|
||||
"user" {
|
||||
datetime_3_ createdAt
|
||||
boolean disabled
|
||||
varchar_255_ email
|
||||
varchar_32_ firstName
|
||||
varchar id PK
|
||||
date lastActiveAt
|
||||
varchar_32_ lastName
|
||||
boolean mfaEnabled
|
||||
TEXT mfaRecoveryCodes
|
||||
TEXT mfaSecret
|
||||
varchar password
|
||||
TEXT personalizationAnswers
|
||||
varchar_128_ roleSlug FK
|
||||
TEXT settings
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"agent_skill_dependency" {
|
||||
varchar_36_ agentId PK
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ skillId PK
|
||||
varchar skillVersionId FK
|
||||
}
|
||||
"agent_version_skill_dependency" {
|
||||
varchar_36_ agentVersionId PK
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ skillId PK
|
||||
varchar skillVersionId FK
|
||||
}
|
||||
"skill_version" {
|
||||
varchar_64_ contentHash
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_1024_ description
|
||||
TEXT frontmatter
|
||||
varchar id PK
|
||||
TEXT instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
datetime_3_ updatedAt
|
||||
INTEGER version
|
||||
}
|
||||
"project" {
|
||||
datetime_3_ createdAt
|
||||
varchar creatorId FK
|
||||
TEXT customTelemetryTags
|
||||
varchar_512_ description
|
||||
TEXT icon
|
||||
varchar_36_ id PK
|
||||
varchar_255_ name
|
||||
varchar_36_ type
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
70
docs/generated/sqlite-schema/skill_file.md
generated
Normal file
70
docs/generated/sqlite-schema/skill_file.md
generated
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# skill_file
|
||||
|
||||
## Description
|
||||
|
||||
<details>
|
||||
<summary><strong>Table Definition</strong></summary>
|
||||
|
||||
```sql
|
||||
CREATE TABLE "skill_file" ("skillVersionId" varchar NOT NULL, "path" varchar(512) NOT NULL, "content" text NOT NULL, "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 "FK_e9ab50b795ec8858702b7333d13" FOREIGN KEY ("skillVersionId") REFERENCES "skill_version" ("id") ON DELETE CASCADE, PRIMARY KEY ("skillVersionId", "path"))
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| content | TEXT | | false | | | |
|
||||
| createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
| path | varchar(512) | | false | | | |
|
||||
| skillVersionId | varchar | | false | | [skill_version](skill_version.md) | |
|
||||
| updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| - (Foreign key ID: 0) | FOREIGN KEY | FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
|
||||
| path | PRIMARY KEY | PRIMARY KEY (path) |
|
||||
| skillVersionId | PRIMARY KEY | PRIMARY KEY (skillVersionId) |
|
||||
| sqlite_autoindex_skill_file_1 | PRIMARY KEY | PRIMARY KEY (skillVersionId, path) |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| sqlite_autoindex_skill_file_1 | PRIMARY KEY (skillVersionId, path) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"skill_file" |o--|| "skill_version" : "FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
|
||||
"skill_file" {
|
||||
TEXT content
|
||||
datetime_3_ createdAt
|
||||
varchar_512_ path PK
|
||||
varchar skillVersionId PK
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"skill_version" {
|
||||
varchar_64_ contentHash
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_1024_ description
|
||||
TEXT frontmatter
|
||||
varchar id PK
|
||||
TEXT instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
datetime_3_ updatedAt
|
||||
INTEGER version
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
121
docs/generated/sqlite-schema/skill_version.md
generated
Normal file
121
docs/generated/sqlite-schema/skill_version.md
generated
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
# skill_version
|
||||
|
||||
## Description
|
||||
|
||||
<details>
|
||||
<summary><strong>Table Definition</strong></summary>
|
||||
|
||||
```sql
|
||||
CREATE TABLE "skill_version" ("id" varchar PRIMARY KEY NOT NULL, "skillId" varchar(36) NOT NULL, "version" integer NOT NULL, "name" varchar(128) NOT NULL, "description" varchar(1024) NOT NULL, "instructions" text NOT NULL, "frontmatter" text, "contentHash" varchar(64) 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 "UQ_93bb78a1eb4e0890fac6b69df95" UNIQUE ("skillId", "version"), CONSTRAINT "CHK_skill_version_version" CHECK ("version" > 0), CONSTRAINT "FK_f87064c0a29efd2d5195328d91a" FOREIGN KEY ("skillId") REFERENCES "skill" ("id") ON DELETE CASCADE, CONSTRAINT "FK_9565d0fbf32cd463f9b47c7adab" FOREIGN KEY ("createdById") REFERENCES "user" ("id") ON DELETE SET NULL)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| contentHash | varchar(64) | | false | | | |
|
||||
| createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
| createdById | varchar | | true | | [user](user.md) | |
|
||||
| description | varchar(1024) | | false | | | |
|
||||
| frontmatter | TEXT | | true | | | |
|
||||
| id | varchar | | false | [agent_skill_dependency](agent_skill_dependency.md) [agent_version_skill_dependency](agent_version_skill_dependency.md) [skill_file](skill_file.md) | | |
|
||||
| instructions | TEXT | | false | | | |
|
||||
| name | varchar(128) | | false | | | |
|
||||
| skillId | varchar(36) | | false | | [skill](skill.md) | |
|
||||
| updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
| version | INTEGER | | false | | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| - | CHECK | CHECK ("version" > 0) |
|
||||
| - (Foreign key ID: 0) | FOREIGN KEY | FOREIGN KEY (createdById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE |
|
||||
| - (Foreign key ID: 1) | FOREIGN KEY | FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
|
||||
| id | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
| sqlite_autoindex_skill_version_1 | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
| sqlite_autoindex_skill_version_2 | UNIQUE | UNIQUE (skillId, version) |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| sqlite_autoindex_skill_version_1 | PRIMARY KEY (id) |
|
||||
| sqlite_autoindex_skill_version_2 | UNIQUE (skillId, version) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"skill_version" }o--o| "user" : "FOREIGN KEY (createdById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"agent_skill_dependency" }o--o| "skill_version" : "FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE"
|
||||
"agent_version_skill_dependency" }o--|| "skill_version" : "FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE NO ACTION MATCH NONE"
|
||||
"skill_file" |o--|| "skill_version" : "FOREIGN KEY (skillVersionId) REFERENCES skill_version (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill_version" }o--|| "skill" : "FOREIGN KEY (skillId) REFERENCES skill (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
|
||||
"skill_version" {
|
||||
varchar_64_ contentHash
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_1024_ description
|
||||
TEXT frontmatter
|
||||
varchar id PK
|
||||
TEXT instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
datetime_3_ updatedAt
|
||||
INTEGER version
|
||||
}
|
||||
"user" {
|
||||
datetime_3_ createdAt
|
||||
boolean disabled
|
||||
varchar_255_ email
|
||||
varchar_32_ firstName
|
||||
varchar id PK
|
||||
date lastActiveAt
|
||||
varchar_32_ lastName
|
||||
boolean mfaEnabled
|
||||
TEXT mfaRecoveryCodes
|
||||
TEXT mfaSecret
|
||||
varchar password
|
||||
TEXT personalizationAnswers
|
||||
varchar_128_ roleSlug FK
|
||||
TEXT settings
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"agent_skill_dependency" {
|
||||
varchar_36_ agentId PK
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ skillId PK
|
||||
varchar skillVersionId FK
|
||||
}
|
||||
"agent_version_skill_dependency" {
|
||||
varchar_36_ agentVersionId PK
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ skillId PK
|
||||
varchar skillVersionId FK
|
||||
}
|
||||
"skill_file" {
|
||||
TEXT content
|
||||
datetime_3_ createdAt
|
||||
varchar_512_ path PK
|
||||
varchar skillVersionId PK
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"skill" {
|
||||
varchar_36_ agentId FK
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_36_ id PK
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
26
docs/generated/sqlite-schema/user.md
generated
26
docs/generated/sqlite-schema/user.md
generated
|
|
@ -19,7 +19,7 @@ CREATE TABLE "user" ("id" varchar PRIMARY KEY, "email" varchar(255), "firstName"
|
|||
| disabled | boolean | FALSE | false | | | |
|
||||
| email | varchar(255) | | true | | | |
|
||||
| firstName | varchar(32) | | true | | | |
|
||||
| id | varchar | | true | [activity_event](activity_event.md) [agent_eval_dataset](agent_eval_dataset.md) [agent_eval_rating](agent_eval_rating.md) [agent_eval_run](agent_eval_run.md) [agent_execution_threads](agent_execution_threads.md) [agent_history](agent_history.md) [ai_preference](ai_preference.md) [auth_identity](auth_identity.md) [chat_hub_agents](chat_hub_agents.md) [chat_hub_sessions](chat_hub_sessions.md) [chat_hub_tools](chat_hub_tools.md) [dynamic_credential_user_entry](dynamic_credential_user_entry.md) [evaluation_collection](evaluation_collection.md) [idempotency_key](idempotency_key.md) [instance_ai_mcp_registry_connections](instance_ai_mcp_registry_connections.md) [instance_ai_pending_confirmations](instance_ai_pending_confirmations.md) [instance_ai_thread_grants](instance_ai_thread_grants.md) [instance_ai_thread_tabs](instance_ai_thread_tabs.md) [migration_workflow_owner](migration_workflow_owner.md) [oauth_access_tokens](oauth_access_tokens.md) [oauth_authorization_codes](oauth_authorization_codes.md) [oauth_refresh_tokens](oauth_refresh_tokens.md) [oauth_user_consents](oauth_user_consents.md) [project](project.md) [project_relation](project_relation.md) [self_healing_investigation](self_healing_investigation.md) [self_healing_result](self_healing_result.md) [trusted_source_identity](trusted_source_identity.md) [user_api_keys](user_api_keys.md) [user_favorites](user_favorites.md) [workflow_builder_session](workflow_builder_session.md) [workflow_publish_history](workflow_publish_history.md) [workflow_review_activity](workflow_review_activity.md) [workflow_review_activity_comment](workflow_review_activity_comment.md) [workflow_review_request](workflow_review_request.md) [workflow_review_request_authors](workflow_review_request_authors.md) [workflow_review_request_reviewers](workflow_review_request_reviewers.md) [workflow_suggestion](workflow_suggestion.md) [workflow_suggestion_activity](workflow_suggestion_activity.md) | | |
|
||||
| id | varchar | | true | [activity_event](activity_event.md) [agent_eval_dataset](agent_eval_dataset.md) [agent_eval_rating](agent_eval_rating.md) [agent_eval_run](agent_eval_run.md) [agent_execution_threads](agent_execution_threads.md) [agent_history](agent_history.md) [ai_preference](ai_preference.md) [auth_identity](auth_identity.md) [chat_hub_agents](chat_hub_agents.md) [chat_hub_sessions](chat_hub_sessions.md) [chat_hub_tools](chat_hub_tools.md) [dynamic_credential_user_entry](dynamic_credential_user_entry.md) [evaluation_collection](evaluation_collection.md) [idempotency_key](idempotency_key.md) [instance_ai_mcp_registry_connections](instance_ai_mcp_registry_connections.md) [instance_ai_pending_confirmations](instance_ai_pending_confirmations.md) [instance_ai_thread_grants](instance_ai_thread_grants.md) [instance_ai_thread_tabs](instance_ai_thread_tabs.md) [migration_workflow_owner](migration_workflow_owner.md) [oauth_access_tokens](oauth_access_tokens.md) [oauth_authorization_codes](oauth_authorization_codes.md) [oauth_refresh_tokens](oauth_refresh_tokens.md) [oauth_user_consents](oauth_user_consents.md) [project](project.md) [project_relation](project_relation.md) [self_healing_investigation](self_healing_investigation.md) [self_healing_result](self_healing_result.md) [skill](skill.md) [skill_version](skill_version.md) [trusted_source_identity](trusted_source_identity.md) [user_api_keys](user_api_keys.md) [user_favorites](user_favorites.md) [workflow_builder_session](workflow_builder_session.md) [workflow_publish_history](workflow_publish_history.md) [workflow_review_activity](workflow_review_activity.md) [workflow_review_activity_comment](workflow_review_activity_comment.md) [workflow_review_request](workflow_review_request.md) [workflow_review_request_authors](workflow_review_request_authors.md) [workflow_review_request_reviewers](workflow_review_request_reviewers.md) [workflow_suggestion](workflow_suggestion.md) [workflow_suggestion_activity](workflow_suggestion_activity.md) | | |
|
||||
| lastActiveAt | date | | true | | | |
|
||||
| lastName | varchar(32) | | true | | | |
|
||||
| mfaEnabled | boolean | FALSE | false | | | |
|
||||
|
|
@ -83,6 +83,8 @@ erDiagram
|
|||
"self_healing_investigation" }o--|| "user" : "FOREIGN KEY (backgroundUserId) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"self_healing_result" }o--o| "user" : "FOREIGN KEY (dismissedById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"self_healing_result" }o--|| "user" : "FOREIGN KEY (backgroundUserId) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"skill" }o--o| "user" : "FOREIGN KEY (createdById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"skill_version" }o--o| "user" : "FOREIGN KEY (createdById) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"trusted_source_identity" }o--|| "user" : "FOREIGN KEY (userId) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"user_api_keys" }o--|| "user" : "FOREIGN KEY (userId) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"user_favorites" }o--|| "user" : "FOREIGN KEY (userId) REFERENCES user (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
|
|
@ -420,6 +422,28 @@ erDiagram
|
|||
TEXT usage
|
||||
varchar_36_ workflowId FK
|
||||
}
|
||||
"skill" {
|
||||
varchar_36_ agentId FK
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_36_ id PK
|
||||
varchar_36_ projectId FK
|
||||
varchar_16_ source
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"skill_version" {
|
||||
varchar_64_ contentHash
|
||||
datetime_3_ createdAt
|
||||
varchar createdById FK
|
||||
varchar_1024_ description
|
||||
TEXT frontmatter
|
||||
varchar id PK
|
||||
TEXT instructions
|
||||
varchar_128_ name
|
||||
varchar_36_ skillId FK
|
||||
datetime_3_ updatedAt
|
||||
INTEGER version
|
||||
}
|
||||
"trusted_source_identity" {
|
||||
datetime_3_ createdAt
|
||||
datetime_3_ lastSeenAt
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-monorepo",
|
||||
"version": "2.43.0",
|
||||
"version": "2.44.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=24.0.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@n8n/agents",
|
||||
"version": "0.28.0",
|
||||
"version": "0.29.0",
|
||||
"description": "AI agent SDK for n8n's code-first execution engine",
|
||||
"engines": {
|
||||
"node": ">=24.0.0"
|
||||
|
|
|
|||
|
|
@ -753,6 +753,27 @@ describe('createDelegateSubAgentTool', () => {
|
|||
expect(runSubAgent.mock.calls[0]?.[0]).not.toHaveProperty('parentAbortSignal');
|
||||
expect(runSubAgent.mock.calls[0]?.[0]).not.toHaveProperty('parentExecutionCounter');
|
||||
expect(runSubAgent.mock.calls[0]?.[0]).not.toHaveProperty('parentTelemetry');
|
||||
expect(runSubAgent.mock.calls[0]?.[0]).not.toHaveProperty('parentOnGuardrailNotice');
|
||||
});
|
||||
|
||||
it('forwards the guardrail notice sink from the tool context to the runner callback', async () => {
|
||||
const runSubAgent = vi
|
||||
.fn<DelegateSubAgentRunner>()
|
||||
.mockResolvedValue({ status: 'completed', taskPath: '/root/research_api', answer: 'done' });
|
||||
const tool = createDelegateSubAgentTool({ runSubAgent });
|
||||
const parentOnGuardrailNotice = vi.fn();
|
||||
|
||||
await tool.handler?.(input, {
|
||||
runId: 'parent-run-1',
|
||||
onGuardrailNotice: parentOnGuardrailNotice,
|
||||
});
|
||||
|
||||
expect(runSubAgent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ parentOnGuardrailNotice }),
|
||||
expect.objectContaining({
|
||||
runInlineSubAgent: expect.any(Function),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('forwards the parent telemetry from the tool context to the runner callback', async () => {
|
||||
|
|
@ -1203,6 +1224,66 @@ describe('generateResultToDelegateSubAgentOutput', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('maps a guardrail stop to failed with the session cap reason', () => {
|
||||
const result: GenerateResult = {
|
||||
runId: 'child-run-6',
|
||||
messages: [],
|
||||
finishReason: 'guardrail',
|
||||
guardrail: { code: 'budget.session' },
|
||||
getState: () => ({
|
||||
status: 'failed',
|
||||
messageList: { messages: [], historyIds: [], inputIds: [], responseIds: [] },
|
||||
pendingToolCalls: {},
|
||||
}),
|
||||
};
|
||||
|
||||
expect(generateResultToDelegateSubAgentOutput('/root/x_0', result)).toMatchObject({
|
||||
status: 'failed',
|
||||
finishReason: 'guardrail',
|
||||
error: 'The sub-agent run was stopped: the session cost cap is spent.',
|
||||
});
|
||||
});
|
||||
|
||||
it('maps a monthly budget stop to failed with the monthly reason', () => {
|
||||
const result: GenerateResult = {
|
||||
runId: 'child-run-7',
|
||||
messages: [],
|
||||
finishReason: 'guardrail',
|
||||
guardrail: { code: 'budget.monthly' },
|
||||
getState: () => ({
|
||||
status: 'failed',
|
||||
messageList: { messages: [], historyIds: [], inputIds: [], responseIds: [] },
|
||||
pendingToolCalls: {},
|
||||
}),
|
||||
};
|
||||
|
||||
expect(generateResultToDelegateSubAgentOutput('/root/x_0', result)).toMatchObject({
|
||||
status: 'failed',
|
||||
finishReason: 'guardrail',
|
||||
error: 'The sub-agent run was stopped: the monthly budget is spent.',
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps a recorded error over the guardrail stop reason', () => {
|
||||
const result: GenerateResult = {
|
||||
runId: 'child-run-8',
|
||||
messages: [],
|
||||
finishReason: 'guardrail',
|
||||
guardrail: { code: 'budget.session' },
|
||||
error: new Error('boom'),
|
||||
getState: () => ({
|
||||
status: 'failed',
|
||||
messageList: { messages: [], historyIds: [], inputIds: [], responseIds: [] },
|
||||
pendingToolCalls: {},
|
||||
}),
|
||||
};
|
||||
|
||||
expect(generateResultToDelegateSubAgentOutput('/root/x_0', result)).toMatchObject({
|
||||
status: 'failed',
|
||||
error: 'boom',
|
||||
});
|
||||
});
|
||||
|
||||
it('maps a cancelled child run to cancelled, even though it also reports an error', () => {
|
||||
const result: GenerateResult = {
|
||||
runId: 'child-run-5',
|
||||
|
|
|
|||
|
|
@ -625,4 +625,37 @@ describe('McpConnection — approval declaration', () => {
|
|||
|
||||
expect(conn.declaresApproval()).toBe(true);
|
||||
});
|
||||
|
||||
it('does not declare approval for non-interrupting tool configuration', () => {
|
||||
const conn = new McpConnection({
|
||||
name: 's1',
|
||||
url: 'https://example.test/mcp',
|
||||
configureTools: () => ({ toolFilter: { mode: 'exclude', tools: ['x'] } }),
|
||||
configureToolsNonInterrupting: true,
|
||||
});
|
||||
|
||||
expect(conn.declaresApproval()).toBe(false);
|
||||
});
|
||||
|
||||
it('ignores the non-interrupting flag when there is no tool configuration', () => {
|
||||
const conn = new McpConnection({
|
||||
name: 's1',
|
||||
url: 'https://example.test/mcp',
|
||||
configureToolsNonInterrupting: true,
|
||||
});
|
||||
|
||||
expect(conn.declaresApproval()).toBe(false);
|
||||
});
|
||||
|
||||
it('still declares approval for a static requireApproval with non-interrupting tool configuration', () => {
|
||||
const conn = new McpConnection({
|
||||
name: 's1',
|
||||
url: 'https://example.test/mcp',
|
||||
configureTools: () => ({}),
|
||||
configureToolsNonInterrupting: true,
|
||||
requireApproval: ['delete'],
|
||||
});
|
||||
|
||||
expect(conn.declaresApproval()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -302,9 +302,9 @@ export class McpConnection {
|
|||
* without requiring a network connection.
|
||||
*/
|
||||
declaresApproval(): boolean {
|
||||
const { configureTools, requireApproval } = this.config;
|
||||
const { configureTools, configureToolsNonInterrupting, requireApproval } = this.config;
|
||||
return (
|
||||
configureTools !== undefined ||
|
||||
(configureTools !== undefined && !configureToolsNonInterrupting) ||
|
||||
requireApproval === true ||
|
||||
(Array.isArray(requireApproval) && requireApproval.length > 0)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -240,6 +240,9 @@ function createDelegateSubAgentRequest(
|
|||
? { parentExecutionCounter: withoutMessageCount(ctx.executionCounter) }
|
||||
: {}),
|
||||
...(ctx.parentTelemetry !== undefined ? { parentTelemetry: ctx.parentTelemetry } : {}),
|
||||
...(ctx.onGuardrailNotice !== undefined
|
||||
? { parentOnGuardrailNotice: ctx.onGuardrailNotice }
|
||||
: {}),
|
||||
...(policy !== undefined ? { policy } : {}),
|
||||
};
|
||||
}
|
||||
|
|
@ -477,6 +480,10 @@ function resolveDelegateSubAgentStatus(
|
|||
// error, so the state decides which of the two this really was.
|
||||
return result.getState().status === 'cancelled' ? 'cancelled' : 'failed';
|
||||
}
|
||||
// A child agent hit a guardrail stop.
|
||||
if (result.finishReason === 'guardrail') {
|
||||
return 'failed';
|
||||
}
|
||||
if (result.pendingSuspend !== undefined && result.pendingSuspend.length > 0) {
|
||||
return 'suspended';
|
||||
}
|
||||
|
|
@ -497,6 +504,17 @@ export function failedDelegatedChildSuspendOutput(
|
|||
};
|
||||
}
|
||||
|
||||
function guardrailStopMessage(code: string | undefined): string {
|
||||
switch (code) {
|
||||
case 'budget.session':
|
||||
return 'The sub-agent run was stopped: the session cost cap is spent.';
|
||||
case 'budget.monthly':
|
||||
return 'The sub-agent run was stopped: the monthly budget is spent.';
|
||||
default:
|
||||
return `The sub-agent run was stopped by a guardrail (${code ?? 'unknown code'}).`;
|
||||
}
|
||||
}
|
||||
|
||||
/** Map an agent {@link GenerateResult} into the delegate tool's output shape. */
|
||||
export function generateResultToDelegateSubAgentOutput(
|
||||
taskPath: SubAgentTaskPath,
|
||||
|
|
@ -518,7 +536,11 @@ export function generateResultToDelegateSubAgentOutput(
|
|||
}
|
||||
: {}),
|
||||
...(result.finishReason !== undefined ? { finishReason: result.finishReason } : {}),
|
||||
...(result.error !== undefined ? { error: stringifyUnknown(result.error) } : {}),
|
||||
...(result.error !== undefined
|
||||
? { error: stringifyUnknown(result.error) }
|
||||
: result.finishReason === 'guardrail'
|
||||
? { error: guardrailStopMessage(result.guardrail?.code) }
|
||||
: {}),
|
||||
...(status === 'suspended' && result.pendingSuspend !== undefined
|
||||
? { pendingSuspend: result.pendingSuspend }
|
||||
: {}),
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ function createToolContext(
|
|||
abortSignal: executionContext.abortSignal,
|
||||
executionCounter: executionContext.executionCounter,
|
||||
approvalContext: executionContext.approvalContext,
|
||||
onGuardrailNotice: executionContext.onGuardrailNotice,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -401,6 +401,7 @@ export class ToolCallRunner {
|
|||
continuation,
|
||||
resumeSchema,
|
||||
onSuspend,
|
||||
onGuardrailNotice: params.guardrails?.onNotice,
|
||||
}),
|
||||
abortSignal,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -191,8 +191,9 @@ export class McpClient {
|
|||
|
||||
/**
|
||||
* Returns true when any configured server declares per-server approval
|
||||
* requirements (`requireApproval: true` or a non-empty `requireApproval`
|
||||
* string array). Does NOT require a network connection.
|
||||
* requirements (`requireApproval: true`, a non-empty `requireApproval`
|
||||
* string array, or a `configureTools` callback that is not marked
|
||||
* `configureToolsNonInterrupting`). Does NOT require a network connection.
|
||||
*
|
||||
* Used by the Agent builder to validate checkpoint configuration before
|
||||
* attempting to connect.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import type {
|
|||
StreamChunk,
|
||||
TokenUsage,
|
||||
} from '../sdk/agent';
|
||||
import type { GuardrailNotice } from '../sdk/guardrail';
|
||||
import type { BuiltProviderTool, ToolContext } from '../sdk/tool';
|
||||
import type { BuiltTelemetry } from '../telemetry';
|
||||
import type { JSONObject, JSONValue } from '../utils/json';
|
||||
|
|
@ -194,6 +195,7 @@ export interface DelegateSubAgentRequest extends DelegateSubAgentInput {
|
|||
childCount: number;
|
||||
/** Effective policy for this delegation. */
|
||||
policy?: DelegateSubAgentPolicy;
|
||||
parentOnGuardrailNotice?: (notice: GuardrailNotice) => void;
|
||||
}
|
||||
|
||||
/** The result a delegation returns to the parent model and to lifecycle events. */
|
||||
|
|
|
|||
|
|
@ -51,4 +51,10 @@ export interface GuardrailsOptions {
|
|||
hooks: ModelGuardrail[];
|
||||
agentId?: string;
|
||||
threadId?: string;
|
||||
onNotice?: (notice: GuardrailNotice) => void;
|
||||
}
|
||||
|
||||
export type GuardrailNotice = {
|
||||
code: string;
|
||||
agentId?: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -81,6 +81,13 @@ export interface McpServerConfig {
|
|||
*/
|
||||
configureTools?: (tools: McpToolDescriptor[]) => McpToolConfiguration;
|
||||
|
||||
/**
|
||||
* Set when `configureTools` never returns `requireApproval`. The agent then
|
||||
* does not need checkpoint storage for this server just because `configureTools`
|
||||
* exists. A static `requireApproval` still counts.
|
||||
*/
|
||||
configureToolsNonInterrupting?: boolean;
|
||||
|
||||
/**
|
||||
* Custom fetch implementation used by URL-based transports (SSE,
|
||||
* StreamableHTTP).
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { JSONSchema7 } from 'json-schema';
|
|||
import type { ZodType } from 'zod';
|
||||
|
||||
import type { AgentExecutionCounter } from './agent';
|
||||
import type { GuardrailNotice } from './guardrail';
|
||||
import type { McpToolAnnotations } from './mcp';
|
||||
import type { AgentMessage } from './message';
|
||||
import type { ApprovalResumePayload } from '../../sdk/tool';
|
||||
|
|
@ -64,6 +65,8 @@ export interface ToolExecutionContext {
|
|||
continuation?: JSONValue;
|
||||
/** Resume schema restored from persistence for a resumed tool call. */
|
||||
resumeSchema?: ToolSuspendOptions['resumeSchema'];
|
||||
|
||||
onGuardrailNotice?: (notice: GuardrailNotice) => void;
|
||||
}
|
||||
|
||||
export interface ToolContext {
|
||||
|
|
@ -85,6 +88,7 @@ export interface ToolContext {
|
|||
/** Aggregate execution counter for usage telemetry inherited from the current agent run. */
|
||||
executionCounter?: ToolExecutionContext['executionCounter'];
|
||||
approvalContext?: ToolExecutionContext['approvalContext'];
|
||||
onGuardrailNotice?: (notice: GuardrailNotice) => void;
|
||||
}
|
||||
|
||||
export interface InterruptibleToolContext<S = unknown, R = unknown> {
|
||||
|
|
@ -122,6 +126,7 @@ export interface InterruptibleToolContext<S = unknown, R = unknown> {
|
|||
continuation?: JSONValue;
|
||||
/** Resume schema persisted for this suspension. */
|
||||
resumeSchema?: ToolSuspendOptions['resumeSchema'];
|
||||
onGuardrailNotice?: (notice: GuardrailNotice) => void;
|
||||
}
|
||||
|
||||
export interface ToolCancellationContext extends ToolContext {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@n8n/ai-node-sdk",
|
||||
"version": "0.33.0",
|
||||
"version": "0.34.0",
|
||||
"description": "SDK for building AI nodes in n8n",
|
||||
"types": "dist/esm/index.d.ts",
|
||||
"module": "dist/esm/index.js",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@n8n/ai-utilities",
|
||||
"version": "0.36.0",
|
||||
"version": "0.37.0",
|
||||
"description": "Utilities for building AI nodes in n8n",
|
||||
"types": "dist/esm/index.d.ts",
|
||||
"module": "dist/esm/index.js",
|
||||
|
|
|
|||
|
|
@ -153,6 +153,88 @@ describe('model-discovery', () => {
|
|||
{ id: 'models/gemini-2.5-pro', name: 'models/gemini-2.5-pro' },
|
||||
]);
|
||||
});
|
||||
|
||||
it("labels models with the API's displayName, else the id", async () => {
|
||||
const fetch = mockFetch({
|
||||
models: [
|
||||
{
|
||||
name: 'models/gemini-2.5-pro',
|
||||
displayName: 'Gemini 2.5 Pro',
|
||||
supportedGenerationMethods: ['generateContent'],
|
||||
},
|
||||
{
|
||||
name: 'models/gemini-2.5-flash',
|
||||
displayName: ' ',
|
||||
supportedGenerationMethods: ['generateContent'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const models = await listModelsForProvider('google', { apiKey: 'g-key', fetch });
|
||||
|
||||
expect(models).toEqual([
|
||||
{ id: 'models/gemini-2.5-pro', name: 'Gemini 2.5 Pro' },
|
||||
{ id: 'models/gemini-2.5-flash', name: 'models/gemini-2.5-flash' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('lists models with a priority first and passes the priority on', async () => {
|
||||
const fetch = mockFetch({
|
||||
models: [
|
||||
{ name: 'models/gemini-2.5-pro', supportedGenerationMethods: ['generateContent'] },
|
||||
{
|
||||
name: 'models/gemini-featured',
|
||||
priority: 10,
|
||||
supportedGenerationMethods: ['generateContent'],
|
||||
},
|
||||
{ name: 'models/gemini-2.5-flash', supportedGenerationMethods: ['generateContent'] },
|
||||
],
|
||||
});
|
||||
|
||||
const models = await listModelsForProvider('google', { apiKey: 'g-key', fetch });
|
||||
|
||||
expect(models).toEqual([
|
||||
{ id: 'models/gemini-featured', name: 'models/gemini-featured', priority: 10 },
|
||||
{ id: 'models/gemini-2.5-flash', name: 'models/gemini-2.5-flash' },
|
||||
{ id: 'models/gemini-2.5-pro', name: 'models/gemini-2.5-pro' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('google empty response', () => {
|
||||
it('returns no models when the response has no models list', async () => {
|
||||
const fetch = mockFetch({});
|
||||
|
||||
await expect(listModelsForProvider('google', { apiKey: 'g-key', fetch })).resolves.toEqual(
|
||||
[],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('google priority', () => {
|
||||
it('drops a priority that is not a finite number', async () => {
|
||||
const fetch = mockFetch({
|
||||
models: [
|
||||
{
|
||||
name: 'models/gemini-2.5-pro',
|
||||
priority: Number.POSITIVE_INFINITY,
|
||||
supportedGenerationMethods: ['generateContent'],
|
||||
},
|
||||
{
|
||||
name: 'models/gemini-2.5-flash',
|
||||
priority: '5',
|
||||
supportedGenerationMethods: ['generateContent'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const models = await listModelsForProvider('google', { apiKey: 'g-key', fetch });
|
||||
|
||||
expect(models).toEqual([
|
||||
{ id: 'models/gemini-2.5-flash', name: 'models/gemini-2.5-flash' },
|
||||
{ id: 'models/gemini-2.5-pro', name: 'models/gemini-2.5-pro' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('groq', () => {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,14 @@ export {
|
|||
} from './providers/azure';
|
||||
export { listCohereModels } from './providers/cohere';
|
||||
export { listDeepSeekModels } from './providers/deepseek';
|
||||
export { listGoogleModels, shouldIncludeGoogleModel } from './providers/google';
|
||||
export {
|
||||
getGoogleModelLabel,
|
||||
listGoogleModels,
|
||||
parseGoogleModels,
|
||||
shouldIncludeGoogleModel,
|
||||
type GoogleModel,
|
||||
} from './providers/google';
|
||||
export { pinPrioritizedModels } from '@n8n/api-types';
|
||||
export { listGroqModels } from './providers/groq';
|
||||
export { listMiniMaxModels } from './providers/minimax';
|
||||
export { listMistralModels } from './providers/mistral';
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import { pinPrioritizedModels } from '@n8n/api-types';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { baseUrl, byName, getJson } from '../request';
|
||||
import type { ListModelsFn } from '../types';
|
||||
|
||||
|
|
@ -33,22 +36,48 @@ export function shouldIncludeGoogleModel(model: {
|
|||
*/
|
||||
export const listGoogleModels: ListModelsFn = async (options) => {
|
||||
const base = baseUrl(options, 'https://generativelanguage.googleapis.com');
|
||||
const data = (await getJson(
|
||||
const data = await getJson(
|
||||
`${base}/v1beta/models`,
|
||||
{ 'x-goog-api-key': options.apiKey },
|
||||
options,
|
||||
'google',
|
||||
)) as { models?: Array<{ name?: unknown; supportedGenerationMethods?: unknown }> };
|
||||
);
|
||||
|
||||
return (data.models ?? [])
|
||||
.filter(
|
||||
(model): model is { name: string; supportedGenerationMethods?: unknown } =>
|
||||
typeof model.name === 'string' &&
|
||||
shouldIncludeGoogleModel({
|
||||
name: model.name,
|
||||
supportedGenerationMethods: model.supportedGenerationMethods,
|
||||
}),
|
||||
)
|
||||
.map((model) => ({ id: model.name, name: model.name }))
|
||||
const models = parseGoogleModels(data)
|
||||
.filter(shouldIncludeGoogleModel)
|
||||
.map((model) => ({
|
||||
id: model.name,
|
||||
name: getGoogleModelLabel(model.name, model.displayName),
|
||||
...(model.priority !== undefined ? { priority: model.priority } : {}),
|
||||
}))
|
||||
.sort(byName);
|
||||
return pinPrioritizedModels(models, (model) => model.priority);
|
||||
};
|
||||
|
||||
/** A malformed optional field is dropped; a model without a name is skipped. */
|
||||
const googleModelSchema = z.object({
|
||||
name: z.string(),
|
||||
displayName: z.string().optional().catch(undefined),
|
||||
priority: z.number().finite().optional().catch(undefined),
|
||||
supportedGenerationMethods: z.array(z.string()).optional().catch(undefined),
|
||||
});
|
||||
|
||||
const googleModelsResponseSchema = z.object({
|
||||
models: z.array(z.unknown()).optional().catch(undefined),
|
||||
});
|
||||
|
||||
export type GoogleModel = z.infer<typeof googleModelSchema>;
|
||||
|
||||
/** Models of a Google `GET /v1beta/models` response. */
|
||||
export function parseGoogleModels(data: unknown): GoogleModel[] {
|
||||
const response = googleModelsResponseSchema.safeParse(data);
|
||||
return (response.success ? (response.data.models ?? []) : []).flatMap((item) => {
|
||||
const model = googleModelSchema.safeParse(item);
|
||||
return model.success ? [model.data] : [];
|
||||
});
|
||||
}
|
||||
|
||||
/** Dropdown label of a Google model: the API's `displayName`, else the id. */
|
||||
export function getGoogleModelLabel(modelId: string, displayName?: string): string {
|
||||
return displayName?.trim() || modelId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ export interface ProviderModel {
|
|||
id: string;
|
||||
/** Human-readable name when the provider supplies one, otherwise the id. */
|
||||
name: string;
|
||||
/** Pickers list models with a priority first, highest first. Only the AI Gateway reports one. */
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
export interface ListModelsOptions {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@n8n/ai-workflow-builder",
|
||||
"version": "1.43.0",
|
||||
"version": "1.44.0",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist .turbo",
|
||||
"typecheck": "tsc --noEmit",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@n8n/api-types",
|
||||
"version": "1.43.0",
|
||||
"version": "1.44.0",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist .turbo",
|
||||
"dev": "pnpm watch",
|
||||
|
|
@ -42,7 +42,8 @@
|
|||
"n8n-workflow": "workspace:*",
|
||||
"validator": "catalog:",
|
||||
"xss": "catalog:",
|
||||
"@n8n/permissions": "workspace:*"
|
||||
"@n8n/permissions": "workspace:*",
|
||||
"@n8n/inbound-auth": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "catalog:"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
import { isChatHubToolTypeAllowed } from '../chat-hub';
|
||||
|
||||
describe('isChatHubToolTypeAllowed', () => {
|
||||
it.each(['@n8n/n8n-nodes-langchain.chat', '@n8n/n8n-nodes-langchain.chatHitlTool'])(
|
||||
'blocks the %s variant of the Chat tool for all users',
|
||||
(type) => {
|
||||
expect(isChatHubToolTypeAllowed(type, { isChatUser: false })).toBe(false);
|
||||
},
|
||||
);
|
||||
|
||||
it('blocks the Data Table HITL tool for chat users', () => {
|
||||
expect(isChatHubToolTypeAllowed('n8n-nodes-base.dataTableHitlTool', { isChatUser: true })).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('blocks the Chat tool for all users', () => {
|
||||
expect(
|
||||
isChatHubToolTypeAllowed('@n8n/n8n-nodes-langchain.chatTool', { isChatUser: false }),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isChatHubToolTypeAllowed('@n8n/n8n-nodes-langchain.chatTool', { isChatUser: true }),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
'@n8n/n8n-nodes-langchain.toolWorkflow',
|
||||
'n8n-nodes-base.dataTableTool',
|
||||
'n8n-nodes-base.executeCommand',
|
||||
'n8n-nodes-base.executeCommandTool',
|
||||
'n8n-nodes-base.code',
|
||||
'@n8n/n8n-nodes-langchain.toolCode',
|
||||
'n8n-nodes-base.git',
|
||||
'n8n-nodes-base.gitTool',
|
||||
'n8n-nodes-base.messageAnAgentTool',
|
||||
'n8n-nodes-base.executeCommandHitlTool',
|
||||
'n8n-nodes-base.codeHitlTool',
|
||||
'n8n-nodes-base.readWriteFileHitlTool',
|
||||
'n8n-nodes-base.executeWorkflowHitlTool',
|
||||
'n8n-nodes-base.n8nHitlTool',
|
||||
])('blocks %s for chat users', (type) => {
|
||||
expect(isChatHubToolTypeAllowed(type, { isChatUser: true })).toBe(false);
|
||||
});
|
||||
|
||||
it.each(['n8n-nodes-base.executeCommandTool', '@n8n/n8n-nodes-langchain.toolCode'])(
|
||||
'allows %s for users who can build workflows',
|
||||
(type) => {
|
||||
expect(isChatHubToolTypeAllowed(type, { isChatUser: false })).toBe(true);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
'n8n-nodes-base.slackTool',
|
||||
'n8n-nodes-base.gmailTool',
|
||||
'n8n-nodes-base.httpRequestTool',
|
||||
'@n8n/n8n-nodes-langchain.toolHttpRequest',
|
||||
'n8n-nodes-base.graphqlTool',
|
||||
'n8n-nodes-base.rssFeedReadTool',
|
||||
'@n8n/n8n-nodes-langchain.mcpClientTool',
|
||||
'n8n-nodes-base.sshTool',
|
||||
'n8n-nodes-base.ftpHitlTool',
|
||||
'n8n-nodes-base.jenkinsTool',
|
||||
'n8n-nodes-base.rundeckTool',
|
||||
'n8n-nodes-base.awsLambdaTool',
|
||||
])('allows %s for chat users', (type) => {
|
||||
expect(isChatHubToolTypeAllowed(type, { isChatUser: true })).toBe(true);
|
||||
});
|
||||
});
|
||||
37
packages/@n8n/api-types/src/__tests__/model-priority.test.ts
Normal file
37
packages/@n8n/api-types/src/__tests__/model-priority.test.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { pinPrioritizedModels } from '../model-priority';
|
||||
|
||||
describe('pinPrioritizedModels', () => {
|
||||
it('moves models with a priority to the front, highest first, and keeps the order of the rest', () => {
|
||||
const models = [
|
||||
{ id: 'b' },
|
||||
{ id: 'low', priority: 1 },
|
||||
{ id: 'a' },
|
||||
{ id: 'high', priority: 10 },
|
||||
];
|
||||
|
||||
expect(pinPrioritizedModels(models, (model) => model.priority).map((m) => m.id)).toEqual([
|
||||
'high',
|
||||
'low',
|
||||
'b',
|
||||
'a',
|
||||
]);
|
||||
});
|
||||
|
||||
it('ignores priorities that are not finite numbers', () => {
|
||||
const models = [
|
||||
{ id: 'a', priority: '5' },
|
||||
{ id: 'b', priority: Number.NaN },
|
||||
{ id: 'c' },
|
||||
{ id: 'd', priority: Number.POSITIVE_INFINITY },
|
||||
{ id: 'valid', priority: 1 },
|
||||
];
|
||||
|
||||
expect(pinPrioritizedModels(models, (model) => model.priority).map((m) => m.id)).toEqual([
|
||||
'valid',
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -165,10 +165,11 @@ export type AgentSseEvent =
|
|||
| {
|
||||
/**
|
||||
* Preview chat only. Monthly spend crossed the alert line.
|
||||
* The run continues.
|
||||
* The run continues. Also emitted when a delegate subagent hits an alert or cap.
|
||||
*/
|
||||
type: 'budget-notice';
|
||||
code: 'budget.alert';
|
||||
code: AgentBudgetNoticeCode;
|
||||
agentId?: string;
|
||||
}
|
||||
| {
|
||||
type: 'error';
|
||||
|
|
@ -181,3 +182,15 @@ export type AgentSseEvent =
|
|||
missing?: string[];
|
||||
}
|
||||
| { type: 'done'; sessionId?: string; executionId?: string };
|
||||
|
||||
export type AgentBudgetNoticeCode = 'budget.alert' | 'budget.session' | 'budget.monthly';
|
||||
|
||||
/** A budget alert or stop. `agentId` is the budget owner; absent for the run's own agent. */
|
||||
export interface AgentBudgetNotice {
|
||||
code: AgentBudgetNoticeCode;
|
||||
agentId?: string; // Only populated for delegated sub-agents.
|
||||
}
|
||||
|
||||
export function isAgentBudgetNoticeCode(code: string): code is AgentBudgetNoticeCode {
|
||||
return code === 'budget.alert' || code === 'budget.session' || code === 'budget.monthly';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import { agentConfigValidationWarningSchema } from '../agent-config-validation.schema';
|
||||
|
||||
describe('agentConfigValidationWarningSchema', () => {
|
||||
const warning = { code: 'similar_skill_names', capability: { kind: 'skill' } };
|
||||
|
||||
it('accepts a similar_skill_names warning with two names', () => {
|
||||
expect(
|
||||
agentConfigValidationWarningSchema.safeParse({ ...warning, names: ['Tone', 'tone'] }).success,
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it.each([[[]], [['Tone']], [['Tone', '']]])('rejects the names %j', (names) => {
|
||||
expect(agentConfigValidationWarningSchema.safeParse({ ...warning, names }).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -16,7 +16,7 @@ describe('AgentJsonConfigSchema — capability activation', () => {
|
|||
const activation = enabled === undefined ? {} : { enabled };
|
||||
const config = {
|
||||
...minimalConfig,
|
||||
skills: [{ type: 'skill', id: 'notes', ...activation }],
|
||||
skills: [{ type: 'skill', id: 'notes' }],
|
||||
tools: [
|
||||
{ type: 'custom', id: 'search', requireApproval: true, ...activation },
|
||||
{ type: 'workflow', workflow: 'My Workflow', workflowId: 'wf-1', ...activation },
|
||||
|
|
@ -41,7 +41,7 @@ describe('AgentJsonConfigSchema — capability activation', () => {
|
|||
expect(
|
||||
AgentJsonConfigSchema.safeParse({
|
||||
...minimalConfig,
|
||||
skills: [{ type: 'skill', id: 'notes', enabled: 'false' }],
|
||||
tools: [{ type: 'custom', id: 'search', enabled: 'false' }],
|
||||
}).success,
|
||||
).toBe(false);
|
||||
});
|
||||
|
|
@ -636,6 +636,38 @@ describe('AgentJsonConfigSchema — model/credential coupling', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('AgentJsonConfigSchema — MCP permissions', () => {
|
||||
it('accepts category defaults and exact tool overrides', () => {
|
||||
const result = AgentJsonConfigSchema.safeParse({
|
||||
...minimalConfig,
|
||||
mcpServers: [
|
||||
{
|
||||
name: 'github',
|
||||
url: 'https://mcp.example.com',
|
||||
toolPermissions: {
|
||||
categories: { read: 'always_allow', write: 'require_approval' },
|
||||
tools: { delete_repository: 'blocked' },
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ toolFilter: { mode: 'exclude', tools: ['delete_repository'] } },
|
||||
{ approval: { mode: 'global' } },
|
||||
])('rejects migrated legacy MCP settings: %o', (legacySettings) => {
|
||||
const result = AgentJsonConfigSchema.safeParse({
|
||||
...minimalConfig,
|
||||
mcpServers: [{ name: 'github', url: 'https://mcp.example.com', ...legacySettings }],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatAgentConfigZodError', () => {
|
||||
it('formats an invalid MCP server name as path: message without a Zod JSON dump', () => {
|
||||
const result = AgentJsonConfigSchema.safeParse({
|
||||
|
|
|
|||
|
|
@ -43,6 +43,49 @@ describe('InlineAgentJsonConfigSchema', () => {
|
|||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('accepts MCP permissions that do not suspend workflow execution', () => {
|
||||
const result = InlineAgentJsonConfigSchema.safeParse({
|
||||
...baseConfig,
|
||||
mcpServers: [
|
||||
{
|
||||
name: 'docs',
|
||||
url: 'https://mcp.example.com',
|
||||
toolPermissions: {
|
||||
categories: { read: 'always_allow', write: 'blocked' },
|
||||
tools: { publish: 'always_allow', search: 'blocked' },
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
categories: { read: 'require_approval', write: 'blocked' },
|
||||
},
|
||||
{
|
||||
categories: { read: 'always_allow', write: 'require_approval' },
|
||||
},
|
||||
{
|
||||
categories: { read: 'always_allow', write: 'blocked' },
|
||||
tools: { search: 'require_approval' },
|
||||
},
|
||||
])('rejects MCP approval because workflow execution cannot resume: %o', (toolPermissions) => {
|
||||
const result = InlineAgentJsonConfigSchema.safeParse({
|
||||
...baseConfig,
|
||||
mcpServers: [{ name: 'docs', url: 'https://mcp.example.com', toolPermissions }],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0]?.message).toBe(
|
||||
'MCP tool approval is not available for inline agents',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects custom (code) tools — their bodies live on saved agents', () => {
|
||||
const result = InlineAgentJsonConfigSchema.safeParse({
|
||||
...baseConfig,
|
||||
|
|
|
|||
|
|
@ -218,15 +218,14 @@ describe('sanitizeAgentJsonConfig', () => {
|
|||
nodeTypeName: '@n8n/mcp-registry.github',
|
||||
legacyMetadataField: true,
|
||||
},
|
||||
toolFilter: {
|
||||
mode: 'allow',
|
||||
tools: ['create_issue'],
|
||||
legacyToolFilterField: true,
|
||||
},
|
||||
approval: {
|
||||
mode: 'selected',
|
||||
tools: ['create_issue'],
|
||||
legacyApprovalField: true,
|
||||
toolPermissions: {
|
||||
categories: {
|
||||
read: 'always_allow',
|
||||
write: 'require_approval',
|
||||
legacyCategory: 'blocked',
|
||||
},
|
||||
tools: { create_issue: 'require_approval' },
|
||||
legacyPermissionsField: true,
|
||||
},
|
||||
connectionTimeoutMs: 10_000,
|
||||
legacyServerField: true,
|
||||
|
|
@ -247,13 +246,12 @@ describe('sanitizeAgentJsonConfig', () => {
|
|||
metadata: {
|
||||
nodeTypeName: '@n8n/mcp-registry.github',
|
||||
},
|
||||
toolFilter: {
|
||||
mode: 'allow',
|
||||
tools: ['create_issue'],
|
||||
},
|
||||
approval: {
|
||||
mode: 'selected',
|
||||
tools: ['create_issue'],
|
||||
toolPermissions: {
|
||||
categories: {
|
||||
read: 'always_allow',
|
||||
write: 'require_approval',
|
||||
},
|
||||
tools: { create_issue: 'require_approval' },
|
||||
},
|
||||
connectionTimeoutMs: 10_000,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
import {
|
||||
CreateSkillDto,
|
||||
ListSkillsQueryDto,
|
||||
SaveSkillDto,
|
||||
SKILLS_MAX_PAGE_SIZE,
|
||||
} from '../skills.schema';
|
||||
|
||||
describe('ListSkillsQueryDto', () => {
|
||||
it('leaves take unset when the client sends none, so the whole list comes back', () => {
|
||||
expect(ListSkillsQueryDto.parse({})).toEqual({ skip: 0, take: undefined });
|
||||
});
|
||||
|
||||
it('caps take at the page limit', () => {
|
||||
expect(ListSkillsQueryDto.parse({ take: '500' }).take).toBe(SKILLS_MAX_PAGE_SIZE);
|
||||
});
|
||||
|
||||
it('parses skip, take and the filters', () => {
|
||||
expect(
|
||||
ListSkillsQueryDto.parse({
|
||||
skip: '10',
|
||||
take: '5',
|
||||
search: ' voice ',
|
||||
scope: 'project',
|
||||
projectId: 'p1',
|
||||
}),
|
||||
).toEqual({ skip: 10, take: 5, search: 'voice', scope: 'project', projectId: 'p1' });
|
||||
});
|
||||
|
||||
it.each([['0'], ['-1'], ['abc']])('rejects take "%s"', (take) => {
|
||||
expect(ListSkillsQueryDto.safeParse({ take }).success).toBe(false);
|
||||
});
|
||||
|
||||
it('parses the project an agent attaches skills for', () => {
|
||||
expect(ListSkillsQueryDto.parse({ attachableTo: 'p1' })).toMatchObject({ attachableTo: 'p1' });
|
||||
});
|
||||
|
||||
it.each(['team', 'user'])('rejects the unknown scope %s', (scope) => {
|
||||
expect(ListSkillsQueryDto.safeParse({ scope }).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CreateSkillDto', () => {
|
||||
const skill = { name: 'Brand voice', description: 'How we write', instructions: 'Be brief.' };
|
||||
|
||||
it.each([
|
||||
[{ scope: 'project', projectId: 'p1' }],
|
||||
[{ scope: 'agent', agentId: 'a1' }],
|
||||
[{ scope: 'instance' }],
|
||||
])('accepts %o', (target) => {
|
||||
expect(CreateSkillDto.safeParse({ ...target, skill }).success).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects allowed tools, which hub skills ignore', () => {
|
||||
expect(
|
||||
CreateSkillDto.safeParse({
|
||||
scope: 'instance',
|
||||
skill: { ...skill, allowedTools: ['search'] },
|
||||
}).success,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects a skill without instructions', () => {
|
||||
expect(
|
||||
CreateSkillDto.safeParse({ scope: 'instance', skill: { ...skill, instructions: '' } })
|
||||
.success,
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SaveSkillDto', () => {
|
||||
it('accepts changed fields with the base hash', () => {
|
||||
expect(SaveSkillDto.safeParse({ instructions: 'Be brief.', baseSkillHash: 'h1' }).success).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it.each([[{ instructions: 'Be brief.' }], [{ instructions: 'Be brief.', baseSkillHash: '' }]])(
|
||||
'rejects a save without a base hash: %o',
|
||||
(payload) => {
|
||||
expect(SaveSkillDto.safeParse(payload).success).toBe(false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
@ -58,8 +58,24 @@ export const agentConfigValidationIssueSchema = z.object({
|
|||
});
|
||||
export type AgentConfigValidationIssue = z.infer<typeof agentConfigValidationIssueSchema>;
|
||||
|
||||
/** Stable, i18n-free code for a validation warning. A warning does not make a config invalid. */
|
||||
export const agentConfigValidationWarningCodeSchema = z.enum(['similar_skill_names']);
|
||||
export type AgentConfigValidationWarningCode = z.infer<
|
||||
typeof agentConfigValidationWarningCodeSchema
|
||||
>;
|
||||
|
||||
export const agentConfigValidationWarningSchema = z.object({
|
||||
code: agentConfigValidationWarningCodeSchema,
|
||||
capability: agentConfigValidationCapabilityRefSchema,
|
||||
/** For `similar_skill_names`: the skill names that are hard to tell apart, at least two. */
|
||||
names: z.array(z.string().min(1)).min(2),
|
||||
});
|
||||
export type AgentConfigValidationWarning = z.infer<typeof agentConfigValidationWarningSchema>;
|
||||
|
||||
export const agentConfigValidationResponseSchema = z.object({
|
||||
status: z.enum(['valid', 'invalid']),
|
||||
issues: z.array(agentConfigValidationIssueSchema),
|
||||
/** Absent when there are no warnings. */
|
||||
warnings: z.array(agentConfigValidationWarningSchema).optional(),
|
||||
});
|
||||
export type AgentConfigValidationResponse = z.infer<typeof agentConfigValidationResponseSchema>;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { z, type ZodError } from 'zod';
|
||||
|
||||
import { isDraftAgentConfig } from './agent-config-lifecycle';
|
||||
import { AgentApprovalSchema, AgentIntegrationConfigSchema } from './agent-integration.schema';
|
||||
import { AgentIntegrationConfigSchema } from './agent-integration.schema';
|
||||
import { AGENT_MODEL_STRING_REGEX } from './model-providers';
|
||||
import { AGENT_REASONING_LEVELS } from './reasoning';
|
||||
/**
|
||||
|
|
@ -15,6 +15,7 @@ import {
|
|||
SUB_AGENT_MAX_CHILDREN_MIN,
|
||||
} from './sub-agent.schema';
|
||||
import { jsonValueSchema } from '../schemas/json-value.schema';
|
||||
import { mcpToolPermissionsSchema } from '../schemas/mcp-tool-permissions.schema';
|
||||
|
||||
export const MANAGED_CREDENTIAL_TOKEN = 'managed' as const;
|
||||
|
||||
|
|
@ -187,9 +188,14 @@ export const NodeConfigSchema = z.object({
|
|||
credentials: z.record(NodeToolCredentialSchema).optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* A ref to a skill. A skill has no on/off state: a ref is attached or it is
|
||||
* not. `versionId` pins the ref of a saved agent to one saved version of the
|
||||
* skill. Without it, the agent draft runs the latest saved version.
|
||||
*/
|
||||
const AgentJsonSkillConfigSchema = z.object({
|
||||
type: z.literal('skill'),
|
||||
enabled: z.boolean().optional(),
|
||||
versionId: z.string().uuid().optional(),
|
||||
id: z
|
||||
.string()
|
||||
.min(1)
|
||||
|
|
@ -265,26 +271,11 @@ export const McpServerConfigSchema = z
|
|||
.describe(
|
||||
'Server-generated metadata. Do not set this manually; only copy it from an MCP discovery result when present',
|
||||
),
|
||||
toolFilter: z
|
||||
.discriminatedUnion('mode', [
|
||||
z
|
||||
.object({
|
||||
mode: z.literal('allow'),
|
||||
tools: z.array(z.string().min(1)).default([]),
|
||||
})
|
||||
.strict(),
|
||||
z
|
||||
.object({
|
||||
mode: z.literal('exclude'),
|
||||
tools: z.array(z.string().min(1)).default([]),
|
||||
})
|
||||
.strict(),
|
||||
])
|
||||
toolPermissions: mcpToolPermissionsSchema
|
||||
.optional()
|
||||
.describe('Restricts which tools are surfaced. Tools matched by original un-prefixed name'),
|
||||
approval: AgentApprovalSchema.optional().describe(
|
||||
'Human-in-the-loop approval. Absent = no approval required',
|
||||
),
|
||||
.describe(
|
||||
'Optional read/write category permissions with exact-name tool overrides. Omission allows all tools without approval',
|
||||
),
|
||||
connectionTimeoutMs: z
|
||||
.number()
|
||||
.int()
|
||||
|
|
@ -590,8 +581,9 @@ export type AgentJsonConfig = z.infer<typeof AgentJsonConfigSchema>;
|
|||
declare const storedSkillRefsBrand: unique symbol;
|
||||
|
||||
/**
|
||||
* Opaque type of the legacy `skills` key in a stored config. Only the skill
|
||||
* refs seam reads or writes this key.
|
||||
* Opaque type of the legacy `skills` key in a stored config. Nothing reads
|
||||
* or writes this key: the skill refs live in the skills hub tables. The data
|
||||
* migration removes the key from stored configs.
|
||||
*/
|
||||
export interface OpaqueStoredSkillRefs {
|
||||
readonly [storedSkillRefsBrand]: true;
|
||||
|
|
@ -599,8 +591,8 @@ export interface OpaqueStoredSkillRefs {
|
|||
|
||||
/**
|
||||
* Agent config as the backend stores it on the agent draft and on each
|
||||
* published version. Its `skills` key is opaque: read and write skill refs
|
||||
* through `AgentSkillRefsService`. `AgentJsonConfig` (with `skills`) stays the
|
||||
* published version. It holds no skill refs: read and write them through
|
||||
* `AgentSkillRefsService`. `AgentJsonConfig` (with `skills`) stays the
|
||||
* document format of REST, MCP and the agent builder.
|
||||
*
|
||||
* Because the `skills` key is opaque, a stored config is not assignable to
|
||||
|
|
|
|||
|
|
@ -85,3 +85,11 @@ export const agentSkillShape = {
|
|||
};
|
||||
|
||||
export const agentSkillSchema = z.object(agentSkillShape).strict();
|
||||
|
||||
export const agentSkillAvailabilitySchema = z.enum(['project', 'agent', 'instance']);
|
||||
|
||||
export type AgentSkillAvailability = z.infer<typeof agentSkillAvailabilitySchema>;
|
||||
|
||||
export const createAgentSkillSchema = agentSkillSchema.extend({
|
||||
availableTo: agentSkillAvailabilitySchema.optional(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@ import {
|
|||
} from './agent-chat-attachments.constants';
|
||||
import { AgentApprovalSchema, AgentTeamsSettingsSchema } from './agent-integration.schema';
|
||||
import { AgentVectorStoreConfigSchema, AgentJsonConfigSchema } from './agent-json-config.schema';
|
||||
import { agentSkillSchema, agentSkillShape } from './agent-skill.schema';
|
||||
import {
|
||||
agentSkillAvailabilitySchema,
|
||||
agentSkillSchema,
|
||||
agentSkillShape,
|
||||
createAgentSkillSchema,
|
||||
} from './agent-skill.schema';
|
||||
import { agentTaskSchema } from './agent-task.schema';
|
||||
import { N8N_CHAT_INTEGRATION_TYPE } from './types';
|
||||
import { paginationSchema } from '../dto/pagination/pagination.dto';
|
||||
|
|
@ -200,19 +205,26 @@ const updateAgentSkillShape = {
|
|||
|
||||
const updateAgentSkillSchema = z.object(updateAgentSkillShape).strict();
|
||||
|
||||
export class CreateAgentSkillDto extends Z.class(agentSkillShape) {
|
||||
static override schema = agentSkillSchema;
|
||||
const updateAttachedAgentSkillShape = {
|
||||
...updateAgentSkillShape,
|
||||
availableTo: agentSkillAvailabilitySchema.optional(),
|
||||
};
|
||||
|
||||
constructor(data: z.infer<typeof agentSkillSchema>) {
|
||||
super(agentSkillSchema.parse(data));
|
||||
const updateAttachedAgentSkillSchema = z.object(updateAttachedAgentSkillShape).strict();
|
||||
|
||||
export class CreateAgentSkillDto extends Z.class(createAgentSkillSchema.shape) {
|
||||
static override schema = createAgentSkillSchema;
|
||||
|
||||
constructor(data: z.infer<typeof createAgentSkillSchema>) {
|
||||
super(createAgentSkillSchema.parse(data));
|
||||
}
|
||||
|
||||
static override safeParse(data: unknown) {
|
||||
return agentSkillSchema.safeParse(data);
|
||||
return createAgentSkillSchema.safeParse(data);
|
||||
}
|
||||
|
||||
static override parse(data: unknown) {
|
||||
return agentSkillSchema.parse(data);
|
||||
return createAgentSkillSchema.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,6 +244,22 @@ export class UpdateAgentSkillDto extends Z.class(updateAgentSkillShape) {
|
|||
}
|
||||
}
|
||||
|
||||
export class UpdateAttachedAgentSkillDto extends Z.class(updateAttachedAgentSkillShape) {
|
||||
static override schema = updateAttachedAgentSkillSchema;
|
||||
|
||||
constructor(data: z.infer<typeof updateAttachedAgentSkillSchema>) {
|
||||
super(updateAttachedAgentSkillSchema.parse(data));
|
||||
}
|
||||
|
||||
static override safeParse(data: unknown) {
|
||||
return updateAttachedAgentSkillSchema.safeParse(data);
|
||||
}
|
||||
|
||||
static override parse(data: unknown) {
|
||||
return updateAttachedAgentSkillSchema.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
export const agentChatAttachmentSchema = z.object({
|
||||
fileName: z.string().min(1).max(MAX_AGENT_CHAT_ATTACHMENT_FILENAME_LENGTH),
|
||||
mimeType: z.string().min(1).max(MAX_AGENT_CHAT_ATTACHMENT_MIMETYPE_LENGTH),
|
||||
|
|
@ -373,6 +401,34 @@ export class AgentTeamsPackageDto extends Z.class({
|
|||
settings: AgentTeamsSettingsSchema.optional(),
|
||||
}) {}
|
||||
|
||||
/**
|
||||
* Reaches Microsoft inside a URL path, so the shape is checked here rather than
|
||||
* escaped at each use: Microsoft issues these as GUIDs, and nothing else is a
|
||||
* legitimate value.
|
||||
*/
|
||||
const microsoftId = z.string().uuid();
|
||||
|
||||
/** Names the Microsoft sign-in the Entra app is registered with. */
|
||||
export class AgentTeamsProvisionAppDto extends Z.class({
|
||||
managerCredentialId: z.string().min(1),
|
||||
}) {}
|
||||
|
||||
/**
|
||||
* The Entra app the bot signs for is named explicitly because the bot is
|
||||
* created before the channel is connected, so the agent holds no credential yet.
|
||||
*/
|
||||
export class AgentTeamsProvisionBotDto extends Z.class({
|
||||
managerCredentialId: z.string().min(1),
|
||||
credentialId: z.string().min(1),
|
||||
subscriptionId: microsoftId,
|
||||
}) {}
|
||||
|
||||
export class AgentTeamsPublishDto extends Z.class({
|
||||
managerCredentialId: z.string().min(1),
|
||||
credentialId: z.string().min(1),
|
||||
settings: AgentTeamsSettingsSchema.optional(),
|
||||
}) {}
|
||||
|
||||
export class AgentDisconnectIntegrationDto extends Z.class({
|
||||
type: z.string().min(1),
|
||||
// Empty string targets a draft integration entry (`credentialId: ''`).
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export * from './agent-json-config.schema';
|
|||
export * from './agent-node-tool-operations';
|
||||
export * from './agent-personalisation';
|
||||
export * from './agent-skill.schema';
|
||||
export * from './skills.schema';
|
||||
export * from './child-trace';
|
||||
export type * from './background-job';
|
||||
export * from './inline-agent-config.schema';
|
||||
|
|
@ -24,11 +25,14 @@ export * from './sub-agent.schema';
|
|||
export * from './types';
|
||||
export * from './workflow-wait';
|
||||
export type {
|
||||
AgentBudgetNotice,
|
||||
AgentBudgetNoticeCode,
|
||||
AgentSseEvent,
|
||||
AgentSseMessage,
|
||||
ForwardedChildChunkWire,
|
||||
ToolSuspendedPayload,
|
||||
} from '../agent-sse';
|
||||
export { isAgentBudgetNoticeCode } from '../agent-sse';
|
||||
export { AGENT_BUILDER_HIDDEN_AVAILABLE_TOOL_NODE_TYPES } from '../agent-builder-tool-node-types';
|
||||
// ASK_QUESTIONS_TOOL_NAME / CONFIGURE_CHANNEL_TOOL_NAME come from
|
||||
// ./agent-interaction.schema (re-exported below via `export *`).
|
||||
|
|
|
|||
|
|
@ -56,7 +56,16 @@ export const InlineAgentJsonConfigSchema = AgentJsonConfigBaseSchema.pick({
|
|||
// don't support — same reason the tool variants above omit
|
||||
// `requireApproval`.
|
||||
mcpServers: AgentJsonConfigBaseSchema.shape.mcpServers.refine(
|
||||
(servers) => (servers ?? []).every((server) => server.approval === undefined),
|
||||
(servers) =>
|
||||
(servers ?? []).every((server) => {
|
||||
const permissions = server.toolPermissions;
|
||||
return (
|
||||
permissions === undefined ||
|
||||
(permissions.categories.read !== 'require_approval' &&
|
||||
permissions.categories.write !== 'require_approval' &&
|
||||
!Object.values(permissions.tools ?? {}).includes('require_approval'))
|
||||
);
|
||||
}),
|
||||
{ message: 'MCP tool approval is not available for inline agents' },
|
||||
),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ export interface AgentCatalogModel {
|
|||
toolCall: boolean;
|
||||
cost?: { input: number; output: number; cacheRead?: number; cacheWrite?: number };
|
||||
limits?: { context?: number; output?: number };
|
||||
/** Pickers list models with a priority first, highest first. Reported by the models API. */
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
/** Response of `GET /agents/v2/catalog/models/:provider`. */
|
||||
|
|
|
|||
129
packages/@n8n/api-types/src/agents/skills.schema.ts
Normal file
129
packages/@n8n/api-types/src/agents/skills.schema.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import { agentSkillSchema, agentSkillShape } from './agent-skill.schema';
|
||||
import type { AgentSkill } from './types';
|
||||
import { paginationSchema } from '../dto/pagination/pagination.dto';
|
||||
import { Z } from '../zod-class';
|
||||
|
||||
/**
|
||||
* PostHog flag for the rollout of the skills hub, read for the instance once at startup.
|
||||
* Off: agents keep their inline skills and the skills routes answer 404.
|
||||
*/
|
||||
export const SKILLS_FLAG = '125_context_skills';
|
||||
|
||||
export const SKILLS_MAX_PAGE_SIZE = 100;
|
||||
|
||||
/** Optional on purpose: without `take` the caller gets every visible skill. */
|
||||
const optionalTake = z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((value) => (value === undefined || value === '' ? undefined : Number(value)))
|
||||
.refine((value) => value === undefined || (Number.isInteger(value) && value > 0), {
|
||||
message: 'Param `take` must be a positive integer',
|
||||
})
|
||||
.transform((value) => (value === undefined ? undefined : Math.min(value, SKILLS_MAX_PAGE_SIZE)));
|
||||
|
||||
/** Who owns a skill: one project, one agent (only that agent can use it), or the instance. */
|
||||
export const skillScopeSchema = z.enum(['project', 'agent', 'instance']);
|
||||
|
||||
export type SkillScope = z.infer<typeof skillScopeSchema>;
|
||||
|
||||
export type SkillSource = 'ui' | 'agent';
|
||||
|
||||
/** One row of the skills list. Name and description come from the latest version. */
|
||||
export type SkillListItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
scope: SkillScope;
|
||||
/** The project of a project skill, or the project of the agent of an agent skill. */
|
||||
projectId: string | null;
|
||||
projectName: string | null;
|
||||
/** Set for an agent skill: the only agent that can use it. */
|
||||
agentId: string | null;
|
||||
agentName: string | null;
|
||||
source: SkillSource;
|
||||
/** Number of the latest version, the one following agents run. */
|
||||
latestVersion: number;
|
||||
/** Distinct agents that use the skill: draft refs plus published pins. */
|
||||
usedByAgents: number;
|
||||
canEdit: boolean;
|
||||
canDelete: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type SkillListResponse = {
|
||||
count: number;
|
||||
data: SkillListItem[];
|
||||
};
|
||||
|
||||
/** The agents that use a skill, limited to the agents the caller may read. */
|
||||
export type SkillUsage = {
|
||||
/** Agents whose draft references the skill. */
|
||||
drafts: Array<{ agentId: string; agentName: string; projectId: string }>;
|
||||
/** Agents with a published version that pins the skill, one row each. */
|
||||
pins: Array<{
|
||||
agentId: string;
|
||||
agentName: string;
|
||||
projectId: string;
|
||||
/** The skill version the current published version runs. Null when only older ones pin it. */
|
||||
version: number | null;
|
||||
}>;
|
||||
/** Agents in projects the caller may not read. */
|
||||
hiddenAgents: number;
|
||||
};
|
||||
|
||||
/** One skill with the content of its latest version. */
|
||||
export type SkillDetail = SkillListItem & {
|
||||
skill: AgentSkill;
|
||||
/** Hash of `skill`. Send it back as `baseSkillHash` so a save cannot overwrite a newer version. */
|
||||
skillHash: string;
|
||||
usedBy: SkillUsage;
|
||||
};
|
||||
|
||||
export type SkillSaveResponse = {
|
||||
id: string;
|
||||
versionId: string;
|
||||
version: number;
|
||||
/** False when the content matched the latest version and nothing was saved. */
|
||||
created: boolean;
|
||||
/** The content of the latest version after the save. */
|
||||
skill: AgentSkill;
|
||||
skillHash: string;
|
||||
};
|
||||
|
||||
export class ListSkillsQueryDto extends Z.class({
|
||||
skip: paginationSchema.skip,
|
||||
take: optionalTake,
|
||||
search: z.string().trim().max(200).optional(),
|
||||
scope: skillScopeSchema.optional(),
|
||||
projectId: z.string().max(36).optional(),
|
||||
/**
|
||||
* The id of an agent: the skills it may attach, which are its project's skills, its own
|
||||
* agent skills and instance skills. Not combined with `scope` or `projectId`.
|
||||
*/
|
||||
attachableTo: z.string().max(36).optional(),
|
||||
}) {}
|
||||
|
||||
export class CreateSkillDto extends Z.class({
|
||||
scope: skillScopeSchema,
|
||||
/** Required for a project skill, absent otherwise. */
|
||||
projectId: z.string().max(36).optional(),
|
||||
/** Required for an agent skill, absent otherwise. */
|
||||
agentId: z.string().max(36).optional(),
|
||||
/** Hub skills ignore `allowedTools`, so a create that sends it is refused. */
|
||||
skill: agentSkillSchema.omit({ allowedTools: true }),
|
||||
}) {}
|
||||
|
||||
/**
|
||||
* Saves the next version of a skill. `baseSkillHash` is the `skillHash` the editor
|
||||
* started from: a save based on an older version is refused with 409.
|
||||
*/
|
||||
export class SaveSkillDto extends Z.class({
|
||||
name: agentSkillShape.name.optional(),
|
||||
description: agentSkillShape.description.optional(),
|
||||
instructions: agentSkillShape.instructions.optional(),
|
||||
references: agentSkillShape.references.optional(),
|
||||
baseSkillHash: z.string().min(1),
|
||||
}) {}
|
||||
|
|
@ -40,6 +40,7 @@ export interface TeamsAgentAppManifest {
|
|||
/** Required alongside resource-specific permissions; omitted without them. */
|
||||
webApplicationInfo?: {
|
||||
id: string;
|
||||
resource: string;
|
||||
};
|
||||
/** Present only when a read-all permission is turned on. */
|
||||
authorization?: {
|
||||
|
|
@ -73,6 +74,12 @@ export interface TeamsAgentSetupState {
|
|||
*/
|
||||
defaultDisplayName: string;
|
||||
defaultDescription: string;
|
||||
/**
|
||||
* Whether the connected credential is one the recommended setup made for
|
||||
* this agent. A channel wired up by hand is left to the manual flow even
|
||||
* where the recommended one could have run.
|
||||
*/
|
||||
provisionedByN8n: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,3 +93,87 @@ export type TeamsCredentialCheck =
|
|||
status: 'failed';
|
||||
reason: 'certificate' | 'incomplete' | 'rejected' | 'unreachable' | 'cloud';
|
||||
};
|
||||
|
||||
/**
|
||||
* One Microsoft sign-in the user can provision with. Several may exist in a
|
||||
* project, so the setup offers a picker rather than always signing in again.
|
||||
*/
|
||||
export interface TeamsManagerCredentialSummary {
|
||||
id: string;
|
||||
name: string;
|
||||
/** False while the credential exists but the OAuth round trip has not finished. */
|
||||
connected: boolean;
|
||||
/**
|
||||
* True when the stored grant is missing a scope this flow now needs, which
|
||||
* happens when the scope list grew after the user last signed in.
|
||||
*/
|
||||
reconnectRequired: boolean;
|
||||
/** Tenant the account belongs to, shown so the user can tell two sign-ins apart. */
|
||||
organizationName: string | null;
|
||||
tenantId: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the recommended setup can run at all, and what the user can pick.
|
||||
*
|
||||
* `managedSetupAvailable` is false on any instance without credential
|
||||
* overwrites for the manager credential, which is every self-hosted instance
|
||||
* whose operator has not supplied their own client ID and secret.
|
||||
*/
|
||||
export interface TeamsManagedSetupState {
|
||||
managedSetupAvailable: boolean;
|
||||
managerCredentials: TeamsManagerCredentialSummary[];
|
||||
/**
|
||||
* One prompt an administrator uses to approve every permission n8n needs,
|
||||
* across both Microsoft Graph and Azure management. A sign-in cannot ask for
|
||||
* both, because a code is redeemed for one resource at a time.
|
||||
*/
|
||||
adminConsentUrl: string | null;
|
||||
}
|
||||
|
||||
export interface CreateTeamsManagerCredentialResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
type: 'microsoftTeamsManagerOAuth2Api';
|
||||
isResolvable: false;
|
||||
}
|
||||
|
||||
/** What the Entra app step leaves behind, and what the setup shows for it. */
|
||||
export interface TeamsProvisionedAppSummary {
|
||||
/** The channel credential n8n wrote, ready for the steps that follow. */
|
||||
credentialId: string;
|
||||
appId: string;
|
||||
appName: string;
|
||||
organizationName: string | null;
|
||||
entraAppUrl: string;
|
||||
/**
|
||||
* When the client secret stops working. n8n created it, so the user cannot
|
||||
* replace it by hand — the channel settings offer a renew instead.
|
||||
*/
|
||||
secretExpiresAt: string;
|
||||
}
|
||||
|
||||
/** An Azure subscription the signed-in account can create the bot in. */
|
||||
export interface TeamsAzureSubscription {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/** What the bot step created, once n8n did it rather than the Azure portal. */
|
||||
export interface TeamsProvisionedBotSummary {
|
||||
botName: string;
|
||||
resourceGroup: string;
|
||||
subscriptionId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where the Teams app stands in the organisation's catalogue, read back from
|
||||
* Microsoft rather than tracked by n8n.
|
||||
*
|
||||
* `unknown` covers an app that is not listed yet: published policies take a day
|
||||
* or two to apply, so an app missing from the listing has not been refused.
|
||||
*/
|
||||
export interface TeamsCatalogState {
|
||||
status: 'published' | 'submitted' | 'rejected' | 'unknown';
|
||||
teamsAppId: string | null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import type { Scope } from '@n8n/permissions';
|
|||
import {
|
||||
CHAT_TOOL_NODE_TYPE,
|
||||
type ChunkType,
|
||||
CODE_NODE_TYPE,
|
||||
CODE_TOOL_LANGCHAIN_NODE_TYPE,
|
||||
DATA_TABLE_TOOL_NODE_TYPE,
|
||||
EXECUTE_WORKFLOW_NODE_TYPE,
|
||||
type INode,
|
||||
INodeSchema,
|
||||
WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE,
|
||||
|
|
@ -709,12 +712,46 @@ export interface ChatHubToolDto {
|
|||
/** Tool types blocked for ALL users in the Chat Hub. */
|
||||
export const ALWAYS_BLOCKED_CHAT_HUB_TOOL_TYPES: string[] = [CHAT_TOOL_NODE_TYPE];
|
||||
|
||||
/** Additional tool types blocked for chat-only users (global:chatUser). */
|
||||
/**
|
||||
* Additional tool types blocked for chat-only users (global:chatUser).
|
||||
* These tools run code or commands on the n8n instance, or access its files,
|
||||
* workflows, data tables or agents. Tools that act on a remote system through
|
||||
* a credential stay allowed: the credential limits what they can do.
|
||||
* Chat users cannot build workflows, so they cannot add these tools either.
|
||||
* A `*Tool` or `*HitlTool` variant of a listed node type is blocked too.
|
||||
* Any node can have a `*HitlTool` variant, so the list also holds node types
|
||||
* that are not usable as tools.
|
||||
*/
|
||||
export const CHAT_USER_BLOCKED_CHAT_HUB_TOOL_TYPES: string[] = [
|
||||
WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE,
|
||||
DATA_TABLE_TOOL_NODE_TYPE,
|
||||
CODE_NODE_TYPE,
|
||||
CODE_TOOL_LANGCHAIN_NODE_TYPE,
|
||||
EXECUTE_WORKFLOW_NODE_TYPE,
|
||||
'n8n-nodes-base.executeCommand',
|
||||
'n8n-nodes-base.messageAnAgent',
|
||||
'n8n-nodes-base.git',
|
||||
'n8n-nodes-base.readWriteFile',
|
||||
'n8n-nodes-base.n8n',
|
||||
];
|
||||
|
||||
/** Matches the node type registry, which resolves both suffixes to the base node type. */
|
||||
function withoutToolSuffix(type: string): string {
|
||||
return type.replace(/HitlTool$/, '').replace(/Tool$/, '');
|
||||
}
|
||||
|
||||
/** Compares base node types, so an entry also covers the base, `*Tool` and `*HitlTool` forms. */
|
||||
function isListed(list: string[], type: string): boolean {
|
||||
const baseType = withoutToolSuffix(type);
|
||||
return list.some((entry) => withoutToolSuffix(entry) === baseType);
|
||||
}
|
||||
|
||||
/** Returns whether a user can add or run a tool of this type in the Chat Hub. */
|
||||
export function isChatHubToolTypeAllowed(type: string, user: { isChatUser: boolean }): boolean {
|
||||
if (isListed(ALWAYS_BLOCKED_CHAT_HUB_TOOL_TYPES, type)) return false;
|
||||
return !(user.isChatUser && isListed(CHAT_USER_BLOCKED_CHAT_HUB_TOOL_TYPES, type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Request schema for creating a chat hub tool
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1 @@
|
|||
export interface ConsentUiHints {
|
||||
/** design-system icon name (updatedIconSet) for the consent logo tile; omit to fall back to the generic mcp icon */
|
||||
icon?: string;
|
||||
/** noun key inserted into first-party consent copy, e.g. 'form' (later 'webhook'); resolved to a localized word on the frontend */
|
||||
consentType?: string;
|
||||
}
|
||||
export type { ConsentUiHints } from '@n8n/inbound-auth';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import { NotifyMigrationOwnersRequestDto } from '../notify-migration-owners-request.dto';
|
||||
|
||||
describe('NotifyMigrationOwnersRequestDto', () => {
|
||||
it('should accept a list of workflow ids', () => {
|
||||
expect(
|
||||
NotifyMigrationOwnersRequestDto.safeParse({ workflowIds: ['wf-1', 'wf-2'] }).success,
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test.each([
|
||||
{ name: 'an empty workflow list', request: { workflowIds: [] } },
|
||||
{ name: 'an empty workflow id', request: { workflowIds: [''] } },
|
||||
{ name: 'a missing workflow list', request: {} },
|
||||
])('should reject $name', ({ request }) => {
|
||||
expect(NotifyMigrationOwnersRequestDto.safeParse(request).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import { Z } from '../../zod-class';
|
||||
|
||||
export class NotifyMigrationOwnersRequestDto extends Z.class({
|
||||
workflowIds: z.array(z.string().min(1)).min(1),
|
||||
}) {}
|
||||
|
|
@ -56,6 +56,7 @@ export { BreakingChangeReportQueryDto } from './breaking-changes/breaking-change
|
|||
export { UpdateMigrationFindingStatusRequestDto } from './breaking-changes/update-migration-finding-status-request.dto';
|
||||
export { UpdateMigrationFindingStatusesRequestDto } from './breaking-changes/update-migration-finding-statuses-request.dto';
|
||||
export { AssignMigrationOwnerDto } from './breaking-changes/assign-migration-owner.dto';
|
||||
export { NotifyMigrationOwnersRequestDto } from './breaking-changes/notify-migration-owners-request.dto';
|
||||
|
||||
export {
|
||||
AuditPublicDto,
|
||||
|
|
@ -245,6 +246,11 @@ export { GetWorkflowQueryDto } from './workflows/get-workflow-query.dto';
|
|||
export { UpdateWorkflowPublicDto } from './workflows/update-workflow-public.dto';
|
||||
export { UpdateWorkflowQueryDto } from './workflows/update-workflow-query.dto';
|
||||
export { ListWorkflowsQueryDto } from './workflows/list-workflows-query.dto';
|
||||
export {
|
||||
ListCallableSubworkflowsQueryDto,
|
||||
type CallableSubworkflow,
|
||||
type CallableSubworkflowsResponse,
|
||||
} from './workflows/list-callable-subworkflows-query.dto';
|
||||
export {
|
||||
WorkflowPublicDto,
|
||||
CreatedWorkflowPublicDto,
|
||||
|
|
@ -792,3 +798,5 @@ export {
|
|||
} from './promotions/promotion-operations.dto';
|
||||
|
||||
export { ScimConfigPatchDto } from './scim';
|
||||
export { CreateTrustedSourceDto } from './trusted-source/create-trusted-source.dto';
|
||||
export { UpdateTrustedSourceDto } from './trusted-source/update-trusted-source.dto';
|
||||
|
|
|
|||
|
|
@ -198,6 +198,13 @@ describe('ExportPackageRequestDto', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('unknown fields', () => {
|
||||
it('rejects a key the DTO does not declare', () => {
|
||||
const result = ExportPackageRequestDto.safeParse({ workflowIds: ['a'], evil: 'x' });
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('includeArchivedWorkflows', () => {
|
||||
it.each([true, false])('accepts %s', (includeArchivedWorkflows) => {
|
||||
const result = ExportPackageRequestDto.safeParse({
|
||||
|
|
|
|||
|
|
@ -245,6 +245,19 @@ describe('ImportPackageRequestDto', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('accepts create-with-values credentialMissingMode', () => {
|
||||
const result = ImportPackageRequestDto.safeParse(
|
||||
withPackageFile({
|
||||
credentialMissingMode: 'create-with-values',
|
||||
workflowConflictPolicy: 'fail',
|
||||
}),
|
||||
);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.credentialMissingMode).toBe('create-with-values');
|
||||
}
|
||||
});
|
||||
|
||||
it('parses bindings from a JSON object string keyed by entity type', () => {
|
||||
const result = ImportPackageRequestDto.safeParse(
|
||||
withPackageFile({
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ describe('ImportResultDto', () => {
|
|||
workflows: { 'wf-1': 'wf-1' },
|
||||
credentials: { 'cred-1': 'cred-1' },
|
||||
},
|
||||
credentials: { matched: ['cred-1'], stubbed: [] },
|
||||
credentials: { matched: ['cred-1'], stubbed: [], seeded: [] },
|
||||
dataTables: { matched: 1, created: 0, updated: 0 },
|
||||
variables: { matched: [], missing: [], created: [], stubbed: [], updated: [] },
|
||||
tags: { matched: [], created: [], renamed: [], reconciled: [], skipped: [] },
|
||||
|
|
@ -91,7 +91,7 @@ describe('ImportResultDto', () => {
|
|||
folders: [],
|
||||
projects: [],
|
||||
bindings: { workflows: {}, credentials: {} },
|
||||
credentials: { matched: [], stubbed: [] },
|
||||
credentials: { matched: [], stubbed: [], seeded: [] },
|
||||
dataTables: { matched: 0, created: 0, updated: 0 },
|
||||
variables: { matched: [], missing: [], created: [], stubbed: [], updated: [] },
|
||||
tags: { matched: [], created: [], renamed: [], reconciled: [], skipped: [] },
|
||||
|
|
|
|||
|
|
@ -1,25 +1,59 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import '../../openapi-extend';
|
||||
import { exportPackageRequestFieldDocs } from './export-package-request.openapi';
|
||||
import { Z } from '../../zod-class';
|
||||
|
||||
export class ExportPackageRequestDto extends Z.class({
|
||||
agentIds: z.array(z.string().trim().min(1)).min(1).max(300).optional(),
|
||||
workflowIds: z.array(z.string().trim().min(1)).min(1).max(300).optional(),
|
||||
folderIds: z.array(z.string().trim().min(1)).min(1).max(300).optional(),
|
||||
projectIds: z.array(z.string().trim().min(1)).min(1).max(300).optional(),
|
||||
includeVariableValues: z.boolean().default(true),
|
||||
includeTags: z.boolean().default(true),
|
||||
dependencyPolicy: z
|
||||
.enum(['fail', 'reference-only', 'include-in-package'])
|
||||
.optional()
|
||||
.default('fail'),
|
||||
versionPolicy: z
|
||||
.enum(['published-strict', 'prefer-published', 'ignore-unpublished', 'latest'])
|
||||
.optional()
|
||||
.default('latest'),
|
||||
credentialExportPolicy: z
|
||||
.enum(['expression-values-only', 'no-values'])
|
||||
.optional()
|
||||
.default('expression-values-only'),
|
||||
includeArchivedWorkflows: z.boolean().default(false),
|
||||
}) {}
|
||||
export class ExportPackageRequestDto extends Z.class(
|
||||
{
|
||||
agentIds: z
|
||||
.array(z.string().trim().min(1))
|
||||
.min(1)
|
||||
.max(300)
|
||||
.optional()
|
||||
.openapi(exportPackageRequestFieldDocs.agentIds),
|
||||
workflowIds: z
|
||||
.array(z.string().trim().min(1))
|
||||
.min(1)
|
||||
.max(300)
|
||||
.optional()
|
||||
.openapi(exportPackageRequestFieldDocs.workflowIds),
|
||||
folderIds: z
|
||||
.array(z.string().trim().min(1))
|
||||
.min(1)
|
||||
.max(300)
|
||||
.optional()
|
||||
.openapi(exportPackageRequestFieldDocs.folderIds),
|
||||
projectIds: z
|
||||
.array(z.string().trim().min(1))
|
||||
.min(1)
|
||||
.max(300)
|
||||
.optional()
|
||||
.openapi(exportPackageRequestFieldDocs.projectIds),
|
||||
includeVariableValues: z
|
||||
.boolean()
|
||||
.default(true)
|
||||
.openapi(exportPackageRequestFieldDocs.includeVariableValues),
|
||||
includeTags: z.boolean().default(true).openapi(exportPackageRequestFieldDocs.includeTags),
|
||||
dependencyPolicy: z
|
||||
.enum(['fail', 'reference-only', 'include-in-package'])
|
||||
.optional()
|
||||
.default('fail')
|
||||
.openapi(exportPackageRequestFieldDocs.dependencyPolicy),
|
||||
versionPolicy: z
|
||||
.enum(['published-strict', 'prefer-published', 'ignore-unpublished', 'latest'])
|
||||
.optional()
|
||||
.default('latest')
|
||||
.openapi(exportPackageRequestFieldDocs.versionPolicy),
|
||||
credentialExportPolicy: z
|
||||
.enum(['expression-values-only', 'no-values'])
|
||||
.optional()
|
||||
.default('expression-values-only')
|
||||
.openapi(exportPackageRequestFieldDocs.credentialExportPolicy),
|
||||
includeArchivedWorkflows: z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.openapi(exportPackageRequestFieldDocs.includeArchivedWorkflows),
|
||||
},
|
||||
{ strict: true },
|
||||
) {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
import type { ZodOpenAPIMetadata } from '@asteasolutions/zod-to-openapi';
|
||||
|
||||
export const exportPackageRequestFieldDocs = {
|
||||
agentIds: {
|
||||
description: 'IDs of the agents to include. Requires the agents module.',
|
||||
example: ['uT9LdQx7rK2MvB4f'],
|
||||
},
|
||||
workflowIds: {
|
||||
description: 'IDs of the workflows to include in the exported package.',
|
||||
example: ['2tUt1wbLX592XDdX'],
|
||||
},
|
||||
folderIds: {
|
||||
description:
|
||||
'IDs of the folders to include in the exported package. Each folder is exported with its nested folders.',
|
||||
example: ['9xKp2mNqRzAbCdEf'],
|
||||
},
|
||||
projectIds: {
|
||||
description:
|
||||
'IDs of the projects to include in the exported package. Includes their agents when the agents module is enabled.',
|
||||
example: ['Ox8O54VQrmBrb4qL'],
|
||||
},
|
||||
includeVariableValues: {
|
||||
description:
|
||||
'Whether values of variables referenced by the exported entities are bundled into the package. When `false`, variables still travel as name/type files and are listed in the package requirements, but no values travel with the package.',
|
||||
},
|
||||
includeTags: {
|
||||
description:
|
||||
'Whether tags assigned to the exported workflows are bundled into the package. When `false`, no tag files, tag references, or tag requirements travel with the package.',
|
||||
},
|
||||
dependencyPolicy: {
|
||||
description:
|
||||
'Policy for workflow and agent dependencies outside the selected package contents. Includes static workflow references and disabled agent references. `fail` aborts when a required workflow or agent is absent from the package. `include-in-package` adds accessible dependencies recursively. `reference-only` records external requirements without including those definitions. The same policy applies to workflows and agents.',
|
||||
example: 'fail' as const,
|
||||
},
|
||||
versionPolicy: {
|
||||
description:
|
||||
'Which version of each workflow and agent travels in the package. `latest` exports the current draft. `published-strict` requires a published version. `prefer-published` uses the published version when available and the draft otherwise. `ignore-unpublished` skips unpublished selections. Dependencies that cannot be included cause the export to fail only when `dependencyPolicy` is `fail` or `include-in-package`. With `reference-only`, the export records them as external requirements. The selected version supplies the definition and its references. Workflow names, settings, and tags always use their current values. Agent IDs, names, and MCP availability always use their current values.',
|
||||
example: 'latest' as const,
|
||||
},
|
||||
credentialExportPolicy: {
|
||||
description:
|
||||
'Whether expression values from credential data are bundled into the package. `expression-values-only` includes credential fields whose value is an n8n expression (for example `={{ $secrets.apiKey }}`); literal values never travel either way. `no-values` keeps credential data out of the package entirely, so each credential file carries only its id, name and type.',
|
||||
example: 'expression-values-only' as const,
|
||||
},
|
||||
includeArchivedWorkflows: {
|
||||
description:
|
||||
'Whether folder and project exports include their archived workflows. When `false` (default) they are left out. When `true` they travel with `isArchived: true` and are archived on import. Workflows listed in `workflowIds` always export, also when archived.',
|
||||
example: false,
|
||||
},
|
||||
} satisfies Record<string, ZodOpenAPIMetadata>;
|
||||
|
|
@ -108,9 +108,10 @@ export class ImportPackageRequestDto extends Z.class(
|
|||
['id-only', 'name-and-type', 'type-only'],
|
||||
'id-only',
|
||||
).openapi(importPackageRequestFieldDocs.credentialMatchingMode),
|
||||
credentialMissingMode: optionalEnum(['must-preexist', 'create-stub'], 'create-stub').openapi(
|
||||
importPackageRequestFieldDocs.credentialMissingMode,
|
||||
),
|
||||
credentialMissingMode: optionalEnum(
|
||||
['must-preexist', 'create-stub', 'create-with-values'],
|
||||
'create-stub',
|
||||
).openapi(importPackageRequestFieldDocs.credentialMissingMode),
|
||||
bindings: bindingsSchema.openapi(importPackageRequestFieldDocs.bindings),
|
||||
// Required (unlike every other mode/policy field here), matching the legacy handler, which
|
||||
// required it even though this schema alone would otherwise default it to `new-version`.
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ export const importPackageRequestFieldDocs = {
|
|||
description:
|
||||
'What to do when a credential reference cannot be resolved. `create-stub` (default) ' +
|
||||
'creates empty credential placeholders in the target project for missing references. ' +
|
||||
'`create-with-values` also creates them, seeded from the expression data bundled in the ' +
|
||||
'package (see the export option `credentialExportPolicy`); a reference the package ' +
|
||||
'carries no data for falls back to an empty stub. Seeded credentials are listed under ' +
|
||||
'`credentials.seeded` and only allow workflow publishing when the package confirms ' +
|
||||
'that all source data was retained. Partial or unverified data blocks publishing ' +
|
||||
'with `stub-credential`. ' +
|
||||
'`must-preexist` requires every referenced credential to already exist.',
|
||||
},
|
||||
bindings: {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const workflowPublishingOutcomeSchema = z.object({
|
|||
state: z.enum(['published', 'unpublished', 'unchanged', 'blocked', 'failed']).openapi({
|
||||
description:
|
||||
'`blocked` means the imported version could not be published and no version is active ' +
|
||||
'(for example because the workflow uses a stubbed credential, or uses a node type this ' +
|
||||
'(for example because the workflow uses an empty or partially seeded credential, or uses a node type this ' +
|
||||
'instance does not have). When a prior published version remains active, `state` is ' +
|
||||
'`unchanged` with `skippedPublishReason` instead. `failed` means publish or unpublish was ' +
|
||||
'attempted but did not succeed.',
|
||||
|
|
@ -39,7 +39,7 @@ const workflowPublishingOutcomeSchema = z.object({
|
|||
description:
|
||||
'Present when `state` is `unchanged` but the policy wanted to publish the imported ' +
|
||||
'version: a prior published version is still active (for example after an update with ' +
|
||||
'stubbed credentials).',
|
||||
'incomplete credentials).',
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -144,11 +144,16 @@ const importCredentialSummarySchema = z
|
|||
description:
|
||||
'Source credential ids for which empty placeholder credentials were created in the target project.',
|
||||
}),
|
||||
seeded: z.array(z.string()).openapi({
|
||||
description:
|
||||
'Source credential ids for which credentials were created with bundled expression values ' +
|
||||
'in the target project. Partial or unverified bundled data still blocks workflow publishing.',
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
description:
|
||||
'Source credential ids grouped by whether they matched an existing credential or were ' +
|
||||
'created as stubs. Full source→target id mapping is in `bindings.credentials`.',
|
||||
'created as empty stubs or seeded credentials. Full source→target id mapping is in `bindings.credentials`.',
|
||||
});
|
||||
|
||||
const importDataTableSummarySchema = z.object({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
import { CreateTrustedSourceDto } from '../create-trusted-source.dto';
|
||||
|
||||
const config = {
|
||||
version: 1,
|
||||
authentication: { type: 'oauth2' },
|
||||
surfaces: { 'public-api': {} },
|
||||
};
|
||||
|
||||
const request = (overrides: Record<string, unknown> = {}) => ({
|
||||
name: 'Acme',
|
||||
issuer: 'https://acme.example.com',
|
||||
config,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe('CreateTrustedSourceDto', () => {
|
||||
test('parses a minimal request and fills the config defaults', () => {
|
||||
const result = CreateTrustedSourceDto.safeParse(request());
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data?.config.identity.subject).toBe('binding');
|
||||
});
|
||||
|
||||
test('keeps the path of an issue raised by the config schema', () => {
|
||||
const result = CreateTrustedSourceDto.safeParse(
|
||||
request({ config: { ...config, identity: { subject: 'n8n-user-id' } } }),
|
||||
);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error?.issues[0]).toMatchObject({ path: ['config', 'identity', 'subject'] });
|
||||
});
|
||||
|
||||
test.each([
|
||||
{ name: 'at the top level', config: { ...config, typo: true }, path: ['config'] },
|
||||
{
|
||||
name: 'named like a prototype member',
|
||||
config: { ...config, constructor: true },
|
||||
path: ['config'],
|
||||
key: 'constructor',
|
||||
},
|
||||
{
|
||||
name: 'in a nested block',
|
||||
config: { ...config, identity: { typo: true } },
|
||||
path: ['config', 'identity'],
|
||||
},
|
||||
{
|
||||
name: 'in an array item',
|
||||
config: {
|
||||
...config,
|
||||
identity: {
|
||||
roleMapping: {
|
||||
mode: 'continuous',
|
||||
fallbackInstanceRole: 'global:member',
|
||||
instanceRoleRules: [
|
||||
{ id: 'r1', expression: 'true', role: 'global:admin', enabled: true, typo: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
path: ['config', 'identity', 'roleMapping', 'instanceRoleRules', 0],
|
||||
},
|
||||
])('refuses an unknown config key $name', ({ config, path, key = 'typo' }) => {
|
||||
const result = CreateTrustedSourceDto.safeParse(request({ config }));
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error?.issues[0]).toMatchObject({
|
||||
code: 'unrecognized_keys',
|
||||
keys: [key],
|
||||
path,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import { HttpsUrl, trustedSourceConfigSchemaFor } from '@n8n/inbound-auth';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Z } from '../../zod-class';
|
||||
|
||||
export const trustedSourceNameSchema = z.string().trim().min(1).max(128);
|
||||
// Discovery fetches the issuer's metadata and refuses http, so the DTO refuses it up front.
|
||||
export const trustedSourceIssuerSchema = HttpsUrl;
|
||||
// Admin rows only: the system row has no write route.
|
||||
const adminConfigSchema = trustedSourceConfigSchemaFor('admin');
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
/** The first input key that parsing dropped, as its parent path plus the key. */
|
||||
function droppedKey(
|
||||
input: unknown,
|
||||
parsed: unknown,
|
||||
path: Array<string | number> = [],
|
||||
): { path: Array<string | number>; key: string } | undefined {
|
||||
if (Array.isArray(input) && Array.isArray(parsed)) {
|
||||
for (const [index, item] of input.entries()) {
|
||||
const hit = droppedKey(item, parsed[index], [...path, index]);
|
||||
if (hit) return hit;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (!isRecord(input) || !isRecord(parsed)) return undefined;
|
||||
for (const key of Object.keys(input)) {
|
||||
if (!Object.hasOwn(parsed, key)) return { path, key };
|
||||
const hit = droppedKey(input[key], parsed[key], [...path, key]);
|
||||
if (hit) return hit;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// The shared schema strips unknown keys so an older process tolerates a newer document. The write
|
||||
// path refuses them instead, so a misspelled setting cannot pass as its default.
|
||||
export const adminTrustedSourceConfigSchema = z.unknown().transform((input, ctx) => {
|
||||
const result = adminConfigSchema.safeParse(input);
|
||||
if (!result.success) {
|
||||
for (const issue of result.error.issues) ctx.addIssue(issue);
|
||||
return z.NEVER;
|
||||
}
|
||||
const dropped = droppedKey(input, result.data);
|
||||
if (dropped) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.unrecognized_keys,
|
||||
keys: [dropped.key],
|
||||
path: dropped.path,
|
||||
message: `Unrecognized key in object: '${dropped.key}'`,
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return result.data;
|
||||
});
|
||||
|
||||
export class CreateTrustedSourceDto extends Z.class({
|
||||
name: trustedSourceNameSchema,
|
||||
issuer: trustedSourceIssuerSchema,
|
||||
config: adminTrustedSourceConfigSchema,
|
||||
}) {}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
adminTrustedSourceConfigSchema,
|
||||
trustedSourceIssuerSchema,
|
||||
trustedSourceNameSchema,
|
||||
} from './create-trusted-source.dto';
|
||||
import { Z } from '../../zod-class';
|
||||
|
||||
export class UpdateTrustedSourceDto extends Z.class({
|
||||
name: trustedSourceNameSchema.optional(),
|
||||
issuer: trustedSourceIssuerSchema.optional(),
|
||||
config: adminTrustedSourceConfigSchema.optional(),
|
||||
/** Drops every (subject, user) binding of the source. Set it when the issuer moves to another IdP. */
|
||||
clearBindings: z.boolean().optional(),
|
||||
}) {}
|
||||
|
|
@ -212,6 +212,11 @@ describe('CreateWorkflowDto', () => {
|
|||
request: { name: 'a'.repeat(129), nodes: [], connections: {} },
|
||||
expectedErrorPath: ['name'],
|
||||
},
|
||||
...['__proto__', 'constructor', 'prototype'].map((id) => ({
|
||||
name: `reserved id ${id}`,
|
||||
request: { id, name: 'My Workflow', nodes: [], connections: {} },
|
||||
expectedErrorPath: ['id'],
|
||||
})),
|
||||
{
|
||||
name: 'missing nodes',
|
||||
request: { name: 'Test', connections: {} },
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import { baseWorkflowShape } from './base-workflow.dto';
|
||||
import { isSafeObjectKey } from '../../schemas/instance-ai.schema';
|
||||
import { Z } from '../../zod-class';
|
||||
|
||||
export const workflowIdSchema = z.string();
|
||||
// Workflow ids are used as object keys, so `__proto__`, `constructor` and `prototype` are not allowed
|
||||
export const workflowIdSchema = z
|
||||
.string()
|
||||
.refine(isSafeObjectKey, { message: 'Invalid workflow id' });
|
||||
|
||||
export class CreateWorkflowDto extends Z.class({
|
||||
// Spread base fields (name, nodes, connections, settings, etc.)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import { Z } from '../../zod-class';
|
||||
import { paginationSchema } from '../pagination/pagination.dto';
|
||||
|
||||
/**
|
||||
* Query for the list of workflows that a parent workflow can call as
|
||||
* sub-workflows. Only name search and pagination are accepted, so callers
|
||||
* cannot probe workflow contents.
|
||||
*/
|
||||
export class ListCallableSubworkflowsQueryDto extends Z.class({
|
||||
...paginationSchema,
|
||||
query: z.string().trim().max(256).optional(),
|
||||
}) {}
|
||||
|
||||
export type CallableSubworkflow = {
|
||||
id: string;
|
||||
name: string;
|
||||
active: boolean;
|
||||
isArchived: boolean;
|
||||
/** Set only when the workflow belongs to a team project. */
|
||||
projectName?: string;
|
||||
};
|
||||
|
||||
export type CallableSubworkflowsResponse = {
|
||||
count: number;
|
||||
data: CallableSubworkflow[];
|
||||
};
|
||||
|
|
@ -413,8 +413,6 @@ export type FrontendModuleSettings = {
|
|||
sandboxUnavailableReason: string | null;
|
||||
/** When true, orchestrator LLM step / workflow code debug is captured (`N8N_INSTANCE_AI_RUN_DEBUG_ENABLED`). */
|
||||
runDebugEnabled: boolean;
|
||||
/** Whether this instance is in the activation-capped trial cohort (`N8N_INSTANCE_AI_ACTIVATION_CAPPED`). Optional. */
|
||||
activationCapped?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -465,6 +463,8 @@ export type FrontendModuleSettings = {
|
|||
knowledgeBaseEnabled: boolean;
|
||||
/** Whether the AI Assistant proxy is available to the agents module. */
|
||||
proxyEnabled: boolean;
|
||||
/** Whether agents use the skills hub (PostHog flag `125_context_skills`, read at startup). */
|
||||
skillsHubEnabled?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ export {
|
|||
ChatHubUpdateToolRequest,
|
||||
ALWAYS_BLOCKED_CHAT_HUB_TOOL_TYPES,
|
||||
CHAT_USER_BLOCKED_CHAT_HUB_TOOL_TYPES,
|
||||
isChatHubToolTypeAllowed,
|
||||
chatHubVectorStoreProviderSchema,
|
||||
type ChatHubVectorStoreProvider,
|
||||
VECTOR_STORE_PROVIDER_CREDENTIAL_TYPE_MAP,
|
||||
|
|
@ -343,6 +344,9 @@ export type {
|
|||
BreakingChangeVersion,
|
||||
MigrationFindingStatus,
|
||||
MigrationFindingTriageStatus,
|
||||
MigrationFindingListedStatus,
|
||||
MigrationNotifyResult,
|
||||
MigrationNotifySkipReason,
|
||||
WorkflowMigrationResult,
|
||||
} from './schemas/breaking-changes.schema';
|
||||
|
||||
|
|
@ -352,6 +356,8 @@ export {
|
|||
breakingChangeVersionSchema,
|
||||
migrationFindingStatusSchema,
|
||||
migrationFindingTriageStatusSchema,
|
||||
migrationFindingListedStatusSchema,
|
||||
migrationNotifySkipReasonSchema,
|
||||
migrationOwnerSourceSchema,
|
||||
} from './schemas/breaking-changes.schema';
|
||||
|
||||
|
|
@ -497,6 +503,7 @@ export {
|
|||
InstanceAiEvalExecutionRequest,
|
||||
InstanceAiEvalAgentExecutionRequest,
|
||||
InstanceAiEvalCredentialAllowlistRequest,
|
||||
InstanceAiEvalCredentialTestTargetRequest,
|
||||
INSTANCE_AI_MEMORY_TASK_WAIT_TIMEOUT_MS,
|
||||
AI_GATEWAY_MANAGED_TAG,
|
||||
InstanceAiEvalRestoreThreadRequest,
|
||||
|
|
@ -735,11 +742,17 @@ export {
|
|||
} from './schemas/instance-ai-setup-credential-selection';
|
||||
|
||||
export type {
|
||||
McpRegistryDiscoveredConnection,
|
||||
McpRegistryDiscoveredTool,
|
||||
McpRegistryDiscoveryFailureReason,
|
||||
McpRegistryDiscoveryRequest,
|
||||
McpRegistryDiscoveryResponse,
|
||||
McpRegistryServerStatus,
|
||||
McpRegistryServerIconResponse,
|
||||
McpRegistryServerToolResponse,
|
||||
McpRegistryServerResponse,
|
||||
} from './schemas/mcp-registry.schema';
|
||||
export { mcpRegistryDiscoveryRequestSchema } from './schemas/mcp-registry.schema';
|
||||
|
||||
export {
|
||||
createInitialState,
|
||||
|
|
@ -749,6 +762,7 @@ export {
|
|||
stateFromAgentTree,
|
||||
normalizeLegacyReasoningTimeline,
|
||||
normalizeAgentTree,
|
||||
orchestratorAgentId,
|
||||
} from './schemas/agent-run-reducer';
|
||||
|
||||
export type { AgentRunState } from './schemas/agent-run-reducer';
|
||||
|
|
@ -966,3 +980,4 @@ export { compareExecutionListItems } from './dto/executions/compare-execution-li
|
|||
|
||||
export type * from './workflow-suggestions';
|
||||
export * from './self-healing-results';
|
||||
export * from './model-priority';
|
||||
|
|
|
|||
23
packages/@n8n/api-types/src/model-priority.ts
Normal file
23
packages/@n8n/api-types/src/model-priority.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Moves models that have a priority to the front, highest first, and keeps the
|
||||
* order of the rest. A models API reports a priority to promote a model, for
|
||||
* example at its launch.
|
||||
*/
|
||||
export function pinPrioritizedModels<T>(models: T[], getPriority: (model: T) => unknown): T[] {
|
||||
const entries = models.map((model) => {
|
||||
const priority = getPriority(model);
|
||||
return {
|
||||
model,
|
||||
priority: typeof priority === 'number' && Number.isFinite(priority) ? priority : null,
|
||||
};
|
||||
});
|
||||
const prioritized = entries
|
||||
.flatMap((entry) =>
|
||||
entry.priority === null ? [] : [{ model: entry.model, priority: entry.priority }],
|
||||
)
|
||||
.sort((a, b) => b.priority - a.priority);
|
||||
return [
|
||||
...prioritized.map((entry) => entry.model),
|
||||
...entries.filter((entry) => entry.priority === null).map((entry) => entry.model),
|
||||
];
|
||||
}
|
||||
|
|
@ -542,6 +542,70 @@ describe('agent-run-reducer', () => {
|
|||
expectStateMapsNotPolluted(state);
|
||||
});
|
||||
|
||||
it('routes a follow-up run to the tree after the state is rebuilt from a snapshot', () => {
|
||||
// A two-run group, as a run-sync frame or history hydration delivers it.
|
||||
const original = stateWithRun('run-1', 'orchestrator-run-1');
|
||||
reduceEvent(original, makeToolCall('run-1', 'orchestrator-run-1', 'tc-plan', 'create-tasks'));
|
||||
reduceEvent(original, makeRunStart('run-2', 'orchestrator-run-2'));
|
||||
reduceEvent(original, makeToolCall('run-2', 'orchestrator-run-2', 'tc-folder', 'workspace'));
|
||||
const state = stateFromAgentTree(deepCopy(toAgentTree(original)))!;
|
||||
|
||||
// The snapshot holds no alias for run-2, and its run-start is not replayed.
|
||||
expect(findAgent(state, 'orchestrator-run-2')).toBeUndefined();
|
||||
|
||||
reduceEvent(
|
||||
state,
|
||||
makeToolResult('run-2', 'orchestrator-run-2', 'tc-folder', { id: 'folder-1' }),
|
||||
);
|
||||
reduceEvent(state, makeToolCall('run-2', 'orchestrator-run-2', 'tc-move', 'workspace'));
|
||||
reduceEvent(state, makeConfirmationRequest('run-2', 'orchestrator-run-2', 'tc-move'));
|
||||
|
||||
const moveTc = state.toolCallsById['tc-move'];
|
||||
expect(moveTc?.isLoading).toBe(true);
|
||||
expect(moveTc?.confirmation?.requestId).toBe('req-1');
|
||||
const root = toAgentTree(state);
|
||||
expect(root.agentId).toBe('orchestrator-run-1');
|
||||
expect(root.timeline.at(-1)).toEqual(
|
||||
expect.objectContaining({ type: 'tool-call', toolCallId: 'tc-move' }),
|
||||
);
|
||||
expectStateMapsNotPolluted(state);
|
||||
});
|
||||
|
||||
it('attaches a sub-agent spawned by a follow-up run after a snapshot rebuild', () => {
|
||||
const original = stateWithRun('run-1', 'orchestrator-run-1');
|
||||
reduceEvent(original, makeToolCall('run-1', 'orchestrator-run-1', 'tc-plan', 'create-tasks'));
|
||||
const state = stateFromAgentTree(deepCopy(toAgentTree(original)))!;
|
||||
|
||||
reduceEvent(
|
||||
state,
|
||||
makeAgentSpawned('run-2', 'builder-1', 'orchestrator-run-2', 'workflow-builder'),
|
||||
);
|
||||
|
||||
expect(toAgentTree(state).children.map((child) => child.agentId)).toEqual(['builder-1']);
|
||||
});
|
||||
|
||||
it('does not alias an agent whose id is not the orchestrator id of its run', () => {
|
||||
const original = stateWithRun('run-1', 'orchestrator-run-1');
|
||||
reduceEvent(original, makeToolCall('run-1', 'orchestrator-run-1', 'tc-plan', 'create-tasks'));
|
||||
const state = stateFromAgentTree(deepCopy(toAgentTree(original)))!;
|
||||
|
||||
reduceEvent(state, makeToolCall('run-2', 'orchestrator-run-3', 'tc-stray', 'workspace'));
|
||||
reduceEvent(state, makeToolCall('run-2', 'unknown-agent', 'tc-unknown', 'workspace'));
|
||||
|
||||
expect(state.toolCallsById['tc-stray']).toBeUndefined();
|
||||
expect(state.toolCallsById['tc-unknown']).toBeUndefined();
|
||||
});
|
||||
|
||||
it('does not alias into a fresh state before its run-start', () => {
|
||||
const state = createInitialState();
|
||||
|
||||
reduceEvent(state, makeToolCall('run-1', 'orchestrator-run-1', 'tc-early', 'workspace'));
|
||||
reduceEvent(state, makeRunStart('run-1', 'orchestrator-run-1'));
|
||||
|
||||
expect(state.rootAgentId).toBe('orchestrator-run-1');
|
||||
expect(state.toolCallsById['tc-early']).toBeUndefined();
|
||||
});
|
||||
|
||||
it('run-start with unsafe agentId is ignored', () => {
|
||||
const state = createInitialState();
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user