mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 13:39:44 +02:00
chore: Resolve master→3.x sync conflict (#41149)
This commit is contained in:
commit
67e02474b1
|
|
@ -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.
|
||||
|
|
@ -21,8 +21,7 @@ check the code, not license to drop a team default.
|
|||
## Non-negotiable rules
|
||||
|
||||
- New endpoints are `@PublicApiController` classes under `v1/controllers/`, one
|
||||
`*.public.controller.ts` per feature. A controller is a class — never
|
||||
`export =` (the legacy tuple style; `require-public-api-controller` flags it).
|
||||
`*.public.controller.ts` per feature.
|
||||
- Public API and internal REST are separate HTTP surfaces. A public controller
|
||||
never calls an internal controller/endpoint; both reuse the same service.
|
||||
- Controllers and handlers delegate to a service — never import a repository or
|
||||
|
|
@ -31,7 +30,6 @@ check the code, not license to drop a team default.
|
|||
`@ApiResponse(Dto)`.
|
||||
- Register each controller via a side-effect import in `v1/controllers/index.ts`
|
||||
(`public-api-controllers.test.ts` fails otherwise).
|
||||
- Don't add business logic to legacy `express-openapi-validator` (EOV) handlers.
|
||||
- Migrating a legacy endpoint must not change its public contract.
|
||||
|
||||
These are `n8n-local-rules` ESLint rules (see `packages/cli/eslint.config.mjs`)
|
||||
|
|
@ -200,6 +198,5 @@ existing tests.
|
|||
- [Test-before-save endpoints](reference.md#test-before-save-endpoints)
|
||||
- [Errors](reference.md#errors)
|
||||
- [Testing matrix](reference.md#testing-matrix)
|
||||
- [Migrating legacy EOV endpoints](reference.md#migrating-legacy-eov-endpoints)
|
||||
- [Verifying a migration](reference.md#verifying-a-migration)
|
||||
- [CI and merging](reference.md#ci-and-merging)
|
||||
|
|
|
|||
|
|
@ -122,11 +122,10 @@ app-wide `bodyParser` before the registry sees it. Declaring
|
|||
- An unknown field (one the DTO doesn't declare, on a `{ strict: true }` DTO)
|
||||
fails with `Unexpected form field "<name>"` — not the default "unrecognized
|
||||
keys" wording.
|
||||
- Multer's own parsing errors map to the same statuses express-openapi-validator
|
||||
used: `413` for a size/count limit, `400` for any other multer error, `500`
|
||||
— unmasked, since this is a `ResponseError` — for anything else (a
|
||||
malformed body; a missing boundary gives `400` with `multipart file(s)
|
||||
required`).
|
||||
- Multer parsing errors map to these statuses: `413` for a size or count limit,
|
||||
`500` for `LIMIT_UNEXPECTED_FILE`, `400` for any other multer error, and `500`
|
||||
for anything else (a malformed body). The `500` is not masked, because it is a
|
||||
`ResponseError`. A missing boundary gives `400` with `multipart file(s) required`.
|
||||
- The body is parsed **after** every auth/scope/license/quota gate and
|
||||
**before** controller/route middlewares — a caller those gates would reject
|
||||
never has their (possibly huge) body read off the socket, and a middleware
|
||||
|
|
@ -175,58 +174,6 @@ example an `application/gzip` stream. The framework treats options whose
|
|||
The runtime part is `sendBinaryResponse` in
|
||||
`packages/cli/src/public-api/media-types/binary-response.ts`.
|
||||
|
||||
## Migrating legacy EOV endpoints
|
||||
|
||||
Legacy `express-openapi-validator` endpoints live under
|
||||
`v1/handlers/`, wired through `openapi.yml` with `x-eov-operation-*` and request
|
||||
types in `packages/cli/src/public-api/types.ts`. Treat these as migration targets,
|
||||
not templates.
|
||||
|
||||
- Prefer migrating to `@PublicApiController` over extending the handler.
|
||||
- Preserve the public contract: path, method, scopes, status codes, response
|
||||
shape, and pagination.
|
||||
- Move HTTP concerns into the controller and business orchestration into the
|
||||
shared service; keep the controller a thin HTTP boundary. If the legacy
|
||||
handler was itself already just a thin wrapper around an internal
|
||||
`@RestController` (calling its methods directly, e.g.
|
||||
`Container.get(SomeController).createThing(req, res, payload)`), the new
|
||||
public controller can call that same internal controller directly — no need
|
||||
to duplicate its validation/business logic.
|
||||
- A route must be served by either the EOV handler or a controller, not both —
|
||||
the build's `mergeDecoratorDocument` (`v1/openapi-gen/generate.ts`) throws on
|
||||
a path+method declared by both sides. Remove the legacy wiring (its path's
|
||||
`$ref` entry in `openapi.yml`, the `x-eov-operation-*` handler, and its
|
||||
`handler.ts`) only after the new controller is registered, `pnpm build`
|
||||
regenerates the spec cleanly, and tests are updated.
|
||||
- Fully delete the migrated legacy files: the handler's `.ts`, its
|
||||
`spec/paths/*.yml` and `spec/schemas/*.yml`, and any now-dead request type
|
||||
in `packages/cli/src/public-api/types.ts`. Then check
|
||||
`v1/shared/spec/schemas/_index.yml` and `v1/shared/spec/parameters/_index.yml`
|
||||
for entries that `$ref` one of the deleted schema/parameter files — those are
|
||||
separate from the path's own `$ref` in `openapi.yml` and are easy to miss;
|
||||
left dangling, the next bundle fails on a broken `$ref`.
|
||||
- If the legacy handler gated on a license (`isLicensed('feat:x')` middleware),
|
||||
`@Licensed('feat:x')` now replicates that for a controller route (see the
|
||||
decorator table in [SKILL.md](SKILL.md#declaring-a-controller)) — but only for
|
||||
a single feature. If the legacy check was an any-of/all-of over several flags
|
||||
(e.g. `LicenseState.isProvisioningLicensed()`), `@Licensed` can't express
|
||||
that; replicate it manually in the controller instead, don't drop it - this
|
||||
is exactly what the internal `provisioning.controller.ee.ts` and
|
||||
`role-mapping-rule.controller.ee.ts` already do, since neither uses
|
||||
`@Licensed` for that reason.
|
||||
- As a legacy file drops repository access / the `export =` tuple, remove its
|
||||
entry from the `off` allowlists for `no-repository-in-public-api-handler` and
|
||||
`require-public-api-controller` in `packages/cli/eslint.config.mjs` (shrink-only
|
||||
— never extend them).
|
||||
- A `multipart/form-data` legacy endpoint migrates onto `@Body({ mediaType:
|
||||
'multipart/form-data', uploadLimits })` — see
|
||||
[Request body media types](#request-body-media-types). For any other
|
||||
non-standard endpoint, study the nearest existing handler first.
|
||||
- Keep each field in its original position when you extract a request shape shared
|
||||
by two routes, and destructure out the ones a route doesn't take. The generator
|
||||
emits properties in shape order, so a moved field rewrites the `*.generated.yml`
|
||||
of a route the PR wasn't changing.
|
||||
|
||||
## Verifying a migration
|
||||
|
||||
Tests are written against the new code, so they can't show that the old behavior
|
||||
|
|
|
|||
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 |
|
||||
|
||||
|
|
|
|||
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)
|
||||
|
||||
|
||||
|
|
|
|||
20
OWNERS
20
OWNERS
|
|
@ -30,6 +30,7 @@ packages/@n8n/backend-network/ @n8n-io/catalysts
|
|||
packages/@n8n/backend-services/ @n8n-io/catalysts
|
||||
packages/@n8n/backend-services/src/services/role.service.ts @n8n-io/iam
|
||||
packages/@n8n/backend-services/src/services/role-cache.service.ts @n8n-io/iam
|
||||
packages/@n8n/backend-services/src/services/jwt.service.ts @n8n-io/iam
|
||||
packages/@n8n/backend-test-utils/ @n8n-io/catalysts
|
||||
packages/@n8n/di/ @n8n-io/catalysts
|
||||
packages/@n8n/errors/ @n8n-io/catalysts
|
||||
|
|
@ -115,12 +116,12 @@ 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
|
||||
packages/cli/src/modules/mcp/ @n8n-io/context
|
||||
packages/cli/src/modules/quick-connect/ @n8n-io/ai
|
||||
packages/modules/quick-connect/ @n8n-io/ai
|
||||
packages/cli/src/modules/chat-hub/ @n8n-io/ai-assistant
|
||||
packages/cli/src/modules/instance-ai/ @n8n-io/ai-assistant
|
||||
|
||||
|
|
@ -154,7 +155,6 @@ packages/cli/src/controllers/dynamic-node-parameters.controller.ts @n8n-io/node
|
|||
packages/cli/src/controllers/e2e.controller.ts @n8n-io/qa-dx
|
||||
|
||||
# CLI services
|
||||
packages/cli/src/services/jwt.service.ts @n8n-io/iam
|
||||
packages/cli/src/services/user.service.ts @n8n-io/iam
|
||||
packages/cli/src/services/password.utility.ts @n8n-io/iam
|
||||
packages/cli/src/services/public-api-key.service.ts @n8n-io/iam
|
||||
|
|
@ -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
|
||||
|
|
@ -281,3 +281,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 |
|
||||
|
|
@ -131,6 +133,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 |
|
||||
|
|
@ -223,10 +228,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"
|
||||
|
|
@ -369,6 +380,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"
|
||||
|
|
@ -679,6 +696,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
|
||||
|
|
@ -714,6 +737,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
|
||||
|
|
@ -1651,6 +1680,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_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_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 | | | |
|
||||
|
|
@ -56,6 +56,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"
|
||||
|
|
@ -255,6 +256,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_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_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 | | | |
|
||||
|
|
@ -76,6 +76,8 @@ erDiagram
|
|||
"public.project_relation" }o--|| "public.user" : "FOREIGN KEY (#quot;userId#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"
|
||||
|
|
@ -398,6 +400,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 |
|
||||
|
|
@ -131,6 +133,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 |
|
||||
|
|
@ -205,10 +210,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"
|
||||
|
|
@ -351,6 +362,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"
|
||||
|
|
@ -665,6 +682,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
|
||||
|
|
@ -700,6 +723,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
|
||||
|
|
@ -1637,6 +1666,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_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_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 | | | |
|
||||
|
|
@ -62,6 +62,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"
|
||||
|
|
@ -261,6 +262,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_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_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 | | | |
|
||||
|
|
@ -82,6 +82,8 @@ erDiagram
|
|||
"project_relation" |o--|| "user" : "FOREIGN KEY (userId) 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"
|
||||
|
|
@ -404,6 +406,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);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -873,6 +873,19 @@ describe('createModel', () => {
|
|||
expect(model.useDeploymentBasedUrls).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should use the deployment name on a Foundry endpoint when provided', () => {
|
||||
// A Foundry deployment can be named differently from the model it
|
||||
// serves, so the user's deployment name must reach the request path.
|
||||
const model = createModel({
|
||||
id: 'azure-openai/gpt-4.1-nano',
|
||||
apiKey: 'az-key',
|
||||
endpointType: 'foundry',
|
||||
baseURL: 'https://my-resource.services.ai.azure.com/openai/v1',
|
||||
deploymentName: 'my-chat-model',
|
||||
}) as unknown as Record<string, unknown>;
|
||||
expect(model.modelId).toBe('my-chat-model');
|
||||
});
|
||||
|
||||
it('should treat a foundry endpoint without resourceName as valid', () => {
|
||||
expect(() =>
|
||||
createModel({
|
||||
|
|
@ -993,6 +1006,19 @@ describe('createModel', () => {
|
|||
expect(mockClientOAuth2.credentials.getToken).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('uses the deployment name on the Foundry Entra path when provided', () => {
|
||||
// The deployment name can differ from the model it serves; it reaches
|
||||
// the OpenAI-compatible path as the model id.
|
||||
const model = createModel({
|
||||
id: 'azure-openai/gpt-4.1-nano',
|
||||
endpointType: 'foundry',
|
||||
baseURL: 'https://my-resource.services.ai.azure.com/openai/v1',
|
||||
deploymentName: 'my-chat-model',
|
||||
...entraCreds,
|
||||
}) as unknown as Record<string, unknown>;
|
||||
expect(model.modelId).toBe('my-chat-model');
|
||||
});
|
||||
|
||||
it('rejects credentials that supply both apiKey and Entra', () => {
|
||||
expect(() =>
|
||||
createModel({
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -412,6 +412,9 @@ const LANGUAGE_PROVIDERS: ProviderRegistry = {
|
|||
// `…/openai/v1/openai`. Drive it as a plain OpenAI-compatible endpoint
|
||||
// so the configured base is used verbatim.
|
||||
if (endpointType === 'foundry') {
|
||||
// The path segment Foundry matches against is the user-chosen
|
||||
// deployment name, which can differ from the model it serves.
|
||||
const deploymentId = deploymentName ?? model;
|
||||
if (isEntra) {
|
||||
// `buildOpenAiCompatible` has no `tokenProvider` slot, so wrap the
|
||||
// transport to inject `Authorization: Bearer <token>` per request.
|
||||
|
|
@ -420,11 +423,17 @@ const LANGUAGE_PROVIDERS: ProviderRegistry = {
|
|||
'azure-openai',
|
||||
undefined,
|
||||
{ baseURL },
|
||||
model,
|
||||
deploymentId,
|
||||
withBearerAuth(fetch, tokenProvider),
|
||||
);
|
||||
}
|
||||
return buildOpenAiCompatible('azure-openai', undefined, { apiKey, baseURL }, model, fetch);
|
||||
return buildOpenAiCompatible(
|
||||
'azure-openai',
|
||||
undefined,
|
||||
{ apiKey, baseURL },
|
||||
deploymentId,
|
||||
fetch,
|
||||
);
|
||||
}
|
||||
|
||||
// Classic Azure OpenAI (`*.openai.azure.com`, or `resourceName` only).
|
||||
|
|
@ -435,9 +444,10 @@ const LANGUAGE_PROVIDERS: ProviderRegistry = {
|
|||
// selects the Responses API + the `/v1/` path, which Azure rejects with
|
||||
// "API version not supported" for date-based versions.
|
||||
//
|
||||
// Azure deployments are user-named and surfaced in the deployment-based
|
||||
// URL path. The catalog model id is not the deployment id, so prefer the
|
||||
// user's `deploymentName` when provided and fall back to the model id.
|
||||
// Azure deployments are user-named and surfaced in the URL path on
|
||||
// both endpoint types. The catalog model id is not the deployment id,
|
||||
// so prefer the user's `deploymentName` when provided and fall back to
|
||||
// the model id.
|
||||
const { createAzure } = require('@ai-sdk/azure') as typeof import('@ai-sdk/azure');
|
||||
let normalizedBaseURL = baseURL;
|
||||
// SDK expects url like `https://resourceName.openai.azure.com/openai`
|
||||
|
|
|
|||
|
|
@ -75,9 +75,10 @@ export const PROVIDER_CREDENTIAL_SCHEMAS = {
|
|||
*/
|
||||
endpointType: z.enum(['classic', 'foundry']).optional(),
|
||||
/**
|
||||
* User-defined Azure deployment name for classic endpoints. The catalog
|
||||
* model id is not the deployment id, so the agent flow must carry this
|
||||
* separately. Only used by the classic branch.
|
||||
* User-defined Azure deployment name. The catalog model id is not the
|
||||
* deployment id, so the agent flow must carry this separately. Used by
|
||||
* both the classic and Foundry branches; on Foundry it overrides the
|
||||
* model id in the request path.
|
||||
*/
|
||||
deploymentName: z.string().optional(),
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
import { AgentIntegrationConfigSchema, AgentIntegrationSchema } from '../agent-integration.schema';
|
||||
import {
|
||||
AgentIntegrationConfigSchema,
|
||||
AgentIntegrationSchema,
|
||||
resolveWhatsAppSettings,
|
||||
} from '../agent-integration.schema';
|
||||
import { AgentJsonConfigSchema } from '../agent-json-config.schema';
|
||||
|
||||
describe('AgentIntegrationSchema', () => {
|
||||
|
|
@ -210,4 +214,79 @@ describe('AgentIntegrationSchema', () => {
|
|||
expect(parse({ somethingElse: true }).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WhatsApp settings', () => {
|
||||
const parse = (settings: Record<string, unknown>) =>
|
||||
AgentIntegrationSchema.safeParse({ type: 'whatsapp', credentialId: 'cred-123', settings });
|
||||
|
||||
it('accepts a WhatsApp integration without settings', () => {
|
||||
expect(
|
||||
AgentIntegrationSchema.safeParse({ type: 'whatsapp', credentialId: 'cred-123' }).success,
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('accepts the typing indicator, media download and retention settings', () => {
|
||||
const result = parse({
|
||||
sessionIdleTimeoutMinutes: 30,
|
||||
typingIndicator: false,
|
||||
downloadMedia: false,
|
||||
mediaRetentionDays: 7,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data?.settings).toEqual({
|
||||
sessionIdleTimeoutMinutes: 30,
|
||||
typingIndicator: false,
|
||||
downloadMedia: false,
|
||||
mediaRetentionDays: 7,
|
||||
});
|
||||
});
|
||||
|
||||
it.each([1, 365])('accepts a retention of %i days', (mediaRetentionDays) => {
|
||||
expect(parse({ mediaRetentionDays }).success).toBe(true);
|
||||
});
|
||||
|
||||
it.each([0, 366, 1.5, -1])('rejects a retention of %s days', (mediaRetentionDays) => {
|
||||
expect(parse({ mediaRetentionDays }).success).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['typingIndicator', 'yes'],
|
||||
['downloadMedia', 1],
|
||||
])('rejects a non-boolean %s', (key, value) => {
|
||||
expect(parse({ [key]: value }).success).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects an unknown setting', () => {
|
||||
expect(parse({ accessMode: 'public' }).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveWhatsAppSettings', () => {
|
||||
it('applies the defaults when no settings are saved', () => {
|
||||
const defaults = { typingIndicator: true, downloadMedia: true, mediaRetentionDays: 30 };
|
||||
|
||||
expect(resolveWhatsAppSettings()).toEqual(defaults);
|
||||
expect(resolveWhatsAppSettings({})).toEqual(defaults);
|
||||
expect(resolveWhatsAppSettings({ sessionIdleTimeoutMinutes: 10 })).toEqual(defaults);
|
||||
});
|
||||
|
||||
it('keeps the saved values', () => {
|
||||
expect(
|
||||
resolveWhatsAppSettings({
|
||||
typingIndicator: false,
|
||||
downloadMedia: false,
|
||||
mediaRetentionDays: 90,
|
||||
}),
|
||||
).toEqual({ typingIndicator: false, downloadMedia: false, mediaRetentionDays: 90 });
|
||||
});
|
||||
|
||||
it('fills only the settings that are unset', () => {
|
||||
expect(resolveWhatsAppSettings({ downloadMedia: false })).toEqual({
|
||||
typingIndicator: true,
|
||||
downloadMedia: false,
|
||||
mediaRetentionDays: 30,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -104,9 +104,44 @@ export type AgentDiscordIntegrationSettings = z.infer<typeof AgentDiscordSetting
|
|||
export const AgentLinearSettingsSchema = AgentSessionOnlySettingsSchema;
|
||||
export type AgentLinearIntegrationSettings = z.infer<typeof AgentLinearSettingsSchema>;
|
||||
|
||||
export const AgentWhatsAppSettingsSchema = AgentSessionOnlySettingsSchema;
|
||||
export const WHATSAPP_MEDIA_RETENTION_DAYS_MAX = 365;
|
||||
|
||||
/**
|
||||
* Applied when a setting is unset, so channels saved before these settings
|
||||
* existed get the same behavior as new ones.
|
||||
*/
|
||||
export const WHATSAPP_SETTING_DEFAULTS = {
|
||||
typingIndicator: true,
|
||||
downloadMedia: true,
|
||||
mediaRetentionDays: 30,
|
||||
} as const;
|
||||
|
||||
export const AgentWhatsAppSettingsSchema = z
|
||||
.object({
|
||||
sessionIdleTimeoutMinutes,
|
||||
/**
|
||||
* Shows WhatsApp's "typing…" indicator while the agent works. The Cloud API
|
||||
* sends it together with a read receipt, so this also marks the message read.
|
||||
*/
|
||||
typingIndicator: z.boolean().optional(),
|
||||
/** Whether the agent receives the images, documents and audio customers send. */
|
||||
downloadMedia: z.boolean().optional(),
|
||||
/** Days to keep downloaded media before it is deleted. */
|
||||
mediaRetentionDays: z.number().int().min(1).max(WHATSAPP_MEDIA_RETENTION_DAYS_MAX).optional(),
|
||||
})
|
||||
.strict();
|
||||
export type AgentWhatsAppIntegrationSettings = z.infer<typeof AgentWhatsAppSettingsSchema>;
|
||||
|
||||
/** The WhatsApp settings in effect, with the defaults for any that were never saved. */
|
||||
export function resolveWhatsAppSettings(settings?: AgentWhatsAppIntegrationSettings) {
|
||||
return {
|
||||
typingIndicator: settings?.typingIndicator ?? WHATSAPP_SETTING_DEFAULTS.typingIndicator,
|
||||
downloadMedia: settings?.downloadMedia ?? WHATSAPP_SETTING_DEFAULTS.downloadMedia,
|
||||
mediaRetentionDays:
|
||||
settings?.mediaRetentionDays ?? WHATSAPP_SETTING_DEFAULTS.mediaRetentionDays,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Where the Teams app may be used, and how much it may read there.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -473,12 +464,12 @@ export const AgentJsonConfigBaseSchema = z.object({
|
|||
model: DraftAgentModelSchema,
|
||||
credential: z.string().optional(),
|
||||
/**
|
||||
* Azure OpenAI classic deployments are user-named in Azure and surfaced in
|
||||
* the deployment-based URL path. The catalog model id (e.g. `gpt-4o`) is not
|
||||
* Azure OpenAI deployments are user-named in Azure and surfaced in the URL
|
||||
* path on both endpoint types. The catalog model id (e.g. `gpt-4o`) is not
|
||||
* the deployment id, so the agent flow must capture the user's deployment
|
||||
* name separately. Only meaningful for the `azure-openai` provider with a
|
||||
* classic endpoint; ignored by Foundry and other providers. An empty
|
||||
* string is a deliberate clear of a previously stored value.
|
||||
* name separately. Only meaningful for the `azure-openai` provider — on
|
||||
* classic it is required, on Foundry it is optional and overrides the model
|
||||
* id. An empty string is a deliberate clear of a previously stored value.
|
||||
*/
|
||||
modelDeploymentName: z.string().trim().optional(),
|
||||
instructions: z.string(),
|
||||
|
|
@ -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';
|
||||
|
|
@ -14,6 +15,7 @@ export * from './sanitize-agent-json-config';
|
|||
export * from './sanitize-agent-tool-name';
|
||||
export * from './slack';
|
||||
export type * from './teams';
|
||||
export type * from './whatsapp';
|
||||
export * from './agent-task.schema';
|
||||
export * from './dto';
|
||||
export * from './model-providers';
|
||||
|
|
@ -24,11 +26,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,25 @@ 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;
|
||||
/**
|
||||
* Where an administrator approves the app's file access in one step. Only
|
||||
* for an app the recommended setup made, which registers the return address
|
||||
* the consent screen needs.
|
||||
*/
|
||||
fileAccessConsentUrl: string | null;
|
||||
/** The Azure Bot the recommended setup made. Null for one made by hand or before n8n kept it. */
|
||||
azureBot: { name: string; portalUrl: string } | null;
|
||||
/**
|
||||
* The saved channel would build a different app than the one n8n last
|
||||
* handed over, so Teams holds an older version. False when n8n has no record.
|
||||
*/
|
||||
appOutdated: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,3 +106,104 @@ export type TeamsCredentialCheck =
|
|||
status: 'failed';
|
||||
reason: 'certificate' | 'incomplete' | 'rejected' | 'unreachable' | 'cloud';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the app may read files attached in channels and group chats. Read
|
||||
* from a fresh token, so it shows an approval at once, while a running agent
|
||||
* keeps its older token for up to an hour. `unknown` when Microsoft gave no
|
||||
* token to read it from.
|
||||
*/
|
||||
export interface TeamsFileAccessCheck {
|
||||
status: 'approved' | 'notApproved' | 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent through Microsoft's admin consent screen and handed back unchanged, so
|
||||
* the agent page knows to open the Teams settings.
|
||||
*/
|
||||
export type TeamsFileAccessConsentState = 'teams-file-access';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
fileAccessConsentUrl: string | null;
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
|
|
|||
1
packages/@n8n/api-types/src/agents/whatsapp/index.ts
Normal file
1
packages/@n8n/api-types/src/agents/whatsapp/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export type * from './types';
|
||||
35
packages/@n8n/api-types/src/agents/whatsapp/types.ts
Normal file
35
packages/@n8n/api-types/src/agents/whatsapp/types.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* How the WhatsApp number appears to customers, as set in Meta. Every field is
|
||||
* optional: a business can leave its profile empty, and the setup still works.
|
||||
*/
|
||||
export interface WhatsAppBusinessProfile {
|
||||
/** The display name Meta approved for the number. */
|
||||
verifiedName?: string;
|
||||
/** The number as WhatsApp shows it, e.g. `+1 555-010-0199`. */
|
||||
displayPhoneNumber?: string;
|
||||
description?: string;
|
||||
/** A short-lived Meta CDN URL. Do not store it. */
|
||||
profilePictureUrl?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of checking a WhatsApp credential against Meta before the channel is
|
||||
* connected. A wrong ID or secret otherwise surfaces only when the first
|
||||
* message fails.
|
||||
*
|
||||
* - `incomplete`: a required field is empty.
|
||||
* - `temporaryToken`: the access token expires soon, like the one on API Setup.
|
||||
* - `mismatch`: Meta refuses the token, the IDs, or the app secret.
|
||||
* - `unreachable`: n8n could not reach Meta.
|
||||
*/
|
||||
export type WhatsAppCredentialCheck =
|
||||
| { status: 'ok'; profile: WhatsAppBusinessProfile }
|
||||
| {
|
||||
status: 'failed';
|
||||
reason: 'incomplete' | 'temporaryToken' | 'mismatch' | 'unreachable';
|
||||
};
|
||||
|
||||
/** Whether Meta has completed the webhook handshake with this agent. */
|
||||
export interface WhatsAppWebhookStatus {
|
||||
verified: boolean;
|
||||
}
|
||||
|
|
@ -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),
|
||||
}) {}
|
||||
|
|
@ -55,6 +55,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,
|
||||
|
|
@ -243,6 +244,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,
|
||||
|
|
@ -789,3 +795,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>;
|
||||
|
|
@ -78,6 +78,46 @@ export const credentialUnresolvedIssueOpenApi: ZodOpenAPIMetadata = {
|
|||
description: 'A credential reference that could not be resolved in the target project.',
|
||||
};
|
||||
|
||||
export const credentialConflictFieldDocs = {
|
||||
kind: {
|
||||
description:
|
||||
'`differs`: under `credentialConflictPolicy=fail`, the package would change the ' +
|
||||
'name or values of the destination credential. `divergent`: under `overwrite`, two or ' +
|
||||
'more package credentials with different names or values resolve to one destination ' +
|
||||
'credential. The import does not select one of them.',
|
||||
},
|
||||
targetId: { description: 'Id of the destination credential.' },
|
||||
sourceIds: {
|
||||
description:
|
||||
'Ids of the package credentials that resolve to the destination credential. For ' +
|
||||
'`differs`, the package credentials that would change it.',
|
||||
},
|
||||
name: { description: 'Name of the destination credential.' },
|
||||
usedBy: { description: 'Package consumers that use the listed package credentials.' },
|
||||
} as const satisfies Record<string, ZodOpenAPIMetadata>;
|
||||
|
||||
export const credentialConflictIssueOpenApi: ZodOpenAPIMetadata = {
|
||||
description:
|
||||
'A matched credential that the credential conflict policy does not let the import ' +
|
||||
'change. Only a credential that the import can update is checked: a credential owned by ' +
|
||||
'a project of the import. Values are never reported.',
|
||||
};
|
||||
|
||||
export const credentialUpdateForbiddenFieldDocs = {
|
||||
targetId: { description: 'Id of the destination credential.' },
|
||||
sourceIds: { description: 'Ids of the package credentials that resolve to it.' },
|
||||
name: { description: 'Name of the destination credential.' },
|
||||
usedBy: { description: 'Package consumers that use the listed package credentials.' },
|
||||
} as const satisfies Record<string, ZodOpenAPIMetadata>;
|
||||
|
||||
export const credentialUpdateForbiddenIssueOpenApi: ZodOpenAPIMetadata = {
|
||||
description:
|
||||
'A matched credential that the import can update, but the caller has no ' +
|
||||
'`credential:update` permission on it. Only under `credentialConflictPolicy` `overwrite` ' +
|
||||
'or `fail`. The import does not read its values, so this blocks also when the package ' +
|
||||
'would not change it. Use `keep-existing` to bind it without a change.',
|
||||
};
|
||||
|
||||
export const projectConflictFieldDocs = {
|
||||
name: { description: "The project's name as it appears in the package." },
|
||||
} as const satisfies Record<string, ZodOpenAPIMetadata>;
|
||||
|
|
|
|||
|
|
@ -3,10 +3,14 @@ import '../../openapi-extend';
|
|||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
credentialConflictFieldDocs,
|
||||
credentialConflictIssueOpenApi,
|
||||
credentialPolicyViolationFieldDocs,
|
||||
credentialPolicyViolationIssueOpenApi,
|
||||
credentialUnresolvedFieldDocs,
|
||||
credentialUnresolvedIssueOpenApi,
|
||||
credentialUpdateForbiddenFieldDocs,
|
||||
credentialUpdateForbiddenIssueOpenApi,
|
||||
dataTableSchemaOperationOpenApi,
|
||||
dataTableUnresolvedFieldDocs,
|
||||
dataTableUnresolvedIssueOpenApi,
|
||||
|
|
@ -134,6 +138,29 @@ const credentialUnresolvedIssueSchema = z
|
|||
})
|
||||
.openapi(credentialUnresolvedIssueOpenApi);
|
||||
|
||||
const credentialConflictIssueSchema = z
|
||||
.object({
|
||||
type: z.literal('credential-conflict'),
|
||||
kind: z.enum(['differs', 'divergent']).openapi(credentialConflictFieldDocs.kind),
|
||||
targetId: z.string().openapi(credentialConflictFieldDocs.targetId),
|
||||
sourceIds: z.array(z.string()).openapi(credentialConflictFieldDocs.sourceIds),
|
||||
name: z.string().openapi(credentialConflictFieldDocs.name),
|
||||
usedBy: z.array(packageRequirementConsumerSchema).openapi(credentialConflictFieldDocs.usedBy),
|
||||
})
|
||||
.openapi(credentialConflictIssueOpenApi);
|
||||
|
||||
const credentialUpdateForbiddenIssueSchema = z
|
||||
.object({
|
||||
type: z.literal('credential-update-forbidden'),
|
||||
targetId: z.string().openapi(credentialUpdateForbiddenFieldDocs.targetId),
|
||||
sourceIds: z.array(z.string()).openapi(credentialUpdateForbiddenFieldDocs.sourceIds),
|
||||
name: z.string().openapi(credentialUpdateForbiddenFieldDocs.name),
|
||||
usedBy: z
|
||||
.array(packageRequirementConsumerSchema)
|
||||
.openapi(credentialUpdateForbiddenFieldDocs.usedBy),
|
||||
})
|
||||
.openapi(credentialUpdateForbiddenIssueOpenApi);
|
||||
|
||||
const projectConflictIssueSchema = z
|
||||
.object({
|
||||
type: z.literal('project-conflict'),
|
||||
|
|
@ -376,6 +403,8 @@ export const importBlockingIssueSchema = z
|
|||
workflowFolderConflictIssueSchema,
|
||||
workflowArchiveForbiddenIssueSchema,
|
||||
credentialUnresolvedIssueSchema,
|
||||
credentialConflictIssueSchema,
|
||||
credentialUpdateForbiddenIssueSchema,
|
||||
projectConflictIssueSchema,
|
||||
folderConflictIssueSchema,
|
||||
workflowRemovalForbiddenIssueSchema,
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ const dataTableChangeSchema = z.discriminatedUnion('kind', [
|
|||
]);
|
||||
|
||||
/**
|
||||
* Continue can confirm a `destructive-change` conflict.
|
||||
* Continue can confirm a `destructive-change` or `project-deletion` conflict.
|
||||
* The other conflicts need a package or target correction before creation can proceed.
|
||||
*/
|
||||
export const promotionBindingConflictSchema = z.discriminatedUnion('code', [
|
||||
|
|
@ -165,6 +165,12 @@ export const promotionBindingConflictSchema = z.discriminatedUnion('code', [
|
|||
filePath: z.string(),
|
||||
workflows: z.array(workflowSchema),
|
||||
}),
|
||||
/** A full apply deletes each team project that the package does not hold. */
|
||||
z.object({
|
||||
kind: z.literal('project'),
|
||||
code: z.literal('project-deletion'),
|
||||
project: promotionBindingProjectSchema,
|
||||
}),
|
||||
z.object({
|
||||
kind: z.literal('data-table'),
|
||||
code: z.literal('destructive-change'),
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user